diff options
| author | Eric Anholt <eric@anholt.net> | 2015-12-30 15:25:44 -0500 |
|---|---|---|
| committer | Eric Anholt <eric@anholt.net> | 2016-02-16 14:24:08 -0500 |
| commit | fc04023fafecf19ebd09278d8d67dc5ed1f68b46 (patch) | |
| tree | 25acc41475384d9d364b6278d3cceea0139ac786 | |
| parent | fe4cd8476928a66e109ab50a430362fcee8a5716 (diff) | |
drm/vc4: Add support for YUV planes.
This supports 420 and 422 subsampling with 2 or 3 planes, tested with
modetest. It doesn't set up chroma subsampling position (which it
appears KMS doesn't deal with yet).
The LBM memory is overallocated in many cases, but apparently the docs
aren't quite correct and I'll probably need to look at the hardware
source to really figure it out.
Signed-off-by: Eric Anholt <eric@anholt.net>
| -rw-r--r-- | drivers/gpu/drm/vc4/vc4_plane.c | 256 | ||||
| -rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 56 |
2 files changed, 253 insertions, 59 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 013ebff60fb5..7b0c72ae02a0 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c | |||
| @@ -54,15 +54,19 @@ struct vc4_plane_state { | |||
| 54 | /* Clipped coordinates of the plane on the display. */ | 54 | /* Clipped coordinates of the plane on the display. */ |
| 55 | int crtc_x, crtc_y, crtc_w, crtc_h; | 55 | int crtc_x, crtc_y, crtc_w, crtc_h; |
| 56 | /* Clipped area being scanned from in the FB. */ | 56 | /* Clipped area being scanned from in the FB. */ |
| 57 | u32 src_x, src_y, src_w, src_h; | 57 | u32 src_x, src_y; |
| 58 | 58 | ||
| 59 | enum vc4_scaling_mode x_scaling, y_scaling; | 59 | u32 src_w[2], src_h[2]; |
| 60 | |||
| 61 | /* Scaling selection for the RGB/Y plane and the Cb/Cr planes. */ | ||
| 62 | enum vc4_scaling_mode x_scaling[2], y_scaling[2]; | ||
| 60 | bool is_unity; | 63 | bool is_unity; |
| 64 | bool is_yuv; | ||
| 61 | 65 | ||
| 62 | /* Offset to start scanning out from the start of the plane's | 66 | /* Offset to start scanning out from the start of the plane's |
| 63 | * BO. | 67 | * BO. |
| 64 | */ | 68 | */ |
| 65 | u32 offset; | 69 | u32 offsets[3]; |
| 66 | 70 | ||
| 67 | /* Our allocation in LBM for temporary storage during scaling. */ | 71 | /* Our allocation in LBM for temporary storage during scaling. */ |
| 68 | struct drm_mm_node lbm; | 72 | struct drm_mm_node lbm; |
| @@ -79,6 +83,7 @@ static const struct hvs_format { | |||
| 79 | u32 hvs; /* HVS_FORMAT_* */ | 83 | u32 hvs; /* HVS_FORMAT_* */ |
| 80 | u32 pixel_order; | 84 | u32 pixel_order; |
| 81 | bool has_alpha; | 85 | bool has_alpha; |
| 86 | bool flip_cbcr; | ||
| 82 | } hvs_formats[] = { | 87 | } hvs_formats[] = { |
| 83 | { | 88 | { |
| 84 | .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, | 89 | .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, |
| @@ -104,6 +109,32 @@ static const struct hvs_format { | |||
| 104 | .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, | 109 | .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, |
| 105 | .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, | 110 | .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, |
| 106 | }, | 111 | }, |
| 112 | { | ||
| 113 | .drm = DRM_FORMAT_YUV422, | ||
| 114 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | ||
| 115 | }, | ||
| 116 | { | ||
| 117 | .drm = DRM_FORMAT_YVU422, | ||
| 118 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | ||
| 119 | .flip_cbcr = true, | ||
| 120 | }, | ||
| 121 | { | ||
| 122 | .drm = DRM_FORMAT_YUV420, | ||
| 123 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | ||
| 124 | }, | ||
| 125 | { | ||
| 126 | .drm = DRM_FORMAT_YVU420, | ||
| 127 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | ||
| 128 | .flip_cbcr = true, | ||
| 129 | }, | ||
| 130 | { | ||
| 131 | .drm = DRM_FORMAT_NV12, | ||
| 132 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, | ||
| 133 | }, | ||
| 134 | { | ||
| 135 | .drm = DRM_FORMAT_NV16, | ||
| 136 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, | ||
| 137 | }, | ||
| 107 | }; | 138 | }; |
| 108 | 139 | ||
| 109 | static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) | 140 | static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) |
| @@ -219,11 +250,11 @@ static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) | |||
| 219 | * | 250 | * |
| 220 | * This is a replication of a table from the spec. | 251 | * This is a replication of a table from the spec. |
| 221 | */ | 252 | */ |
| 222 | static u32 vc4_get_scl_field(struct drm_plane_state *state) | 253 | static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane) |
| 223 | { | 254 | { |
| 224 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | 255 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
| 225 | 256 | ||
| 226 | switch (vc4_state->x_scaling << 2 | vc4_state->y_scaling) { | 257 | switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) { |
| 227 | case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: | 258 | case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: |
| 228 | return SCALER_CTL0_SCL_H_PPF_V_PPF; | 259 | return SCALER_CTL0_SCL_H_PPF_V_PPF; |
| 229 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: | 260 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: |
| @@ -254,9 +285,16 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
| 254 | struct drm_plane *plane = state->plane; | 285 | struct drm_plane *plane = state->plane; |
| 255 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | 286 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
| 256 | struct drm_framebuffer *fb = state->fb; | 287 | struct drm_framebuffer *fb = state->fb; |
| 288 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); | ||
| 257 | u32 subpixel_src_mask = (1 << 16) - 1; | 289 | u32 subpixel_src_mask = (1 << 16) - 1; |
| 290 | u32 format = fb->pixel_format; | ||
| 291 | int num_planes = drm_format_num_planes(format); | ||
| 292 | u32 h_subsample = 1; | ||
| 293 | u32 v_subsample = 1; | ||
| 294 | int i; | ||
| 258 | 295 | ||
| 259 | vc4_state->offset = fb->offsets[0]; | 296 | for (i = 0; i < num_planes; i++) |
| 297 | vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; | ||
| 260 | 298 | ||
| 261 | /* We don't support subpixel source positioning for scaling. */ | 299 | /* We don't support subpixel source positioning for scaling. */ |
| 262 | if ((state->src_x & subpixel_src_mask) || | 300 | if ((state->src_x & subpixel_src_mask) || |
| @@ -268,20 +306,48 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
| 268 | 306 | ||
| 269 | vc4_state->src_x = state->src_x >> 16; | 307 | vc4_state->src_x = state->src_x >> 16; |
| 270 | vc4_state->src_y = state->src_y >> 16; | 308 | vc4_state->src_y = state->src_y >> 16; |
| 271 | vc4_state->src_w = state->src_w >> 16; | 309 | vc4_state->src_w[0] = state->src_w >> 16; |
| 272 | vc4_state->src_h = state->src_h >> 16; | 310 | vc4_state->src_h[0] = state->src_h >> 16; |
| 273 | 311 | ||
| 274 | vc4_state->crtc_x = state->crtc_x; | 312 | vc4_state->crtc_x = state->crtc_x; |
| 275 | vc4_state->crtc_y = state->crtc_y; | 313 | vc4_state->crtc_y = state->crtc_y; |
| 276 | vc4_state->crtc_w = state->crtc_w; | 314 | vc4_state->crtc_w = state->crtc_w; |
| 277 | vc4_state->crtc_h = state->crtc_h; | 315 | vc4_state->crtc_h = state->crtc_h; |
| 278 | 316 | ||
| 279 | vc4_state->x_scaling = vc4_get_scaling_mode(vc4_state->src_w, | 317 | vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], |
| 280 | vc4_state->crtc_w); | 318 | vc4_state->crtc_w); |
| 281 | vc4_state->y_scaling = vc4_get_scaling_mode(vc4_state->src_h, | 319 | vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], |
| 282 | vc4_state->crtc_h); | 320 | vc4_state->crtc_h); |
| 283 | vc4_state->is_unity = (vc4_state->x_scaling == VC4_SCALING_NONE && | 321 | |
| 284 | vc4_state->y_scaling == VC4_SCALING_NONE); | 322 | if (num_planes > 1) { |
| 323 | vc4_state->is_yuv = true; | ||
| 324 | |||
| 325 | h_subsample = drm_format_horz_chroma_subsampling(format); | ||
| 326 | v_subsample = drm_format_vert_chroma_subsampling(format); | ||
| 327 | vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; | ||
| 328 | vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; | ||
| 329 | |||
| 330 | vc4_state->x_scaling[1] = | ||
| 331 | vc4_get_scaling_mode(vc4_state->src_w[1], | ||
| 332 | vc4_state->crtc_w); | ||
| 333 | vc4_state->y_scaling[1] = | ||
| 334 | vc4_get_scaling_mode(vc4_state->src_h[1], | ||
| 335 | vc4_state->crtc_h); | ||
| 336 | |||
| 337 | /* YUV conversion requires that scaling be enabled, | ||
| 338 | * even on a plane that's otherwise 1:1. Choose TPZ | ||
| 339 | * for simplicity. | ||
| 340 | */ | ||
| 341 | if (vc4_state->x_scaling[0] == VC4_SCALING_NONE) | ||
| 342 | vc4_state->x_scaling[0] = VC4_SCALING_TPZ; | ||
| 343 | if (vc4_state->y_scaling[0] == VC4_SCALING_NONE) | ||
| 344 | vc4_state->y_scaling[0] = VC4_SCALING_TPZ; | ||
| 345 | } | ||
| 346 | |||
| 347 | vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && | ||
| 348 | vc4_state->y_scaling[0] == VC4_SCALING_NONE && | ||
| 349 | vc4_state->x_scaling[1] == VC4_SCALING_NONE && | ||
| 350 | vc4_state->y_scaling[1] == VC4_SCALING_NONE); | ||
| 285 | 351 | ||
| 286 | /* No configuring scaling on the cursor plane, since it gets | 352 | /* No configuring scaling on the cursor plane, since it gets |
| 287 | non-vblank-synced updates, and scaling requires requires | 353 | non-vblank-synced updates, and scaling requires requires |
| @@ -294,16 +360,27 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
| 294 | * support negative y, and negative x wastes bandwidth. | 360 | * support negative y, and negative x wastes bandwidth. |
| 295 | */ | 361 | */ |
| 296 | if (vc4_state->crtc_x < 0) { | 362 | if (vc4_state->crtc_x < 0) { |
| 297 | vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, | 363 | for (i = 0; i < num_planes; i++) { |
| 298 | 0) * | 364 | u32 cpp = drm_format_plane_cpp(fb->pixel_format, i); |
| 299 | -vc4_state->crtc_x); | 365 | u32 subs = ((i == 0) ? 1 : h_subsample); |
| 300 | vc4_state->src_w += vc4_state->crtc_x; | 366 | |
| 367 | vc4_state->offsets[i] += (cpp * | ||
| 368 | (-vc4_state->crtc_x) / subs); | ||
| 369 | } | ||
| 370 | vc4_state->src_w[0] += vc4_state->crtc_x; | ||
| 371 | vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample; | ||
| 301 | vc4_state->crtc_x = 0; | 372 | vc4_state->crtc_x = 0; |
| 302 | } | 373 | } |
| 303 | |||
