1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2015 Freescale Semiconductor, Inc. |
4 | * |
5 | * Freescale DCU drm device driver |
6 | */ |
7 | |
8 | #include <linux/regmap.h> |
9 | |
10 | #include <drm/drm_atomic.h> |
11 | #include <drm/drm_atomic_helper.h> |
12 | #include <drm/drm_crtc.h> |
13 | #include <drm/drm_fb_dma_helper.h> |
14 | #include <drm/drm_fourcc.h> |
15 | #include <drm/drm_framebuffer.h> |
16 | #include <drm/drm_gem_dma_helper.h> |
17 | #include <drm/drm_plane_helper.h> |
18 | #include <drm/drm_probe_helper.h> |
19 | |
20 | #include "fsl_dcu_drm_drv.h" |
21 | #include "fsl_dcu_drm_plane.h" |
22 | |
23 | static int fsl_dcu_drm_plane_index(struct drm_plane *plane) |
24 | { |
25 | struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; |
26 | unsigned int total_layer = fsl_dev->soc->total_layer; |
27 | unsigned int index; |
28 | |
29 | index = drm_plane_index(plane); |
30 | if (index < total_layer) |
31 | return total_layer - index - 1; |
32 | |
33 | dev_err(fsl_dev->dev, "No more layer left\n" ); |
34 | return -EINVAL; |
35 | } |
36 | |
37 | static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane, |
38 | struct drm_atomic_state *state) |
39 | { |
40 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
41 | plane); |
42 | struct drm_framebuffer *fb = new_plane_state->fb; |
43 | |
44 | if (!new_plane_state->fb || !new_plane_state->crtc) |
45 | return 0; |
46 | |
47 | switch (fb->format->format) { |
48 | case DRM_FORMAT_RGB565: |
49 | case DRM_FORMAT_RGB888: |
50 | case DRM_FORMAT_XRGB8888: |
51 | case DRM_FORMAT_ARGB8888: |
52 | case DRM_FORMAT_XRGB4444: |
53 | case DRM_FORMAT_ARGB4444: |
54 | case DRM_FORMAT_XRGB1555: |
55 | case DRM_FORMAT_ARGB1555: |
56 | case DRM_FORMAT_YUV422: |
57 | return 0; |
58 | default: |
59 | return -EINVAL; |
60 | } |
61 | } |
62 | |
63 | static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane, |
64 | struct drm_atomic_state *state) |
65 | { |
66 | struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; |
67 | unsigned int value; |
68 | int index; |
69 | |
70 | index = fsl_dcu_drm_plane_index(plane); |
71 | if (index < 0) |
72 | return; |
73 | |
74 | regmap_read(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), val: &value); |
75 | value &= ~DCU_LAYER_EN; |
76 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), val: value); |
77 | } |
78 | |
79 | static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, |
80 | struct drm_atomic_state *state) |
81 | |
82 | { |
83 | struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; |
84 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
85 | plane); |
86 | struct drm_framebuffer *fb = plane->state->fb; |
87 | struct drm_gem_dma_object *gem; |
88 | unsigned int alpha = DCU_LAYER_AB_NONE, bpp; |
89 | int index; |
90 | |
91 | if (!fb) |
92 | return; |
93 | |
94 | index = fsl_dcu_drm_plane_index(plane); |
95 | if (index < 0) |
96 | return; |
97 | |
98 | gem = drm_fb_dma_get_gem_obj(fb, plane: 0); |
99 | |
100 | switch (fb->format->format) { |
101 | case DRM_FORMAT_RGB565: |
102 | bpp = FSL_DCU_RGB565; |
103 | break; |
104 | case DRM_FORMAT_RGB888: |
105 | bpp = FSL_DCU_RGB888; |
106 | break; |
107 | case DRM_FORMAT_ARGB8888: |
108 | alpha = DCU_LAYER_AB_WHOLE_FRAME; |
109 | fallthrough; |
110 | case DRM_FORMAT_XRGB8888: |
111 | bpp = FSL_DCU_ARGB8888; |
112 | break; |
113 | case DRM_FORMAT_ARGB4444: |
114 | alpha = DCU_LAYER_AB_WHOLE_FRAME; |
115 | fallthrough; |
116 | case DRM_FORMAT_XRGB4444: |
117 | bpp = FSL_DCU_ARGB4444; |
118 | break; |
119 | case DRM_FORMAT_ARGB1555: |
120 | alpha = DCU_LAYER_AB_WHOLE_FRAME; |
121 | fallthrough; |
122 | case DRM_FORMAT_XRGB1555: |
123 | bpp = FSL_DCU_ARGB1555; |
124 | break; |
125 | case DRM_FORMAT_YUV422: |
126 | bpp = FSL_DCU_YUV422; |
127 | break; |
128 | default: |
129 | return; |
130 | } |
131 | |
132 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 1), |
133 | DCU_LAYER_HEIGHT(new_state->crtc_h) | |
134 | DCU_LAYER_WIDTH(new_state->crtc_w)); |
135 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 2), |
136 | DCU_LAYER_POSY(new_state->crtc_y) | |
137 | DCU_LAYER_POSX(new_state->crtc_x)); |
138 | regmap_write(map: fsl_dev->regmap, |
139 | DCU_CTRLDESCLN(index, 3), val: gem->dma_addr); |
140 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), |
141 | DCU_LAYER_EN | |
142 | DCU_LAYER_TRANS(0xff) | |
143 | DCU_LAYER_BPP(bpp) | |
144 | alpha); |
145 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 5), |
146 | DCU_LAYER_CKMAX_R(0xFF) | |
147 | DCU_LAYER_CKMAX_G(0xFF) | |
148 | DCU_LAYER_CKMAX_B(0xFF)); |
149 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 6), |
150 | DCU_LAYER_CKMIN_R(0) | |
151 | DCU_LAYER_CKMIN_G(0) | |
152 | DCU_LAYER_CKMIN_B(0)); |
153 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), val: 0); |
154 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 8), |
155 | DCU_LAYER_FG_FCOLOR(0)); |
156 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 9), |
157 | DCU_LAYER_BG_BCOLOR(0)); |
158 | |
159 | if (!strcmp(fsl_dev->soc->name, "ls1021a" )) { |
160 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(index, 10), |
161 | DCU_LAYER_POST_SKIP(0) | |
162 | DCU_LAYER_PRE_SKIP(0)); |
163 | } |
164 | |
165 | return; |
166 | } |
167 | |
168 | static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = { |
169 | .atomic_check = fsl_dcu_drm_plane_atomic_check, |
170 | .atomic_disable = fsl_dcu_drm_plane_atomic_disable, |
171 | .atomic_update = fsl_dcu_drm_plane_atomic_update, |
172 | }; |
173 | |
174 | static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = { |
175 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
176 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
177 | .destroy = drm_plane_helper_destroy, |
178 | .disable_plane = drm_atomic_helper_disable_plane, |
179 | .reset = drm_atomic_helper_plane_reset, |
180 | .update_plane = drm_atomic_helper_update_plane, |
181 | }; |
182 | |
183 | static const u32 fsl_dcu_drm_plane_formats[] = { |
184 | DRM_FORMAT_RGB565, |
185 | DRM_FORMAT_RGB888, |
186 | DRM_FORMAT_XRGB8888, |
187 | DRM_FORMAT_ARGB8888, |
188 | DRM_FORMAT_XRGB4444, |
189 | DRM_FORMAT_ARGB4444, |
190 | DRM_FORMAT_XRGB1555, |
191 | DRM_FORMAT_ARGB1555, |
192 | DRM_FORMAT_YUV422, |
193 | }; |
194 | |
195 | void fsl_dcu_drm_init_planes(struct drm_device *dev) |
196 | { |
197 | struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; |
198 | int i, j; |
199 | |
200 | for (i = 0; i < fsl_dev->soc->total_layer; i++) { |
201 | for (j = 1; j <= fsl_dev->soc->layer_regs; j++) |
202 | regmap_write(map: fsl_dev->regmap, DCU_CTRLDESCLN(i, j), val: 0); |
203 | } |
204 | } |
205 | |
206 | struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev) |
207 | { |
208 | struct drm_plane *primary; |
209 | int ret; |
210 | |
211 | primary = kzalloc(size: sizeof(*primary), GFP_KERNEL); |
212 | if (!primary) { |
213 | DRM_DEBUG_KMS("Failed to allocate primary plane\n" ); |
214 | return NULL; |
215 | } |
216 | |
217 | /* possible_crtc's will be filled in later by crtc_init */ |
218 | ret = drm_universal_plane_init(dev, plane: primary, possible_crtcs: 0, |
219 | funcs: &fsl_dcu_drm_plane_funcs, |
220 | formats: fsl_dcu_drm_plane_formats, |
221 | ARRAY_SIZE(fsl_dcu_drm_plane_formats), |
222 | NULL, type: DRM_PLANE_TYPE_PRIMARY, NULL); |
223 | if (ret) { |
224 | kfree(objp: primary); |
225 | primary = NULL; |
226 | } |
227 | drm_plane_helper_add(plane: primary, funcs: &fsl_dcu_drm_plane_helper_funcs); |
228 | |
229 | return primary; |
230 | } |
231 | |