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: Koji Matsuoka <koji.matsuoka.xm@renesas.com> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
This commit is contained in:
Родитель
82e7c5e496
Коммит
6bc2e15cf2
|
@ -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,6 +58,86 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
|
|||
|
||||
rcar_lvds_write(lvds, LVDPLLCR, pllcr);
|
||||
|
||||
/* Select the input, hardcode mode 0, enable LVDS operation and turn
|
||||
* bias circuitry on.
|
||||
*/
|
||||
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN;
|
||||
if (rcrtc->index == 2)
|
||||
lvdcr0 |= LVDCR0_DUSEL;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
/* Turn all the channels on. */
|
||||
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.
|
||||
*/
|
||||
lvdcr0 |= LVDCR0_PLLON;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
usleep_range(100, 150);
|
||||
|
||||
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
|
||||
|
@ -87,30 +158,14 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
|
|||
|
||||
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
|
||||
|
||||
/* Select the input, hardcode mode 0, enable LVDS operation and turn
|
||||
* bias circuitry on.
|
||||
*/
|
||||
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN;
|
||||
if (rcrtc->index == 2)
|
||||
lvdcr0 |= LVDCR0_DUSEL;
|
||||
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);
|
||||
|
||||
/* Turn the PLL on, wait for the startup delay, and turn the output
|
||||
* on.
|
||||
*/
|
||||
lvdcr0 |= LVDCR0_PLLON;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
usleep_range(100, 150);
|
||||
|
||||
lvdcr0 |= LVDCR0_LVRES;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
/* 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,
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче