Commit 8f6644ca authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'drm-armada-fixes' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into drm-fixes

A range of fixes for the Armada DRM driver:
- A missing wakeup could result in overlay frames being delayed, causing
  video playback to hiccup.
- Avoid unmapping a dma-buf attachment which was never mapped
- Fix the overlay when partially off the screen by switching to the
  drm_plane_helper_check_update() helper and using the calculated
  coordinates to set the start address.
- Remove an incorrect assignment to crtc->mode - which should be the
  unadjusted mode.
- Fix a missing call to drm_plane_cleanup() in the overlay code.

* 'drm-armada-fixes' of git://ftp.arm.linux.org.uk/~rmk/linux-arm:
  drm/armada: avoid saving the adjusted mode to crtc->mode
  drm/armada: fix overlay when partially off-screen
  drm/armada: convert overlay to use drm_plane_helper_check_update()
  drm/armada: fix gem object free after failed prime import
  drm/armada: fix incorrect overlay plane cleanup
  drm/armada: fix missing overlay wake-up
parents 61f2669f c62ff6b3
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -531,8 +531,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,

	drm_crtc_vblank_off(crtc);

	crtc->mode = *adj;

	val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA;
	if (val != dcrtc->dumb_ctrl) {
		dcrtc->dumb_ctrl = val;
+3 −2
Original line number Diff line number Diff line
@@ -69,8 +69,9 @@ void armada_gem_free_object(struct drm_gem_object *obj)

	if (dobj->obj.import_attach) {
		/* We only ever display imported data */
		dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
					 DMA_TO_DEVICE);
		if (dobj->sgt)
			dma_buf_unmap_attachment(dobj->obj.import_attach,
						 dobj->sgt, DMA_TO_DEVICE);
		drm_prime_gem_destroy(&dobj->obj, NULL);
	}

+75 −46
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
 * published by the Free Software Foundation.
 */
#include <drm/drmP.h>
#include <drm/drm_plane_helper.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
@@ -85,16 +86,8 @@ static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)

	if (fb)
		armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
}

static unsigned armada_limit(int start, unsigned size, unsigned max)
{
	int end = start + size;
	if (end < 0)
		return 0;
	if (start < 0)
		start = 0;
	return (unsigned)end > max ? max - start : end - start;
	wake_up(&dplane->vbl.wait);
}

static int
@@ -105,26 +98,39 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
{
	struct armada_plane *dplane = drm_to_armada_plane(plane);
	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
	struct drm_rect src = {
		.x1 = src_x,
		.y1 = src_y,
		.x2 = src_x + src_w,
		.y2 = src_y + src_h,
	};
	struct drm_rect dest = {
		.x1 = crtc_x,
		.y1 = crtc_y,
		.x2 = crtc_x + crtc_w,
		.y2 = crtc_y + crtc_h,
	};
	const struct drm_rect clip = {
		.x2 = crtc->mode.hdisplay,
		.y2 = crtc->mode.vdisplay,
	};
	uint32_t val, ctrl0;
	unsigned idx = 0;
	bool visible;
	int ret;

	crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
	crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
	ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
					    0, INT_MAX, true, false, &visible);
	if (ret)
		return ret;

	ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
		CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
		CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;

	/* Does the position/size result in nothing to display? */
	if (crtc_w == 0 || crtc_h == 0) {
	if (!visible)
		ctrl0 &= ~CFG_DMA_ENA;
	}

	/*
	 * FIXME: if the starting point is off screen, we need to
	 * adjust src_x, src_y, src_w, src_h appropriately, and
	 * according to the scale.
	 */

	if (!dcrtc->plane) {
		dcrtc->plane = plane;
@@ -134,15 +140,19 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
	/* FIXME: overlay on an interlaced display */
	/* Just updating the position/size? */
	if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
		val = (src_h & 0xffff0000) | src_w >> 16;
		val = (drm_rect_height(&src) & 0xffff0000) |
		      drm_rect_width(&src) >> 16;
		dplane->src_hw = val;
		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
		val = crtc_h << 16 | crtc_w;

		val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
		dplane->dst_hw = val;
		writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
		val = crtc_y << 16 | crtc_x;

		val = dest.y1 << 16 | dest.x1;
		dplane->dst_yx = val;
		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);

		return 0;
	} else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
		/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
