diff options
| -rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.c | 98 | ||||
| -rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.h | 17 | ||||
| -rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_layer.c | 4 |
3 files changed, 119 insertions, 0 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 8aaa74d09954..9bad54f3de38 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c | |||
| @@ -42,6 +42,24 @@ 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 | |||
| 45 | static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) | 63 | static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) |
| 46 | { | 64 | { |
| 47 | switch (format) { | 65 | switch (format) { |
| @@ -198,6 +216,61 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, | |||
| 198 | return 0; | 216 | return 0; |
| 199 | } | 217 | } |
| 200 | 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 | |||
| 201 | int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | 274 | int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, |
| 202 | int layer, struct drm_plane *plane) | 275 | int layer, struct drm_plane *plane) |
| 203 | { | 276 | { |
| @@ -207,6 +280,10 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |||
| 207 | u32 val; | 280 | u32 val; |
| 208 | int ret; | 281 | int ret; |
| 209 | 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 | |||
| 210 | if (plane->state->crtc) | 287 | if (plane->state->crtc) |
| 211 | interlaced = plane->state->crtc->state->adjusted_mode.flags | 288 | interlaced = plane->state->crtc->state->adjusted_mode.flags |
| 212 | & DRM_MODE_FLAG_INTERLACE; | 289 | & DRM_MODE_FLAG_INTERLACE; |
| @@ -218,6 +295,9 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |||
| 218 | DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", | 295 | DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", |
| 219 | interlaced ? "on" : "off"); | 296 | interlaced ? "on" : "off"); |
| 220 | 297 | ||
| 298 | if (sun4i_backend_format_is_yuv(fb->format->format)) | ||
| 299 | return sun4i_backend_update_yuv_format(backend, layer, plane); | ||
| 300 | |||
| 221 | ret = sun4i_backend_drm_format_to_layer(fb->format->format, &val); | 301 | ret = sun4i_backend_drm_format_to_layer(fb->format->format, &val); |
| 222 | if (ret) { | 302 | if (ret) { |
| 223 | DRM_DEBUG_DRIVER("Invalid format\n"); | 303 | DRM_DEBUG_DRIVER("Invalid format\n"); |
| @@ -255,6 +335,21 @@ int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend, | |||
| 255 | return 0; | 335 | return 0; |
| 256 | } | 336 | } |
| 257 | 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 | |||
| 258 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | 353 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, |
| 259 | int layer, struct drm_plane *plane) | 354 | int layer, struct drm_plane *plane) |
| 260 | { | 355 | { |
| @@ -280,6 +375,9 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |||
| 280 | */ | 375 | */ |
| 281 | paddr -= PHYS_OFFSET; | 376 | paddr -= PHYS_OFFSET; |
| 282 | 377 | ||
| 378 | if (sun4i_backend_format_is_yuv(fb->format->format)) | ||
| 379 | return sun4i_backend_update_yuv_buffer(backend, fb, paddr); | ||
| 380 | |||
| 283 | /* Write the 32 lower bits of the address (in bits) */ | 381 | /* Write the 32 lower bits of the address (in bits) */ |
| 284 | lo_paddr = paddr << 3; | 382 | lo_paddr = paddr << 3; |
| 285 | 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); |
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 835408281309..316f2179e9e1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h | |||
| @@ -72,6 +72,7 @@ | |||
| 72 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15) | 72 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15) |
| 73 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10) | 73 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10) |
| 74 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10) | 74 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10) |
| 75 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN BIT(2) | ||
| 75 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN BIT(1) | 76 | #define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN BIT(1) |
| 76 | 77 | ||
| 77 | #define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l))) | 78 | #define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l))) |
| @@ -110,7 +111,23 @@ | |||
| 110 | #define SUN4I_BACKEND_SPREN_REG 0x900 | 111 | #define SUN4I_BACKEND_SPREN_REG 0x900 |
| 111 | #define SUN4I_BACKEND_SPRFMTCTL_REG 0x908 | 112 | #define SUN4I_BACKEND_SPRFMTCTL_REG 0x908 |
| 112 | #define SUN4I_BACKEND_SPRALPHACTL_REG 0x90c | 113 | #define SUN4I_BACKEND_SPRALPHACTL_REG 0x90c |
| 114 | |||
| 113 | #define SUN4I_BACKEND_IYUVCTL_REG 0x920 | 115 | #define SUN4I_BACKEND_IYUVCTL_REG 0x920 |
| 116 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_MASK GENMASK(14, 12) | ||
| 117 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV444 (4 << 12) | ||
| 118 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422 (3 << 12) | ||
| 119 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_PLANAR_YUV444 (2 << 12) | ||
| 120 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_PLANAR_YUV222 (1 << 12) | ||
| 121 | #define SUN4I_BACKEND_IYUVCTL_FBFMT_PLANAR_YUV111 (0 << 12) | ||
| 122 | #define SUN4I_BACKEND_IYUVCTL_FBPS_MASK GENMASK(9, 8) | ||
| 123 | #define SUN4I_BACKEND_IYUVCTL_FBPS_YVYU (3 << 8) | ||
| 124 | #define SUN4I_BACKEND_IYUVCTL_FBPS_VYUY (2 << 8) | ||
| 125 | #define SUN4I_BACKEND_IYUVCTL_FBPS_YUYV (1 << 8) | ||
| 126 | #define SUN4I_BACKEND_IYUVCTL_FBPS_UYVY (0 << 8) | ||
| 127 | #define SUN4I_BACKEND_IYUVCTL_FBPS_VUYA (1 << 8) | ||
| 128 | #define SUN4I_BACKEND_IYUVCTL_FBPS_AYUV (0 << 8) | ||
| 129 | #define SUN4I_BACKEND_IYUVCTL_EN BIT(0) | ||
| 130 | |||
| 114 | #define SUN4I_BACKEND_IYUVADD_REG(c) (0x930 + (0x4 * (c))) | 131 | #define SUN4I_BACKEND_IYUVADD_REG(c) (0x930 + (0x4 * (c))) |
| 115 | 132 | ||
| 116 | #define SUN4I_BACKEND_IYUVLINEWIDTH_REG(c) (0x940 + (0x4 * (c))) | 133 | #define SUN4I_BACKEND_IYUVLINEWIDTH_REG(c) (0x940 + (0x4 * (c))) |
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c index 33ad377569ec..2949a3c912c1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_layer.c +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c | |||
| @@ -134,7 +134,11 @@ static const uint32_t sun4i_backend_layer_formats[] = { | |||
| 134 | DRM_FORMAT_RGBA4444, | 134 | DRM_FORMAT_RGBA4444, |
| 135 | DRM_FORMAT_RGB888, | 135 | DRM_FORMAT_RGB888, |
| 136 | DRM_FORMAT_RGB565, | 136 | DRM_FORMAT_RGB565, |
| 137 | DRM_FORMAT_UYVY, | ||
| 138 | DRM_FORMAT_VYUY, | ||
| 137 | DRM_FORMAT_XRGB8888, | 139 | DRM_FORMAT_XRGB8888, |
| 140 | DRM_FORMAT_YUYV, | ||
| 141 | DRM_FORMAT_YVYU, | ||
| 138 | }; | 142 | }; |
| 139 | 143 | ||
| 140 | static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, | 144 | static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, |
