Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 915e846d authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'imx-drm-next-2016-04-01' of git://git.pengutronix.de/git/pza/linux into drm-fixes

imx-drm: stricter plane parameter checking, dw_hdmi-imx and dmfc fixes

- Check whether plane parameters comply with IPU IDMAC limitations and
  fix planar YUV 4:2:0 U/V offsets and stride
- Cleanup encoder in dw_hdmi-imx bind error path and
  remove a superfluous platform_set_drvdata in dw_hdmi-imx
- DMFC setup fixes: lock the ipu_dmfc_init_channel function against
  concurrent use, rename it to ipu_dmfc_config_wait4eot, and call
  it after the FIFO size has been determined.

* tag 'imx-drm-next-2016-04-01' of git://git.pengutronix.de/git/pza/linux:
  drm/imx: Don't set a gamma table size
  drm/imx: ipuv3-plane: Configure DMFC wait4eot bit after slots are determined
  gpu: ipu-v3: ipu-dmfc: Rename ipu_dmfc_init_channel to ipu_dmfc_config_wait4eot
  gpu: ipu-v3: ipu-dmfc: Make function ipu_dmfc_init_channel() return void
  gpu: ipu-v3: ipu-dmfc: Protect function ipu_dmfc_init_channel() with mutex
  drm/imx: dw_hdmi: Don't call platform_set_drvdata()
  drm/imx: dw_hdmi: Call drm_encoder_cleanup() in error path
  drm/imx: ipuv3-plane: fix planar YUV 4:2:0 support
  drm/imx: ipuv3-plane: Add more thorough checks for plane parameter limitations
  gpu: ipu-cpmem: modify ipu_cpmem_set_yuv_planar_full for better control
parents 541d8f4d e51f17a0
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -225,8 +225,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
	if (!iores)
		return -ENXIO;

	platform_set_drvdata(pdev, hdmi);

	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
	/*
	 * If we failed to find the CRTC(s) which this encoder is
@@ -245,7 +243,16 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
	drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
			 DRM_MODE_ENCODER_TMDS, NULL);

	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
	ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);

	/*
	 * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
	 * which would have called the encoder cleanup.  Do it manually.
	 */
	if (ret)
		drm_encoder_cleanup(encoder);

	return ret;
}

