Skip to content

Commit 6bc2e15

Browse files
KojiMatsuokaLaurent Pinchart
authored andcommitted
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>
1 parent 82e7c5e commit 6bc2e15

File tree

4 files changed

+123
-48
lines changed

4 files changed

+123
-48
lines changed

drivers/gpu/drm/rcar-du/Kconfig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ config DRM_RCAR_HDMI
2121
config DRM_RCAR_LVDS
2222
bool "R-Car DU LVDS Encoder Support"
2323
depends on DRM_RCAR_DU
24-
depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
2524
help
26-
Enable support for the R-Car Display Unit embedded LVDS encoders
27-
(currently only on R8A7790 and R8A7791).
25+
Enable support for the R-Car Display Unit embedded LVDS encoders.
2826

2927
config DRM_RCAR_VSP
3028
bool "R-Car DU VSP Compositor Support"

drivers/gpu/drm/rcar-du/rcar_du_drv.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,21 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
140140
| RCAR_DU_FEATURE_VSP1_SOURCE,
141141
.num_crtcs = 4,
142142
.routes = {
143-
/* R8A7795 has one RGB output, and two HDMI and one LVDS
144-
* (currently unsupported) outputs
143+
/* R8A7795 has one RGB output, one LVDS output and two
144+
* (currently unsupported) HDMI outputs.
145145
*/
146146
[RCAR_DU_OUTPUT_DPAD0] = {
147147
.possible_crtcs = BIT(3),
148148
.encoder_type = DRM_MODE_ENCODER_NONE,
149149
.port = 0,
150150
},
151+
[RCAR_DU_OUTPUT_LVDS0] = {
152+
.possible_crtcs = BIT(0),
153+
.encoder_type = DRM_MODE_ENCODER_LVDS,
154+
.port = 3,
155+
},
151156
},
157+
.num_lvds = 1,
152158
};
153159

154160
static const struct of_device_id rcar_du_of_table[] = {

drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c

Lines changed: 97 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,13 @@ static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
3838
iowrite32(data, lvds->mmio + reg);
3939
}
4040

41-
static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
42-
struct rcar_du_crtc *rcrtc)
41+
static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds,
42+
struct rcar_du_crtc *rcrtc)
4343
{
4444
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
4545
unsigned int freq = mode->clock;
4646
u32 lvdcr0;
47-
u32 lvdhcr;
4847
u32 pllcr;
49-
int ret;
50-
51-
if (lvds->enabled)
52-
return 0;
53-
54-
ret = clk_prepare_enable(lvds->clock);
55-
if (ret < 0)
56-
return ret;
5748

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

6859
rcar_lvds_write(lvds, LVDPLLCR, pllcr);
6960

70-
/* Hardcode the channels and control signals routing for now.
71-
*
72-
* HSYNC -> CTRL0
73-
* VSYNC -> CTRL1
74-
* DISP -> CTRL2
75-
* 0 -> CTRL3
76-
*/
77-
rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
78-
LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
79-
LVDCTRCR_CTR0SEL_HSYNC);
80-
81-
if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
82-
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
83-
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
84-
else
85-
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
86-
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
87-
88-
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
89-
9061
/* Select the input, hardcode mode 0, enable LVDS operation and turn
9162
* bias circuitry on.
9263
*/
@@ -96,8 +67,10 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
9667
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
9768

9869
/* Turn all the channels on. */
99-
rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
100-
LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
70+
rcar_lvds_write(lvds, LVDCR1,
71+
LVDCR1_CHSTBY_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) |
72+
LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) |
73+
LVDCR1_CLKSTBY_GEN2);
10174

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

