diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_backend.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.c | 154 |
1 files changed, 152 insertions, 2 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 092ade4ff6a5..9bad54f3de38 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c | |||
@@ -42,6 +42,56 @@ static const u32 sunxi_rgb2yuv_coef[12] = { | |||
42 | 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 | 42 | 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 |
43 | }; | 43 | }; |
44 | 44 | ||
45 | /* | ||
46 | * These coefficients are taken from the A33 BSP from Allwinner. | ||
47 | * | ||
48 | * The formula is for each component, each coefficient being multiplied by | ||
49 | * 1024 and each constant being multiplied by 16: | ||
50 | * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135 | ||
51 | * R = 1.164 * Y + 1.596 * V - 222 | ||
52 | * B = 1.164 * Y + 2.018 * U + 276 | ||
53 | * | ||
54 | * This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255], | ||
55 | * following the BT601 spec. | ||
56 | */ | ||
57 | static const u32 sunxi_bt601_yuv2rgb_coef[12] = { | ||
58 | 0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877, | ||
59 | 0x000004a7, 0x00000000, 0x00000662, 0x00003211, | ||
60 | 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, | ||
61 | }; | ||
62 | |||
63 | static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) | ||
64 | { | ||
65 | switch (format) { | ||
66 | case DRM_FORMAT_YUV411: | ||
67 | case DRM_FORMAT_YUV422: | ||
68 | case DRM_FORMAT_YUV444: | ||
69 | return true; | ||
70 | default: | ||
71 | return false; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static inline bool sun4i_backend_format_is_packed_yuv422(uint32_t format) | ||
76 | { | ||
77 | switch (format) { | ||
78 | case DRM_FORMAT_YUYV: | ||
79 | case DRM_FORMAT_YVYU: | ||
80 | case DRM_FORMAT_UYVY: | ||
81 | case DRM_FORMAT_VYUY: | ||
82 | return true; | ||
83 | |||
84 | default: | ||
85 | return false; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static inline bool sun4i_backend_format_is_yuv(uint32_t format) | ||
90 | { | ||
91 | return sun4i_backend_format_is_planar_yuv(format) || | ||
92 | sun4i_backend_format_is_packed_yuv422(format); | ||
93 | } | ||
94 | |||
45 | static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) | 95 | static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) |
46 | { | 96 | { |
47 | int i; | 97 | int i; |
@@ -166,6 +216,61 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, | |||
166 | return 0; | 216 | return 0; |
167 | } | 217 | } |
168 | 218 | ||
219 | static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, | ||
220 | int layer, struct drm_plane *plane) | ||
221 | { | ||
222 | struct drm_plane_state *state = plane->state; | ||
223 | struct drm_framebuffer *fb = state->fb; | ||
224 | uint32_t format = fb->format->format; | ||
225 | u32 val = SUN4I_BACKEND_IYUVCTL_EN; | ||
226 | int i; | ||
227 | |||
228 | for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++) | ||
229 | regmap_write(backend->engine.regs, | ||
230 | SUN4I_BACKEND_YGCOEF_REG(i), | ||
231 | sunxi_bt601_yuv2rgb_coef[i]); | ||
232 | |||
233 | /* | ||
234 | * We should do that only for a single plane, but the | ||
235 | * framebuffer's atomic_check has our back on this. | ||
236 | */ | ||
237 | regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer), | ||
238 | SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, | ||
239 | SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN); | ||
240 | |||
241 | /* TODO: Add support for the multi-planar YUV formats */ | ||
242 | if (sun4i_backend_format_is_packed_yuv422(format)) | ||
243 | val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422; | ||
244 | else | ||
245 | DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", format); | ||
246 | |||
247 | /* | ||
248 | * Allwinner seems to list the pixel sequence from right to left, while | ||
249 | * DRM lists it from left to right. | ||
250 | */ | ||
251 | switch (format) { | ||
252 | case DRM_FORMAT_YUYV: | ||
253 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY; | ||
254 | break; | ||
255 | case DRM_FORMAT_YVYU: | ||
256 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_UYVY; | ||
257 | break; | ||
258 | case DRM_FORMAT_UYVY: | ||
259 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_YVYU; | ||
260 | break; | ||
261 | case DRM_FORMAT_VYUY: | ||
262 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_YUYV; | ||
263 | break; | ||
264 | default: | ||
265 | DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n", | ||
266 | format); | ||
267 | } | ||
268 | |||
269 | regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
169 | int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | 274 | int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, |
170 | int layer, struct drm_plane *plane) | 275 | int layer, struct drm_plane *plane) |
171 | { | 276 | { |
@@ -175,6 +280,10 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |||
175 | u32 val; | 280 | u32 val; |
176 | int ret; | 281 | int ret; |
177 | 282 | ||
283 | /* Clear the YUV mode */ | ||
284 | regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer), | ||
285 | SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0); | ||
286 | |||
178 | if (plane->state->crtc) | 287 | if (plane->state->crtc) |
179 | interlaced = plane->state->crtc->state->adjusted_mode.flags | 288 | interlaced = plane->state->crtc->state->adjusted_mode.flags |
180 | & DRM_MODE_FLAG_INTERLACE; | 289 | & DRM_MODE_FLAG_INTERLACE; |
@@ -186,6 +295,9 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |||
186 | DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", | 295 | DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", |
187 | interlaced ? "on" : "off"); | 296 | interlaced ? "on" : "off"); |
188 | 297 | ||
298 | if (sun4i_backend_format_is_yuv(fb->format->format)) | ||
299 | return sun4i_backend_update_yuv_format(backend, layer, plane); | ||
300 | |||
189 | ret = sun4i_backend_drm_format_to_layer(fb->format->format, &val); | 301 | ret = sun4i_backend_drm_format_to_layer(fb->format->format, &val); |
190 | if (ret) { | 302 | if (ret) { |
191 | DRM_DEBUG_DRIVER("Invalid format\n"); | 303 | DRM_DEBUG_DRIVER("Invalid format\n"); |
@@ -223,6 +335,21 @@ int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend, | |||
223 | return 0; | 335 | return 0; |
224 | } | 336 | } |
225 | 337 | ||
338 | static int sun4i_backend_update_yuv_buffer(struct sun4i_backend *backend, | ||
339 | struct drm_framebuffer *fb, | ||
340 | dma_addr_t paddr) | ||
341 | { | ||
342 | /* TODO: Add support for the multi-planar YUV formats */ | ||
343 | DRM_DEBUG_DRIVER("Setting packed YUV buffer address to %pad\n", &paddr); | ||
344 | regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVADD_REG(0), paddr); | ||
345 | |||
346 | DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); | ||
347 | regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVLINEWIDTH_REG(0), | ||
348 | fb->pitches[0] * 8); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
226 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | 353 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, |
227 | int layer, struct drm_plane *plane) | 354 | int layer, struct drm_plane *plane) |
228 | { | 355 | { |
@@ -248,6 +375,9 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |||
248 | */ | 375 | */ |
249 | paddr -= PHYS_OFFSET; | 376 | paddr -= PHYS_OFFSET; |
250 | 377 | ||
378 | if (sun4i_backend_format_is_yuv(fb->format->format)) | ||
379 | return sun4i_backend_update_yuv_buffer(backend, fb, paddr); | ||
380 | |||
251 | /* Write the 32 lower bits of the address (in bits) */ | 381 | /* Write the 32 lower bits of the address (in bits) */ |
252 | lo_paddr = paddr << 3; | 382 | lo_paddr = paddr << 3; |
253 | DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); | 383 | DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); |
@@ -330,6 +460,7 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | |||
330 | unsigned int num_planes = 0; | 460 | unsigned int num_planes = 0; |
331 | unsigned int num_alpha_planes = 0; | 461 | unsigned int num_alpha_planes = 0; |
332 | unsigned int num_frontend_planes = 0; | 462 | unsigned int num_frontend_planes = 0; |
463 | unsigned int num_yuv_planes = 0; | ||
333 | unsigned int current_pipe = 0; | 464 | unsigned int current_pipe = 0; |
334 | unsigned int i; | 465 | unsigned int i; |
335 | 466 | ||
@@ -362,6 +493,11 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | |||
362 | if (fb->format->has_alpha) | 493 | if (fb->format->has_alpha) |
363 | num_alpha_planes++; | 494 | num_alpha_planes++; |
364 | 495 | ||
496 | if (sun4i_backend_format_is_yuv(fb->format->format)) { | ||
497 | DRM_DEBUG_DRIVER("Plane FB format is YUV\n"); | ||
498 | num_yuv_planes++; | ||
499 | } | ||
500 | |||
365 | DRM_DEBUG_DRIVER("Plane zpos is %d\n", | 501 | DRM_DEBUG_DRIVER("Plane zpos is %d\n", |
366 | plane_state->normalized_zpos); | 502 | plane_state->normalized_zpos); |
367 | 503 | ||
@@ -430,13 +566,20 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | |||
430 | s_state->pipe = current_pipe; | 566 | s_state->pipe = current_pipe; |
431 | } | 567 | } |
432 | 568 | ||
569 | /* We can only have a single YUV plane at a time */ | ||
570 | if (num_yuv_planes > SUN4I_BACKEND_NUM_YUV_PLANES) { | ||
571 | DRM_DEBUG_DRIVER("Too many planes with YUV, rejecting...\n"); | ||
572 | return -EINVAL; | ||
573 | } | ||
574 | |||
433 | if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { | 575 | if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { |
434 | DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); | 576 | DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); |
435 | return -EINVAL; | 577 | return -EINVAL; |
436 | } | 578 | } |
437 | 579 | ||
438 | DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n", | 580 | DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video, %u YUV\n", |
439 | num_planes, num_alpha_planes, num_frontend_planes); | 581 | num_planes, num_alpha_planes, num_frontend_planes, |
582 | num_yuv_planes); | ||
440 | 583 | ||
441 | return 0; | 584 | return 0; |
442 | } | 585 | } |
@@ -793,6 +936,9 @@ static const struct sun4i_backend_quirks sun7i_backend_quirks = { | |||
793 | static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { | 936 | static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { |
794 | }; | 937 | }; |
795 | 938 | ||
939 | static const struct sun4i_backend_quirks sun9i_backend_quirks = { | ||
940 | }; | ||
941 | |||
796 | static const struct of_device_id sun4i_backend_of_table[] = { | 942 | static const struct of_device_id sun4i_backend_of_table[] = { |
797 | { | 943 | { |
798 | .compatible = "allwinner,sun4i-a10-display-backend", | 944 | .compatible = "allwinner,sun4i-a10-display-backend", |
@@ -814,6 +960,10 @@ static const struct of_device_id sun4i_backend_of_table[] = { | |||
814 | .compatible = "allwinner,sun8i-a33-display-backend", | 960 | .compatible = "allwinner,sun8i-a33-display-backend", |
815 | .data = &sun8i_a33_backend_quirks, | 961 | .data = &sun8i_a33_backend_quirks, |
816 | }, | 962 | }, |
963 | { | ||
964 | .compatible = "allwinner,sun9i-a80-display-backend", | ||
965 | .data = &sun9i_backend_quirks, | ||
966 | }, | ||
817 | { } | 967 | { } |
818 | }; | 968 | }; |
819 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); | 969 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); |