Commit c6193dc5 authored by Yannick Fertre's avatar Yannick Fertre Committed by Philippe Cornu
Browse files

drm/stm: ltdc: add support of horizontal & vertical mirroring



Support of vertical & horizontal mirroring features thanks to
the plane rotation property.

Signed-off-by: default avatarYannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: default avatarPhilippe Cornu <philippe.cornu@foss.st.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220603134547.593790-1-yannick.fertre@foss.st.com
parent 62467fcc
Loading
Loading
Loading
Loading
+107 −56
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@
#define LXCR_LEN	BIT(0)		/* Layer ENable */
#define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
#define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
#define LXCR_HMEN	BIT(8)		/* Horizontal Mirroring ENable */

#define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
#define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
@@ -200,7 +201,7 @@
#define LXBFCR_BOR	GENMASK(18, 16) /* Blending ORder */

#define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
#define LXCFBLR_CFBP	GENMASK(28, 16)	/* Color Frame Buffer Pitch in bytes */
#define LXCFBLR_CFBP	GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */

#define LXCFBLNR_CFBLN	GENMASK(10, 0)	/* Color Frame Buffer Line Number */

@@ -1240,7 +1241,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
	u32 y0 = newstate->crtc_y;
	u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
	u32 src_x, src_y, src_w, src_h;
	u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
	u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
	u32 paddr, paddr1, paddr2;
	enum ltdc_pix_fmt pf;

	if (!newstate->crtc || !fb) {
@@ -1292,13 +1294,6 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
	}
	regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);

	/* Configures the color frame buffer pitch in bytes & line length */
	pitch_in_bytes = fb->pitches[0];
	line_length = fb->format->cpp[0] *
		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
	val = ((pitch_in_bytes << 16) | line_length);
	regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);

	/* Specifies the constant alpha value */
	val = newstate->alpha >> 8;
	regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
@@ -1322,74 +1317,113 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
				  LXBFCR_BF2 | LXBFCR_BF1, val);
	}

	/* Configures the frame buffer line number */
	line_number = y1 - y0 + 1;
	regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);

	/* Sets the FB address */
	paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);

	if (newstate->rotation & DRM_MODE_REFLECT_X)
		paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;

	if (newstate->rotation & DRM_MODE_REFLECT_Y)
		paddr += (fb->pitches[0] * (y1 - y0));

	DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
	regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);

	/* Configures the color frame buffer pitch in bytes & line length */
	line_length = fb->format->cpp[0] *
		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;

	if (newstate->rotation & DRM_MODE_REFLECT_Y)
		/* Compute negative value (signed on 16 bits) for the picth */
		pitch_in_bytes = 0x10000 - fb->pitches[0];
	else
		pitch_in_bytes = fb->pitches[0];

	val = (pitch_in_bytes << 16) | line_length;
	regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);

	/* Configures the frame buffer line number */
	line_number = y1 - y0 + 1;
	regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);

	if (ldev->caps.ycbcr_input) {
		if (fb->format->is_yuv) {
			switch (fb->format->format) {
			case DRM_FORMAT_NV12:
			case DRM_FORMAT_NV21:
			/* Configure the auxiliary frame buffer address 0 & 1 */
			paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
			/* Configure the auxiliary frame buffer address 0 */
			paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);

			/* Configure the buffer length */
			val = ((pitch_in_bytes << 16) | line_length);
			regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
			if (newstate->rotation & DRM_MODE_REFLECT_X)
				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;

			/* Configure the frame buffer line number */
			val = (line_number >> 1);
			regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
			if (newstate->rotation & DRM_MODE_REFLECT_Y)
				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;

			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
			break;
			case DRM_FORMAT_YUV420:
			/* Configure the auxiliary frame buffer address 0 */
			paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);

			/* Configure the auxiliary frame buffer address 1 */
			paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
			/* Configure the auxiliary frame buffer address 0 & 1 */
			paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
			paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);

			line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
				      (ldev->caps.bus_width >> 3) - 1;
			if (newstate->rotation & DRM_MODE_REFLECT_X) {
				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
			}

			/* Configure the buffer length */
			val = (((pitch_in_bytes >> 1) << 16) | line_length);
			regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
			}

			/* Configure the frame buffer line number */
			val = (line_number >> 1);
			regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
			break;
			case DRM_FORMAT_YVU420:
			/* Configure the auxiliary frame buffer address 0 */
			paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
			/* Configure the auxiliary frame buffer address 0 & 1 */
			paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
			paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);

			if (newstate->rotation & DRM_MODE_REFLECT_X) {
				paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
				paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
			}

			if (newstate->rotation & DRM_MODE_REFLECT_Y) {
				paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
				paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
			}

			regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
			break;
			}

			/* Configure the auxiliary frame buffer address 1 */
			paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
			regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
			/*
			 * Set the length and the number of lines of the auxiliary
			 * buffers if the framebuffer contains more than one plane.
			 */
			if (fb->format->num_planes > 1) {
				if (newstate->rotation & DRM_MODE_REFLECT_Y)
					/*
					 * Compute negative value (signed on 16 bits)
					 * for the picth
					 */
					pitch_in_bytes = 0x10000 - fb->pitches[1];
				else
					pitch_in_bytes = fb->pitches[1];

			line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
				line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
					      (ldev->caps.bus_width >> 3) - 1;

			/* Configure the buffer length */
			val = (((pitch_in_bytes >> 1) << 16) | line_length);
				/* Configure the auxiliary buffer length */
				val = (pitch_in_bytes << 16) | line_length;
				regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);

			/* Configure the frame buffer line number */
			val = (line_number >> 1);
				/* Configure the auxiliary frame buffer line number */
				val = line_number >> 1;
				regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
			break;
			}

			/* Configure YCbC conversion coefficient */