11083
lvdcr0 |= LVDCR0_LVRES;
11184
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
85+
}
86+
87+
static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
88+
struct rcar_du_crtc *rcrtc)
89+
{
90+
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
91+
unsigned int freq = mode->clock;
92+
u32 lvdcr0;
93+
u32 pllcr;
94+
95+
/* PLL clock configuration */
96+
if (freq < 42000)
97+
pllcr = LVDPLLCR_PLLDIVCNT_42M;
98+
else if (freq < 85000)
99+
pllcr = LVDPLLCR_PLLDIVCNT_85M;
100+
else if (freq < 128000)
101+
pllcr = LVDPLLCR_PLLDIVCNT_128M;
102+
else
103+
pllcr = LVDPLLCR_PLLDIVCNT_148M;
104+
105+
rcar_lvds_write(lvds, LVDPLLCR, pllcr);
106+
107+
/* Turn the PLL on, set it to LVDS normal mode, wait for the startup
108+
* delay and turn the output on.
109+
*/
110+
lvdcr0 = LVDCR0_PLLON;
111+
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
112+
113+
lvdcr0 |= LVDCR0_PWD;
114+
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
115+
116+
usleep_range(100, 150);
117+
118+
lvdcr0 |= LVDCR0_LVRES;
119+
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
120+
121+
/* Turn all the channels on. */
122+
rcar_lvds_write(lvds, LVDCR1,
123+
LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
124+
LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
125+
LVDCR1_CLKSTBY_GEN3);
126+
}
127+
128+
static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
129+
struct rcar_du_crtc *rcrtc)
130+
{
131+
u32 lvdhcr;
132+
int ret;
133+
134+
if (lvds->enabled)
135+
return 0;
136+
137+
ret = clk_prepare_enable(lvds->clock);
138+
if (ret < 0)
139+
return ret;
140+
141+
/* Hardcode the channels and control signals routing for now.
142+
*
143+
* HSYNC -> CTRL0
144+
* VSYNC -> CTRL1
145+
* DISP -> CTRL2
146+
* 0 -> CTRL3
147+
*/
148+
rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
149+
LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
150+
LVDCTRCR_CTR0SEL_HSYNC);
151+
152+
if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
153+
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
154+
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
155+
else
156+
lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
157+
| LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
158+
159+
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
160+
161+
/* Perform generation-specific initialization. */
162+
if (lvds->dev->info->gen < 3)
163+
rcar_du_lvdsenc_start_gen2(lvds, rcrtc);
164+
else
165+
rcar_du_lvdsenc_start_gen3(lvds, rcrtc);
112166

113167
lvds->enabled = true;
168+
114169
return 0;
115170
}
116171

@@ -143,10 +198,16 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
143198
void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
144199
struct drm_display_mode *mode)
145200
{
146-
/* The internal LVDS encoder has a clock frequency operating range of
147-
* 30MHz to 150MHz. Clamp the clock accordingly.
201+
struct rcar_du_device *rcdu = lvds->dev;
202+
203+
/* The internal LVDS encoder has a restricted clock frequency operating
204+
* range (30MHz to 150MHz on Gen2, 25.175MHz to 148.5MHz on Gen3). Clamp
205+
* the clock accordingly.
148206
*/
149-
mode->clock = clamp(mode->clock, 30000, 150000);
207+
if (rcdu->info->gen < 3)
208+
mode->clock = clamp(mode->clock, 30000, 150000);
209+
else
210+
mode->clock = clamp(mode->clock, 25175, 148500);
150211
}
151212

152213
static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,

drivers/gpu/drm/rcar-du/rcar_lvds_regs.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions
33
*
4-
* Copyright (C) 2013 Renesas Electronics Corporation
4+
* Copyright (C) 2013-2015 Renesas Electronics Corporation
55
*
66
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
77
*
@@ -15,28 +15,38 @@
1515

1616
#define LVDCR0 0x0000
1717
#define LVDCR0_DUSEL (1 << 15)
18-
#define LVDCR0_DMD (1 << 12)
18+
#define LVDCR0_DMD (1 << 12) /* Gen2 only */
1919
#define LVDCR0_LVMD_MASK (0xf << 8)
2020
#define LVDCR0_LVMD_SHIFT 8
2121
#define LVDCR0_PLLON (1 << 4)
22-
#define LVDCR0_BEN (1 << 2)
23-
#define LVDCR0_LVEN (1 << 1)
22+
#define LVDCR0_PWD (1 << 2) /* Gen3 only */
23+
#define LVDCR0_BEN (1 << 2) /* Gen2 only */
24+
#define LVDCR0_LVEN (1 << 1) /* Gen2 only */
2425
#define LVDCR0_LVRES (1 << 0)
2526

2627
#define LVDCR1 0x0004
27-
#define LVDCR1_CKSEL (1 << 15)
28-
#define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2))
29-
#define LVDCR1_CLKSTBY (3 << 0)
28+
#define LVDCR1_CKSEL (1 << 15) /* Gen2 only */
29+
#define LVDCR1_CHSTBY_GEN2(n) (3 << (2 + (n) * 2)) /* Gen2 only */
30+
#define LVDCR1_CHSTBY_GEN3(n) (1 << (2 + (n) * 2)) /* Gen3 only */
31+
#define LVDCR1_CLKSTBY_GEN2 (3 << 0) /* Gen2 only */
32+
#define LVDCR1_CLKSTBY_GEN3 (1 << 0) /* Gen3 only */
3033

3134
#define LVDPLLCR 0x0008
3235
#define LVDPLLCR_CEEN (1 << 14)
3336
#define LVDPLLCR_FBEN (1 << 13)
3437
#define LVDPLLCR_COSEL (1 << 12)
38+
/* Gen2 */
3539
#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0)
3640
#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0)
3741
#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0)
3842
#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0)
3943
#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0)
44+
/* Gen3 */
45+
#define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0)
46+
#define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0)
47+
#define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0)
48+
#define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0)
49+
#define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0)
4050

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

0 commit comments

Comments
 (0)