mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-24 05:04:00 +08:00
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:
parent
82e7c5e496
commit
6bc2e15cf2
@ -21,10 +21,8 @@ config DRM_RCAR_HDMI
|
|||||||
config DRM_RCAR_LVDS
|
config DRM_RCAR_LVDS
|
||||||
bool "R-Car DU LVDS Encoder Support"
|
bool "R-Car DU LVDS Encoder Support"
|
||||||
depends on DRM_RCAR_DU
|
depends on DRM_RCAR_DU
|
||||||
depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
|
|
||||||
help
|
help
|
||||||
Enable support for the R-Car Display Unit embedded LVDS encoders
|
Enable support for the R-Car Display Unit embedded LVDS encoders.
|
||||||
(currently only on R8A7790 and R8A7791).
|
|
||||||
|
|
||||||
config DRM_RCAR_VSP
|
config DRM_RCAR_VSP
|
||||||
bool "R-Car DU VSP Compositor Support"
|
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,
|
| RCAR_DU_FEATURE_VSP1_SOURCE,
|
||||||
.num_crtcs = 4,
|
.num_crtcs = 4,
|
||||||
.routes = {
|
.routes = {
|
||||||
/* R8A7795 has one RGB output, and two HDMI and one LVDS
|
/* R8A7795 has one RGB output, one LVDS output and two
|
||||||
* (currently unsupported) outputs
|
* (currently unsupported) HDMI outputs.
|
||||||
*/
|
*/
|
||||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||||
.possible_crtcs = BIT(3),
|
.possible_crtcs = BIT(3),
|
||||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||||
.port = 0,
|
.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[] = {
|
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);
|
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)
|
struct rcar_du_crtc *rcrtc)
|
||||||
{
|
{
|
||||||
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
|
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
|
||||||
unsigned int freq = mode->clock;
|
unsigned int freq = mode->clock;
|
||||||
u32 lvdcr0;
|
u32 lvdcr0;
|
||||||
u32 lvdhcr;
|
|
||||||
u32 pllcr;
|
u32 pllcr;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (lvds->enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(lvds->clock);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* PLL clock configuration */
|
/* PLL clock configuration */
|
||||||
if (freq < 39000)
|
if (freq < 39000)
|
||||||
@ -67,6 +58,86 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
|
|||||||
|
|
||||||
rcar_lvds_write(lvds, LVDPLLCR, pllcr);
|
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.
|
/* Hardcode the channels and control signals routing for now.
|
||||||
*
|
*
|
||||||
* HSYNC -> CTRL0
|
* HSYNC -> CTRL0
|
||||||
@ -87,30 +158,14 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
|
|||||||
|
|
||||||
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
|
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
|
||||||
|
|
||||||
/* Select the input, hardcode mode 0, enable LVDS operation and turn
|
/* Perform generation-specific initialization. */
|
||||||
* bias circuitry on.
|
if (lvds->dev->info->gen < 3)
|
||||||
*/
|
rcar_du_lvdsenc_start_gen2(lvds, rcrtc);
|
||||||
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN;
|
else
|
||||||
if (rcrtc->index == 2)
|
rcar_du_lvdsenc_start_gen3(lvds, rcrtc);
|
||||||
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);
|
|
||||||
|
|
||||||
lvds->enabled = true;
|
lvds->enabled = true;
|
||||||
|
|
||||||
return 0;
|
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,
|
void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
|
||||||
struct drm_display_mode *mode)
|
struct drm_display_mode *mode)
|
||||||
{
|
{
|
||||||
/* The internal LVDS encoder has a clock frequency operating range of
|
struct rcar_du_device *rcdu = lvds->dev;
|
||||||
* 30MHz to 150MHz. Clamp the clock accordingly.
|
|
||||||
|
/* 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,
|
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
|
* 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)
|
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||||
*
|
*
|
||||||
@ -15,28 +15,38 @@
|
|||||||
|
|
||||||
#define LVDCR0 0x0000
|
#define LVDCR0 0x0000
|
||||||
#define LVDCR0_DUSEL (1 << 15)
|
#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_MASK (0xf << 8)
|
||||||
#define LVDCR0_LVMD_SHIFT 8
|
#define LVDCR0_LVMD_SHIFT 8
|
||||||
#define LVDCR0_PLLON (1 << 4)
|
#define LVDCR0_PLLON (1 << 4)
|
||||||
#define LVDCR0_BEN (1 << 2)
|
#define LVDCR0_PWD (1 << 2) /* Gen3 only */
|
||||||
#define LVDCR0_LVEN (1 << 1)
|
#define LVDCR0_BEN (1 << 2) /* Gen2 only */
|
||||||
|
#define LVDCR0_LVEN (1 << 1) /* Gen2 only */
|
||||||
#define LVDCR0_LVRES (1 << 0)
|
#define LVDCR0_LVRES (1 << 0)
|
||||||
|
|
||||||
#define LVDCR1 0x0004
|
#define LVDCR1 0x0004
|
||||||
#define LVDCR1_CKSEL (1 << 15)
|
#define LVDCR1_CKSEL (1 << 15) /* Gen2 only */
|
||||||
#define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2))
|
#define LVDCR1_CHSTBY_GEN2(n) (3 << (2 + (n) * 2)) /* Gen2 only */
|
||||||
#define LVDCR1_CLKSTBY (3 << 0)
|
#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 0x0008
|
||||||
#define LVDPLLCR_CEEN (1 << 14)
|
#define LVDPLLCR_CEEN (1 << 14)
|
||||||
#define LVDPLLCR_FBEN (1 << 13)
|
#define LVDPLLCR_FBEN (1 << 13)
|
||||||
#define LVDPLLCR_COSEL (1 << 12)
|
#define LVDPLLCR_COSEL (1 << 12)
|
||||||
|
/* Gen2 */
|
||||||
#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0)
|
#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0)
|
||||||
#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0)
|
#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0)
|
||||||
#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0)
|
#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0)
|
||||||
#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0)
|
#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0)
|
||||||
#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 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 0x000c
|
||||||
#define LVDCTRCR_CTR3SEL_ZERO (0 << 12)
|
#define LVDCTRCR_CTR3SEL_ZERO (0 << 12)
|
||||||
|
Loading…
Reference in New Issue
Block a user