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

Commit 6bc2e15c authored by Koji Matsuoka's avatar Koji Matsuoka Committed by Laurent Pinchart
Browse files

drm: rcar-du: lvds: Add R-Car Gen3 support



The LVDS encoder differs slightly in Gen3 SoCs in its PLL configuration.
Add support for the Gen3 LVDS PLL parameters and startup procedure.

Signed-off-by: default avatarKoji Matsuoka <koji.matsuoka.xm@renesas.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
parent 82e7c5e4
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -21,10 +21,8 @@ config DRM_RCAR_HDMI
config DRM_RCAR_LVDS
	bool "R-Car DU LVDS Encoder Support"
	depends on DRM_RCAR_DU
	depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
	help
	  Enable support for the R-Car Display Unit embedded LVDS encoders
	  (currently only on R8A7790 and R8A7791).
	  Enable support for the R-Car Display Unit embedded LVDS encoders.

config DRM_RCAR_VSP
	bool "R-Car DU VSP Compositor Support"
+8 −2
Original line number Diff line number Diff line
@@ -140,15 +140,21 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
		  | RCAR_DU_FEATURE_VSP1_SOURCE,
	.num_crtcs = 4,
	.routes = {
		/* R8A7795 has one RGB output, and two HDMI and one LVDS
		 * (currently unsupported) outputs
		/* R8A7795 has one RGB output, one LVDS output and two
		 * (currently unsupported) HDMI outputs.
		 */
		[RCAR_DU_OUTPUT_DPAD0] = {
			.possible_crtcs = BIT(3),
			.encoder_type = DRM_MODE_ENCODER_NONE,
			.port = 0,
		},
		[RCAR_DU_OUTPUT_LVDS0] = {
			.possible_crtcs = BIT(0),
			.encoder_type = DRM_MODE_ENCODER_LVDS,
			.port = 3,
		},
	},
	.num_lvds = 1,
};

static const struct of_device_id rcar_du_of_table[] = {
+97 −36
Original line number Diff line number Diff line
@@ -38,22 +38,13 @@ static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
	iowrite32(data, lvds->mmio + reg);
}

static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds,
				       struct rcar_du_crtc *rcrtc)
{
	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
	unsigned int freq = mode->clock;
	u32 lvdcr0;
	u32 lvdhcr;
	u32 pllcr;
	int ret;

	if (lvds->enabled)
		return 0;

	ret = clk_prepare_enable(lvds->clock);
	if (ret < 0)
		return ret;

	/* PLL clock configuration */
	if (freq < 39000)
@@ -67,26 +58,6 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,

	rcar_lvds_write(lvds, LVDPLLCR, pllcr);

	/* Hardcode the channels and control signals routing for now.
	 *
	 * HSYNC -> CTRL0
	 * VSYNC -> CTRL1
	 * DISP  -> CTRL2
	 * 0     -> CTRL3
	 */
	rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
			LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
			LVDCTRCR_CTR0SEL_HSYNC);

	if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
		lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
		       | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
	else
		lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
		       | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);

	rcar_lvds_write(lvds, LVDCHCR, lvdhcr);

	/* Select the input, hardcode mode 0, enable LVDS operation and turn
	 * bias circuitry on.
	 */
@@ -96,8 +67,10 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
	rcar_lvds_write(lvds, LVDCR0, lvdcr0);

	/* Turn all the channels on. */
	rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
			LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
	rcar_lvds_write(lvds, LVDCR1,
			LVDCR1_CHSTBY_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) |
			LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) |
			LVDCR1_CLKSTBY_GEN2);

	/* Turn the PLL on, wait for the startup delay, and turn the output
	 * on.
@@ -109,8 +82,90 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,

	lvdcr0 |= LVDCR0_LVRES;
	rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}

static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
				       struct rcar_du_crtc *rcrtc)
{
	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
	unsigned int freq = mode->clock;
	u32 lvdcr0;
	u32 pllcr;

	/* PLL clock configuration */
	if (freq < 42000)
		pllcr = LVDPLLCR_PLLDIVCNT_42M;
	else if (freq < 85000)
		pllcr = LVDPLLCR_PLLDIVCNT_85M;
	else if (freq < 128000)
		pllcr = LVDPLLCR_PLLDIVCNT_128M;
	else
		pllcr = LVDPLLCR_PLLDIVCNT_148M;

	rcar_lvds_write(lvds, LVDPLLCR, pllcr);

	/* Turn the PLL on, set it to LVDS normal mode, wait for the startup
	 * delay and turn the output on.
	 */
	lvdcr0 = LVDCR0_PLLON;
	rcar_lvds_write(lvds, LVDCR0, lvdcr0);

	lvdcr0 |= LVDCR0_PWD;
	rcar_lvds_write(lvds, LVDCR0, lvdcr0);

	usleep_range(100, 150);

	lvdcr0 |= LVDCR0_LVRES;
	rcar_lvds_write(lvds, LVDCR0, lvdcr0);

	/* Turn all the channels on. */
	rcar_lvds_write(lvds, LVDCR1,
			LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
			LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
			LVDCR1_CLKSTBY_GEN3);
}

