diff options
author | Andy Yan <andy.yan@rock-chips.com> | 2015-01-07 02:48:27 -0500 |
---|---|---|
committer | Philipp Zabel <p.zabel@pengutronix.de> | 2015-01-07 12:32:00 -0500 |
commit | 12b9f204e804b2a6c65a6662b1fbe2449bca677f (patch) | |
tree | 83cc469366dae142129fdc7e4d5fd1429b5d1bb1 | |
parent | 74af9e4d03b80c79ee0c21be0a35ec348ffc74ea (diff) |
drm: bridge/dw_hdmi: add rockchip rk3288 support
Rockchip RK3288 hdmi is compatible with dw_hdmi
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r-- | drivers/gpu/drm/bridge/dw_hdmi.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 341 | ||||
-rw-r--r-- | include/drm/bridge/dw_hdmi.h | 1 |
5 files changed, 357 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index 3a97c8419d4f..80bb512a869f 100644 --- a/drivers/gpu/drm/bridge/dw_hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c | |||
@@ -852,6 +852,9 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, | |||
852 | dw_hdmi_phy_gen2_txpwron(hdmi, 1); | 852 | dw_hdmi_phy_gen2_txpwron(hdmi, 1); |
853 | dw_hdmi_phy_gen2_pddq(hdmi, 0); | 853 | dw_hdmi_phy_gen2_pddq(hdmi, 0); |
854 | 854 | ||
855 | if (hdmi->dev_type == RK3288_HDMI) | ||
856 | dw_hdmi_phy_enable_spare(hdmi, 1); | ||
857 | |||
855 | /*Wait for PHY PLL lock */ | 858 | /*Wait for PHY PLL lock */ |
856 | msec = 5; | 859 | msec = 5; |
857 | do { | 860 | do { |
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index ca9f085efa92..0d87bf6ddd96 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig | |||
@@ -15,3 +15,13 @@ config DRM_ROCKCHIP | |||
15 | management to userspace. This driver does not provide | 15 | management to userspace. This driver does not provide |
16 | 2D or 3D acceleration; acceleration is performed by other | 16 | 2D or 3D acceleration; acceleration is performed by other |
17 | IP found on the SoC. | 17 | IP found on the SoC. |
18 | |||
19 | config ROCKCHIP_DW_HDMI | ||
20 | tristate "Rockchip specific extensions for Synopsys DW HDMI" | ||
21 | depends on DRM_ROCKCHIP | ||
22 | select DRM_DW_HDMI | ||
23 | help | ||
24 | This selects support for Rockchip SoC specific extensions | ||
25 | for the Synopsys DesignWare HDMI driver. If you want to | ||
26 | enable HDMI on RK3288 based SoC, you should selet this | ||
27 | option. | ||
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 2cb0672f57ed..f3d8a19c641f 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile | |||
@@ -5,4 +5,6 @@ | |||
5 | rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ | 5 | rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ |
6 | rockchip_drm_gem.o | 6 | rockchip_drm_gem.o |
7 | 7 | ||
8 | obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o | ||
9 | |||
8 | obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o | 10 | obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o |
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c new file mode 100644 index 000000000000..d236faa05b19 --- /dev/null +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | |||
@@ -0,0 +1,341 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/mfd/syscon.h> | ||
13 | #include <linux/regmap.h> | ||
14 | #include <drm/drm_of.h> | ||
15 | #include <drm/drmP.h> | ||
16 | #include <drm/drm_crtc_helper.h> | ||
17 | #include <drm/drm_edid.h> | ||
18 | #include <drm/drm_encoder_slave.h> | ||
19 | #include <drm/bridge/dw_hdmi.h> | ||
20 | |||
21 | #include "rockchip_drm_drv.h" | ||
22 | #include "rockchip_drm_vop.h" | ||
23 | |||
24 | #define GRF_SOC_CON6 0x025c | ||
25 | #define HDMI_SEL_VOP_LIT (1 << 4) | ||
26 | |||
27 | struct rockchip_hdmi { | ||
28 | struct device *dev; | ||
29 | struct regmap *regmap; | ||
30 | struct drm_encoder encoder; | ||
31 | }; | ||
32 | |||
33 | #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) | ||
34 | |||
35 | static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { | ||
36 | { | ||
37 | 27000000, { | ||
38 | { 0x00b3, 0x0000}, | ||
39 | { 0x2153, 0x0000}, | ||
40 | { 0x40f3, 0x0000} | ||
41 | }, | ||
42 | }, { | ||
43 | 36000000, { | ||
44 | { 0x00b3, 0x0000}, | ||
45 | { 0x2153, 0x0000}, | ||
46 | { 0x40f3, 0x0000} | ||
47 | }, | ||
48 | }, { | ||
49 | 40000000, { | ||
50 | { 0x00b3, 0x0000}, | ||
51 | { 0x2153, 0x0000}, | ||
52 | { 0x40f3, 0x0000} | ||
53 | }, | ||
54 | }, { | ||
55 | 54000000, { | ||
56 | { 0x0072, 0x0001}, | ||
57 | { 0x2142, 0x0001}, | ||
58 | { 0x40a2, 0x0001}, | ||
59 | }, | ||
60 | }, { | ||
61 | 65000000, { | ||
62 | { 0x0072, 0x0001}, | ||
63 | { 0x2142, 0x0001}, | ||
64 | { 0x40a2, 0x0001}, | ||
65 | }, | ||
66 | }, { | ||
67 | 66000000, { | ||
68 | { 0x013e, 0x0003}, | ||
69 | { 0x217e, 0x0002}, | ||
70 | { 0x4061, 0x0002} | ||
71 | }, | ||
72 | }, { | ||
73 | 74250000, { | ||
74 | { 0x0072, 0x0001}, | ||
75 | { 0x2145, 0x0002}, | ||
76 | { 0x4061, 0x0002} | ||
77 | }, | ||
78 | }, { | ||
79 | 83500000, { | ||
80 | { 0x0072, 0x0001}, | ||
81 | }, | ||
82 | }, { | ||
83 | 108000000, { | ||
84 | { 0x0051, 0x0002}, | ||
85 | { 0x2145, 0x0002}, | ||
86 | { 0x4061, 0x0002} | ||
87 | }, | ||
88 | }, { | ||
89 | 106500000, { | ||
90 | { 0x0051, 0x0002}, | ||
91 | { 0x2145, 0x0002}, | ||
92 | { 0x4061, 0x0002} | ||
93 | }, | ||
94 | }, { | ||
95 | 146250000, { | ||
96 | { 0x0051, 0x0002}, | ||
97 | { 0x2145, 0x0002}, | ||
98 | { 0x4061, 0x0002} | ||
99 | }, | ||
100 | }, { | ||
101 | 148500000, { | ||
102 | { 0x0051, 0x0003}, | ||
103 | { 0x214c, 0x0003}, | ||
104 | { 0x4064, 0x0003} | ||
105 | }, | ||
106 | }, { | ||
107 | ~0UL, { | ||
108 | { 0x00a0, 0x000a }, | ||
109 | { 0x2001, 0x000f }, | ||
110 | { 0x4002, 0x000f }, | ||
111 | }, | ||
112 | } | ||
113 | }; | ||
114 | |||
115 | static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { | ||
116 | /* pixelclk bpp8 bpp10 bpp12 */ | ||
117 | { | ||
118 | 40000000, { 0x0018, 0x0018, 0x0018 }, | ||
119 | }, { | ||
120 | 65000000, { 0x0028, 0x0028, 0x0028 }, | ||
121 | }, { | ||
122 | 66000000, { 0x0038, 0x0038, 0x0038 }, | ||
123 | }, { | ||
124 | 74250000, { 0x0028, 0x0038, 0x0038 }, | ||
125 | }, { | ||
126 | 83500000, { 0x0028, 0x0038, 0x0038 }, | ||
127 | }, { | ||
128 | 146250000, { 0x0038, 0x0038, 0x0038 }, | ||
129 | }, { | ||
130 | 148500000, { 0x0000, 0x0038, 0x0038 }, | ||
131 | }, { | ||
132 | ~0UL, { 0x0000, 0x0000, 0x0000}, | ||
133 | } | ||
134 | }; | ||
135 | |||
136 | static const struct dw_hdmi_sym_term rockchip_sym_term[] = { | ||
137 | /*pixelclk symbol term*/ | ||
138 | { 74250000, 0x8009, 0x0004 }, | ||
139 | { 148500000, 0x8029, 0x0004 }, | ||
140 | { 297000000, 0x8039, 0x0005 }, | ||
141 | { ~0UL, 0x0000, 0x0000 } | ||
142 | }; | ||
143 | |||
144 | static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) | ||
145 | { | ||
146 | struct device_node *np = hdmi->dev->of_node; | ||
147 | |||
148 | hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); | ||
149 | if (IS_ERR(hdmi->regmap)) { | ||
150 | dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); | ||
151 | return PTR_ERR(hdmi->regmap); | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static enum drm_mode_status | ||
158 | dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, | ||
159 | struct drm_display_mode *mode) | ||
160 | { | ||
161 | const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; | ||
162 | int pclk = mode->clock * 1000; | ||
163 | bool valid = false; | ||
164 | int i; | ||
165 | |||
166 | for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { | ||
167 | if (pclk == mpll_cfg[i].mpixelclock) { | ||
168 | valid = true; | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return (valid) ? MODE_OK : MODE_BAD; | ||
174 | } | ||
175 | |||
176 | static struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = { | ||
177 | .destroy = drm_encoder_cleanup, | ||
178 | }; | ||
179 | |||
180 | static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) | ||
181 | { | ||
182 | } | ||
183 | |||
184 | static bool | ||
185 | dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder, | ||
186 | const struct drm_display_mode *mode, | ||
187 | struct drm_display_mode *adj_mode) | ||
188 | { | ||
189 | return true; | ||
190 | } | ||
191 | |||
192 | static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, | ||
193 | struct drm_display_mode *mode, | ||
194 | struct drm_display_mode *adj_mode) | ||
195 | { | ||
196 | } | ||
197 | |||
198 | static void dw_hdmi_rockchip_encoder_commit(struct drm_encoder *encoder) | ||
199 | { | ||
200 | struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); | ||
201 | u32 val; | ||
202 | int mux; | ||
203 | |||
204 | mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); | ||
205 | if (mux) | ||
206 | val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16); | ||
207 | else | ||
208 | val = HDMI_SEL_VOP_LIT << 16; | ||
209 | |||
210 | regmap_write(hdmi->regmap, GRF_SOC_CON6, val); | ||
211 | dev_dbg(hdmi->dev, "vop %s output to hdmi\n", | ||
212 | (mux) ? "LIT" : "BIG"); | ||
213 | } | ||
214 | |||
215 | static void dw_hdmi_rockchip_encoder_prepare(struct drm_encoder *encoder) | ||
216 | { | ||
217 | rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA, | ||
218 | ROCKCHIP_OUT_MODE_AAAA); | ||
219 | } | ||
220 | |||
221 | static struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { | ||
222 | .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, | ||
223 | .mode_set = dw_hdmi_rockchip_encoder_mode_set, | ||
224 | .prepare = dw_hdmi_rockchip_encoder_prepare, | ||
225 | .commit = dw_hdmi_rockchip_encoder_commit, | ||
226 | .disable = dw_hdmi_rockchip_encoder_disable, | ||
227 | }; | ||
228 | |||
229 | static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { | ||
230 | .mode_valid = dw_hdmi_rockchip_mode_valid, | ||
231 | .mpll_cfg = rockchip_mpll_cfg, | ||
232 | .cur_ctr = rockchip_cur_ctr, | ||
233 | .sym_term = rockchip_sym_term, | ||
234 | .dev_type = RK3288_HDMI, | ||
235 | }; | ||
236 | |||
237 | static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { | ||
238 | { .compatible = "rockchip,rk3288-dw-hdmi", | ||
239 | .data = &rockchip_hdmi_drv_data | ||
240 | }, | ||
241 | {}, | ||
242 | }; | ||
243 | MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); | ||
244 | |||
245 | static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, | ||
246 | void *data) | ||
247 | { | ||
248 | struct platform_device *pdev = to_platform_device(dev); | ||
249 | const struct dw_hdmi_plat_data *plat_data; | ||
250 | const struct of_device_id *match; | ||
251 | struct drm_device *drm = data; | ||
252 | struct drm_encoder *encoder; | ||
253 | struct rockchip_hdmi *hdmi; | ||
254 | struct resource *iores; | ||
255 | int irq; | ||
256 | int ret; | ||
257 | |||
258 | if (!pdev->dev.of_node) | ||
259 | return -ENODEV; | ||
260 | |||
261 | hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); | ||
262 | if (!hdmi) | ||
263 | return -ENOMEM; | ||
264 | |||
265 | match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); | ||
266 | plat_data = match->data; | ||
267 | hdmi->dev = &pdev->dev; | ||
268 | encoder = &hdmi->encoder; | ||
269 | |||
270 | irq = platform_get_irq(pdev, 0); | ||
271 | if (irq < 0) | ||
272 | return irq; | ||
273 | |||
274 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
275 | if (!iores) | ||
276 | return -ENXIO; | ||
277 | |||
278 | platform_set_drvdata(pdev, hdmi); | ||
279 | |||
280 | encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); | ||
281 | /* | ||
282 | * If we failed to find the CRTC(s) which this encoder is | ||
283 | * supposed to be connected to, it's because the CRTC has | ||
284 | * not been registered yet. Defer probing, and hope that | ||
285 | * the required CRTC is added later. | ||
286 | */ | ||
287 | if (encoder->possible_crtcs == 0) | ||
288 | return -EPROBE_DEFER; | ||
289 | |||
290 | ret = rockchip_hdmi_parse_dt(hdmi); | ||
291 | if (ret) { | ||
292 | dev_err(hdmi->dev, "Unable to parse OF data\n"); | ||
293 | return ret; | ||
294 | } | ||
295 | |||
296 | drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); | ||
297 | drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, | ||
298 | DRM_MODE_ENCODER_TMDS); | ||
299 | |||
300 | return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); | ||
301 | } | ||
302 | |||
303 | static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, | ||
304 | void *data) | ||
305 | { | ||
306 | return dw_hdmi_unbind(dev, master, data); | ||
307 | } | ||
308 | |||
309 | static const struct component_ops dw_hdmi_rockchip_ops = { | ||
310 | .bind = dw_hdmi_rockchip_bind, | ||
311 | .unbind = dw_hdmi_rockchip_unbind, | ||
312 | }; | ||
313 | |||
314 | static int dw_hdmi_rockchip_probe(struct platform_device *pdev) | ||
315 | { | ||
316 | return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); | ||
317 | } | ||
318 | |||
319 | static int dw_hdmi_rockchip_remove(struct platform_device *pdev) | ||
320 | { | ||
321 | component_del(&pdev->dev, &dw_hdmi_rockchip_ops); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static struct platform_driver dw_hdmi_rockchip_pltfm_driver = { | ||
327 | .probe = dw_hdmi_rockchip_probe, | ||
328 | .remove = dw_hdmi_rockchip_remove, | ||
329 | .driver = { | ||
330 | .name = "dwhdmi-rockchip", | ||
331 | .of_match_table = dw_hdmi_rockchip_dt_ids, | ||
332 | }, | ||
333 | }; | ||
334 | |||
335 | module_platform_driver(dw_hdmi_rockchip_pltfm_driver); | ||
336 | |||
337 | MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); | ||
338 | MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); | ||
339 | MODULE_DESCRIPTION("Rockchip Specific DW-HDMI Driver Extension"); | ||
340 | MODULE_LICENSE("GPL"); | ||
341 | MODULE_ALIAS("platform:dwhdmi-rockchip"); | ||
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index b64e58a92057..5a4f49005169 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h | |||
@@ -22,6 +22,7 @@ enum { | |||
22 | enum dw_hdmi_devtype { | 22 | enum dw_hdmi_devtype { |
23 | IMX6Q_HDMI, | 23 | IMX6Q_HDMI, |
24 | IMX6DL_HDMI, | 24 | IMX6DL_HDMI, |
25 | RK3288_HDMI, | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | struct dw_hdmi_mpll_config { | 28 | struct dw_hdmi_mpll_config { |