@@ -150,15 +160,14 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
			       dcrtc->base + LCD_SPU_SRAM_PARA1);
	}

	ret = wait_event_timeout(dplane->vbl.wait,
	wait_event_timeout(dplane->vbl.wait,
			   list_empty(&dplane->vbl.update.node),
			   HZ/25);
	if (ret < 0)
		return ret;

	if (plane->fb != fb) {
		struct armada_gem_object *obj = drm_fb_obj(fb);
		uint32_t sy, su, sv;
		uint32_t addr[3], pixel_format;
		int i, num_planes, hsub;

		/*
		 * Take a reference on the new framebuffer - we want to
@@ -178,26 +187,39 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
							    older_fb);
		}

		src_y >>= 16;
		src_x >>= 16;
		sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
			src_x * fb->bits_per_pixel / 8;
		su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
			src_x;
		sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
			src_x;
		src_y = src.y1 >> 16;
		src_x = src.x1 >> 16;

		pixel_format = fb->pixel_format;
		hsub = drm_format_horz_chroma_subsampling(pixel_format);
		num_planes = drm_format_num_planes(pixel_format);

		/*
		 * Annoyingly, shifting a YUYV-format image by one pixel
		 * causes the U/V planes to toggle.  Toggle the UV swap.
		 * (Unfortunately, this causes momentary colour flickering.)
		 */
		if (src_x & (hsub - 1) && num_planes == 1)
			ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);

		for (i = 0; i < num_planes; i++)
			addr[i] = obj->dev_addr + fb->offsets[i] +
				  src_y * fb->pitches[i] +
				  src_x * drm_format_plane_cpp(pixel_format, i);
		for (; i < ARRAY_SIZE(addr); i++)
			addr[i] = 0;

		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
				     LCD_SPU_DMA_START_ADDR_Y0);
		armada_reg_queue_set(dplane->vbl.regs, idx, su,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
				     LCD_SPU_DMA_START_ADDR_U0);
		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
				     LCD_SPU_DMA_START_ADDR_V0);
		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
				     LCD_SPU_DMA_START_ADDR_Y1);
		armada_reg_queue_set(dplane->vbl.regs, idx, su,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
				     LCD_SPU_DMA_START_ADDR_U1);
		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
		armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
				     LCD_SPU_DMA_START_ADDR_V1);

		val = fb->pitches[0] << 16 | fb->pitches[0];
@@ -208,24 +230,27 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
				     LCD_SPU_DMA_PITCH_UV);
	}

	val = (src_h & 0xffff0000) | src_w >> 16;
	val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16;
	if (dplane->src_hw != val) {
		dplane->src_hw = val;
		armada_reg_queue_set(dplane->vbl.regs, idx, val,
				     LCD_SPU_DMA_HPXL_VLN);
	}
	val = crtc_h << 16 | crtc_w;

	val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
	if (dplane->dst_hw != val) {
		dplane->dst_hw = val;
		armada_reg_queue_set(dplane->vbl.regs, idx, val,
				     LCD_SPU_DZM_HPXL_VLN);
	}
	val = crtc_y << 16 | crtc_x;

	val = dest.y1 << 16 | dest.x1;
	if (dplane->dst_yx != val) {
		dplane->dst_yx = val;
		armada_reg_queue_set(dplane->vbl.regs, idx, val,
				     LCD_SPU_DMA_OVSA_HPXL_VLN);
	}

	if (dplane->ctrl0 != ctrl0) {
		dplane->ctrl0 = ctrl0;
		armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
@@ -279,7 +304,11 @@ static int armada_plane_disable(struct drm_plane *plane)

static void armada_plane_destroy(struct drm_plane *plane)
{
	kfree(plane);
	struct armada_plane *dplane = drm_to_armada_plane(plane);

	drm_plane_cleanup(plane);

	kfree(dplane);
}

static int armada_plane_set_property(struct drm_plane *plane,