aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2018-01-04 18:43:46 -0500
committerDave Airlie <airlied@redhat.com>2018-01-04 18:51:43 -0500
commitbcd21a4786eae9f661acf552463fb718b24ee5f4 (patch)
tree24fd61d7ea7e75209e04aed5b952105ef91fb7cf /drivers/gpu
parent066f9eb47d0ff9f5e7f8e8b0b2710c7557834fbb (diff)
parent27ab688f70b6f6b51da4bf56b1237f7beb64d847 (diff)
Merge branch 'drm-armada-devel-4.15' of git://git.armlinux.org.uk/~rmk/linux-arm into drm-next
This series builds upon the set of fixes previously submitted to move Armada DRM closer to atomic modeset. We're nowhere near yet, but this series helps to get us closer by unifying some of the differences between the primary and overlay planes. New features added allows userspace to disable the primary plane if overlay is full screen and there's nothing obscuring the colorkey - this saves having to fetch an entire buffer containing nothing but colorkey when displaying full screen video. [airlied: fixup for atomic plane helper rename: a01cb8ba3f6282934cff65e89ab36b18b14cbe27 Author: Ville Syrjälä <ville.syrjala@linux.intel.com> Date: Wed Nov 1 22:16:19 2017 +0200 drm: Move drm_plane_helper_check_state() into drm_atomic_helper.c ] * 'drm-armada-devel-4.15' of git://git.armlinux.org.uk/~rmk/linux-arm: (29 commits) drm/armada: expand overlay trace entry drm/armada: implement primary plane update drm/armada: extract register generation from armada_drm_primary_set() drm/armada: wait for previous work when moving overlay window drm/armada: move overlay plane register update generation drm/armada: re-organise overlay register update generation drm/armada: disable planes at next blanking period drm/armada: avoid work allocation drm/armada: allow armada_drm_plane_work_queue() to silently fail drm/armada: use drm_plane_helper_check_state() drm/armada: only enable HSMOOTH if scaling horizontally drm/armada: move writes of LCD_SPU_SRAM_PARA1 under lock drm/armada: move regs into armada_plane_work drm/armada: move event sending into armada_plane_work drm/armada: move fb retirement into armada_plane_work drm/armada: move overlay plane work out from under spinlock drm/armada: clear plane enable bit when disabling drm/armada: clean up armada_drm_crtc_plane_disable() drm/armada: allow the primary plane to be disabled drm/armada: wait and cancel any pending frame work at disable ...
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c434
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.h26
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c302
-rw-r--r--drivers/gpu/drm/armada/armada_trace.h24
4 files changed, 496 insertions, 290 deletions
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 2e065facdce7..e2adfbef7d6b 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -13,6 +13,7 @@
13#include <drm/drmP.h> 13#include <drm/drmP.h>
14#include <drm/drm_crtc_helper.h> 14#include <drm/drm_crtc_helper.h>
15#include <drm/drm_plane_helper.h> 15#include <drm/drm_plane_helper.h>
16#include <drm/drm_atomic_helper.h>
16#include "armada_crtc.h" 17#include "armada_crtc.h"
17#include "armada_drm.h" 18#include "armada_drm.h"
18#include "armada_fb.h" 19#include "armada_fb.h"
@@ -20,13 +21,6 @@
20#include "armada_hw.h" 21#include "armada_hw.h"
21#include "armada_trace.h" 22#include "armada_trace.h"
22 23
23struct armada_frame_work {
24 struct armada_plane_work work;
25 struct drm_pending_vblank_event *event;
26 struct armada_regs regs[4];
27 struct drm_framebuffer *old_fb;
28};
29
30enum csc_mode { 24enum csc_mode {
31 CSC_AUTO = 0, 25 CSC_AUTO = 0,
32 CSC_YUV_CCIR601 = 1, 26 CSC_YUV_CCIR601 = 1,
@@ -168,16 +162,23 @@ static void armada_drm_crtc_update(struct armada_crtc *dcrtc)
168void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, 162void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb,
169 int x, int y) 163 int x, int y)
170{ 164{
165 const struct drm_format_info *format = fb->format;
166 unsigned int num_planes = format->num_planes;
171 u32 addr = drm_fb_obj(fb)->dev_addr; 167 u32 addr = drm_fb_obj(fb)->dev_addr;
172 int num_planes = fb->format->num_planes;
173 int i; 168 int i;
174 169
175 if (num_planes > 3) 170 if (num_planes > 3)
176 num_planes = 3; 171 num_planes = 3;
177 172
178 for (i = 0; i < num_planes; i++) 173 addrs[0] = addr + fb->offsets[0] + y * fb->pitches[0] +
174 x * format->cpp[0];
175
176 y /= format->vsub;
177 x /= format->hsub;
178
179 for (i = 1; i < num_planes; i++)
179 addrs[i] = addr + fb->offsets[i] + y * fb->pitches[i] + 180 addrs[i] = addr + fb->offsets[i] + y * fb->pitches[i] +
180 x * fb->format->cpp[i]; 181 x * format->cpp[i];
181 for (; i < 3; i++) 182 for (; i < 3; i++)
182 addrs[i] = 0; 183 addrs[i] = 0;
183} 184}
@@ -209,6 +210,38 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
209 return i; 210 return i;
210} 211}
211 212
213static void armada_drm_plane_work_call(struct armada_crtc *dcrtc,
214 struct armada_plane_work *work,
215 void (*fn)(struct armada_crtc *, struct armada_plane_work *))
216{
217 struct armada_plane *dplane = drm_to_armada_plane(work->plane);
218 struct drm_pending_vblank_event *event;
219 struct drm_framebuffer *fb;
220
221 if (fn)
222 fn(dcrtc, work);
223 drm_crtc_vblank_put(&dcrtc->crtc);
224
225 event = work->event;
226 fb = work->old_fb;
227 if (event || fb) {
228 struct drm_device *dev = dcrtc->crtc.dev;
229 unsigned long flags;
230
231 spin_lock_irqsave(&dev->event_lock, flags);
232 if (event)
233 drm_crtc_send_vblank_event(&dcrtc->crtc, event);
234 if (fb)
235 __armada_drm_queue_unref_work(dev, fb);
236 spin_unlock_irqrestore(&dev->event_lock, flags);
237 }
238
239 if (work->need_kfree)
240 kfree(work);
241
242 wake_up(&dplane->frame_wait);
243}
244
212static void armada_drm_plane_work_run(struct armada_crtc *dcrtc, 245static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
213 struct drm_plane *plane) 246 struct drm_plane *plane)
214{ 247{
@@ -216,24 +249,19 @@ static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
216 struct armada_plane_work *work = xchg(&dplane->work, NULL); 249 struct armada_plane_work *work = xchg(&dplane->work, NULL);
217 250
218 /* Handle any pending frame work. */ 251 /* Handle any pending frame work. */
219 if (work) { 252 if (work)
220 work->fn(dcrtc, dplane, work); 253 armada_drm_plane_work_call(dcrtc, work, work->fn);
221 drm_crtc_vblank_put(&dcrtc->crtc);
222 }
223
224 wake_up(&dplane->frame_wait);
225} 254}
226 255
227int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, 256int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
228 struct armada_plane *plane, struct armada_plane_work *work) 257 struct armada_plane_work *work)
229{ 258{
259 struct armada_plane *plane = drm_to_armada_plane(work->plane);
230 int ret; 260 int ret;
231 261
232 ret = drm_crtc_vblank_get(&dcrtc->crtc); 262 ret = drm_crtc_vblank_get(&dcrtc->crtc);
233 if (ret) { 263 if (ret)
234 DRM_ERROR("failed to acquire vblank counter\n");
235 return ret; 264 return ret;
236 }
237 265
238 ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0; 266 ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
239 if (ret) 267 if (ret)
@@ -247,51 +275,60 @@ int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
247 return wait_event_timeout(plane->frame_wait, !plane->work, timeout); 275 return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
248} 276}
249 277
250struct armada_plane_work *armada_drm_plane_work_cancel( 278void armada_drm_plane_work_cancel(struct armada_crtc *dcrtc,
251 struct armada_crtc *dcrtc, struct armada_plane *plane) 279 struct armada_plane *dplane)
252{ 280{
253 struct armada_plane_work *work = xchg(&plane->work, NULL); 281 struct armada_plane_work *work = xchg(&dplane->work, NULL);
254 282
255 if (work) 283 if (work)
256 drm_crtc_vblank_put(&dcrtc->crtc); 284 armada_drm_plane_work_call(dcrtc, work, work->cancel);
257
258 return work;
259} 285}
260 286
261static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc, 287static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
262 struct armada_frame_work *work) 288 struct armada_plane_work *work)
263{ 289{
264 struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary); 290 unsigned long flags;
265 291
266 return armada_drm_plane_work_queue(dcrtc, plane, &work->work); 292 spin_lock_irqsave(&dcrtc->irq_lock, flags);
293 armada_drm_crtc_update_regs(dcrtc, work->regs);
294 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
267} 295}
268 296
269static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc, 297static void armada_drm_crtc_complete_disable_work(struct armada_crtc *dcrtc,
270 struct armada_plane *plane, struct armada_plane_work *work) 298 struct armada_plane_work *work)
271{ 299{
272 struct armada_frame_work *fwork = container_of(work, struct armada_frame_work, work);
273 struct drm_device *dev = dcrtc->crtc.dev;
274 unsigned long flags; 300 unsigned long flags;
275 301
302 if (dcrtc->plane == work->plane)
303 dcrtc->plane = NULL;
304
276 spin_lock_irqsave(&dcrtc->irq_lock, flags); 305 spin_lock_irqsave(&dcrtc->irq_lock, flags);
277 armada_drm_crtc_update_regs(dcrtc, fwork->regs); 306 armada_drm_crtc_update_regs(dcrtc, work->regs);
278 spin_unlock_irqrestore(&dcrtc->irq_lock, flags); 307 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
308}
279 309
280 if (fwork->event) { 310static struct armada_plane_work *
281 spin_lock_irqsave(&dev->event_lock, flags); 311armada_drm_crtc_alloc_plane_work(struct drm_plane *plane)
282 drm_crtc_send_vblank_event(&dcrtc->crtc, fwork->event); 312{
283 spin_unlock_irqrestore(&dev->event_lock, flags); 313 struct armada_plane_work *work;
284 } 314 int i = 0;
315
316 work = kzalloc(sizeof(*work), GFP_KERNEL);
317 if (!work)
318 return NULL;
285 319
286 /* Finally, queue the process-half of the cleanup. */ 320 work->plane = plane;
287 __armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb); 321 work->fn = armada_drm_crtc_complete_frame_work;
288 kfree(fwork); 322 work->need_kfree = true;
323 armada_reg_queue_end(work->regs, i);
324
325 return work;
289} 326}
290 327
291static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, 328static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
292 struct drm_framebuffer *fb, bool force) 329 struct drm_framebuffer *fb, bool force)
293{ 330{
294 struct armada_frame_work *work; 331 struct armada_plane_work *work;
295 332
296 if (!fb) 333 if (!fb)
297 return; 334 return;
@@ -302,15 +339,11 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
302 return; 339 return;
303 } 340 }
304 341
305 work = kmalloc(sizeof(*work), GFP_KERNEL); 342 work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary);
306 if (work) { 343 if (work) {
307 int i = 0;
308 work->work.fn = armada_drm_crtc_complete_frame_work;
309 work->event = NULL;
310 work->old_fb = fb; 344 work->old_fb = fb;
311 armada_reg_queue_end(work->regs, i);
312 345
313 if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0) 346 if (armada_drm_plane_work_queue(dcrtc, work) == 0)
314 return; 347 return;
315 348
316 kfree(work); 349 kfree(work);
@@ -373,8 +406,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc)
373 * the new mode parameters. 406 * the new mode parameters.
374 */ 407 */
375 plane = dcrtc->plane; 408 plane = dcrtc->plane;
376 if (plane) 409 if (plane) {
377 drm_plane_force_disable(plane); 410 drm_plane_force_disable(plane);
411 WARN_ON(!armada_drm_plane_work_wait(drm_to_armada_plane(plane),
412 HZ));
413 }
378} 414}
379 415
380/* The mode_config.mutex will be held for this call */ 416/* The mode_config.mutex will be held for this call */
@@ -440,11 +476,11 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
440 if (stat & VSYNC_IRQ) 476 if (stat & VSYNC_IRQ)
441 drm_crtc_handle_vblank(&dcrtc->crtc); 477 drm_crtc_handle_vblank(&dcrtc->crtc);
442 478
443 spin_lock(&dcrtc->irq_lock);
444 ovl_plane = dcrtc->plane; 479 ovl_plane = dcrtc->plane;
445 if (ovl_plane) 480 if (ovl_plane)
446 armada_drm_plane_work_run(dcrtc, ovl_plane); 481 armada_drm_plane_work_run(dcrtc, ovl_plane);
447 482
483 spin_lock(&dcrtc->irq_lock);
448 if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { 484 if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
449 int i = stat & GRA_FRAME_IRQ0 ? 0 : 1; 485 int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
450 uint32_t val; 486 uint32_t val;
@@ -536,18 +572,14 @@ static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
536 return val; 572 return val;
537} 573}
538 574
539static void armada_drm_primary_set(struct drm_crtc *crtc, 575static void armada_drm_gra_plane_regs(struct armada_regs *regs,
540 struct drm_plane *plane, int x, int y) 576 struct drm_framebuffer *fb, struct armada_plane_state *state,
577 int x, int y, bool interlaced)
541{ 578{
542 struct armada_plane_state *state = &drm_to_armada_plane(plane)->state; 579 unsigned int i;
543 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
544 struct armada_regs regs[8];
545 bool interlaced = dcrtc->interlaced;
546 unsigned i;
547 u32 ctrl0; 580 u32 ctrl0;
548 581
549 i = armada_drm_crtc_calc_fb(plane->fb, x, y, regs, interlaced); 582 i = armada_drm_crtc_calc_fb(fb, x, y, regs, interlaced);
550
551 armada_reg_queue_set(regs, i, state->dst_yx, LCD_SPU_GRA_OVSA_HPXL_VLN); 583 armada_reg_queue_set(regs, i, state->dst_yx, LCD_SPU_GRA_OVSA_HPXL_VLN);
552 armada_reg_queue_set(regs, i, state->src_hw, LCD_SPU_GRA_HPXL_VLN); 584 armada_reg_queue_set(regs, i, state->src_hw, LCD_SPU_GRA_HPXL_VLN);
553 armada_reg_queue_set(regs, i, state->dst_hw, LCD_SPU_GZM_HPXL_VLN); 585 armada_reg_queue_set(regs, i, state->dst_hw, LCD_SPU_GZM_HPXL_VLN);
@@ -559,9 +591,21 @@ static void armada_drm_primary_set(struct drm_crtc *crtc,
559 armada_reg_queue_mod(regs, i, ctrl0, CFG_GRAFORMAT | 591 armada_reg_queue_mod(regs, i, ctrl0, CFG_GRAFORMAT |
560 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | 592 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
561 CFG_SWAPYU | CFG_YUV2RGB) | 593 CFG_SWAPYU | CFG_YUV2RGB) |
562 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE, 594 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
595 CFG_GRA_HSMOOTH | CFG_GRA_ENA,
563 LCD_SPU_DMA_CTRL0); 596 LCD_SPU_DMA_CTRL0);
564 armada_reg_queue_end(regs, i); 597 armada_reg_queue_end(regs, i);
598}
599
600static void armada_drm_primary_set(struct drm_crtc *crtc,
601 struct drm_plane *plane, int x, int y)
602{
603 struct armada_plane_state *state = &drm_to_armada_plane(plane)->state;
604 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
605 struct armada_regs regs[8];
606 bool interlaced = dcrtc->interlaced;
607
608 armada_drm_gra_plane_regs(regs, plane->fb, state, x, y, interlaced);
565 armada_drm_crtc_update_regs(dcrtc, regs); 609 armada_drm_crtc_update_regs(dcrtc, regs);
566} 610}
567 611
@@ -581,7 +625,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
581 625
582 interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); 626 interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
583 627
584 val = CFG_GRA_ENA | CFG_GRA_HSMOOTH; 628 val = CFG_GRA_ENA;
585 val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt); 629 val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt);
586 val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->mod); 630 val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->mod);
587 631
@@ -633,8 +677,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
633 /* Now compute the divider for real */ 677 /* Now compute the divider for real */
634 dcrtc->variant->compute_clock(dcrtc, adj, &sclk); 678 dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
635 679
636 /* Ensure graphic fifo is enabled */
637 armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
638 armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV); 680 armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV);
639 681
640 if (interlaced ^ dcrtc->interlaced) { 682 if (interlaced ^ dcrtc->interlaced) {
@@ -647,6 +689,9 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
647 689
648 spin_lock_irqsave(&dcrtc->irq_lock, flags); 690 spin_lock_irqsave(&dcrtc->irq_lock, flags);
649 691
692 /* Ensure graphic fifo is enabled */
693 armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
694
650 /* Even interlaced/progressive frame */ 695 /* Even interlaced/progressive frame */
651 dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 | 696 dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
652 adj->crtc_htotal; 697 adj->crtc_htotal;
@@ -729,47 +774,13 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
729 return 0; 774 return 0;
730} 775}
731 776
732void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
733 struct drm_plane *plane)
734{
735 u32 sram_para1, dma_ctrl0_mask;
736
737 /*
738 * Drop our reference on any framebuffer attached to this plane.
739 * We don't need to NULL this out as drm_plane_force_disable(),
740 * and __setplane_internal() will do so for an overlay plane, and
741 * __drm_helper_disable_unused_functions() will do so for the
742 * primary plane.
743 */
744 if (plane->fb)
745 drm_framebuffer_put(plane->fb);
746
747 /* Power down the Y/U/V FIFOs */
748 sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
749
750 /* Power down most RAMs and FIFOs if this is the primary plane */
751 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
752 sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
753 CFG_PDWN32x32 | CFG_PDWN64x66;
754 dma_ctrl0_mask = CFG_GRA_ENA;
755 } else {
756 dma_ctrl0_mask = CFG_DMA_ENA;
757 }
758
759 spin_lock_irq(&dcrtc->irq_lock);
760 armada_updatel(0, dma_ctrl0_mask, dcrtc->base + LCD_SPU_DMA_CTRL0);
761 spin_unlock_irq(&dcrtc->irq_lock);
762
763 armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1);
764}
765
766/* The mode_config.mutex will be held for this call */ 777/* The mode_config.mutex will be held for this call */
767static void armada_drm_crtc_disable(struct drm_crtc *crtc) 778static void armada_drm_crtc_disable(struct drm_crtc *crtc)
768{ 779{
769 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
770
771 armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 780 armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
772 armada_drm_crtc_plane_disable(dcrtc, crtc->primary); 781
782 /* Disable our primary plane when we disable the CRTC. */
783 crtc->primary->funcs->disable_plane(crtc->primary, NULL);
773} 784}
774 785
775static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { 786static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
@@ -879,9 +890,11 @@ static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
879 return 0; 890 return 0;
880 } 891 }
881 892
893 spin_lock_irq(&dcrtc->irq_lock);
882 para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1); 894 para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1);
883 armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32, 895 armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32,
884 dcrtc->base + LCD_SPU_SRAM_PARA1); 896 dcrtc->base + LCD_SPU_SRAM_PARA1);
897 spin_unlock_irq(&dcrtc->irq_lock);
885 898
886 /* 899 /*
887 * Initialize the transparency if the SRAM was powered down. 900 * Initialize the transparency if the SRAM was powered down.
@@ -1021,7 +1034,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
1021 struct drm_modeset_acquire_ctx *ctx) 1034 struct drm_modeset_acquire_ctx *ctx)
1022{ 1035{
1023 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 1036 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
1024 struct armada_frame_work *work; 1037 struct armada_plane_work *work;
1025 unsigned i; 1038 unsigned i;
1026 int ret; 1039 int ret;
1027 1040
@@ -1029,11 +1042,10 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
1029 if (fb->format != crtc->primary->fb->format) 1042 if (fb->format != crtc->primary->fb->format)
1030 return -EINVAL; 1043 return -EINVAL;
1031 1044
1032 work = kmalloc(sizeof(*work), GFP_KERNEL); 1045 work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary);
1033 if (!work) 1046 if (!work)
1034 return -ENOMEM; 1047 return -ENOMEM;
1035 1048
1036 work->work.fn = armada_drm_crtc_complete_frame_work;
1037 work->event = event; 1049 work->event = event;
1038 work->old_fb = dcrtc->crtc.primary->fb; 1050 work->old_fb = dcrtc->crtc.primary->fb;
1039 1051
@@ -1047,7 +1059,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
1047 */ 1059 */
1048 drm_framebuffer_get(fb); 1060 drm_framebuffer_get(fb);
1049 1061
1050 ret = armada_drm_crtc_queue_frame_work(dcrtc, work); 1062 ret = armada_drm_plane_work_queue(dcrtc, work);
1051 if (ret) { 1063 if (ret) {
1052 /* Undo our reference above */ 1064 /* Undo our reference above */
1053 drm_framebuffer_put(fb); 1065 drm_framebuffer_put(fb);
@@ -1127,14 +1139,195 @@ static const struct drm_crtc_funcs armada_crtc_funcs = {
1127 .disable_vblank = armada_drm_crtc_disable_vblank, 1139 .disable_vblank = armada_drm_crtc_disable_vblank,
1128}; 1140};
1129 1141
1142static void armada_drm_primary_update_state(struct drm_plane_state *state,
1143 struct armada_regs *regs)
1144{
1145 struct armada_plane *dplane = drm_to_armada_plane(state->plane);
1146 struct armada_crtc *dcrtc = drm_to_armada_crtc(state->crtc);
1147 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb);
1148 bool was_disabled;
1149 unsigned int idx = 0;
1150 u32 val;
1151
1152 val = CFG_GRA_FMT(dfb->fmt) | CFG_GRA_MOD(dfb->mod);
1153 if (dfb->fmt > CFG_420)
1154 val |= CFG_PALETTE_ENA;
1155 if (state->visible)
1156 val |= CFG_GRA_ENA;
1157 if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst))
1158 val |= CFG_GRA_HSMOOTH;
1159
1160 was_disabled = !(dplane->state.ctrl0 & CFG_GRA_ENA);
1161 if (was_disabled)
1162 armada_reg_queue_mod(regs, idx,
1163 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
1164
1165 dplane->state.ctrl0 = val;
1166 dplane->state.src_hw = (drm_rect_height(&state->src) & 0xffff0000) |
1167 drm_rect_width(&state->src) >> 16;
1168 dplane->state.dst_hw = drm_rect_height(&state->dst) << 16 |
1169 drm_rect_width(&state->dst);
1170 dplane->state.dst_yx = state->dst.y1 << 16 | state->dst.x1;
1171
1172 armada_drm_gra_plane_regs(regs + idx, &dfb->fb, &dplane->state,
1173 state->src.x1 >> 16, state->src.y1 >> 16,
1174 dcrtc->interlaced);
1175
1176 dplane->state.vsync_update = !was_disabled;
1177 dplane->state.changed = true;
1178}
1179
1180static int armada_drm_primary_update(struct drm_plane *plane,
1181 struct drm_crtc *crtc, struct drm_framebuffer *fb,
1182 int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h,
1183 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
1184 struct drm_modeset_acquire_ctx *ctx)
1185{
1186 struct armada_plane *dplane = drm_to_armada_plane(plane);
1187 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
1188 struct armada_plane_work *work;
1189 struct drm_plane_state state = {
1190 .plane = plane,
1191 .crtc = crtc,
1192 .fb = fb,
1193 .src_x = src_x,
1194 .src_y = src_y,
1195 .src_w = src_w,
1196 .src_h = src_h,
1197 .crtc_x = crtc_x,
1198 .crtc_y = crtc_y,
1199 .crtc_w = crtc_w,
1200 .crtc_h = crtc_h,
1201 .rotation = DRM_MODE_ROTATE_0,
1202 };
1203 const struct drm_rect clip = {
1204 .x2 = crtc->mode.hdisplay,
1205 .y2 = crtc->mode.vdisplay,
1206 };
1207 int ret;
1208
1209 ret = drm_atomic_helper_check_plane_state(&state, crtc->state, &clip, 0,
1210 INT_MAX, true, false);
1211 if (ret)
1212 return ret;
1213
1214 work = &dplane->works[dplane->next_work];
1215 work->fn = armada_drm_crtc_complete_frame_work;
1216
1217 if (plane->fb != fb) {
1218 /*
1219 * Take a reference on the new framebuffer - we want to
1220 * hold on to it while the hardware is displaying it.
1221 */
1222 drm_framebuffer_reference(fb);
1223
1224 work->old_fb = plane->fb;
1225 } else {
1226 work->old_fb = NULL;
1227 }
1228
1229 armada_drm_primary_update_state(&state, work->regs);
1230
1231 if (!dplane->state.changed)
1232 return 0;
1233
1234 /* Wait for pending work to complete */
1235 if (armada_drm_plane_work_wait(dplane, HZ / 10) == 0)
1236 armada_drm_plane_work_cancel(dcrtc, dplane);
1237
1238 if (!dplane->state.vsync_update) {
1239 work->fn(dcrtc, work);
1240 if (work->old_fb)
1241 drm_framebuffer_unreference(work->old_fb);
1242 return 0;
1243 }
1244
1245 /* Queue it for update on the next interrupt if we are enabled */
1246 ret = armada_drm_plane_work_queue(dcrtc, work);
1247 if (ret) {
1248 work->fn(dcrtc, work);
1249 if (work->old_fb)
1250 drm_framebuffer_unreference(work->old_fb);
1251 }
1252
1253 dplane->next_work = !dplane->next_work;
1254
1255 return 0;
1256}
1257
1258int armada_drm_plane_disable(struct drm_plane *plane,
1259 struct drm_modeset_acquire_ctx *ctx)
1260{
1261 struct armada_plane *dplane = drm_to_armada_plane(plane);
1262 struct armada_crtc *dcrtc;
1263 struct armada_plane_work *work;
1264 unsigned int idx = 0;
1265 u32 sram_para1, enable_mask;
1266
1267 if (!plane->crtc)
1268 return 0;
1269
1270 /*
1271 * Arrange to power down most RAMs and FIFOs if this is the primary
1272 * plane, otherwise just the YUV FIFOs for the overlay plane.
1273 */
1274 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
1275 sram_para1 = CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
1276 CFG_PDWN32x32 | CFG_PDWN64x66;
1277 enable_mask = CFG_GRA_ENA;
1278 } else {
1279 sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
1280 enable_mask = CFG_DMA_ENA;
1281 }
1282
1283 dplane->state.ctrl0 &= ~enable_mask;
1284
1285 dcrtc = drm_to_armada_crtc(plane->crtc);
1286
1287 /*
1288 * Try to disable the plane and drop our ref on the framebuffer
1289 * at the next frame update. If we fail for any reason, disable
1290 * the plane immediately.
1291 */
1292 work = &dplane->works[dplane->next_work];
1293 work->fn = armada_drm_crtc_complete_disable_work;
1294 work->cancel = armada_drm_crtc_complete_disable_work;
1295 work->old_fb = plane->fb;
1296
1297 armada_reg_queue_mod(work->regs, idx,
1298 0, enable_mask, LCD_SPU_DMA_CTRL0);
1299 armada_reg_queue_mod(work->regs, idx,
1300 sram_para1, 0, LCD_SPU_SRAM_PARA1);
1301 armada_reg_queue_end(work->regs, idx);
1302
1303 /* Wait for any preceding work to complete, but don't wedge */
1304 if (WARN_ON(!armada_drm_plane_work_wait(dplane, HZ)))
1305 armada_drm_plane_work_cancel(dcrtc, dplane);
1306
1307 if (armada_drm_plane_work_queue(dcrtc, work)) {
1308 work->fn(dcrtc, work);
1309 if (work->old_fb)
1310 drm_framebuffer_unreference(work->old_fb);
1311 }
1312
1313 dplane->next_work = !dplane->next_work;
1314
1315 return 0;
1316}
1317
1130static const struct drm_plane_funcs armada_primary_plane_funcs = { 1318static const struct drm_plane_funcs armada_primary_plane_funcs = {
1131 .update_plane = drm_primary_helper_update, 1319 .update_plane = armada_drm_primary_update,
1132 .disable_plane = drm_primary_helper_disable, 1320 .disable_plane = armada_drm_plane_disable,
1133 .destroy = drm_primary_helper_destroy, 1321 .destroy = drm_primary_helper_destroy,
1134}; 1322};
1135 1323
1136int armada_drm_plane_init(struct armada_plane *plane) 1324int armada_drm_plane_init(struct armada_plane *plane)
1137{ 1325{
1326 unsigned int i;
1327
1328 for (i = 0; i < ARRAY_SIZE(plane->works); i++)
1329 plane->works[i].plane = &plane->base;
1330
1138 init_waitqueue_head(&plane->frame_wait); 1331 init_waitqueue_head(&plane->frame_wait);
1139 1332
1140 return 0; 1333 return 0;
@@ -1225,17 +1418,13 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
1225 1418
1226 ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc", 1419 ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
1227 dcrtc); 1420 dcrtc);
1228 if (ret < 0) { 1421 if (ret < 0)
1229 kfree(dcrtc); 1422 goto err_crtc;
1230 return ret;
1231 }
1232 1423
1233 if (dcrtc->variant->init) { 1424 if (dcrtc->variant->init) {
1234 ret = dcrtc->variant->init(dcrtc, dev); 1425 ret = dcrtc->variant->init(dcrtc, dev);
1235 if (ret) { 1426 if (ret)
1236 kfree(dcrtc); 1427 goto err_crtc;
1237 return ret;
1238 }
1239 } 1428 }
1240 1429
1241 /* Ensure AXI pipeline is enabled */ 1430 /* Ensure AXI pipeline is enabled */
@@ -1246,13 +1435,15 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
1246 dcrtc->crtc.port = port; 1435 dcrtc->crtc.port = port;
1247 1436
1248 primary = kzalloc(sizeof(*primary), GFP_KERNEL); 1437 primary = kzalloc(sizeof(*primary), GFP_KERNEL);
1249 if (!primary) 1438 if (!primary) {
1250 return -ENOMEM; 1439 ret = -ENOMEM;
1440 goto err_crtc;
1441 }
1251 1442
1252 ret = armada_drm_plane_init(primary); 1443 ret = armada_drm_plane_init(primary);
1253 if (ret) { 1444 if (ret) {
1254 kfree(primary); 1445 kfree(primary);
1255 return ret; 1446 goto err_crtc;
1256 } 1447 }
1257 1448
1258 ret = drm_universal_plane_init(drm, &primary->base, 0, 1449 ret = drm_universal_plane_init(drm, &primary->base, 0,
@@ -1263,7 +1454,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
1263 DRM_PLANE_TYPE_PRIMARY, NULL); 1454 DRM_PLANE_TYPE_PRIMARY, NULL);
1264 if (ret) { 1455 if (ret) {
1265 kfree(primary); 1456 kfree(primary);
1266 return ret; 1457 goto err_crtc;
1267 } 1458 }
1268 1459
1269 ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL, 1460 ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL,
@@ -1282,6 +1473,9 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
1282 1473
1283err_crtc_init: 1474err_crtc_init:
1284 primary->base.funcs->destroy(&primary->base); 1475 primary->base.funcs->destroy(&primary->base);
1476err_crtc:
1477 kfree(dcrtc);
1478
1285 return ret; 1479 return ret;
1286} 1480}
1287 1481
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index bab11f483575..445829b8877a 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -36,21 +36,31 @@ struct armada_plane;
36struct armada_variant; 36struct armada_variant;
37 37
38struct armada_plane_work { 38struct armada_plane_work {
39 void (*fn)(struct armada_crtc *, 39 void (*fn)(struct armada_crtc *, struct armada_plane_work *);
40 struct armada_plane *, 40 void (*cancel)(struct armada_crtc *, struct armada_plane_work *);
41 struct armada_plane_work *); 41 bool need_kfree;
42 struct drm_plane *plane;
43 struct drm_framebuffer *old_fb;
44 struct drm_pending_vblank_event *event;
45 struct armada_regs regs[14];
42}; 46};
43 47
44struct armada_plane_state { 48struct armada_plane_state {
49 u16 src_x;
50 u16 src_y;
45 u32 src_hw; 51 u32 src_hw;
46 u32 dst_hw; 52 u32 dst_hw;
47 u32 dst_yx; 53 u32 dst_yx;
48 u32 ctrl0; 54 u32 ctrl0;
55 bool changed;
56 bool vsync_update;
49}; 57};
50 58
51struct armada_plane { 59struct armada_plane {
52 struct drm_plane base; 60 struct drm_plane base;
53 wait_queue_head_t frame_wait; 61 wait_queue_head_t frame_wait;
62 bool next_work;
63 struct armada_plane_work works[2];
54 struct armada_plane_work *work; 64 struct armada_plane_work *work;
55 struct armada_plane_state state; 65 struct armada_plane_state state;
56}; 66};
@@ -58,10 +68,10 @@ struct armada_plane {
58 68
59int armada_drm_plane_init(struct armada_plane *plane); 69int armada_drm_plane_init(struct armada_plane *plane);
60int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, 70int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
61 struct armada_plane *plane, struct armada_plane_work *work); 71 struct armada_plane_work *work);
62int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout); 72int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout);
63struct armada_plane_work *armada_drm_plane_work_cancel( 73void armada_drm_plane_work_cancel(struct armada_crtc *dcrtc,
64 struct armada_crtc *dcrtc, struct armada_plane *plane); 74 struct armada_plane *plane);
65void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, 75void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb,
66 int x, int y); 76 int x, int y);
67 77
@@ -104,8 +114,8 @@ struct armada_crtc {
104 114
105void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); 115void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
106 116
107void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, 117int armada_drm_plane_disable(struct drm_plane *plane,
108 struct drm_plane *plane); 118 struct drm_modeset_acquire_ctx *ctx);
109 119
110extern struct platform_driver armada_lcd_platform_driver; 120extern struct platform_driver armada_lcd_platform_driver;
111 121
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index b411b608821a..77b55adaa2ac 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -7,7 +7,7 @@
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 */ 8 */
9#include <drm/drmP.h> 9#include <drm/drmP.h>
10#include <drm/drm_plane_helper.h> 10#include <drm/drm_atomic_helper.h>
11#include "armada_crtc.h" 11#include "armada_crtc.h"
12#include "armada_drm.h" 12#include "armada_drm.h"
13#include "armada_fb.h" 13#include "armada_fb.h"
@@ -32,11 +32,6 @@ struct armada_ovl_plane_properties {
32 32
33struct armada_ovl_plane { 33struct armada_ovl_plane {
34 struct armada_plane base; 34 struct armada_plane base;
35 struct drm_framebuffer *old_fb;
36 struct {
37 struct armada_plane_work work;
38 struct armada_regs regs[13];
39 } vbl;
40 struct armada_ovl_plane_properties prop; 35 struct armada_ovl_plane_properties prop;
41}; 36};
42#define drm_to_armada_ovl_plane(p) \ 37#define drm_to_armada_ovl_plane(p) \
@@ -67,218 +62,204 @@ armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
67 spin_unlock_irq(&dcrtc->irq_lock); 62 spin_unlock_irq(&dcrtc->irq_lock);
68} 63}
69 64
70static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
71 struct drm_framebuffer *fb)
72{
73 struct drm_framebuffer *old_fb;
74
75 old_fb = xchg(&dplane->old_fb, fb);
76
77 if (old_fb)
78 armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
79}
80
81/* === Plane support === */ 65/* === Plane support === */
82static void armada_ovl_plane_work(struct armada_crtc *dcrtc, 66static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
83 struct armada_plane *plane, struct armada_plane_work *work) 67 struct armada_plane_work *work)
84{ 68{
85 struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base); 69 unsigned long flags;
86 70
87 trace_armada_ovl_plane_work(&dcrtc->crtc, &plane->base); 71 trace_armada_ovl_plane_work(&dcrtc->crtc, work->plane);
88 72
89 armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); 73 spin_lock_irqsave(&dcrtc->irq_lock, flags);
90 armada_ovl_retire_fb(dplane, NULL); 74 armada_drm_crtc_update_regs(dcrtc, work->regs);
75 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
91} 76}
92 77
93static int 78static void armada_ovl_plane_update_state(struct drm_plane_state *state,
94armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, 79 struct armada_regs *regs)
95 struct drm_framebuffer *fb,
96 int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
97 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
98 struct drm_modeset_acquire_ctx *ctx)
99{ 80{
100 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 81 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(state->plane);
101 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 82 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb);
102 struct drm_rect src = { 83 const struct drm_format_info *format;
103 .x1 = src_x, 84 unsigned int idx = 0;
104 .y1 = src_y, 85 bool fb_changed;
105 .x2 = src_x + src_w, 86 u32 val, ctrl0;
106 .y2 = src_y + src_h, 87 u16 src_x, src_y;
107 }; 88
108 struct drm_rect dest = { 89 ctrl0 = CFG_DMA_FMT(dfb->fmt) | CFG_DMA_MOD(dfb->mod) | CFG_CBSH_ENA;
109 .x1 = crtc_x, 90 if (state->visible)
110 .y1 = crtc_y, 91 ctrl0 |= CFG_DMA_ENA;
111 .x2 = crtc_x + crtc_w, 92 if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst))
112 .y2 = crtc_y + crtc_h, 93 ctrl0 |= CFG_DMA_HSMOOTH;
113 }; 94
114 const struct drm_rect clip = { 95 /*
115 .x2 = crtc->mode.hdisplay, 96 * Shifting a YUV packed format image by one pixel causes the U/V
116 .y2 = crtc->mode.vdisplay, 97 * planes to swap. Compensate for it by also toggling the UV swap.
117 }; 98 */
118 uint32_t val, ctrl0; 99 format = dfb->fb.format;
119 unsigned idx = 0; 100 if (format->num_planes == 1 && state->src.x1 >> 16 & (format->hsub - 1))
120 bool visible; 101 ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);
121 int ret; 102
122 103 if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) {
123 trace_armada_ovl_plane_update(plane, crtc, fb,
124 crtc_x, crtc_y, crtc_w, crtc_h,
125 src_x, src_y, src_w, src_h);
126
127 ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
128 DRM_MODE_ROTATE_0,
129 0, INT_MAX, true, false, &visible);
130 if (ret)
131 return ret;
132
133 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
134 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
135 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
136
137 /* Does the position/size result in nothing to display? */
138 if (!visible)
139 ctrl0 &= ~CFG_DMA_ENA;
140
141 if (!dcrtc->plane) {
142 dcrtc->plane = plane;
143 armada_ovl_update_attr(&dplane->prop, dcrtc);
144 }
145
146 /* FIXME: overlay on an interlaced display */
147 /* Just updating the position/size? */
148 if (plane->fb == fb && dplane->base.state.ctrl0 == ctrl0) {
149 val = (drm_rect_height(&src) & 0xffff0000) |
150 drm_rect_width(&src) >> 16;
151 dplane->base.state.src_hw = val;
152 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
153
154 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
155 dplane->base.state.dst_hw = val;
156 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
157
158 val = dest.y1 << 16 | dest.x1;
159 dplane->base.state.dst_yx = val;
160 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
161
162 return 0;
163 } else if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) {
164 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ 104 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
165 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66, 105 armada_reg_queue_mod(regs, idx,
166 dcrtc->base + LCD_SPU_SRAM_PARA1); 106 0, CFG_PDWN16x66 | CFG_PDWN32x66,
107 LCD_SPU_SRAM_PARA1);
167 } 108 }
168 109
169 if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) 110 fb_changed = dplane->base.base.fb != &dfb->fb ||
170 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 111 dplane->base.state.src_x != state->src.x1 >> 16 ||
171 112 dplane->base.state.src_y != state->src.y1 >> 16;
172 if (plane->fb != fb) {
173 u32 addrs[3], pixel_format;
174 int num_planes, hsub;
175
176 /*
177 * Take a reference on the new framebuffer - we want to
178 * hold on to it while the hardware is displaying it.
179 */
180 drm_framebuffer_get(fb);
181
182 if (plane->fb)
183 armada_ovl_retire_fb(dplane, plane->fb);
184 113
185 src_y = src.y1 >> 16; 114 dplane->base.state.vsync_update = fb_changed;
186 src_x = src.x1 >> 16;
187 115
188 armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y); 116 /* FIXME: overlay on an interlaced display */
117 if (fb_changed) {
118 u32 addrs[3];
189 119
190 pixel_format = fb->format->format; 120 dplane->base.state.src_y = src_y = state->src.y1 >> 16;
191 hsub = drm_format_horz_chroma_subsampling(pixel_format); 121 dplane->base.state.src_x = src_x = state->src.x1 >> 16;
192 num_planes = fb->format->num_planes;
193 122
194 /* 123 armada_drm_plane_calc_addrs(addrs, &dfb->fb, src_x, src_y);
195 * Annoyingly, shifting a YUYV-format image by one pixel
196 * causes the U/V planes to toggle. Toggle the UV swap.
197 * (Unfortunately, this causes momentary colour flickering.)
198 */
199 if (src_x & (hsub - 1) && num_planes == 1)
200 ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);
201 124
202 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0], 125 armada_reg_queue_set(regs, idx, addrs[0],
203 LCD_SPU_DMA_START_ADDR_Y0); 126 LCD_SPU_DMA_START_ADDR_Y0);
204 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1], 127 armada_reg_queue_set(regs, idx, addrs[1],
205 LCD_SPU_DMA_START_ADDR_U0); 128 LCD_SPU_DMA_START_ADDR_U0);
206 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2], 129 armada_reg_queue_set(regs, idx, addrs[2],
207 LCD_SPU_DMA_START_ADDR_V0); 130 LCD_SPU_DMA_START_ADDR_V0);
208 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[0], 131 armada_reg_queue_set(regs, idx, addrs[0],
209 LCD_SPU_DMA_START_ADDR_Y1); 132 LCD_SPU_DMA_START_ADDR_Y1);
210 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[1], 133 armada_reg_queue_set(regs, idx, addrs[1],
211 LCD_SPU_DMA_START_ADDR_U1); 134 LCD_SPU_DMA_START_ADDR_U1);
212 armada_reg_queue_set(dplane->vbl.regs, idx, addrs[2], 135 armada_reg_queue_set(regs, idx, addrs[2],
213 LCD_SPU_DMA_START_ADDR_V1); 136 LCD_SPU_DMA_START_ADDR_V1);
214 137
215 val = fb->pitches[0] << 16 | fb->pitches[0]; 138 val = dfb->fb.pitches[0] << 16 | dfb->fb.pitches[0];
216 armada_reg_queue_set(dplane->vbl.regs, idx, val, 139 armada_reg_queue_set(regs, idx, val,
217 LCD_SPU_DMA_PITCH_YC); 140 LCD_SPU_DMA_PITCH_YC);
218 val = fb->pitches[1] << 16 | fb->pitches[2]; 141 val = dfb->fb.pitches[1] << 16 | dfb->fb.pitches[2];
219 armada_reg_queue_set(dplane->vbl.regs, idx, val, 142 armada_reg_queue_set(regs, idx, val,
220 LCD_SPU_DMA_PITCH_UV); 143 LCD_SPU_DMA_PITCH_UV);
221 } 144 }
222 145
223 val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16; 146 val = (drm_rect_height(&state->src) & 0xffff0000) |
147 drm_rect_width(&state->src) >> 16;
224 if (dplane->base.state.src_hw != val) { 148 if (dplane->base.state.src_hw != val) {
225 dplane->base.state.src_hw = val; 149 dplane->base.state.src_hw = val;
226 armada_reg_queue_set(dplane->vbl.regs, idx, val, 150 armada_reg_queue_set(regs, idx, val,
227 LCD_SPU_DMA_HPXL_VLN); 151 LCD_SPU_DMA_HPXL_VLN);
228 } 152 }
229 153
230 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest); 154 val = drm_rect_height(&state->dst) << 16 | drm_rect_width(&state->dst);
231 if (dplane->base.state.dst_hw != val) { 155 if (dplane->base.state.dst_hw != val) {
232 dplane->base.state.dst_hw = val; 156 dplane->base.state.dst_hw = val;
233 armada_reg_queue_set(dplane->vbl.regs, idx, val, 157 armada_reg_queue_set(regs, idx, val,
234 LCD_SPU_DZM_HPXL_VLN); 158 LCD_SPU_DZM_HPXL_VLN);
235 } 159 }
236 160
237 val = dest.y1 << 16 | dest.x1; 161 val = state->dst.y1 << 16 | state->dst.x1;
238 if (dplane->base.state.dst_yx != val) { 162 if (dplane->base.state.dst_yx != val) {
239 dplane->base.state.dst_yx = val; 163 dplane->base.state.dst_yx = val;
240 armada_reg_queue_set(dplane->vbl.regs, idx, val, 164 armada_reg_queue_set(regs, idx, val,
241 LCD_SPU_DMA_OVSA_HPXL_VLN); 165 LCD_SPU_DMA_OVSA_HPXL_VLN);
242 } 166 }
243 167
244 if (dplane->base.state.ctrl0 != ctrl0) { 168 if (dplane->base.state.ctrl0 != ctrl0) {
245 dplane->base.state.ctrl0 = ctrl0; 169 dplane->base.state.ctrl0 = ctrl0;
246 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0, 170 armada_reg_queue_mod(regs, idx, ctrl0,
247 CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE | 171 CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
248 CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE | 172 CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
249 CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU | 173 CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
250 CFG_YUV2RGB) | CFG_DMA_ENA, 174 CFG_YUV2RGB) | CFG_DMA_ENA,
251 LCD_SPU_DMA_CTRL0); 175 LCD_SPU_DMA_CTRL0);
176 dplane->base.state.vsync_update = true;
252 } 177 }
253 if (idx) { 178
254 armada_reg_queue_end(dplane->vbl.regs, idx); 179 dplane->base.state.changed = idx != 0;
255 armada_drm_plane_work_queue(dcrtc, &dplane->base, 180
256 &dplane->vbl.work); 181 armada_reg_queue_end(regs, idx);
257 }
258 return 0;
259} 182}
260 183
261static int armada_ovl_plane_disable(struct drm_plane *plane, 184static int
262 struct drm_modeset_acquire_ctx *ctx) 185armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
186 struct drm_framebuffer *fb,
187 int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
188 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
189 struct drm_modeset_acquire_ctx *ctx)
263{ 190{
264 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); 191 struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
265 struct drm_framebuffer *fb; 192 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
266 struct armada_crtc *dcrtc; 193 struct armada_plane_work *work;
194 struct drm_plane_state state = {
195 .plane = plane,
196 .crtc = crtc,
197 .fb = fb,
198 .src_x = src_x,
199 .src_y = src_y,
200 .src_w = src_w,
201 .src_h = src_h,
202 .crtc_x = crtc_x,
203 .crtc_y = crtc_y,
204 .crtc_w = crtc_w,
205 .crtc_h = crtc_h,
206 .rotation = DRM_MODE_ROTATE_0,
207 };
208 const struct drm_rect clip = {
209 .x2 = crtc->mode.hdisplay,
210 .y2 = crtc->mode.vdisplay,
211 };
212 int ret;
267 213
268 if (!dplane->base.base.crtc) 214 trace_armada_ovl_plane_update(plane, crtc, fb,
215 crtc_x, crtc_y, crtc_w, crtc_h,
216 src_x, src_y, src_w, src_h);
217
218 ret = drm_atomic_helper_check_plane_state(&state, crtc->state, &clip, 0,
219 INT_MAX, true, false);
220 if (ret)
221 return ret;
222
223 work = &dplane->base.works[dplane->base.next_work];
224
225 if (plane->fb != fb) {
226 /*
227 * Take a reference on the new framebuffer - we want to
228 * hold on to it while the hardware is displaying it.
229 */
230 drm_framebuffer_reference(fb);
231
232 work->old_fb = plane->fb;
233 } else {
234 work->old_fb = NULL;
235 }
236
237 armada_ovl_plane_update_state(&state, work->regs);
238
239 if (!dplane->base.state.changed)
269 return 0; 240 return 0;
270 241
271 dcrtc = drm_to_armada_crtc(dplane->base.base.crtc); 242 /* Wait for pending work to complete */
243 if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
244 armada_drm_plane_work_cancel(dcrtc, &dplane->base);
245
246 /* Just updating the position/size? */
247 if (!dplane->base.state.vsync_update) {
248 armada_ovl_plane_work(dcrtc, work);
249 return 0;
250 }
272 251
273 armada_drm_plane_work_cancel(dcrtc, &dplane->base); 252 if (!dcrtc->plane) {
274 armada_drm_crtc_plane_disable(dcrtc, plane); 253 dcrtc->plane = plane;
254 armada_ovl_update_attr(&dplane->prop, dcrtc);
255 }
275 256
276 dcrtc->plane = NULL; 257 /* Queue it for update on the next interrupt if we are enabled */
277 dplane->base.state.ctrl0 = 0; 258 ret = armada_drm_plane_work_queue(dcrtc, work);
259 if (ret)
260 DRM_ERROR("failed to queue plane work: %d\n", ret);
278 261
279 fb = xchg(&dplane->old_fb, NULL); 262 dplane->base.next_work = !dplane->base.next_work;
280 if (fb)
281 drm_framebuffer_put(fb);
282 263
283 return 0; 264 return 0;
284} 265}
@@ -362,7 +343,7 @@ static int armada_ovl_plane_set_property(struct drm_plane *plane,
362 343
363static const struct drm_plane_funcs armada_ovl_plane_funcs = { 344static const struct drm_plane_funcs armada_ovl_plane_funcs = {
364 .update_plane = armada_ovl_plane_update, 345 .update_plane = armada_ovl_plane_update,
365 .disable_plane = armada_ovl_plane_disable, 346 .disable_plane = armada_drm_plane_disable,
366 .destroy = armada_ovl_plane_destroy, 347 .destroy = armada_ovl_plane_destroy,
367 .set_property = armada_ovl_plane_set_property, 348 .set_property = armada_ovl_plane_set_property,
368}; 349};
@@ -454,7 +435,8 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
454 return ret; 435 return ret;
455 } 436 }
456 437
457 dplane->vbl.work.fn = armada_ovl_plane_work; 438 dplane->base.works[0].fn = armada_ovl_plane_work;
439 dplane->base.works[1].fn = armada_ovl_plane_work;
458 440
459 ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs, 441 ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
460 &armada_ovl_plane_funcs, 442 &armada_ovl_plane_funcs,
diff --git a/drivers/gpu/drm/armada/armada_trace.h b/drivers/gpu/drm/armada/armada_trace.h
index 8dbfea7a00fe..f03a56bda596 100644
--- a/drivers/gpu/drm/armada/armada_trace.h
+++ b/drivers/gpu/drm/armada/armada_trace.h
@@ -34,14 +34,34 @@ TRACE_EVENT(armada_ovl_plane_update,
34 __field(struct drm_plane *, plane) 34 __field(struct drm_plane *, plane)
35 __field(struct drm_crtc *, crtc) 35 __field(struct drm_crtc *, crtc)
36 __field(struct drm_framebuffer *, fb) 36 __field(struct drm_framebuffer *, fb)
37 __field(int, crtc_x)
38 __field(int, crtc_y)
39 __field(unsigned int, crtc_w)
40 __field(unsigned int, crtc_h)
41 __field(u32, src_x)
42 __field(u32, src_y)
43 __field(u32, src_w)
44 __field(u32, src_h)
37 ), 45 ),
38 TP_fast_assign( 46 TP_fast_assign(
39 __entry->plane = plane; 47 __entry->plane = plane;
40 __entry->crtc = crtc; 48 __entry->crtc = crtc;
41 __entry->fb = fb; 49 __entry->fb = fb;
50 __entry->crtc_x = crtc_x;
51 __entry->crtc_y = crtc_y;
52 __entry->crtc_w = crtc_w;
53 __entry->crtc_h = crtc_h;
54 __entry->src_x = src_x;
55 __entry->src_y = src_y;
56 __entry->src_w = src_w;
57 __entry->src_h = src_h;
42 ), 58 ),
43 TP_printk("plane %p crtc %p fb %p", 59 TP_printk("plane %p crtc %p fb %p crtc @ (%d,%d, %ux%u) src @ (%u,%u, %ux%u)",
44 __entry->plane, __entry->crtc, __entry->fb) 60 __entry->plane, __entry->crtc, __entry->fb,
61 __entry->crtc_x, __entry->crtc_y,
62 __entry->crtc_w, __entry->crtc_h,
63 __entry->src_x >> 16, __entry->src_y >> 16,
64 __entry->src_w >> 16, __entry->src_h >> 16)
45); 65);
46 66
47TRACE_EVENT(armada_ovl_plane_work, 67TRACE_EVENT(armada_ovl_plane_work,