]>
Commit | Line | Data |
---|---|---|
b8d181e4 PZ |
1 | /* |
2 | * i.MX IPUv3 DP Overlay Planes | |
3 | * | |
4 | * Copyright (C) 2013 Philipp Zabel, Pengutronix | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | #include <drm/drmP.h> | |
5f2f9115 | 17 | #include <drm/drm_atomic.h> |
255c35f8 | 18 | #include <drm/drm_atomic_helper.h> |
b8d181e4 PZ |
19 | #include <drm/drm_fb_cma_helper.h> |
20 | #include <drm/drm_gem_cma_helper.h> | |
33f14235 | 21 | #include <drm/drm_plane_helper.h> |
b8d181e4 | 22 | |
39b9004d | 23 | #include "video/imx-ipu-v3.h" |
b8d181e4 PZ |
24 | #include "ipuv3-plane.h" |
25 | ||
00514e85 LS |
26 | struct ipu_plane_state { |
27 | struct drm_plane_state base; | |
28 | bool use_pre; | |
29 | }; | |
30 | ||
31 | static inline struct ipu_plane_state * | |
32 | to_ipu_plane_state(struct drm_plane_state *p) | |
33 | { | |
34 | return container_of(p, struct ipu_plane_state, base); | |
35 | } | |
36 | ||
3df07390 PZ |
37 | static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p) |
38 | { | |
39 | return container_of(p, struct ipu_plane, base); | |
40 | } | |
b8d181e4 PZ |
41 | |
42 | static const uint32_t ipu_plane_formats[] = { | |
c639a1cf | 43 | DRM_FORMAT_ARGB1555, |
b8d181e4 | 44 | DRM_FORMAT_XRGB1555, |
c639a1cf | 45 | DRM_FORMAT_ABGR1555, |
b8d181e4 | 46 | DRM_FORMAT_XBGR1555, |
c639a1cf PZ |
47 | DRM_FORMAT_RGBA5551, |
48 | DRM_FORMAT_BGRA5551, | |
cb166a30 | 49 | DRM_FORMAT_ARGB4444, |
b8d181e4 PZ |
50 | DRM_FORMAT_ARGB8888, |
51 | DRM_FORMAT_XRGB8888, | |
52 | DRM_FORMAT_ABGR8888, | |
53 | DRM_FORMAT_XBGR8888, | |
59d6b718 PZ |
54 | DRM_FORMAT_RGBA8888, |
55 | DRM_FORMAT_RGBX8888, | |
56 | DRM_FORMAT_BGRA8888, | |
f2ad99fc | 57 | DRM_FORMAT_BGRX8888, |
7932131f PZ |
58 | DRM_FORMAT_UYVY, |
59 | DRM_FORMAT_VYUY, | |
b8d181e4 PZ |
60 | DRM_FORMAT_YUYV, |
61 | DRM_FORMAT_YVYU, | |
62 | DRM_FORMAT_YUV420, | |
63 | DRM_FORMAT_YVU420, | |
eae13c93 PZ |
64 | DRM_FORMAT_YUV422, |
65 | DRM_FORMAT_YVU422, | |
66 | DRM_FORMAT_YUV444, | |
67 | DRM_FORMAT_YVU444, | |
68 | DRM_FORMAT_NV12, | |
69 | DRM_FORMAT_NV16, | |
33bee520 | 70 | DRM_FORMAT_RGB565, |
f6b50ef1 PZ |
71 | DRM_FORMAT_RGB565_A8, |
72 | DRM_FORMAT_BGR565_A8, | |
73 | DRM_FORMAT_RGB888_A8, | |
74 | DRM_FORMAT_BGR888_A8, | |
75 | DRM_FORMAT_RGBX8888_A8, | |
76 | DRM_FORMAT_BGRX8888_A8, | |
b8d181e4 PZ |
77 | }; |
78 | ||
79 | int ipu_plane_irq(struct ipu_plane *ipu_plane) | |
80 | { | |
81 | return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, | |
82 | IPU_IRQ_EOF); | |
83 | } | |
84 | ||
33f14235 | 85 | static inline unsigned long |
0bfd56f0 | 86 | drm_plane_state_to_eba(struct drm_plane_state *state, int plane) |
b8d181e4 | 87 | { |
33f14235 LY |
88 | struct drm_framebuffer *fb = state->fb; |
89 | struct drm_gem_cma_object *cma_obj; | |
03ee3da8 PZ |
90 | int x = state->src.x1 >> 16; |
91 | int y = state->src.y1 >> 16; | |
b8d181e4 | 92 | |
0bfd56f0 | 93 | cma_obj = drm_fb_cma_get_gem_obj(fb, plane); |
33f14235 | 94 | BUG_ON(!cma_obj); |
b8d181e4 | 95 | |
0bfd56f0 PZ |
96 | return cma_obj->paddr + fb->offsets[plane] + fb->pitches[plane] * y + |
97 | fb->format->cpp[plane] * x; | |
33f14235 | 98 | } |
08a89018 | 99 | |
33f14235 LY |
100 | static inline unsigned long |
101 | drm_plane_state_to_ubo(struct drm_plane_state *state) | |
102 | { | |
103 | struct drm_framebuffer *fb = state->fb; | |
104 | struct drm_gem_cma_object *cma_obj; | |
0bfd56f0 | 105 | unsigned long eba = drm_plane_state_to_eba(state, 0); |
03ee3da8 PZ |
106 | int x = state->src.x1 >> 16; |
107 | int y = state->src.y1 >> 16; | |
b8d181e4 | 108 | |
33f14235 LY |
109 | cma_obj = drm_fb_cma_get_gem_obj(fb, 1); |
110 | BUG_ON(!cma_obj); | |
9a666030 | 111 | |
438b74a5 VS |
112 | x /= drm_format_horz_chroma_subsampling(fb->format->format); |
113 | y /= drm_format_vert_chroma_subsampling(fb->format->format); | |
f2fa3536 PZ |
114 | |
115 | return cma_obj->paddr + fb->offsets[1] + fb->pitches[1] * y + | |
353c8598 | 116 | fb->format->cpp[1] * x - eba; |
33f14235 | 117 | } |
9a666030 | 118 | |
33f14235 LY |
119 | static inline unsigned long |
120 | drm_plane_state_to_vbo(struct drm_plane_state *state) | |
121 | { | |
122 | struct drm_framebuffer *fb = state->fb; | |
123 | struct drm_gem_cma_object *cma_obj; | |
0bfd56f0 | 124 | unsigned long eba = drm_plane_state_to_eba(state, 0); |
03ee3da8 PZ |
125 | int x = state->src.x1 >> 16; |
126 | int y = state->src.y1 >> 16; | |
33f14235 LY |
127 | |
128 | cma_obj = drm_fb_cma_get_gem_obj(fb, 2); | |
129 | BUG_ON(!cma_obj); | |
130 | ||
438b74a5 VS |
131 | x /= drm_format_horz_chroma_subsampling(fb->format->format); |
132 | y /= drm_format_vert_chroma_subsampling(fb->format->format); | |
f2fa3536 PZ |
133 | |
134 | return cma_obj->paddr + fb->offsets[2] + fb->pitches[2] * y + | |
353c8598 | 135 | fb->format->cpp[2] * x - eba; |
33f14235 LY |
136 | } |
137 | ||
b8d181e4 PZ |
138 | void ipu_plane_put_resources(struct ipu_plane *ipu_plane) |
139 | { | |
140 | if (!IS_ERR_OR_NULL(ipu_plane->dp)) | |
141 | ipu_dp_put(ipu_plane->dp); | |
142 | if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) | |
143 | ipu_dmfc_put(ipu_plane->dmfc); | |
144 | if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) | |
145 | ipu_idmac_put(ipu_plane->ipu_ch); | |
f6b50ef1 PZ |
146 | if (!IS_ERR_OR_NULL(ipu_plane->alpha_ch)) |
147 | ipu_idmac_put(ipu_plane->alpha_ch); | |
b8d181e4 PZ |
148 | } |
149 | ||
150 | int ipu_plane_get_resources(struct ipu_plane *ipu_plane) | |
151 | { | |
152 | int ret; | |
f6b50ef1 | 153 | int alpha_ch; |
b8d181e4 PZ |
154 | |
155 | ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); | |
156 | if (IS_ERR(ipu_plane->ipu_ch)) { | |
157 | ret = PTR_ERR(ipu_plane->ipu_ch); | |
158 | DRM_ERROR("failed to get idmac channel: %d\n", ret); | |
159 | return ret; | |
160 | } | |
161 | ||
f6b50ef1 PZ |
162 | alpha_ch = ipu_channel_alpha_channel(ipu_plane->dma); |
163 | if (alpha_ch >= 0) { | |
164 | ipu_plane->alpha_ch = ipu_idmac_get(ipu_plane->ipu, alpha_ch); | |
165 | if (IS_ERR(ipu_plane->alpha_ch)) { | |
166 | ret = PTR_ERR(ipu_plane->alpha_ch); | |
167 | DRM_ERROR("failed to get alpha idmac channel %d: %d\n", | |
168 | alpha_ch, ret); | |
169 | return ret; | |
170 | } | |
171 | } | |
172 | ||
b8d181e4 PZ |
173 | ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); |
174 | if (IS_ERR(ipu_plane->dmfc)) { | |
175 | ret = PTR_ERR(ipu_plane->dmfc); | |
176 | DRM_ERROR("failed to get dmfc: ret %d\n", ret); | |
177 | goto err_out; | |
178 | } | |
179 | ||
180 | if (ipu_plane->dp_flow >= 0) { | |
181 | ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); | |
182 | if (IS_ERR(ipu_plane->dp)) { | |
183 | ret = PTR_ERR(ipu_plane->dp); | |
184 | DRM_ERROR("failed to get dp flow: %d\n", ret); | |
185 | goto err_out; | |
186 | } | |
187 | } | |
188 | ||
189 | return 0; | |
190 | err_out: | |
191 | ipu_plane_put_resources(ipu_plane); | |
192 | ||
193 | return ret; | |
194 | } | |
195 | ||
f6b50ef1 PZ |
196 | static bool ipu_plane_separate_alpha(struct ipu_plane *ipu_plane) |
197 | { | |
198 | switch (ipu_plane->base.state->fb->format->format) { | |
199 | case DRM_FORMAT_RGB565_A8: | |
200 | case DRM_FORMAT_BGR565_A8: | |
201 | case DRM_FORMAT_RGB888_A8: | |
202 | case DRM_FORMAT_BGR888_A8: | |
203 | case DRM_FORMAT_RGBX8888_A8: | |
204 | case DRM_FORMAT_BGRX8888_A8: | |
205 | return true; | |
206 | default: | |
207 | return false; | |
208 | } | |
209 | } | |
210 | ||
33f14235 | 211 | static void ipu_plane_enable(struct ipu_plane *ipu_plane) |
b8d181e4 | 212 | { |
285bbb01 PZ |
213 | if (ipu_plane->dp) |
214 | ipu_dp_enable(ipu_plane->ipu); | |
b8d181e4 PZ |
215 | ipu_dmfc_enable_channel(ipu_plane->dmfc); |
216 | ipu_idmac_enable_channel(ipu_plane->ipu_ch); | |
f6b50ef1 PZ |
217 | if (ipu_plane_separate_alpha(ipu_plane)) |
218 | ipu_idmac_enable_channel(ipu_plane->alpha_ch); | |
b8d181e4 PZ |
219 | if (ipu_plane->dp) |
220 | ipu_dp_enable_channel(ipu_plane->dp); | |
b8d181e4 PZ |
221 | } |
222 | ||
eb8c8880 | 223 | void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel) |
b8d181e4 | 224 | { |
df4b2233 LS |
225 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); |
226 | ||
b8d181e4 PZ |
227 | ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); |
228 | ||
eb8c8880 PZ |
229 | if (ipu_plane->dp && disable_dp_channel) |
230 | ipu_dp_disable_channel(ipu_plane->dp, false); | |
b8d181e4 | 231 | ipu_idmac_disable_channel(ipu_plane->ipu_ch); |
f6b50ef1 PZ |
232 | if (ipu_plane->alpha_ch) |
233 | ipu_idmac_disable_channel(ipu_plane->alpha_ch); | |
b8d181e4 | 234 | ipu_dmfc_disable_channel(ipu_plane->dmfc); |
285bbb01 PZ |
235 | if (ipu_plane->dp) |
236 | ipu_dp_disable(ipu_plane->ipu); | |
00514e85 LS |
237 | if (ipu_prg_present(ipu_plane->ipu)) |
238 | ipu_prg_channel_disable(ipu_plane->ipu_ch); | |
eb8c8880 | 239 | } |
b8d181e4 | 240 | |
eb8c8880 PZ |
241 | void ipu_plane_disable_deferred(struct drm_plane *plane) |
242 | { | |
243 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | |
244 | ||
245 | if (ipu_plane->disabling) { | |
246 | ipu_plane->disabling = false; | |
247 | ipu_plane_disable(ipu_plane, false); | |
248 | } | |
b8d181e4 | 249 | } |
eb8c8880 | 250 | EXPORT_SYMBOL_GPL(ipu_plane_disable_deferred); |
b8d181e4 | 251 | |
33f14235 | 252 | static void ipu_plane_destroy(struct drm_plane *plane) |
b8d181e4 PZ |
253 | { |
254 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | |
255 | ||
256 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | |
257 | ||
33f14235 LY |
258 | drm_plane_cleanup(plane); |
259 | kfree(ipu_plane); | |
260 | } | |
b8d181e4 | 261 | |
00514e85 LS |
262 | void ipu_plane_state_reset(struct drm_plane *plane) |
263 | { | |
264 | struct ipu_plane_state *ipu_state; | |
265 | ||
266 | if (plane->state) { | |
267 | ipu_state = to_ipu_plane_state(plane->state); | |
268 | __drm_atomic_helper_plane_destroy_state(plane->state); | |
269 | kfree(ipu_state); | |
270 | } | |
271 | ||
272 | ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL); | |
273 | ||
274 | if (ipu_state) { | |
275 | ipu_state->base.plane = plane; | |
c2c446ad | 276 | ipu_state->base.rotation = DRM_MODE_ROTATE_0; |
00514e85 LS |
277 | } |
278 | ||
279 | plane->state = &ipu_state->base; | |
280 | } | |
281 | ||
282 | struct drm_plane_state *ipu_plane_duplicate_state(struct drm_plane *plane) | |
283 | { | |
284 | struct ipu_plane_state *state; | |
285 | ||
286 | if (WARN_ON(!plane->state)) | |
287 | return NULL; | |
288 | ||
289 | state = kmalloc(sizeof(*state), GFP_KERNEL); | |
290 | if (state) | |
291 | __drm_atomic_helper_plane_duplicate_state(plane, &state->base); | |
292 | ||
293 | return &state->base; | |
294 | } | |
295 | ||
296 | void ipu_plane_destroy_state(struct drm_plane *plane, | |
297 | struct drm_plane_state *state) | |
298 | { | |
299 | struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); | |
300 | ||
301 | __drm_atomic_helper_plane_destroy_state(state); | |
302 | kfree(ipu_state); | |
303 | } | |
304 | ||
33f14235 | 305 | static const struct drm_plane_funcs ipu_plane_funcs = { |
5f2f9115 LY |
306 | .update_plane = drm_atomic_helper_update_plane, |
307 | .disable_plane = drm_atomic_helper_disable_plane, | |
33f14235 | 308 | .destroy = ipu_plane_destroy, |
00514e85 LS |
309 | .reset = ipu_plane_state_reset, |
310 | .atomic_duplicate_state = ipu_plane_duplicate_state, | |
311 | .atomic_destroy_state = ipu_plane_destroy_state, | |
33f14235 LY |
312 | }; |
313 | ||
314 | static int ipu_plane_atomic_check(struct drm_plane *plane, | |
315 | struct drm_plane_state *state) | |
316 | { | |
317 | struct drm_plane_state *old_state = plane->state; | |
318 | struct drm_crtc_state *crtc_state; | |
319 | struct device *dev = plane->dev->dev; | |
320 | struct drm_framebuffer *fb = state->fb; | |
321 | struct drm_framebuffer *old_fb = old_state->fb; | |
f6b50ef1 | 322 | unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba; |
03ee3da8 PZ |
323 | bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY); |
324 | struct drm_rect clip; | |
5fb57ab3 | 325 | int hsub, vsub; |
03ee3da8 | 326 | int ret; |
33f14235 LY |
327 | |
328 | /* Ok to disable */ | |
329 | if (!fb) | |
5f2f9115 LY |
330 | return 0; |
331 | ||
332 | if (!state->crtc) | |
333 | return -EINVAL; | |
334 | ||
335 | crtc_state = | |
336 | drm_atomic_get_existing_crtc_state(state->state, state->crtc); | |
337 | if (WARN_ON(!crtc_state)) | |
338 | return -EINVAL; | |
33f14235 | 339 | |
03ee3da8 PZ |
340 | clip.x1 = 0; |
341 | clip.y1 = 0; | |
342 | clip.x2 = crtc_state->adjusted_mode.hdisplay; | |
343 | clip.y2 = crtc_state->adjusted_mode.vdisplay; | |
344 | ret = drm_plane_helper_check_state(state, &clip, | |
345 | DRM_PLANE_HELPER_NO_SCALING, | |
346 | DRM_PLANE_HELPER_NO_SCALING, | |
347 | can_position, true); | |
348 | if (ret) | |
349 | return ret; | |
350 | ||
33f14235 | 351 | /* CRTC should be enabled */ |
5f2f9115 | 352 | if (!crtc_state->enable) |
33f14235 LY |
353 | return -EINVAL; |
354 | ||
33f14235 LY |
355 | switch (plane->type) { |
356 | case DRM_PLANE_TYPE_PRIMARY: | |
33f14235 | 357 | /* full plane minimum width is 13 pixels */ |
03ee3da8 | 358 | if (drm_rect_width(&state->dst) < 13) |
33f14235 LY |
359 | return -EINVAL; |
360 | break; | |
361 | case DRM_PLANE_TYPE_OVERLAY: | |
33f14235 LY |
362 | break; |
363 | default: | |
03ee3da8 | 364 | dev_warn(dev, "Unsupported plane type %d\n", plane->type); |
33f14235 LY |
365 | return -EINVAL; |
366 | } | |
367 | ||
03ee3da8 | 368 | if (drm_rect_height(&state->dst) < 2) |
33f14235 LY |
369 | return -EINVAL; |
370 | ||
371 | /* | |
1780999c LY |
372 | * We support resizing active plane or changing its format by |
373 | * forcing CRTC mode change in plane's ->atomic_check callback | |
374 | * and disabling all affected active planes in CRTC's ->atomic_disable | |
375 | * callback. The planes will be reenabled in plane's ->atomic_update | |
376 | * callback. | |
33f14235 | 377 | */ |
03ee3da8 PZ |
378 | if (old_fb && |
379 | (drm_rect_width(&state->dst) != drm_rect_width(&old_state->dst) || | |
380 | drm_rect_height(&state->dst) != drm_rect_height(&old_state->dst) || | |
381 | fb->format != old_fb->format)) | |
1780999c | 382 | crtc_state->mode_changed = true; |
33f14235 | 383 | |
0bfd56f0 | 384 | eba = drm_plane_state_to_eba(state, 0); |
33f14235 LY |
385 | |
386 | if (eba & 0x7) | |
387 | return -EINVAL; | |
388 | ||
389 | if (fb->pitches[0] < 1 || fb->pitches[0] > 16384) | |
390 | return -EINVAL; | |
391 | ||
392 | if (old_fb && fb->pitches[0] != old_fb->pitches[0]) | |
1780999c | 393 | crtc_state->mode_changed = true; |
33f14235 | 394 | |
438b74a5 | 395 | switch (fb->format->format) { |
33f14235 LY |
396 | case DRM_FORMAT_YUV420: |
397 | case DRM_FORMAT_YVU420: | |
eae13c93 PZ |
398 | case DRM_FORMAT_YUV422: |
399 | case DRM_FORMAT_YVU422: | |
400 | case DRM_FORMAT_YUV444: | |
401 | case DRM_FORMAT_YVU444: | |
33f14235 LY |
402 | /* |
403 | * Multiplanar formats have to meet the following restrictions: | |
404 | * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO | |
405 | * - EBA, UBO and VBO are a multiple of 8 | |
406 | * - UBO and VBO are unsigned and not larger than 0xfffff8 | |
407 | * - Only EBA may be changed while scanout is active | |
408 | * - The strides of U and V planes must be identical. | |
409 | */ | |
33f14235 LY |
410 | vbo = drm_plane_state_to_vbo(state); |
411 | ||
eae13c93 | 412 | if (vbo & 0x7 || vbo > 0xfffff8) |
33f14235 LY |
413 | return -EINVAL; |
414 | ||
dbd4d576 | 415 | if (old_fb && (fb->format == old_fb->format)) { |
33f14235 | 416 | old_vbo = drm_plane_state_to_vbo(old_state); |
eae13c93 | 417 | if (vbo != old_vbo) |
181c9bfe | 418 | crtc_state->mode_changed = true; |
33f14235 LY |
419 | } |
420 | ||
421 | if (fb->pitches[1] != fb->pitches[2]) | |
422 | return -EINVAL; | |
423 | ||
eae13c93 PZ |
424 | /* fall-through */ |
425 | case DRM_FORMAT_NV12: | |
426 | case DRM_FORMAT_NV16: | |
427 | ubo = drm_plane_state_to_ubo(state); | |
428 | ||
429 | if (ubo & 0x7 || ubo > 0xfffff8) | |
430 | return -EINVAL; | |
431 | ||
dbd4d576 | 432 | if (old_fb && (fb->format == old_fb->format)) { |
eae13c93 PZ |
433 | old_ubo = drm_plane_state_to_ubo(old_state); |
434 | if (ubo != old_ubo) | |
435 | crtc_state->mode_changed = true; | |
436 | } | |
437 | ||
33f14235 LY |
438 | if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) |
439 | return -EINVAL; | |
440 | ||
441 | if (old_fb && old_fb->pitches[1] != fb->pitches[1]) | |
1780999c | 442 | crtc_state->mode_changed = true; |
5fb57ab3 PZ |
443 | |
444 | /* | |
445 | * The x/y offsets must be even in case of horizontal/vertical | |
446 | * chroma subsampling. | |
447 | */ | |
438b74a5 VS |
448 | hsub = drm_format_horz_chroma_subsampling(fb->format->format); |
449 | vsub = drm_format_vert_chroma_subsampling(fb->format->format); | |
03ee3da8 PZ |
450 | if (((state->src.x1 >> 16) & (hsub - 1)) || |
451 | ((state->src.y1 >> 16) & (vsub - 1))) | |
5fb57ab3 | 452 | return -EINVAL; |
f6b50ef1 PZ |
453 | break; |
454 | case DRM_FORMAT_RGB565_A8: | |
455 | case DRM_FORMAT_BGR565_A8: | |
456 | case DRM_FORMAT_RGB888_A8: | |
457 | case DRM_FORMAT_BGR888_A8: | |
458 | case DRM_FORMAT_RGBX8888_A8: | |
459 | case DRM_FORMAT_BGRX8888_A8: | |
460 | alpha_eba = drm_plane_state_to_eba(state, 1); | |
461 | if (alpha_eba & 0x7) | |
462 | return -EINVAL; | |
463 | ||
464 | if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) | |
465 | return -EINVAL; | |
466 | ||
467 | if (old_fb && old_fb->pitches[1] != fb->pitches[1]) | |
468 | crtc_state->mode_changed = true; | |
469 | break; | |
33f14235 | 470 | } |
b8d181e4 PZ |
471 | |
472 | return 0; | |
473 | } | |
474 | ||
33f14235 LY |
475 | static void ipu_plane_atomic_disable(struct drm_plane *plane, |
476 | struct drm_plane_state *old_state) | |
477 | { | |
eb8c8880 PZ |
478 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); |
479 | ||
480 | if (ipu_plane->dp) | |
481 | ipu_dp_disable_channel(ipu_plane->dp, true); | |
482 | ipu_plane->disabling = true; | |
33f14235 LY |
483 | } |
484 | ||
00514e85 LS |
485 | static int ipu_chan_assign_axi_id(int ipu_chan) |
486 | { | |
487 | switch (ipu_chan) { | |
488 | case IPUV3_CHANNEL_MEM_BG_SYNC: | |
489 | return 1; | |
490 | case IPUV3_CHANNEL_MEM_FG_SYNC: | |
491 | return 2; | |
492 | case IPUV3_CHANNEL_MEM_DC_SYNC: | |
493 | return 3; | |
494 | default: | |
495 | return 0; | |
496 | } | |
497 | } | |
498 | ||
33f14235 LY |
499 | static void ipu_plane_atomic_update(struct drm_plane *plane, |
500 | struct drm_plane_state *old_state) | |
b8d181e4 PZ |
501 | { |
502 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | |
33f14235 | 503 | struct drm_plane_state *state = plane->state; |
00514e85 | 504 | struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); |
3fd8b292 PZ |
505 | struct drm_crtc_state *crtc_state = state->crtc->state; |
506 | struct drm_framebuffer *fb = state->fb; | |
2e9a7121 | 507 | struct drm_rect *dst = &state->dst; |
3fd8b292 | 508 | unsigned long eba, ubo, vbo; |
f6b50ef1 | 509 | unsigned long alpha_eba = 0; |
33f14235 | 510 | enum ipu_color_space ics; |
00514e85 | 511 | unsigned int axi_id = 0; |
3fd8b292 | 512 | int active; |
b8d181e4 | 513 | |
2e9a7121 PZ |
514 | if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_FG) |
515 | ipu_dp_set_window_pos(ipu_plane->dp, dst->x1, dst->y1); | |
516 | ||
0bfd56f0 | 517 | eba = drm_plane_state_to_eba(state, 0); |
5f4df0c7 | 518 | |
00514e85 LS |
519 | /* |
520 | * Configure PRG channel and attached PRE, this changes the EBA to an | |
521 | * internal SRAM location. | |
522 | */ | |
523 | if (ipu_state->use_pre) { | |
524 | axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); | |
525 | ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, | |
526 | drm_rect_width(&state->src) >> 16, | |
527 | drm_rect_height(&state->src) >> 16, | |
528 | state->fb->pitches[0], | |
529 | state->fb->format->format, &eba); | |
530 | } | |
531 | ||
3fd8b292 | 532 | if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) { |
00514e85 LS |
533 | /* nothing to do if PRE is used */ |
534 | if (ipu_state->use_pre) | |
535 | return; | |
3fd8b292 PZ |
536 | active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); |
537 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); | |
538 | ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); | |
f6b50ef1 PZ |
539 | if (ipu_plane_separate_alpha(ipu_plane)) { |
540 | active = ipu_idmac_get_current_buffer(ipu_plane->alpha_ch); | |
541 | ipu_cpmem_set_buffer(ipu_plane->alpha_ch, !active, | |
542 | alpha_eba); | |
543 | ipu_idmac_select_buffer(ipu_plane->alpha_ch, !active); | |
544 | } | |
3fd8b292 | 545 | return; |
33f14235 | 546 | } |
b8d181e4 | 547 | |
5be5dd38 | 548 | ics = ipu_drm_fourcc_to_colorspace(fb->format->format); |
33f14235 LY |
549 | switch (ipu_plane->dp_flow) { |
550 | case IPU_DP_FLOW_SYNC_BG: | |
5be5dd38 | 551 | ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_RGB); |
33f14235 LY |
552 | ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); |
553 | break; | |
554 | case IPU_DP_FLOW_SYNC_FG: | |
33f14235 LY |
555 | ipu_dp_setup_channel(ipu_plane->dp, ics, |
556 | IPUV3_COLORSPACE_UNKNOWN); | |
33f14235 | 557 | /* Enable local alpha on partial plane */ |
438b74a5 | 558 | switch (state->fb->format->format) { |
33f14235 LY |
559 | case DRM_FORMAT_ARGB1555: |
560 | case DRM_FORMAT_ABGR1555: | |
561 | case DRM_FORMAT_RGBA5551: | |
562 | case DRM_FORMAT_BGRA5551: | |
563 | case DRM_FORMAT_ARGB4444: | |
564 | case DRM_FORMAT_ARGB8888: | |
565 | case DRM_FORMAT_ABGR8888: | |
566 | case DRM_FORMAT_RGBA8888: | |
567 | case DRM_FORMAT_BGRA8888: | |
f6b50ef1 PZ |
568 | case DRM_FORMAT_RGB565_A8: |
569 | case DRM_FORMAT_BGR565_A8: | |
570 | case DRM_FORMAT_RGB888_A8: | |
571 | case DRM_FORMAT_BGR888_A8: | |
572 | case DRM_FORMAT_RGBX8888_A8: | |
573 | case DRM_FORMAT_BGRX8888_A8: | |
33f14235 LY |
574 | ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false); |
575 | break; | |
576 | default: | |
86126748 | 577 | ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); |
33f14235 LY |
578 | break; |
579 | } | |
580 | } | |
581 | ||
2e9a7121 | 582 | ipu_dmfc_config_wait4eot(ipu_plane->dmfc, drm_rect_width(dst)); |
33f14235 LY |
583 | |
584 | ipu_cpmem_zero(ipu_plane->ipu_ch); | |
03ee3da8 PZ |
585 | ipu_cpmem_set_resolution(ipu_plane->ipu_ch, |
586 | drm_rect_width(&state->src) >> 16, | |
587 | drm_rect_height(&state->src) >> 16); | |
438b74a5 | 588 | ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->format->format); |
33f14235 LY |
589 | ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); |
590 | ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); | |
591 | ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]); | |
00514e85 | 592 | ipu_cpmem_set_axi_id(ipu_plane->ipu_ch, axi_id); |
438b74a5 | 593 | switch (fb->format->format) { |
3fd8b292 PZ |
594 | case DRM_FORMAT_YUV420: |
595 | case DRM_FORMAT_YVU420: | |
eae13c93 PZ |
596 | case DRM_FORMAT_YUV422: |
597 | case DRM_FORMAT_YVU422: | |
598 | case DRM_FORMAT_YUV444: | |
599 | case DRM_FORMAT_YVU444: | |
3fd8b292 PZ |
600 | ubo = drm_plane_state_to_ubo(state); |
601 | vbo = drm_plane_state_to_vbo(state); | |
438b74a5 VS |
602 | if (fb->format->format == DRM_FORMAT_YVU420 || |
603 | fb->format->format == DRM_FORMAT_YVU422 || | |
604 | fb->format->format == DRM_FORMAT_YVU444) | |
eae13c93 | 605 | swap(ubo, vbo); |
3fd8b292 | 606 | |
eae13c93 PZ |
607 | ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch, |
608 | fb->pitches[1], ubo, vbo); | |
3fd8b292 PZ |
609 | |
610 | dev_dbg(ipu_plane->base.dev->dev, | |
611 | "phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo, | |
03ee3da8 | 612 | state->src.x1 >> 16, state->src.y1 >> 16); |
3fd8b292 | 613 | break; |
eae13c93 PZ |
614 | case DRM_FORMAT_NV12: |
615 | case DRM_FORMAT_NV16: | |
616 | ubo = drm_plane_state_to_ubo(state); | |
617 | ||
618 | ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch, | |
619 | fb->pitches[1], ubo, ubo); | |
620 | ||
621 | dev_dbg(ipu_plane->base.dev->dev, | |
622 | "phy = %lu %lu, x = %d, y = %d", eba, ubo, | |
03ee3da8 | 623 | state->src.x1 >> 16, state->src.y1 >> 16); |
eae13c93 | 624 | break; |
f6b50ef1 PZ |
625 | case DRM_FORMAT_RGB565_A8: |
626 | case DRM_FORMAT_BGR565_A8: | |
627 | case DRM_FORMAT_RGB888_A8: | |
628 | case DRM_FORMAT_BGR888_A8: | |
629 | case DRM_FORMAT_RGBX8888_A8: | |
630 | case DRM_FORMAT_BGRX8888_A8: | |
631 | alpha_eba = drm_plane_state_to_eba(state, 1); | |
632 | ||
633 | dev_dbg(ipu_plane->base.dev->dev, "phys = %lu %lu, x = %d, y = %d", | |
634 | eba, alpha_eba, state->src.x1 >> 16, state->src.y1 >> 16); | |
635 | ||
636 | ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16); | |
637 | ||
638 | ipu_cpmem_zero(ipu_plane->alpha_ch); | |
639 | ipu_cpmem_set_resolution(ipu_plane->alpha_ch, | |
640 | drm_rect_width(&state->src) >> 16, | |
641 | drm_rect_height(&state->src) >> 16); | |
642 | ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8); | |
643 | ipu_cpmem_set_high_priority(ipu_plane->alpha_ch); | |
644 | ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1); | |
645 | ipu_cpmem_set_stride(ipu_plane->alpha_ch, | |
646 | state->fb->pitches[1]); | |
647 | ipu_cpmem_set_burstsize(ipu_plane->alpha_ch, 16); | |
648 | ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 0, alpha_eba); | |
649 | ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 1, alpha_eba); | |
650 | break; | |
3fd8b292 PZ |
651 | default: |
652 | dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d", | |
03ee3da8 | 653 | eba, state->src.x1 >> 16, state->src.y1 >> 16); |
3fd8b292 PZ |
654 | break; |
655 | } | |
656 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); | |
657 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba); | |
33f14235 | 658 | ipu_plane_enable(ipu_plane); |
b8d181e4 PZ |
659 | } |
660 | ||
33f14235 | 661 | static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = { |
782ea2a4 | 662 | .prepare_fb = drm_fb_cma_prepare_fb, |
33f14235 LY |
663 | .atomic_check = ipu_plane_atomic_check, |
664 | .atomic_disable = ipu_plane_atomic_disable, | |
665 | .atomic_update = ipu_plane_atomic_update, | |
b8d181e4 PZ |
666 | }; |
667 | ||
00514e85 LS |
668 | int ipu_planes_assign_pre(struct drm_device *dev, |
669 | struct drm_atomic_state *state) | |
670 | { | |
671 | struct drm_plane_state *plane_state; | |
672 | struct drm_plane *plane; | |
673 | int available_pres = ipu_prg_max_active_channels(); | |
674 | int i; | |
675 | ||
676 | for_each_plane_in_state(state, plane, plane_state, i) { | |
677 | struct ipu_plane_state *ipu_state = | |
678 | to_ipu_plane_state(plane_state); | |
679 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | |
680 | ||
681 | if (ipu_prg_present(ipu_plane->ipu) && available_pres && | |
682 | plane_state->fb && | |
683 | ipu_prg_format_supported(ipu_plane->ipu, | |
684 | plane_state->fb->format->format, | |
685 | plane_state->fb->modifier)) { | |
686 | ipu_state->use_pre = true; | |
687 | available_pres--; | |
688 | } else { | |
689 | ipu_state->use_pre = false; | |
690 | } | |
691 | } | |
692 | ||
693 | return 0; | |
694 | } | |
695 | EXPORT_SYMBOL_GPL(ipu_planes_assign_pre); | |
696 | ||
b8d181e4 PZ |
697 | struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, |
698 | int dma, int dp, unsigned int possible_crtcs, | |
43895599 | 699 | enum drm_plane_type type) |
b8d181e4 PZ |
700 | { |
701 | struct ipu_plane *ipu_plane; | |
702 | int ret; | |
703 | ||
704 | DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", | |
705 | dma, dp, possible_crtcs); | |
706 | ||
707 | ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); | |
708 | if (!ipu_plane) { | |
709 | DRM_ERROR("failed to allocate plane\n"); | |
710 | return ERR_PTR(-ENOMEM); | |
711 | } | |
712 | ||
713 | ipu_plane->ipu = ipu; | |
714 | ipu_plane->dma = dma; | |
715 | ipu_plane->dp_flow = dp; | |
716 | ||
43895599 PZ |
717 | ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs, |
718 | &ipu_plane_funcs, ipu_plane_formats, | |
b0b3b795 VS |
719 | ARRAY_SIZE(ipu_plane_formats), type, |
720 | NULL); | |
b8d181e4 PZ |
721 | if (ret) { |
722 | DRM_ERROR("failed to initialize plane\n"); | |
723 | kfree(ipu_plane); | |
724 | return ERR_PTR(ret); | |
725 | } | |
726 | ||
33f14235 LY |
727 | drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs); |
728 | ||
b8d181e4 PZ |
729 | return ipu_plane; |
730 | } |