static void dw_hdmi_imx_unbind(struct device *dev, struct device *master,
+0 −10
Original line number Diff line number Diff line
@@ -326,7 +326,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
{
	struct imx_drm_device *imxdrm = drm->dev_private;
	struct imx_drm_crtc *imx_drm_crtc;
	int ret;

	/*
	 * The vblank arrays are dimensioned by MAX_CRTC - we can't
@@ -351,10 +350,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,

	*new_crtc = imx_drm_crtc;

	ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256);
	if (ret)
		goto err_register;

	drm_crtc_helper_add(crtc,
			imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs);

@@ -362,11 +357,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
			imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs, NULL);

	return 0;

err_register:
	imxdrm->crtc[--imxdrm->pipes] = NULL;
	kfree(imx_drm_crtc);
	return ret;
}
EXPORT_SYMBOL_GPL(imx_drm_add_crtc);

+105 −18
Original line number Diff line number Diff line
@@ -72,22 +72,101 @@ static inline int calc_bandwidth(int width, int height, unsigned int vref)
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
		       int x, int y)
{
	struct drm_gem_cma_object *cma_obj;
	unsigned long eba;
	int active;

	cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
	if (!cma_obj) {
		DRM_DEBUG_KMS("entry is null.\n");
	struct drm_gem_cma_object *cma_obj[3];
	unsigned long eba, ubo, vbo;
	int active, i;

	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
		cma_obj[i] = drm_fb_cma_get_gem_obj(fb, i);
		if (!cma_obj[i]) {
			DRM_DEBUG_KMS("plane %d entry is null.\n", i);
			return -EFAULT;
		}
	}

	dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
		&cma_obj->paddr, x, y);

	eba = cma_obj->paddr + fb->offsets[0] +
	eba = cma_obj[0]->paddr + fb->offsets[0] +
	      fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;

	if (eba & 0x7) {
		DRM_DEBUG_KMS("base address must be a multiple of 8.\n");
		return -EINVAL;
	}

	if (fb->pitches[0] < 1 || fb->pitches[0] > 16384) {
		DRM_DEBUG_KMS("pitches out of range.\n");
		return -EINVAL;
	}

	if (ipu_plane->enabled && fb->pitches[0] != ipu_plane->stride[0]) {
		DRM_DEBUG_KMS("pitches must not change while plane is enabled.\n");
		return -EINVAL;
	}

	ipu_plane->stride[0] = fb->pitches[0];

	switch (fb->pixel_format) {
	case DRM_FORMAT_YUV420:
	case DRM_FORMAT_YVU420:
		/*
		 * Multiplanar formats have to meet the following restrictions:
		 * - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
		 * - EBA, UBO and VBO are a multiple of 8
		 * - UBO and VBO are unsigned and not larger than 0xfffff8
		 * - Only EBA may be changed while scanout is active
		 * - The strides of U and V planes must be identical.
		 */
		ubo = cma_obj[1]->paddr + fb->offsets[1] +
		      fb->pitches[1] * y / 2 + x / 2 - eba;
		vbo = cma_obj[2]->paddr + fb->offsets[2] +
		      fb->pitches[2] * y / 2 + x / 2 - eba;

		if ((ubo & 0x7) || (vbo & 0x7)) {
			DRM_DEBUG_KMS("U/V buffer offsets must be a multiple of 8.\n");
			return -EINVAL;
		}

		if ((ubo > 0xfffff8) || (vbo > 0xfffff8)) {
			DRM_DEBUG_KMS("U/V buffer offsets must be positive and not larger than 0xfffff8.\n");
			return -EINVAL;
		}

		if (ipu_plane->enabled && ((ipu_plane->u_offset != ubo) ||
					   (ipu_plane->v_offset != vbo))) {
			DRM_DEBUG_KMS("U/V buffer offsets must not change while plane is enabled.\n");
			return -EINVAL;
		}

		if (fb->pitches[1] != fb->pitches[2]) {
			DRM_DEBUG_KMS("U/V pitches must be identical.\n");
			return -EINVAL;
		}

		if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) {
			DRM_DEBUG_KMS("U/V pitches out of range.\n");
			return -EINVAL;
		}

		if (ipu_plane->enabled &&
		    (ipu_plane->stride[1] != fb->pitches[1])) {
			DRM_DEBUG_KMS("U/V pitches must not change while plane is enabled.\n");
			return -EINVAL;
		}

		ipu_plane->u_offset = ubo;
		ipu_plane->v_offset = vbo;
		ipu_plane->stride[1] = fb->pitches[1];

		dev_dbg(ipu_plane->base.dev->dev,
			"phys = %pad %pad %pad, x = %d, y = %d",
			&cma_obj[0]->paddr, &cma_obj[1]->paddr,
			&cma_obj[2]->paddr, x, y);
		break;
	default:
		dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
			&cma_obj[0]->paddr, x, y);
		break;
	}

	if (ipu_plane->enabled) {
		active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
		ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
@@ -201,12 +280,6 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
		}
	}

	ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
	if (ret) {
		dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
		return ret;
	}

	ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
			calc_bandwidth(crtc_w, crtc_h,
				       calc_vref(mode)), 64);
@@ -215,6 +288,8 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
		return ret;
	}

	ipu_dmfc_config_wait4eot(ipu_plane->dmfc, crtc_w);

	ipu_cpmem_zero(ipu_plane->ipu_ch);
	ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
	ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
