aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/sun4i/sun4i_backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_backend.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c154
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 */
57static const u32 sunxi_bt601_yuv2rgb_coef[12] = {
58 0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
59 0x000004a7, 0x00000000, 0x00000662, 0x00003211,
60 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
61};
62
63static 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
75static 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
89static 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
45static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) 95static 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
219static 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
169int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, 274int 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
338static 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
226int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, 353int 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 = {
793static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { 936static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = {
794}; 937};
795 938
939static const struct sun4i_backend_quirks sun9i_backend_quirks = {
940};
941
796static const struct of_device_id sun4i_backend_of_table[] = { 942static 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};
819MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); 969MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);