]>
Commit | Line | Data |
---|---|---|
c6a27fa4 LP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * rcar_lvds.c -- R-Car LVDS Encoder | |
4 | * | |
5 | * Copyright (C) 2013-2018 Renesas Electronics Corporation | |
6 | * | |
7 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | |
8 | */ | |
9 | ||
10 | #include <linux/clk.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/io.h> | |
a16180a7 | 13 | #include <linux/module.h> |
c6a27fa4 LP |
14 | #include <linux/of.h> |
15 | #include <linux/of_device.h> | |
16 | #include <linux/of_graph.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/slab.h> | |
39864571 | 19 | #include <linux/sys_soc.h> |
c6a27fa4 LP |
20 | |
21 | #include <drm/drm_atomic.h> | |
22 | #include <drm/drm_atomic_helper.h> | |
23 | #include <drm/drm_bridge.h> | |
990e378d | 24 | #include <drm/drm_of.h> |
c6a27fa4 | 25 | #include <drm/drm_panel.h> |
a25b988f | 26 | #include <drm/drm_print.h> |
fcd70cd3 | 27 | #include <drm/drm_probe_helper.h> |
c6a27fa4 | 28 | |
02f2b300 | 29 | #include "rcar_lvds.h" |
c6a27fa4 LP |
30 | #include "rcar_lvds_regs.h" |
31 | ||
c25c0136 LP |
32 | struct rcar_lvds; |
33 | ||
c6a27fa4 LP |
34 | /* Keep in sync with the LVDCR0.LVMD hardware register values. */ |
35 | enum rcar_lvds_mode { | |
36 | RCAR_LVDS_MODE_JEIDA = 0, | |
37 | RCAR_LVDS_MODE_MIRROR = 1, | |
38 | RCAR_LVDS_MODE_VESA = 4, | |
39 | }; | |
40 | ||
59c1f061 FC |
41 | enum rcar_lvds_link_type { |
42 | RCAR_LVDS_SINGLE_LINK = 0, | |
43 | RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 1, | |
44 | RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 2, | |
45 | }; | |
46 | ||
c25c0136 LP |
47 | #define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ |
48 | #define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ | |
49 | #define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of Gen3 but E3) */ | |
50 | #define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ | |
51 | #define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */ | |
c6a27fa4 LP |
52 | |
53 | struct rcar_lvds_device_info { | |
54 | unsigned int gen; | |
55 | unsigned int quirks; | |
c25c0136 | 56 | void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); |
c6a27fa4 LP |
57 | }; |
58 | ||
59 | struct rcar_lvds { | |
60 | struct device *dev; | |
61 | const struct rcar_lvds_device_info *info; | |
62 | ||
63 | struct drm_bridge bridge; | |
64 | ||
65 | struct drm_bridge *next_bridge; | |
66 | struct drm_connector connector; | |
67 | struct drm_panel *panel; | |
68 | ||
69 | void __iomem *mmio; | |
c25c0136 LP |
70 | struct { |
71 | struct clk *mod; /* CPG module clock */ | |
72 | struct clk *extal; /* External clock */ | |
73 | struct clk *dotclkin[2]; /* External DU clocks */ | |
74 | } clocks; | |
c6a27fa4 | 75 | |
fa440d87 | 76 | struct drm_bridge *companion; |
59c1f061 | 77 | enum rcar_lvds_link_type link_type; |
c6a27fa4 LP |
78 | }; |
79 | ||
0b936e61 FC |
80 | #define bridge_to_rcar_lvds(b) \ |
81 | container_of(b, struct rcar_lvds, bridge) | |
c6a27fa4 | 82 | |
0b936e61 FC |
83 | #define connector_to_rcar_lvds(c) \ |
84 | container_of(c, struct rcar_lvds, connector) | |
c6a27fa4 LP |
85 | |
86 | static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data) | |
87 | { | |
88 | iowrite32(data, lvds->mmio + reg); | |
89 | } | |
90 | ||
91 | /* ----------------------------------------------------------------------------- | |
92 | * Connector & Panel | |
93 | */ | |
94 | ||
95 | static int rcar_lvds_connector_get_modes(struct drm_connector *connector) | |
96 | { | |
97 | struct rcar_lvds *lvds = connector_to_rcar_lvds(connector); | |
98 | ||
06c4a9c2 | 99 | return drm_panel_get_modes(lvds->panel, connector); |
c6a27fa4 LP |
100 | } |
101 | ||
102 | static int rcar_lvds_connector_atomic_check(struct drm_connector *connector, | |
6f3b6278 | 103 | struct drm_atomic_state *state) |
c6a27fa4 LP |
104 | { |
105 | struct rcar_lvds *lvds = connector_to_rcar_lvds(connector); | |
106 | const struct drm_display_mode *panel_mode; | |
6f3b6278 | 107 | struct drm_connector_state *conn_state; |
c6a27fa4 LP |
108 | struct drm_crtc_state *crtc_state; |
109 | ||
6f3b6278 SP |
110 | conn_state = drm_atomic_get_new_connector_state(state, connector); |
111 | if (!conn_state->crtc) | |
643ca198 LP |
112 | return 0; |
113 | ||
c6a27fa4 LP |
114 | if (list_empty(&connector->modes)) { |
115 | dev_dbg(lvds->dev, "connector: empty modes list\n"); | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
119 | panel_mode = list_first_entry(&connector->modes, | |
120 | struct drm_display_mode, head); | |
121 | ||
122 | /* We're not allowed to modify the resolution. */ | |
6f3b6278 | 123 | crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc); |
b7a3623d SP |
124 | if (IS_ERR(crtc_state)) |
125 | return PTR_ERR(crtc_state); | |
c6a27fa4 LP |
126 | |
127 | if (crtc_state->mode.hdisplay != panel_mode->hdisplay || | |
128 | crtc_state->mode.vdisplay != panel_mode->vdisplay) | |
129 | return -EINVAL; | |
130 | ||
131 | /* The flat panel mode is fixed, just copy it to the adjusted mode. */ | |
132 | drm_mode_copy(&crtc_state->adjusted_mode, panel_mode); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static const struct drm_connector_helper_funcs rcar_lvds_conn_helper_funcs = { | |
138 | .get_modes = rcar_lvds_connector_get_modes, | |
139 | .atomic_check = rcar_lvds_connector_atomic_check, | |
140 | }; | |
141 | ||
142 | static const struct drm_connector_funcs rcar_lvds_conn_funcs = { | |
143 | .reset = drm_atomic_helper_connector_reset, | |
144 | .fill_modes = drm_helper_probe_single_connector_modes, | |
145 | .destroy = drm_connector_cleanup, | |
146 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
147 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
148 | }; | |
149 | ||
150 | /* ----------------------------------------------------------------------------- | |
c25c0136 | 151 | * PLL Setup |
c6a27fa4 LP |
152 | */ |
153 | ||
c25c0136 | 154 | static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq) |
c6a27fa4 | 155 | { |
c25c0136 LP |
156 | u32 val; |
157 | ||
158 | if (freq < 39000000) | |
159 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; | |
160 | else if (freq < 61000000) | |
161 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; | |
162 | else if (freq < 121000000) | |
163 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | |
c6a27fa4 | 164 | else |
c25c0136 LP |
165 | val = LVDPLLCR_PLLDLYCNT_150M; |
166 | ||
167 | rcar_lvds_write(lvds, LVDPLLCR, val); | |
c6a27fa4 LP |
168 | } |
169 | ||
c25c0136 | 170 | static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq) |
c6a27fa4 | 171 | { |
c25c0136 LP |
172 | u32 val; |
173 | ||
174 | if (freq < 42000000) | |
175 | val = LVDPLLCR_PLLDIVCNT_42M; | |
176 | else if (freq < 85000000) | |
177 | val = LVDPLLCR_PLLDIVCNT_85M; | |
178 | else if (freq < 128000000) | |
179 | val = LVDPLLCR_PLLDIVCNT_128M; | |
c6a27fa4 | 180 | else |
c25c0136 LP |
181 | val = LVDPLLCR_PLLDIVCNT_148M; |
182 | ||
183 | rcar_lvds_write(lvds, LVDPLLCR, val); | |
c6a27fa4 LP |
184 | } |
185 | ||
c25c0136 LP |
186 | struct pll_info { |
187 | unsigned long diff; | |
188 | unsigned int pll_m; | |
189 | unsigned int pll_n; | |
190 | unsigned int pll_e; | |
191 | unsigned int div; | |
192 | u32 clksel; | |
193 | }; | |
194 | ||
195 | static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, | |
196 | unsigned long target, struct pll_info *pll, | |
02f2b300 | 197 | u32 clksel, bool dot_clock_only) |
c25c0136 | 198 | { |
02f2b300 | 199 | unsigned int div7 = dot_clock_only ? 1 : 7; |
c25c0136 LP |
200 | unsigned long output; |
201 | unsigned long fin; | |
202 | unsigned int m_min; | |
203 | unsigned int m_max; | |
204 | unsigned int m; | |
205 | int error; | |
206 | ||
207 | if (!clk) | |
208 | return; | |
209 | ||
210 | /* | |
211 | * The LVDS PLL is made of a pre-divider and a multiplier (strangely | |
212 | * enough called M and N respectively), followed by a post-divider E. | |
213 | * | |
214 | * ,-----. ,-----. ,-----. ,-----. | |
215 | * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout | |
216 | * `-----' ,-> | | `-----' | `-----' | |
217 | * | `-----' | | |
218 | * | ,-----. | | |
219 | * `-------- | 1/N | <-------' | |
220 | * `-----' | |
221 | * | |
222 | * The clock output by the PLL is then further divided by a programmable | |
223 | * divider DIV to achieve the desired target frequency. Finally, an | |
224 | * optional fixed /7 divider is used to convert the bit clock to a pixel | |
225 | * clock (as LVDS transmits 7 bits per lane per clock sample). | |
226 | * | |
227 | * ,-------. ,-----. |\ | |
228 | * Fout --> | 1/DIV | --> | 1/7 | --> | | | |
229 | * `-------' | `-----' | | --> dot clock | |
230 | * `------------> | | | |
231 | * |/ | |
232 | * | |
02f2b300 LP |
233 | * The /7 divider is optional, it is enabled when the LVDS PLL is used |
234 | * to drive the LVDS encoder, and disabled when used to generate a dot | |
235 | * clock for the DU RGB output, without using the LVDS encoder. | |
c25c0136 LP |
236 | * |
237 | * The PLL allowed input frequency range is 12 MHz to 192 MHz. | |
238 | */ | |
239 | ||
240 | fin = clk_get_rate(clk); | |
241 | if (fin < 12000000 || fin > 192000000) | |
242 | return; | |
243 | ||
244 | /* | |
245 | * The comparison frequency range is 12 MHz to 24 MHz, which limits the | |
246 | * allowed values for the pre-divider M (normal range 1-8). | |
247 | * | |
248 | * Fpfd = Fin / M | |
249 | */ | |
250 | m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000)); | |
251 | m_max = min_t(unsigned int, 8, fin / 12000000); | |
252 | ||
253 | for (m = m_min; m <= m_max; ++m) { | |
254 | unsigned long fpfd; | |
255 | unsigned int n_min; | |
256 | unsigned int n_max; | |
257 | unsigned int n; | |
258 | ||
259 | /* | |
260 | * The VCO operating range is 900 Mhz to 1800 MHz, which limits | |
261 | * the allowed values for the multiplier N (normal range | |
262 | * 60-120). | |
263 | * | |
264 | * Fvco = Fin * N / M | |
265 | */ | |
266 | fpfd = fin / m; | |
267 | n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd)); | |
268 | n_max = min_t(unsigned int, 120, 1800000000 / fpfd); | |
269 | ||
270 | for (n = n_min; n < n_max; ++n) { | |
271 | unsigned long fvco; | |
272 | unsigned int e_min; | |
273 | unsigned int e; | |
274 | ||
275 | /* | |
276 | * The output frequency is limited to 1039.5 MHz, | |
277 | * limiting again the allowed values for the | |
278 | * post-divider E (normal value 1, 2 or 4). | |
279 | * | |
280 | * Fout = Fvco / E | |
281 | */ | |
282 | fvco = fpfd * n; | |
283 | e_min = fvco > 1039500000 ? 1 : 0; | |
284 | ||
285 | for (e = e_min; e < 3; ++e) { | |
286 | unsigned long fout; | |
287 | unsigned long diff; | |
288 | unsigned int div; | |
289 | ||
290 | /* | |
291 | * Finally we have a programable divider after | |
292 | * the PLL, followed by a an optional fixed /7 | |
293 | * divider. | |
294 | */ | |
02f2b300 | 295 | fout = fvco / (1 << e) / div7; |
167e5354 | 296 | div = max(1UL, DIV_ROUND_CLOSEST(fout, target)); |
c25c0136 LP |
297 | diff = abs(fout / div - target); |
298 | ||
299 | if (diff < pll->diff) { | |
300 | pll->diff = diff; | |
301 | pll->pll_m = m; | |
302 | pll->pll_n = n; | |
303 | pll->pll_e = e; | |
304 | pll->div = div; | |
305 | pll->clksel = clksel; | |
306 | ||
307 | if (diff == 0) | |
308 | goto done; | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | ||
314 | done: | |
315 | output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) | |
02f2b300 | 316 | / div7 / pll->div; |
c25c0136 LP |
317 | error = (long)(output - target) * 10000 / (long)target; |
318 | ||
319 | dev_dbg(lvds->dev, | |
320 | "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n", | |
321 | clk, fin, output, target, error / 100, | |
322 | error < 0 ? -error % 100 : error % 100, | |
323 | pll->pll_m, pll->pll_n, pll->pll_e, pll->div); | |
324 | } | |
325 | ||
02f2b300 LP |
326 | static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, |
327 | unsigned int freq, bool dot_clock_only) | |
c25c0136 LP |
328 | { |
329 | struct pll_info pll = { .diff = (unsigned long)-1 }; | |
330 | u32 lvdpllcr; | |
331 | ||
332 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, | |
02f2b300 | 333 | LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only); |
c25c0136 | 334 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, |
02f2b300 | 335 | LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only); |
c25c0136 | 336 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, |
02f2b300 | 337 | LVDPLLCR_CKSEL_EXTAL, dot_clock_only); |
c25c0136 LP |
338 | |
339 | lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT | |
340 | | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); | |
341 | ||
342 | if (pll.pll_e > 0) | |
343 | lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL | |
344 | | LVDPLLCR_PLLE(pll.pll_e - 1); | |
345 | ||
02f2b300 LP |
346 | if (dot_clock_only) |
347 | lvdpllcr |= LVDPLLCR_OCKSEL; | |
348 | ||
c25c0136 LP |
349 | rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); |
350 | ||
351 | if (pll.div > 1) | |
352 | /* | |
353 | * The DIVRESET bit is a misnomer, setting it to 1 deasserts the | |
354 | * divisor reset. | |
355 | */ | |
356 | rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL | | |
357 | LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1)); | |
358 | else | |
359 | rcar_lvds_write(lvds, LVDDIV, 0); | |
360 | } | |
361 | ||
02f2b300 LP |
362 | static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) |
363 | { | |
364 | __rcar_lvds_pll_setup_d3_e3(lvds, freq, false); | |
365 | } | |
366 | ||
367 | /* ----------------------------------------------------------------------------- | |
368 | * Clock - D3/E3 only | |
369 | */ | |
370 | ||
371 | int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq) | |
372 | { | |
373 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
374 | int ret; | |
375 | ||
376 | if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) | |
377 | return -ENODEV; | |
378 | ||
379 | dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq); | |
380 | ||
02f2b300 LP |
381 | ret = clk_prepare_enable(lvds->clocks.mod); |
382 | if (ret < 0) | |
383 | return ret; | |
384 | ||
385 | __rcar_lvds_pll_setup_d3_e3(lvds, freq, true); | |
386 | ||
02f2b300 LP |
387 | return 0; |
388 | } | |
389 | EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable); | |
390 | ||
391 | void rcar_lvds_clk_disable(struct drm_bridge *bridge) | |
392 | { | |
393 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
394 | ||
395 | if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) | |
396 | return; | |
397 | ||
398 | dev_dbg(lvds->dev, "disabling LVDS PLL\n"); | |
399 | ||
02f2b300 LP |
400 | rcar_lvds_write(lvds, LVDPLLCR, 0); |
401 | ||
402 | clk_disable_unprepare(lvds->clocks.mod); | |
02f2b300 LP |
403 | } |
404 | EXPORT_SYMBOL_GPL(rcar_lvds_clk_disable); | |
405 | ||
c25c0136 LP |
406 | /* ----------------------------------------------------------------------------- |
407 | * Bridge | |
408 | */ | |
409 | ||
593885b0 LP |
410 | static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds, |
411 | const struct drm_connector *connector) | |
412 | { | |
413 | const struct drm_display_info *info; | |
414 | enum rcar_lvds_mode mode; | |
415 | ||
416 | /* | |
417 | * There is no API yet to retrieve LVDS mode from a bridge, only panels | |
418 | * are supported. | |
419 | */ | |
420 | if (!lvds->panel) | |
421 | return RCAR_LVDS_MODE_JEIDA; | |
422 | ||
423 | info = &connector->display_info; | |
424 | if (!info->num_bus_formats || !info->bus_formats) { | |
425 | dev_warn(lvds->dev, | |
426 | "no LVDS bus format reported, using JEIDA\n"); | |
427 | return RCAR_LVDS_MODE_JEIDA; | |
428 | } | |
429 | ||
430 | switch (info->bus_formats[0]) { | |
431 | case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: | |
432 | case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: | |
433 | mode = RCAR_LVDS_MODE_JEIDA; | |
434 | break; | |
435 | case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: | |
436 | mode = RCAR_LVDS_MODE_VESA; | |
437 | break; | |
438 | default: | |
439 | dev_warn(lvds->dev, | |
440 | "unsupported LVDS bus format 0x%04x, using JEIDA\n", | |
441 | info->bus_formats[0]); | |
442 | return RCAR_LVDS_MODE_JEIDA; | |
443 | } | |
444 | ||
445 | if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB) | |
446 | mode |= RCAR_LVDS_MODE_MIRROR; | |
447 | ||
448 | return mode; | |
449 | } | |
450 | ||
451 | static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, | |
452 | struct drm_atomic_state *state, | |
453 | struct drm_crtc *crtc, | |
454 | struct drm_connector *connector) | |
c6a27fa4 LP |
455 | { |
456 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
c6a27fa4 LP |
457 | u32 lvdhcr; |
458 | u32 lvdcr0; | |
459 | int ret; | |
460 | ||
c25c0136 | 461 | ret = clk_prepare_enable(lvds->clocks.mod); |
c6a27fa4 LP |
462 | if (ret < 0) |
463 | return; | |
464 | ||
fa440d87 | 465 | /* Enable the companion LVDS encoder in dual-link mode. */ |
59c1f061 | 466 | if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) |
593885b0 LP |
467 | __rcar_lvds_atomic_enable(lvds->companion, state, crtc, |
468 | connector); | |
fa440d87 | 469 | |
c6a27fa4 LP |
470 | /* |
471 | * Hardcode the channels and control signals routing for now. | |
472 | * | |
473 | * HSYNC -> CTRL0 | |
474 | * VSYNC -> CTRL1 | |
475 | * DISP -> CTRL2 | |
476 | * 0 -> CTRL3 | |
477 | */ | |
478 | rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | | |
479 | LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | | |
480 | LVDCTRCR_CTR0SEL_HSYNC); | |
481 | ||
482 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES) | |
483 | lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | |
484 | | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1); | |
485 | else | |
486 | lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1) | |
487 | | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3); | |
488 | ||
489 | rcar_lvds_write(lvds, LVDCHCR, lvdhcr); | |
490 | ||
c25c0136 | 491 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) { |
59c1f061 FC |
492 | u32 lvdstripe = 0; |
493 | ||
494 | if (lvds->link_type != RCAR_LVDS_SINGLE_LINK) { | |
495 | /* | |
496 | * By default we generate even pixels from the primary | |
497 | * encoder and odd pixels from the companion encoder. | |
498 | * Swap pixels around if the sink requires odd pixels | |
499 | * from the primary encoder and even pixels from the | |
500 | * companion encoder. | |
501 | */ | |
502 | bool swap_pixels = lvds->link_type == | |
503 | RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; | |
504 | ||
505 | /* | |
506 | * Configure vertical stripe since we are dealing with | |
507 | * an LVDS dual-link connection. | |
508 | * | |
509 | * ST_SWAP is reserved for the companion encoder, only | |
510 | * set it in the primary encoder. | |
511 | */ | |
512 | lvdstripe = LVDSTRIPE_ST_ON | |
513 | | (lvds->companion && swap_pixels ? | |
514 | LVDSTRIPE_ST_SWAP : 0); | |
515 | } | |
516 | rcar_lvds_write(lvds, LVDSTRIPE, lvdstripe); | |
c25c0136 LP |
517 | } |
518 | ||
fa440d87 LP |
519 | /* |
520 | * PLL clock configuration on all instances but the companion in | |
521 | * dual-link mode. | |
522 | */ | |
59c1f061 | 523 | if (lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) { |
593885b0 LP |
524 | const struct drm_crtc_state *crtc_state = |
525 | drm_atomic_get_new_crtc_state(state, crtc); | |
526 | const struct drm_display_mode *mode = | |
527 | &crtc_state->adjusted_mode; | |
528 | ||
fa440d87 | 529 | lvds->info->pll_setup(lvds, mode->clock * 1000); |
593885b0 | 530 | } |
c6a27fa4 LP |
531 | |
532 | /* Set the LVDS mode and select the input. */ | |
593885b0 | 533 | lvdcr0 = rcar_lvds_get_lvds_mode(lvds, connector) << LVDCR0_LVMD_SHIFT; |
fa440d87 LP |
534 | |
535 | if (lvds->bridge.encoder) { | |
593885b0 | 536 | if (drm_crtc_index(crtc) == 2) |
fa440d87 LP |
537 | lvdcr0 |= LVDCR0_DUSEL; |
538 | } | |
539 | ||
c6a27fa4 LP |
540 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
541 | ||
542 | /* Turn all the channels on. */ | |
543 | rcar_lvds_write(lvds, LVDCR1, | |
544 | LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | | |
545 | LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); | |
546 | ||
547 | if (lvds->info->gen < 3) { | |
548 | /* Enable LVDS operation and turn the bias circuitry on. */ | |
549 | lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN; | |
550 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | |
551 | } | |
552 | ||
c25c0136 LP |
553 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
554 | /* | |
555 | * Turn the PLL on (simple PLL only, extended PLL is fully | |
556 | * controlled through LVDPLLCR). | |
557 | */ | |
558 | lvdcr0 |= LVDCR0_PLLON; | |
559 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | |
560 | } | |
c6a27fa4 | 561 | |
c25c0136 | 562 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) { |
c6a27fa4 LP |
563 | /* Set LVDS normal mode. */ |
564 | lvdcr0 |= LVDCR0_PWD; | |
565 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | |
566 | } | |
567 | ||
b6eb7102 | 568 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) { |
00d082cc LP |
569 | /* |
570 | * Turn on the LVDS PHY. On D3, the LVEN and LVRES bit must be | |
571 | * set at the same time, so don't write the register yet. | |
572 | */ | |
b6eb7102 | 573 | lvdcr0 |= LVDCR0_LVEN; |
00d082cc LP |
574 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_PWD)) |
575 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | |
b6eb7102 SS |
576 | } |
577 | ||
c25c0136 LP |
578 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
579 | /* Wait for the PLL startup delay (simple PLL only). */ | |
580 | usleep_range(100, 150); | |
581 | } | |
c6a27fa4 LP |
582 | |
583 | /* Turn the output on. */ | |
584 | lvdcr0 |= LVDCR0_LVRES; | |
585 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | |
586 | ||
587 | if (lvds->panel) { | |
588 | drm_panel_prepare(lvds->panel); | |
589 | drm_panel_enable(lvds->panel); | |
590 | } | |
c6a27fa4 LP |
591 | } |
592 | ||
593885b0 | 593 | static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, |
41cf5712 | 594 | struct drm_bridge_state *old_bridge_state) |
593885b0 | 595 | { |
41cf5712 | 596 | struct drm_atomic_state *state = old_bridge_state->base.state; |
593885b0 LP |
597 | struct drm_connector *connector; |
598 | struct drm_crtc *crtc; | |
599 | ||
600 | connector = drm_atomic_get_new_connector_for_encoder(state, | |
601 | bridge->encoder); | |
602 | crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; | |
603 | ||
604 | __rcar_lvds_atomic_enable(bridge, state, crtc, connector); | |
605 | } | |
606 | ||
607 | static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, | |
41cf5712 | 608 | struct drm_bridge_state *old_bridge_state) |
c6a27fa4 LP |
609 | { |
610 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
611 | ||
c6a27fa4 LP |
612 | if (lvds->panel) { |
613 | drm_panel_disable(lvds->panel); | |
614 | drm_panel_unprepare(lvds->panel); | |
615 | } | |
616 | ||
617 | rcar_lvds_write(lvds, LVDCR0, 0); | |
618 | rcar_lvds_write(lvds, LVDCR1, 0); | |
c25c0136 | 619 | rcar_lvds_write(lvds, LVDPLLCR, 0); |
c6a27fa4 | 620 | |
fa440d87 | 621 | /* Disable the companion LVDS encoder in dual-link mode. */ |
59c1f061 | 622 | if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) |
41cf5712 BB |
623 | lvds->companion->funcs->atomic_disable(lvds->companion, |
624 | old_bridge_state); | |
fa440d87 | 625 | |
c25c0136 | 626 | clk_disable_unprepare(lvds->clocks.mod); |
c6a27fa4 LP |
627 | } |
628 | ||
629 | static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge, | |
630 | const struct drm_display_mode *mode, | |
631 | struct drm_display_mode *adjusted_mode) | |
632 | { | |
87137030 LP |
633 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); |
634 | int min_freq; | |
635 | ||
c6a27fa4 LP |
636 | /* |
637 | * The internal LVDS encoder has a restricted clock frequency operating | |
87137030 LP |
638 | * range, from 5MHz to 148.5MHz on D3 and E3, and from 31MHz to |
639 | * 148.5MHz on all other platforms. Clamp the clock accordingly. | |
c6a27fa4 | 640 | */ |
87137030 LP |
641 | min_freq = lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL ? 5000 : 31000; |
642 | adjusted_mode->clock = clamp(adjusted_mode->clock, min_freq, 148500); | |
c6a27fa4 LP |
643 | |
644 | return true; | |
645 | } | |
646 | ||
a25b988f LP |
647 | static int rcar_lvds_attach(struct drm_bridge *bridge, |
648 | enum drm_bridge_attach_flags flags) | |
c6a27fa4 LP |
649 | { |
650 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
651 | struct drm_connector *connector = &lvds->connector; | |
652 | struct drm_encoder *encoder = bridge->encoder; | |
653 | int ret; | |
654 | ||
655 | /* If we have a next bridge just attach it. */ | |
656 | if (lvds->next_bridge) | |
657 | return drm_bridge_attach(bridge->encoder, lvds->next_bridge, | |
a25b988f LP |
658 | bridge, flags); |
659 | ||
660 | if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { | |
661 | DRM_ERROR("Fix bridge driver to make connector optional!"); | |
662 | return -EINVAL; | |
663 | } | |
c6a27fa4 | 664 | |
6e1f8557 LP |
665 | /* Otherwise if we have a panel, create a connector. */ |
666 | if (!lvds->panel) | |
667 | return 0; | |
668 | ||
c6a27fa4 LP |
669 | ret = drm_connector_init(bridge->dev, connector, &rcar_lvds_conn_funcs, |
670 | DRM_MODE_CONNECTOR_LVDS); | |
671 | if (ret < 0) | |
672 | return ret; | |
673 | ||
674 | drm_connector_helper_add(connector, &rcar_lvds_conn_helper_funcs); | |
675 | ||
cde4c44d | 676 | ret = drm_connector_attach_encoder(connector, encoder); |
c6a27fa4 LP |
677 | if (ret < 0) |
678 | return ret; | |
679 | ||
680 | return drm_panel_attach(lvds->panel, connector); | |
681 | } | |
682 | ||
683 | static void rcar_lvds_detach(struct drm_bridge *bridge) | |
684 | { | |
685 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
686 | ||
687 | if (lvds->panel) | |
688 | drm_panel_detach(lvds->panel); | |
689 | } | |
690 | ||
691 | static const struct drm_bridge_funcs rcar_lvds_bridge_ops = { | |
692 | .attach = rcar_lvds_attach, | |
693 | .detach = rcar_lvds_detach, | |
dc8c609b BB |
694 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
695 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, | |
696 | .atomic_reset = drm_atomic_helper_bridge_reset, | |
593885b0 LP |
697 | .atomic_enable = rcar_lvds_atomic_enable, |
698 | .atomic_disable = rcar_lvds_atomic_disable, | |
c6a27fa4 | 699 | .mode_fixup = rcar_lvds_mode_fixup, |
c6a27fa4 LP |
700 | }; |
701 | ||
fa440d87 LP |
702 | bool rcar_lvds_dual_link(struct drm_bridge *bridge) |
703 | { | |
704 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | |
705 | ||
59c1f061 | 706 | return lvds->link_type != RCAR_LVDS_SINGLE_LINK; |
fa440d87 LP |
707 | } |
708 | EXPORT_SYMBOL_GPL(rcar_lvds_dual_link); | |
709 | ||
c6a27fa4 LP |
710 | /* ----------------------------------------------------------------------------- |
711 | * Probe & Remove | |
712 | */ | |
713 | ||
fa440d87 LP |
714 | static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds) |
715 | { | |
716 | const struct of_device_id *match; | |
717 | struct device_node *companion; | |
65112cfa FC |
718 | struct device_node *port0, *port1; |
719 | struct rcar_lvds *companion_lvds; | |
fa440d87 | 720 | struct device *dev = lvds->dev; |
65112cfa | 721 | int dual_link; |
fa440d87 LP |
722 | int ret = 0; |
723 | ||
724 | /* Locate the companion LVDS encoder for dual-link operation, if any. */ | |
725 | companion = of_parse_phandle(dev->of_node, "renesas,companion", 0); | |
a53358a3 JM |
726 | if (!companion) |
727 | return 0; | |
fa440d87 LP |
728 | |
729 | /* | |
730 | * Sanity check: the companion encoder must have the same compatible | |
731 | * string. | |
732 | */ | |
733 | match = of_match_device(dev->driver->of_match_table, dev); | |
734 | if (!of_device_is_compatible(companion, match->compatible)) { | |
735 | dev_err(dev, "Companion LVDS encoder is invalid\n"); | |
736 | ret = -ENXIO; | |
737 | goto done; | |
738 | } | |
739 | ||
65112cfa FC |
740 | /* |
741 | * We need to work out if the sink is expecting us to function in | |
742 | * dual-link mode. We do this by looking at the DT port nodes we are | |
743 | * connected to, if they are marked as expecting even pixels and | |
744 | * odd pixels than we need to enable vertical stripe output. | |
745 | */ | |
746 | port0 = of_graph_get_port_by_id(dev->of_node, 1); | |
747 | port1 = of_graph_get_port_by_id(companion, 1); | |
748 | dual_link = drm_of_lvds_get_dual_link_pixel_order(port0, port1); | |
749 | of_node_put(port0); | |
750 | of_node_put(port1); | |
751 | ||
59c1f061 FC |
752 | switch (dual_link) { |
753 | case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: | |
754 | lvds->link_type = RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; | |
755 | break; | |
756 | case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: | |
757 | lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; | |
758 | break; | |
759 | default: | |
65112cfa FC |
760 | /* |
761 | * Early dual-link bridge specific implementations populate the | |
59c1f061 FC |
762 | * timings field of drm_bridge. If the flag is set, we assume |
763 | * that we are expected to generate even pixels from the primary | |
764 | * encoder, and odd pixels from the companion encoder. | |
65112cfa | 765 | */ |
59c1f061 FC |
766 | if (lvds->next_bridge && lvds->next_bridge->timings && |
767 | lvds->next_bridge->timings->dual_link) | |
768 | lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; | |
769 | else | |
770 | lvds->link_type = RCAR_LVDS_SINGLE_LINK; | |
771 | } | |
65112cfa | 772 | |
59c1f061 | 773 | if (lvds->link_type == RCAR_LVDS_SINGLE_LINK) { |
65112cfa FC |
774 | dev_dbg(dev, "Single-link configuration detected\n"); |
775 | goto done; | |
776 | } | |
777 | ||
fa440d87 LP |
778 | lvds->companion = of_drm_find_bridge(companion); |
779 | if (!lvds->companion) { | |
780 | ret = -EPROBE_DEFER; | |
781 | goto done; | |
782 | } | |
783 | ||
65112cfa FC |
784 | dev_dbg(dev, |
785 | "Dual-link configuration detected (companion encoder %pOF)\n", | |
786 | companion); | |
787 | ||
59c1f061 FC |
788 | if (lvds->link_type == RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) |
789 | dev_dbg(dev, "Data swapping required\n"); | |
790 | ||
65112cfa FC |
791 | /* |
792 | * FIXME: We should not be messing with the companion encoder private | |
793 | * data from the primary encoder, we should rather let the companion | |
794 | * encoder work things out on its own. However, the companion encoder | |
795 | * doesn't hold a reference to the primary encoder, and | |
796 | * drm_of_lvds_get_dual_link_pixel_order needs to be given references | |
797 | * to the output ports of both encoders, therefore leave it like this | |
798 | * for the time being. | |
799 | */ | |
800 | companion_lvds = bridge_to_rcar_lvds(lvds->companion); | |
59c1f061 | 801 | companion_lvds->link_type = lvds->link_type; |
fa440d87 LP |
802 | |
803 | done: | |
804 | of_node_put(companion); | |
805 | ||
806 | return ret; | |
807 | } | |
808 | ||
c6a27fa4 LP |
809 | static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) |
810 | { | |
990e378d | 811 | int ret; |
c6a27fa4 | 812 | |
990e378d FC |
813 | ret = drm_of_find_panel_or_bridge(lvds->dev->of_node, 1, 0, |
814 | &lvds->panel, &lvds->next_bridge); | |
815 | if (ret) | |
c6a27fa4 | 816 | goto done; |
c6a27fa4 | 817 | |
65112cfa | 818 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) |
fa440d87 LP |
819 | ret = rcar_lvds_parse_dt_companion(lvds); |
820 | ||
c6a27fa4 | 821 | done: |
6e1f8557 LP |
822 | /* |
823 | * On D3/E3 the LVDS encoder provides a clock to the DU, which can be | |
824 | * used for the DPAD output even when the LVDS output is not connected. | |
825 | * Don't fail probe in that case as the DU will need the bridge to | |
826 | * control the clock. | |
827 | */ | |
828 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) | |
829 | return ret == -ENODEV ? 0 : ret; | |
830 | ||
c6a27fa4 LP |
831 | return ret; |
832 | } | |
833 | ||
c25c0136 LP |
834 | static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name, |
835 | bool optional) | |
836 | { | |
837 | struct clk *clk; | |
838 | ||
839 | clk = devm_clk_get(lvds->dev, name); | |
840 | if (!IS_ERR(clk)) | |
841 | return clk; | |
842 | ||
843 | if (PTR_ERR(clk) == -ENOENT && optional) | |
844 | return NULL; | |
845 | ||
846 | if (PTR_ERR(clk) != -EPROBE_DEFER) | |
847 | dev_err(lvds->dev, "failed to get %s clock\n", | |
848 | name ? name : "module"); | |
849 | ||
850 | return clk; | |
851 | } | |
852 | ||
853 | static int rcar_lvds_get_clocks(struct rcar_lvds *lvds) | |
854 | { | |
855 | lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false); | |
856 | if (IS_ERR(lvds->clocks.mod)) | |
857 | return PTR_ERR(lvds->clocks.mod); | |
858 | ||
859 | /* | |
860 | * LVDS encoders without an extended PLL have no external clock inputs. | |
861 | */ | |
862 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) | |
863 | return 0; | |
864 | ||
865 | lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true); | |
866 | if (IS_ERR(lvds->clocks.extal)) | |
867 | return PTR_ERR(lvds->clocks.extal); | |
868 | ||
869 | lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true); | |
870 | if (IS_ERR(lvds->clocks.dotclkin[0])) | |
871 | return PTR_ERR(lvds->clocks.dotclkin[0]); | |
872 | ||
873 | lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true); | |
874 | if (IS_ERR(lvds->clocks.dotclkin[1])) | |
875 | return PTR_ERR(lvds->clocks.dotclkin[1]); | |
876 | ||
877 | /* At least one input to the PLL must be available. */ | |
878 | if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] && | |
879 | !lvds->clocks.dotclkin[1]) { | |
880 | dev_err(lvds->dev, | |
881 | "no input clock (extal, dclkin.0 or dclkin.1)\n"); | |
882 | return -EINVAL; | |
883 | } | |
884 | ||
885 | return 0; | |
886 | } | |
887 | ||
39864571 GU |
888 | static const struct rcar_lvds_device_info rcar_lvds_r8a7790es1_info = { |
889 | .gen = 2, | |
890 | .quirks = RCAR_LVDS_QUIRK_LANES, | |
891 | .pll_setup = rcar_lvds_pll_setup_gen2, | |
892 | }; | |
893 | ||
894 | static const struct soc_device_attribute lvds_quirk_matches[] = { | |
895 | { | |
896 | .soc_id = "r8a7790", .revision = "ES1.*", | |
897 | .data = &rcar_lvds_r8a7790es1_info, | |
898 | }, | |
899 | { /* sentinel */ } | |
900 | }; | |
901 | ||
c6a27fa4 LP |
902 | static int rcar_lvds_probe(struct platform_device *pdev) |
903 | { | |
39864571 | 904 | const struct soc_device_attribute *attr; |
c6a27fa4 LP |
905 | struct rcar_lvds *lvds; |
906 | struct resource *mem; | |
907 | int ret; | |
908 | ||
909 | lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); | |
910 | if (lvds == NULL) | |
911 | return -ENOMEM; | |
912 | ||
913 | platform_set_drvdata(pdev, lvds); | |
914 | ||
915 | lvds->dev = &pdev->dev; | |
916 | lvds->info = of_device_get_match_data(&pdev->dev); | |
c6a27fa4 | 917 | |
39864571 GU |
918 | attr = soc_device_match(lvds_quirk_matches); |
919 | if (attr) | |
920 | lvds->info = attr->data; | |
921 | ||
c6a27fa4 LP |
922 | ret = rcar_lvds_parse_dt(lvds); |
923 | if (ret < 0) | |
924 | return ret; | |
925 | ||
926 | lvds->bridge.driver_private = lvds; | |
927 | lvds->bridge.funcs = &rcar_lvds_bridge_ops; | |
928 | lvds->bridge.of_node = pdev->dev.of_node; | |
929 | ||
930 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
931 | lvds->mmio = devm_ioremap_resource(&pdev->dev, mem); | |
932 | if (IS_ERR(lvds->mmio)) | |
933 | return PTR_ERR(lvds->mmio); | |
934 | ||
c25c0136 LP |
935 | ret = rcar_lvds_get_clocks(lvds); |
936 | if (ret < 0) | |
937 | return ret; | |
c6a27fa4 LP |
938 | |
939 | drm_bridge_add(&lvds->bridge); | |
940 | ||
941 | return 0; | |
942 | } | |
943 | ||
944 | static int rcar_lvds_remove(struct platform_device *pdev) | |
945 | { | |
946 | struct rcar_lvds *lvds = platform_get_drvdata(pdev); | |
947 | ||
948 | drm_bridge_remove(&lvds->bridge); | |
949 | ||
950 | return 0; | |
951 | } | |
952 | ||
953 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { | |
954 | .gen = 2, | |
c25c0136 | 955 | .pll_setup = rcar_lvds_pll_setup_gen2, |
c6a27fa4 LP |
956 | }; |
957 | ||
c6a27fa4 LP |
958 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { |
959 | .gen = 3, | |
c25c0136 LP |
960 | .quirks = RCAR_LVDS_QUIRK_PWD, |
961 | .pll_setup = rcar_lvds_pll_setup_gen3, | |
c6a27fa4 LP |
962 | }; |
963 | ||
b6eb7102 SS |
964 | static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { |
965 | .gen = 3, | |
c25c0136 LP |
966 | .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN, |
967 | .pll_setup = rcar_lvds_pll_setup_gen2, | |
968 | }; | |
969 | ||
970 | static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = { | |
971 | .gen = 3, | |
972 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL | |
973 | | RCAR_LVDS_QUIRK_DUAL_LINK, | |
974 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | |
975 | }; | |
976 | ||
977 | static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { | |
978 | .gen = 3, | |
979 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD | |
980 | | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, | |
981 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | |
b6eb7102 SS |
982 | }; |
983 | ||
c6a27fa4 LP |
984 | static const struct of_device_id rcar_lvds_of_table[] = { |
985 | { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info }, | |
fc59d7d4 | 986 | { .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info }, |
62e2da68 | 987 | { .compatible = "renesas,r8a774a1-lvds", .data = &rcar_lvds_gen3_info }, |
cfbcf6eb | 988 | { .compatible = "renesas,r8a774b1-lvds", .data = &rcar_lvds_gen3_info }, |
90b5f679 | 989 | { .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info }, |
39864571 | 990 | { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_gen2_info }, |
c6a27fa4 LP |
991 | { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info }, |
992 | { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info }, | |
993 | { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info }, | |
994 | { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, | |
03e37b50 | 995 | { .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info }, |
b6eb7102 | 996 | { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, |
c4341442 | 997 | { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, |
c25c0136 LP |
998 | { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, |
999 | { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info }, | |
c6a27fa4 LP |
1000 | { } |
1001 | }; | |
1002 | ||
1003 | MODULE_DEVICE_TABLE(of, rcar_lvds_of_table); | |
1004 | ||
1005 | static struct platform_driver rcar_lvds_platform_driver = { | |
1006 | .probe = rcar_lvds_probe, | |
1007 | .remove = rcar_lvds_remove, | |
1008 | .driver = { | |
1009 | .name = "rcar-lvds", | |
1010 | .of_match_table = rcar_lvds_of_table, | |
1011 | }, | |
1012 | }; | |
1013 | ||
1014 | module_platform_driver(rcar_lvds_platform_driver); | |
1015 | ||
1016 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | |
1017 | MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver"); | |
1018 | MODULE_LICENSE("GPL"); |