static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
				 struct rcar_du_crtc *rcrtc)
{
	u32 lvdhcr;
	int ret;

	if (lvds->enabled)
		return 0;

	ret = clk_prepare_enable(lvds->clock);
	if (ret < 0)
		return ret;

	/* Hardcode the channels and control signals routing for now.
	 *
	 * HSYNC -> CTRL0
	 * VSYNC -> CTRL1
	 * DISP  -> CTRL2
	 * 0     -> CTRL3
	 */
	rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
			LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
			LVDCTRCR_CTR0SEL_HSYNC);

	if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
		lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
		       | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
	else
		lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
		       | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);

	rcar_lvds_write(lvds, LVDCHCR, lvdhcr);

	/* Perform generation-specific initialization. */
	if (lvds->dev->info->gen < 3)
		rcar_du_lvdsenc_start_gen2(lvds, rcrtc);
	else
		rcar_du_lvdsenc_start_gen3(lvds, rcrtc);

	lvds->enabled = true;

	return 0;
}

@@ -143,10 +198,16 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
				  struct drm_display_mode *mode)
{
	/* The internal LVDS encoder has a clock frequency operating range of
	 * 30MHz to 150MHz. Clamp the clock accordingly.
	struct rcar_du_device *rcdu = lvds->dev;

	/* The internal LVDS encoder has a restricted clock frequency operating
	 * range (30MHz to 150MHz on Gen2, 25.175MHz to 148.5MHz on Gen3). Clamp
	 * the clock accordingly.
	 */
	if (rcdu->info->gen < 3)
		mode->clock = clamp(mode->clock, 30000, 150000);
	else
		mode->clock = clamp(mode->clock, 25175, 148500);
}

static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
+17 −7
Original line number Diff line number Diff line
/*
 * rcar_lvds_regs.h  --  R-Car LVDS Interface Registers Definitions
 *
 * Copyright (C) 2013 Renesas Electronics Corporation
 * Copyright (C) 2013-2015 Renesas Electronics Corporation
 *
 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 *
@@ -15,28 +15,38 @@

#define LVDCR0				0x0000
#define LVDCR0_DUSEL			(1 << 15)
#define LVDCR0_DMD			(1 << 12)
#define LVDCR0_DMD			(1 << 12)		/* Gen2 only */
#define LVDCR0_LVMD_MASK		(0xf << 8)
#define LVDCR0_LVMD_SHIFT		8
#define LVDCR0_PLLON			(1 << 4)
#define LVDCR0_BEN			(1 << 2)
#define LVDCR0_LVEN			(1 << 1)
#define LVDCR0_PWD			(1 << 2)		/* Gen3 only */
#define LVDCR0_BEN			(1 << 2)		/* Gen2 only */
#define LVDCR0_LVEN			(1 << 1)		/* Gen2 only */
#define LVDCR0_LVRES			(1 << 0)

#define LVDCR1				0x0004
#define LVDCR1_CKSEL			(1 << 15)
#define LVDCR1_CHSTBY(n)		(3 << (2 + (n) * 2))
#define LVDCR1_CLKSTBY			(3 << 0)
#define LVDCR1_CKSEL			(1 << 15)		/* Gen2 only */
#define LVDCR1_CHSTBY_GEN2(n)		(3 << (2 + (n) * 2))	/* Gen2 only */
#define LVDCR1_CHSTBY_GEN3(n)		(1 << (2 + (n) * 2))	/* Gen3 only */
#define LVDCR1_CLKSTBY_GEN2		(3 << 0)		/* Gen2 only */
#define LVDCR1_CLKSTBY_GEN3		(1 << 0)		/* Gen3 only */

#define LVDPLLCR			0x0008
#define LVDPLLCR_CEEN			(1 << 14)
#define LVDPLLCR_FBEN			(1 << 13)
#define LVDPLLCR_COSEL			(1 << 12)
/* Gen2 */
#define LVDPLLCR_PLLDLYCNT_150M		(0x1bf << 0)
#define LVDPLLCR_PLLDLYCNT_121M		(0x22c << 0)
#define LVDPLLCR_PLLDLYCNT_60M		(0x77b << 0)
#define LVDPLLCR_PLLDLYCNT_38M		(0x69a << 0)
#define LVDPLLCR_PLLDLYCNT_MASK		(0x7ff << 0)
/* Gen3 */
#define LVDPLLCR_PLLDIVCNT_42M		(0x014cb << 0)
#define LVDPLLCR_PLLDIVCNT_85M		(0x00a45 << 0)
#define LVDPLLCR_PLLDIVCNT_128M		(0x006c3 << 0)
#define LVDPLLCR_PLLDIVCNT_148M		(0x046c1 << 0)
#define LVDPLLCR_PLLDIVCNT_MASK		(0x7ffff << 0)

#define LVDCTRCR			0x000c
#define LVDCTRCR_CTR3SEL_ZERO		(0 << 12)