aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2018-05-04 10:39:59 -0400
committerThierry Reding <treding@nvidia.com>2018-05-17 08:08:44 -0400
commit3dae08bc076b93487ed2df50bcfa892113e89d9d (patch)
treeddbb3a4b0de1dca357b921ff037aedaea72a59bf
parentacc6a3a9afdd4e0537342012656cdb5c4a3127c5 (diff)
drm/tegra: plane: Implement zpos plane property for older Tegras
Older Tegra's do not support plane's Z position handling in hardware, but the hardware provides knobs to implement it in software. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/dc.c134
-rw-r--r--drivers/gpu/drm/tegra/plane.c193
-rw-r--r--drivers/gpu/drm/tegra/plane.h13
3 files changed, 244 insertions, 96 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index a4dd866fc8be..51581d9da509 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
163 BLEND_COLOR_KEY_NONE; 163 BLEND_COLOR_KEY_NONE;
164 u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); 164 u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
165 struct tegra_plane_state *state; 165 struct tegra_plane_state *state;
166 u32 blending[2];
166 unsigned int i; 167 unsigned int i;
167 168
169 /* disable blending for non-overlapping case */
170 tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
171 tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
172
168 state = to_tegra_plane_state(plane->base.state); 173 state = to_tegra_plane_state(plane->base.state);
169 174
170 /* alpha contribution is 1 minus sum of overlapping windows */ 175 if (state->opaque) {
171 for (i = 0; i < 3; i++) { 176 /*
172 if (state->dependent[i]) 177 * Since custom fix-weight blending isn't utilized and weight
173 background[i] |= BLEND_CONTROL_DEPENDENT; 178 * of top window is set to max, we can enforce dependent
174 } 179 * blending which in this case results in transparent bottom
180 * window if top window is opaque and if top window enables
181 * alpha blending, then bottom window is getting alpha value
182 * of 1 minus the sum of alpha components of the overlapping
183 * plane.
184 */
185 background[0] |= BLEND_CONTROL_DEPENDENT;
186 background[1] |= BLEND_CONTROL_DEPENDENT;
175 187
176 /* enable alpha blending if pixel format has an alpha component */ 188 /*
177 if (!state->opaque) 189 * The region where three windows overlap is the intersection
190 * of the two regions where two windows overlap. It contributes
191 * to the area if all of the windows on top of it have an alpha
192 * component.
193 */
194 switch (state->base.normalized_zpos) {
195 case 0:
196 if (state->blending[0].alpha &&
197 state->blending[1].alpha)
198 background[2] |= BLEND_CONTROL_DEPENDENT;
199 break;
200
201 case 1:
202 background[2] |= BLEND_CONTROL_DEPENDENT;
203 break;
204 }
205 } else {
206 /*
207 * Enable alpha blending if pixel format has an alpha
208 * component.
209 */
178 foreground |= BLEND_CONTROL_ALPHA; 210 foreground |= BLEND_CONTROL_ALPHA;
179 211
180 /* 212 /*
181 * Disable blending and assume Window A is the bottom-most window, 213 * If any of the windows on top of this window is opaque, it
182 * Window C is the top-most window and Window B is in the middle. 214 * will completely conceal this window within that area. If
183 */ 215 * top window has an alpha component, it is blended over the
184 tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); 216 * bottom window.
185 tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); 217 */
218 for (i = 0; i < 2; i++) {
219 if (state->blending[i].alpha &&
220 state->blending[i].top)
221 background[i] |= BLEND_CONTROL_DEPENDENT;
222 }
186 223
187 switch (plane->index) { 224 switch (state->base.normalized_zpos) {
225 case 0:
226 if (state->blending[0].alpha &&
227 state->blending[1].alpha)
228 background[2] |= BLEND_CONTROL_DEPENDENT;
229 break;
230
231 case 1:
232 /*
233 * When both middle and topmost windows have an alpha,
234 * these windows a mixed together and then the result
235 * is blended over the bottom window.
236 */
237 if (state->blending[0].alpha &&
238 state->blending[0].top)
239 background[2] |= BLEND_CONTROL_ALPHA;
240
241 if (state->blending[1].alpha &&
242 state->blending[1].top)
243 background[2] |= BLEND_CONTROL_ALPHA;
244 break;
245 }
246 }
247
248 switch (state->base.normalized_zpos) {
188 case 0: 249 case 0:
189 tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); 250 tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
190 tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 251 tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
@@ -192,8 +253,21 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
192 break; 253 break;
193 254
194 case 1: 255 case 1:
195 tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); 256 /*
196 tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 257 * If window B / C is topmost, then X / Y registers are
258 * matching the order of blending[...] state indices,
259 * otherwise a swap is required.
260 */
261 if (!state->blending[0].top && state->blending[1].top) {
262 blending[0] = foreground;
263 blending[1] = background[1];
264 } else {
265 blending[0] = background[0];
266 blending[1] = foreground;
267 }
268
269 tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
270 tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
197 tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 271 tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
198 break; 272 break;
199 273
@@ -525,14 +599,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
525 struct tegra_bo_tiling *tiling = &plane_state->tiling; 599 struct tegra_bo_tiling *tiling = &plane_state->tiling;
526 struct tegra_plane *tegra = to_tegra_plane(plane); 600 struct tegra_plane *tegra = to_tegra_plane(plane);
527 struct tegra_dc *dc = to_tegra_dc(state->crtc); 601 struct tegra_dc *dc = to_tegra_dc(state->crtc);
528 unsigned int format;
529 int err; 602 int err;
530 603
531 /* no need for further checks if the plane is being disabled */ 604 /* no need for further checks if the plane is being disabled */
532 if (!state->crtc) 605 if (!state->crtc)
533 return 0; 606 return 0;
534 607
535 err = tegra_plane_format(state->fb->format->format, &format, 608 err = tegra_plane_format(state->fb->format->format,
609 &plane_state->format,
536 &plane_state->swap); 610 &plane_state->swap);
537 if (err < 0) 611 if (err < 0)
538 return err; 612 return err;
@@ -544,21 +618,11 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
544 * be emulated by disabling alpha blending for the plane. 618 * be emulated by disabling alpha blending for the plane.
545 */ 619 */
546 if (!dc->soc->supports_blending) { 620 if (!dc->soc->supports_blending) {
547 if (!tegra_plane_format_has_alpha(format)) { 621 err = tegra_plane_setup_legacy_state(tegra, plane_state);
548 err = tegra_plane_format_get_alpha(format, &format); 622 if (err < 0)
549 if (err < 0) 623 return err;
550 return err;
551
552 plane_state->opaque = true;
553 } else {
554 plane_state->opaque = false;
555 }
556
557 tegra_plane_check_dependent(tegra, plane_state);
558 } 624 }
559 625
560 plane_state->format = format;
561
562 err = tegra_fb_get_tiling(state->fb, tiling); 626 err = tegra_fb_get_tiling(state->fb, tiling);
563 if (err < 0) 627 if (err < 0)
564 return err; 628 return err;
@@ -710,9 +774,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
710 } 774 }
711 775
712 drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 776 drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
713 777 drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
714 if (dc->soc->supports_blending)
715 drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
716 778
717 return &plane->base; 779 return &plane->base;
718} 780}
@@ -989,9 +1051,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
989 } 1051 }
990 1052
991 drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 1053 drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
992 1054 drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
993 if (dc->soc->supports_blending)
994 drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
995 1055
996 return &plane->base; 1056 return &plane->base;
997} 1057}
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c
index 176ef46c615c..0406c2ef432c 100644
--- a/drivers/gpu/drm/tegra/plane.c
+++ b/drivers/gpu/drm/tegra/plane.c
@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane)
23 23
24static void tegra_plane_reset(struct drm_plane *plane) 24static void tegra_plane_reset(struct drm_plane *plane)
25{ 25{
26 struct tegra_plane *p = to_tegra_plane(plane);
26 struct tegra_plane_state *state; 27 struct tegra_plane_state *state;
27 28
28 if (plane->state) 29 if (plane->state)
@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
35 if (state) { 36 if (state) {
36 plane->state = &state->base; 37 plane->state = &state->base;
37 plane->state->plane = plane; 38 plane->state->plane = plane;
39 plane->state->zpos = p->index;
40 plane->state->normalized_zpos = p->index;
38 } 41 }
39} 42}
40 43
@@ -55,8 +58,8 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
55 copy->swap = state->swap; 58 copy->swap = state->swap;
56 copy->opaque = state->opaque; 59 copy->opaque = state->opaque;
57 60
58 for (i = 0; i < 3; i++) 61 for (i = 0; i < 2; i++)
59 copy->dependent[i] = state->dependent[i]; 62 copy->blending[i] = state->blending[i];
60 63
61 return &copy->base; 64 return &copy->base;
62} 65}
@@ -267,24 +270,8 @@ static bool __drm_format_has_alpha(u32 format)
267 return false; 270 return false;
268} 271}
269 272
270/* 273static int tegra_plane_format_get_alpha(unsigned int opaque,
271 * This is applicable to Tegra20 and Tegra30 only where the opaque formats can 274 unsigned int *alpha)
272 * be emulated using the alpha formats and alpha blending disabled.
273 */
274bool tegra_plane_format_has_alpha(unsigned int format)
275{
276 switch (format) {
277 case WIN_COLOR_DEPTH_B5G5R5A1:
278 case WIN_COLOR_DEPTH_A1B5G5R5:
279 case WIN_COLOR_DEPTH_R8G8B8A8:
280 case WIN_COLOR_DEPTH_B8G8R8A8:
281 return true;
282 }
283
284 return false;
285}
286
287int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
288{ 275{
289 if (tegra_plane_format_is_yuv(opaque, NULL)) { 276 if (tegra_plane_format_is_yuv(opaque, NULL)) {
290 *alpha = opaque; 277 *alpha = opaque;
@@ -316,6 +303,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
316 return -EINVAL; 303 return -EINVAL;
317} 304}
318 305
306/*
307 * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
308 * be emulated using the alpha formats and alpha blending disabled.
309 */
310static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
311 struct tegra_plane_state *state)
312{
313 unsigned int format;
314 int err;
315
316 switch (state->format) {
317 case WIN_COLOR_DEPTH_B5G5R5A1:
318 case WIN_COLOR_DEPTH_A1B5G5R5:
319 case WIN_COLOR_DEPTH_R8G8B8A8:
320 case WIN_COLOR_DEPTH_B8G8R8A8:
321 state->opaque = false;
322 break;
323
324 default:
325 err = tegra_plane_format_get_alpha(state->format, &format);
326 if (err < 0)
327 return err;
328
329 state->format = format;
330 state->opaque = true;
331 break;
332 }
333
334 return 0;
335}
336
337static int tegra_plane_check_transparency(struct tegra_plane *tegra,
338 struct tegra_plane_state *state)
339{
340 struct drm_plane_state *old, *plane_state;
341 struct drm_plane *plane;
342
343 old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
344
345 /* check if zpos / transparency changed */
346 if (old->normalized_zpos == state->base.normalized_zpos &&
347 to_tegra_plane_state(old)->opaque == state->opaque)
348 return 0;
349
350 /* include all sibling planes into this commit */
351 drm_for_each_plane(plane, tegra->base.dev) {
352 struct tegra_plane *p = to_tegra_plane(plane);
353
354 /* skip this plane and planes on different CRTCs */
355 if (p == tegra || p->dc != tegra->dc)
356 continue;
357
358 plane_state = drm_atomic_get_plane_state(state->base.state,
359 plane);
360 if (IS_ERR(plane_state))
361 return PTR_ERR(plane_state);
362 }
363
364 return 1;
365}
366
319static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, 367static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
320 struct tegra_plane *other) 368 struct tegra_plane *other)
321{ 369{
@@ -336,61 +384,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
336 return index; 384 return index;
337} 385}
338 386
339void tegra_plane_check_dependent(struct tegra_plane *tegra, 387static void tegra_plane_update_transparency(struct tegra_plane *tegra,
340 struct tegra_plane_state *state) 388 struct tegra_plane_state *state)
341{ 389{
342 struct drm_plane_state *old, *new; 390 struct drm_plane_state *new;
343 struct drm_plane *plane; 391 struct drm_plane *plane;
344 unsigned int zpos[2];
345 unsigned int i; 392 unsigned int i;
346 393
347 for (i = 0; i < 2; i++) 394 for_each_new_plane_in_state(state->base.state, plane, new, i) {
348 zpos[i] = 0;
349
350 for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
351 struct tegra_plane *p = to_tegra_plane(plane); 395 struct tegra_plane *p = to_tegra_plane(plane);
352 unsigned index; 396 unsigned index;
353 397
354 /* skip this plane and planes on different CRTCs */ 398 /* skip this plane and planes on different CRTCs */
355 if (p == tegra || new->crtc != state->base.crtc) 399 if (p == tegra || p->dc != tegra->dc)
356 continue; 400 continue;
357 401
358 index = tegra_plane_get_overlap_index(tegra, p); 402 index = tegra_plane_get_overlap_index(tegra, p);
359 403
360 state->dependent[index] = false; 404 if (new->fb && __drm_format_has_alpha(new->fb->format->format))
405 state->blending[index].alpha = true;
406 else
407 state->blending[index].alpha = false;
408
409 if (new->normalized_zpos > state->base.normalized_zpos)
410 state->blending[index].top = true;
411 else
412 state->blending[index].top = false;
361 413
362 /* 414 /*
363 * If any of the other planes is on top of this plane and uses 415 * Missing framebuffer means that plane is disabled, in this
364 * a format with an alpha component, mark this plane as being 416 * case mark B / C window as top to be able to differentiate
365 * dependent, meaning it's alpha value will be 1 minus the sum 417 * windows indices order in regards to zPos for the middle
366 * of alpha components of the overlapping planes. 418 * window X / Y registers programming.
367 */ 419 */
368 if (p->index > tegra->index) { 420 if (!new->fb)
369 if (__drm_format_has_alpha(new->fb->format->format)) 421 state->blending[index].top = (index == 1);
370 state->dependent[index] = true;
371
372 /* keep track of the Z position */
373 zpos[index] = p->index;
374 }
375 } 422 }
423}
424
425static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
426 struct tegra_plane_state *state)
427{
428 struct tegra_plane_state *tegra_state;
429 struct drm_plane_state *new;
430 struct drm_plane *plane;
431 int err;
376 432
377 /* 433 /*
378 * The region where three windows overlap is the intersection of the 434 * If planes zpos / transparency changed, sibling planes blending
379 * two regions where two windows overlap. It contributes to the area 435 * state may require adjustment and in this case they will be included
380 * if any of the windows on top of it have an alpha component. 436 * into this atom commit, otherwise blending state is unchanged.
381 */ 437 */
382 for (i = 0; i < 2; i++) 438 err = tegra_plane_check_transparency(tegra, state);
383 state->dependent[2] = state->dependent[2] || 439 if (err <= 0)
384 state->dependent[i]; 440 return err;
385 441
386 /* 442 /*
387 * However, if any of the windows on top of this window is opaque, it 443 * All planes are now in the atomic state, walk them up and update
388 * will completely conceal this window within that area, so avoid the 444 * transparency state for each plane.
389 * window from contributing to the area.
390 */ 445 */
391 for (i = 0; i < 2; i++) { 446 drm_for_each_plane(plane, tegra->base.dev) {
392 if (zpos[i] > tegra->index) 447 struct tegra_plane *p = to_tegra_plane(plane);
393 state->dependent[2] = state->dependent[2] && 448
394 state->dependent[i]; 449 /* skip planes on different CRTCs */
450 if (p->dc != tegra->dc)
451 continue;
452
453 new = drm_atomic_get_new_plane_state(state->base.state, plane);
454 tegra_state = to_tegra_plane_state(new);
455
456 /*
457 * There is no need to update blending state for the disabled
458 * plane.
459 */
460 if (new->fb)
461 tegra_plane_update_transparency(p, tegra_state);
395 } 462 }
463
464 return 0;
465}
466
467int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
468 struct tegra_plane_state *state)
469{
470 int err;
471
472 err = tegra_plane_setup_opacity(tegra, state);
473 if (err < 0)
474 return err;
475
476 err = tegra_plane_setup_transparency(tegra, state);
477 if (err < 0)
478 return err;
479
480 return 0;
396} 481}
diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h
index 6938719e7e5d..7360ddfafee8 100644
--- a/drivers/gpu/drm/tegra/plane.h
+++ b/drivers/gpu/drm/tegra/plane.h
@@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
34 return container_of(plane, struct tegra_plane, base); 34 return container_of(plane, struct tegra_plane, base);
35} 35}
36 36
37struct tegra_plane_legacy_blending_state {
38 bool alpha;
39 bool top;
40};
41
37struct tegra_plane_state { 42struct tegra_plane_state {
38 struct drm_plane_state base; 43 struct drm_plane_state base;
39 44
@@ -42,8 +47,8 @@ struct tegra_plane_state {
42 u32 swap; 47 u32 swap;
43 48
44 /* used for legacy blending support only */ 49 /* used for legacy blending support only */
50 struct tegra_plane_legacy_blending_state blending[2];
45 bool opaque; 51 bool opaque;
46 bool dependent[3];
47}; 52};
48 53
49static inline struct tegra_plane_state * 54static inline struct tegra_plane_state *
@@ -62,9 +67,7 @@ int tegra_plane_state_add(struct tegra_plane *plane,
62 67
63int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); 68int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap);
64bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); 69bool tegra_plane_format_is_yuv(unsigned int format, bool *planar);
65bool tegra_plane_format_has_alpha(unsigned int format); 70int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
66int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); 71 struct tegra_plane_state *state);
67void tegra_plane_check_dependent(struct tegra_plane *tegra,
68 struct tegra_plane_state *state);
69 72
70#endif /* TEGRA_PLANE_H */ 73#endif /* TEGRA_PLANE_H */