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

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
......@@ -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"
......
......@@ -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[] = {
......
......@@ -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,
......
/*
* 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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment