diff options
author | Koji Matsuoka <koji.matsuoka.xm@renesas.com> | 2015-07-28 07:12:43 -0400 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2016-02-23 02:34:32 -0500 |
commit | 6bc2e15cf21cd96eba69ac83ddd37a7994148b16 (patch) | |
tree | 1d54f430befed85fd0a96762ab06dd2f74b69cb0 | |
parent | 82e7c5e4964545352accff4b44bbcaa2c38e7fc1 (diff) |
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>
-rw-r--r-- | drivers/gpu/drm/rcar-du/Kconfig | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 133 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 24 |
4 files changed, 123 insertions, 48 deletions
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 2171a6f5a573..1f10fa0928b4 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig | |||
@@ -21,10 +21,8 @@ config DRM_RCAR_HDMI | |||
21 | config DRM_RCAR_LVDS | 21 | config DRM_RCAR_LVDS |
22 | bool "R-Car DU LVDS Encoder Support" | 22 | bool "R-Car DU LVDS Encoder Support" |
23 | depends on DRM_RCAR_DU | 23 | depends on DRM_RCAR_DU |
24 | depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST | ||
25 | help | 24 | help |
26 | Enable support for the R-Car Display Unit embedded LVDS encoders | 25 | Enable support for the R-Car Display Unit embedded LVDS encoders. |
27 | (currently only on R8A7790 and R8A7791). | ||
28 | 26 | ||
29 | config DRM_RCAR_VSP | 27 | config DRM_RCAR_VSP |
30 | bool "R-Car DU VSP Compositor Support" | 28 | bool "R-Car DU VSP Compositor Support" |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 24265d75e7de..ed6006bf6bd8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c | |||
@@ -140,15 +140,21 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = { | |||
140 | | RCAR_DU_FEATURE_VSP1_SOURCE, | 140 | | RCAR_DU_FEATURE_VSP1_SOURCE, |
141 | .num_crtcs = 4, | 141 | .num_crtcs = 4, |
142 | .routes = { | 142 | .routes = { |
143 | /* R8A7795 has one RGB output, and two HDMI and one LVDS | 143 | /* R8A7795 has one RGB output, one LVDS output and two |
144 | * (currently unsupported) outputs | 144 | * (currently unsupported) HDMI outputs. |
145 | */ | 145 | */ |
146 | [RCAR_DU_OUTPUT_DPAD0] = { | 146 | [RCAR_DU_OUTPUT_DPAD0] = { |
147 | .possible_crtcs = BIT(3), | 147 | .possible_crtcs = BIT(3), |
148 | .encoder_type = DRM_MODE_ENCODER_NONE, | 148 | .encoder_type = DRM_MODE_ENCODER_NONE, |
149 | .port = 0, | 149 | .port = 0, |
150 | }, | 150 | }, |
151 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
152 | .possible_crtcs = BIT(0), | ||
153 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
154 | .port = 3, | ||
155 | }, | ||
151 | }, | 156 | }, |
157 | .num_lvds = 1, | ||
152 | }; | 158 | }; |
153 | 159 | ||
154 | static const struct of_device_id rcar_du_of_table[] = { | 160 | static const struct of_device_id rcar_du_of_table[] = { |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c index 02acebadf7d6..ef3a50321ecc 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | |||
@@ -38,22 +38,13 @@ static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) | |||
38 | iowrite32(data, lvds->mmio + reg); | 38 | iowrite32(data, lvds->mmio + reg); |
39 | } | 39 | } |
40 | 40 | ||
41 | static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, | 41 | static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds, |
42 | struct rcar_du_crtc *rcrtc) | 42 | struct rcar_du_crtc *rcrtc) |
43 | { | 43 | { |
44 | const struct drm_display_mode *mode = &rcrtc->crtc.mode; | 44 | const struct drm_display_mode *mode = &rcrtc->crtc.mode; |
45 | unsigned int freq = mode->clock; | 45 | unsigned int freq = mode->clock; |
46 | u32 lvdcr0; | 46 | u32 lvdcr0; |
47 | u32 lvdhcr; | ||
48 | u32 pllcr; | 47 | 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; | ||
57 | 48 | ||
58 | /* PLL clock configuration */ | 49 | /* PLL clock configuration */ |
59 | if (freq < 39000) | 50 | if (freq < 39000) |
@@ -67,26 +58,6 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, | |||
67 | 58 | ||
68 | rcar_lvds_write(lvds, LVDPLLCR, pllcr); | 59 | rcar_lvds_write(lvds, LVDPLLCR, pllcr); |
69 | 60 | ||
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 | |||
90 | /* Select the input, hardcode mode 0, enable LVDS operation and turn | 61 | /* Select the input, hardcode mode 0, enable LVDS operation and turn |
91 | * bias circuitry on. | 62 | * bias circuitry on. |
92 | */ | 63 | */ |
@@ -96,8 +67,10 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, | |||
96 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 67 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
97 | 68 | ||
98 | /* Turn all the channels on. */ | 69 | /* Turn all the channels on. */ |
99 | rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | | 70 | rcar_lvds_write(lvds, LVDCR1, |
100 | LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); | 71 | LVDCR1_CHSTBY_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) | |
72 | LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) | | ||
73 | LVDCR1_CLKSTBY_GEN2); | ||
101 | 74 | ||
102 | /* Turn the PLL on, wait for the startup delay, and turn the output | 75 | /* Turn the PLL on, wait for the startup delay, and turn the output |
103 | * on. | 76 | * on. |
@@ -109,8 +82,90 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, | |||
109 | 82 | ||
110 | lvdcr0 |= LVDCR0_LVRES; | 83 | lvdcr0 |= LVDCR0_LVRES; |
111 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 84 | 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); | ||
112 | 166 | ||
113 | lvds->enabled = true; | 167 | lvds->enabled = true; |
168 | |||
114 | return 0; | 169 | return 0; |
115 | } | 170 | } |
116 | 171 | ||
@@ -143,10 +198,16 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc, | |||
143 | void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, | 198 | void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, |
144 | struct drm_display_mode *mode) | 199 | struct drm_display_mode *mode) |
145 | { | 200 | { |
146 | /* The internal LVDS encoder has a clock frequency operating range of | 201 | struct rcar_du_device *rcdu = lvds->dev; |
147 | * 30MHz to 150MHz. Clamp the clock accordingly. | 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. | ||
148 | */ | 206 | */ |
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); | ||
150 | } | 211 | } |
151 | 212 | ||
152 | static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, | 213 | static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, |
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h index b1eafd097a79..d7d294ba2dbe 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions | 2 | * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions |
3 | * | 3 | * |
4 | * Copyright (C) 2013 Renesas Electronics Corporation | 4 | * Copyright (C) 2013-2015 Renesas Electronics Corporation |
5 | * | 5 | * |
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | 6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
7 | * | 7 | * |
@@ -15,28 +15,38 @@ | |||
15 | 15 | ||
16 | #define LVDCR0 0x0000 | 16 | #define LVDCR0 0x0000 |
17 | #define LVDCR0_DUSEL (1 << 15) | 17 | #define LVDCR0_DUSEL (1 << 15) |
18 | #define LVDCR0_DMD (1 << 12) | 18 | #define LVDCR0_DMD (1 << 12) /* Gen2 only */ |
19 | #define LVDCR0_LVMD_MASK (0xf << 8) | 19 | #define LVDCR0_LVMD_MASK (0xf << 8) |
20 | #define LVDCR0_LVMD_SHIFT 8 | 20 | #define LVDCR0_LVMD_SHIFT 8 |
21 | #define LVDCR0_PLLON (1 << 4) | 21 | #define LVDCR0_PLLON (1 << 4) |
22 | #define LVDCR0_BEN (1 << 2) | 22 | #define LVDCR0_PWD (1 << 2) /* Gen3 only */ |
23 | #define LVDCR0_LVEN (1 << 1) | 23 | #define LVDCR0_BEN (1 << 2) /* Gen2 only */ |
24 | #define LVDCR0_LVEN (1 << 1) /* Gen2 only */ | ||
24 | #define LVDCR0_LVRES (1 << 0) | 25 | #define LVDCR0_LVRES (1 << 0) |
25 | 26 | ||
26 | #define LVDCR1 0x0004 | 27 | #define LVDCR1 0x0004 |
27 | #define LVDCR1_CKSEL (1 << 15) | 28 | #define LVDCR1_CKSEL (1 << 15) /* Gen2 only */ |
28 | #define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2)) | 29 | #define LVDCR1_CHSTBY_GEN2(n) (3 << (2 + (n) * 2)) /* Gen2 only */ |
29 | #define LVDCR1_CLKSTBY (3 << 0) | 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 */ | ||
30 | 33 | ||
31 | #define LVDPLLCR 0x0008 | 34 | #define LVDPLLCR 0x0008 |
32 | #define LVDPLLCR_CEEN (1 << 14) | 35 | #define LVDPLLCR_CEEN (1 << 14) |
33 | #define LVDPLLCR_FBEN (1 << 13) | 36 | #define LVDPLLCR_FBEN (1 << 13) |
34 | #define LVDPLLCR_COSEL (1 << 12) | 37 | #define LVDPLLCR_COSEL (1 << 12) |
38 | /* Gen2 */ | ||
35 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) | 39 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) |
36 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) | 40 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) |
37 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) | 41 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) |
38 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) | 42 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) |
39 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) | 43 | #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) | ||
40 | 50 | ||
41 | #define LVDCTRCR 0x000c | 51 | #define LVDCTRCR 0x000c |
42 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) | 52 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) |