@@ -233,6 +308,18 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
	if (interlaced)
		ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]);

	if (fb->pixel_format == DRM_FORMAT_YUV420) {
		ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
					      ipu_plane->stride[1],
					      ipu_plane->u_offset,
					      ipu_plane->v_offset);
	} else if (fb->pixel_format == DRM_FORMAT_YVU420) {
		ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
					      ipu_plane->stride[1],
					      ipu_plane->v_offset,
					      ipu_plane->u_offset);
	}

	ipu_plane->w = src_w;
	ipu_plane->h = src_h;

+4 −0
Original line number Diff line number Diff line
@@ -29,6 +29,10 @@ struct ipu_plane {
	int			w;
	int			h;

	unsigned int		u_offset;
	unsigned int		v_offset;
	unsigned int		stride[2];

	bool			enabled;
};

+36 −43
Original line number Diff line number Diff line
@@ -395,60 +395,48 @@ void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format)
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);

void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
				   u32 pixel_format, int stride,
				   int u_offset, int v_offset)
				   unsigned int uv_stride,
				   unsigned int u_offset, unsigned int v_offset)
{
	switch (pixel_format) {
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YUV422P:
		ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
	ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1);
	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8);
		break;
	case V4L2_PIX_FMT_YVU420:
		ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
		ipu_ch_param_write_field(ch, IPU_FIELD_UBO, v_offset / 8);
		ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV16:
		ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, stride - 1);
		ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
		ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
		break;
	}
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);

void ipu_cpmem_set_yuv_planar(struct ipuv3_channel *ch,
			      u32 pixel_format, int stride, int height)
{
	int u_offset, v_offset;
	int fourcc, u_offset, v_offset;
	int uv_stride = 0;

	switch (pixel_format) {
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
	fourcc = v4l2_pix_fmt_to_drm_fourcc(pixel_format);
	switch (fourcc) {
	case DRM_FORMAT_YUV420:
		uv_stride = stride / 2;
		u_offset = stride * height;
		v_offset = u_offset + (uv_stride * height / 2);
		ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_YUV422P:
	case DRM_FORMAT_YVU420:
		uv_stride = stride / 2;
		v_offset = stride * height;
		u_offset = v_offset + (uv_stride * height / 2);
		break;
	case DRM_FORMAT_YUV422:
		uv_stride = stride / 2;
		u_offset = stride * height;
		v_offset = u_offset + (uv_stride * height);
		ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV16:
	case DRM_FORMAT_NV12:
	case DRM_FORMAT_NV16:
		uv_stride = stride;
		u_offset = stride * height;
		ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
					      u_offset, 0);
		v_offset = 0;
		break;
	default:
		return;
	}
	ipu_cpmem_set_yuv_planar_full(ch, uv_stride, u_offset, v_offset);
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);

@@ -684,17 +672,25 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)

	switch (pix->pixelformat) {
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
		offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
		u_offset = U_OFFSET(pix, image->rect.left,
				    image->rect.top) - offset;
		v_offset = V_OFFSET(pix, image->rect.left,
				    image->rect.top) - offset;

		ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
					      pix->bytesperline,
		ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_YVU420:
		offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
		u_offset = U_OFFSET(pix, image->rect.left,
				    image->rect.top) - offset;
		v_offset = V_OFFSET(pix, image->rect.left,
				    image->rect.top) - offset;

		ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2,
					      v_offset, u_offset);
		break;
	case V4L2_PIX_FMT_YUV422P:
		offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
		u_offset = U2_OFFSET(pix, image->rect.left,
@@ -702,8 +698,7 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
		v_offset = V2_OFFSET(pix, image->rect.left,
				     image->rect.top) - offset;

		ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
					      pix->bytesperline,
		ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_NV12:
@@ -712,8 +707,7 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
				     image->rect.top) - offset;
		v_offset = 0;

		ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
					      pix->bytesperline,
		ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_NV16:
@@ -722,8 +716,7 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
				      image->rect.top) - offset;
		v_offset = 0;

		ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
					      pix->bytesperline,
		ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline,
					      u_offset, v_offset);
		break;
	case V4L2_PIX_FMT_UYVY:
Loading