@@ -1406,7 +1440,12 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
	/* Enable layer and CLUT if needed */
	val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
	val |= LXCR_LEN;
	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);

	/* Enable horizontal mirroring if requested */
	if (newstate->rotation & DRM_MODE_REFLECT_X)
		val |= LXCR_HMEN;

	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);

	/* Commit shadow registers = update plane at next vblank */
	if (ldev->caps.plane_reg_shadow)
@@ -1435,8 +1474,8 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
	struct ltdc_device *ldev = plane_to_ltdc(plane);
	u32 lofs = plane->index * LAY_OFS;

	/* disable layer */
	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
	/* Disable layer */
	regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN |  LXCR_HMEN, 0);

	/* Commit shadow registers = update plane at next vblank */
	if (ldev->caps.plane_reg_shadow)
@@ -1580,6 +1619,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
{
	struct ltdc_device *ldev = ddev->dev_private;
	struct drm_plane *primary, *overlay;
	int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
	unsigned int i;
	int ret;

@@ -1594,6 +1634,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
	else
		drm_plane_create_zpos_immutable_property(primary, 0);

	if (ldev->caps.plane_rotation)
		drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
						   supported_rotations);

	/* Init CRTC according to its hardware features */
	if (ldev->caps.crc)
		ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
@@ -1625,6 +1669,10 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
			drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
		else
			drm_plane_create_zpos_immutable_property(overlay, i);

		if (ldev->caps.plane_rotation)
			drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
							   supported_rotations);
	}

	return 0;
@@ -1755,6 +1803,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		ldev->caps.plane_reg_shadow = false;
		ldev->caps.crc = false;
		ldev->caps.dynamic_zorder = false;
		ldev->caps.plane_rotation = false;
		break;
	case HWVER_20101:
		ldev->caps.layer_ofs = LAY_OFS_0;
@@ -1771,6 +1820,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		ldev->caps.plane_reg_shadow = false;
		ldev->caps.crc = false;
		ldev->caps.dynamic_zorder = false;
		ldev->caps.plane_rotation = false;
		break;
	case HWVER_40100:
		ldev->caps.layer_ofs = LAY_OFS_1;
@@ -1787,6 +1837,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		ldev->caps.plane_reg_shadow = true;
		ldev->caps.crc = true;
		ldev->caps.dynamic_zorder = true;
		ldev->caps.plane_rotation = true;
		break;
	default:
		return -ENODEV;
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ struct ltdc_caps {
	bool plane_reg_shadow;	/* plane shadow registers ability */
	bool crc;		/* cyclic redundancy check supported */
	bool dynamic_zorder;	/* dynamic z-order */
	bool plane_rotation;	/* plane rotation */
};

#define LTDC_MAX_LAYER	4