Skip to content

Commit

Permalink
drm: rcar-du: lvds: Add R-Car Gen3 support
Browse files Browse the repository at this point in the history
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: Koji Matsuoka <koji.matsuoka.xm@renesas.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
  • Loading branch information
Koji Matsuoka authored and Laurent Pinchart committed Feb 23, 2016
1 parent 82e7c5e commit 6bc2e15
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 48 deletions.
4 changes: 1 addition & 3 deletions drivers/gpu/drm/rcar-du/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 8 additions & 2 deletions drivers/gpu/drm/rcar-du/rcar_du_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
133 changes: 97 additions & 36 deletions drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct rcar_du_crtc *rcrtc)
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)
Expand All @@ -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.
*/
Expand All @@ -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.
Expand All @@ -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;
}

Expand Down Expand Up @@ -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.
*/
mode->clock = clamp(mode->clock, 30000, 150000);
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,
Expand Down
24 changes: 17 additions & 7 deletions drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* 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)
*
Expand All @@ -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)
Expand Down

0 comments on commit 6bc2e15

Please sign in to comment.