blob: 1247e26a0559cff3ae59fe53f441fe2fbbca0094 [file] [log] [blame]
Laurent Pinchartc6a27fa2018-01-10 05:47:42 +02001// 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>
13#include <linux/of.h>
14#include <linux/of_device.h>
15#include <linux/of_graph.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18
19#include <drm/drm_atomic.h>
20#include <drm/drm_atomic_helper.h>
21#include <drm/drm_bridge.h>
22#include <drm/drm_crtc_helper.h>
23#include <drm/drm_panel.h>
24
25#include "rcar_lvds_regs.h"
26
27/* Keep in sync with the LVDCR0.LVMD hardware register values. */
28enum rcar_lvds_mode {
29 RCAR_LVDS_MODE_JEIDA = 0,
30 RCAR_LVDS_MODE_MIRROR = 1,
31 RCAR_LVDS_MODE_VESA = 4,
32};
33
34#define RCAR_LVDS_QUIRK_LANES (1 << 0) /* LVDS lanes 1 and 3 inverted */
35
36struct rcar_lvds_device_info {
37 unsigned int gen;
38 unsigned int quirks;
39};
40
41struct rcar_lvds {
42 struct device *dev;
43 const struct rcar_lvds_device_info *info;
44
45 struct drm_bridge bridge;
46
47 struct drm_bridge *next_bridge;
48 struct drm_connector connector;
49 struct drm_panel *panel;
50
51 void __iomem *mmio;
52 struct clk *clock;
53 bool enabled;
54
55 struct drm_display_mode display_mode;
56 enum rcar_lvds_mode mode;
57};
58
59#define bridge_to_rcar_lvds(bridge) \
60 container_of(bridge, struct rcar_lvds, bridge)
61
62#define connector_to_rcar_lvds(connector) \
63 container_of(connector, struct rcar_lvds, connector)
64
65static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data)
66{
67 iowrite32(data, lvds->mmio + reg);
68}
69
70/* -----------------------------------------------------------------------------
71 * Connector & Panel
72 */
73
74static int rcar_lvds_connector_get_modes(struct drm_connector *connector)
75{
76 struct rcar_lvds *lvds = connector_to_rcar_lvds(connector);
77
78 return drm_panel_get_modes(lvds->panel);
79}
80
81static int rcar_lvds_connector_atomic_check(struct drm_connector *connector,
82 struct drm_connector_state *state)
83{
84 struct rcar_lvds *lvds = connector_to_rcar_lvds(connector);
85 const struct drm_display_mode *panel_mode;
86 struct drm_crtc_state *crtc_state;
87
88 if (list_empty(&connector->modes)) {
89 dev_dbg(lvds->dev, "connector: empty modes list\n");
90 return -EINVAL;
91 }
92
93 panel_mode = list_first_entry(&connector->modes,
94 struct drm_display_mode, head);
95
96 /* We're not allowed to modify the resolution. */
97 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
98 if (IS_ERR(crtc_state))
99 return PTR_ERR(crtc_state);
100
101 if (crtc_state->mode.hdisplay != panel_mode->hdisplay ||
102 crtc_state->mode.vdisplay != panel_mode->vdisplay)
103 return -EINVAL;
104
105 /* The flat panel mode is fixed, just copy it to the adjusted mode. */
106 drm_mode_copy(&crtc_state->adjusted_mode, panel_mode);
107
108 return 0;
109}
110
111static const struct drm_connector_helper_funcs rcar_lvds_conn_helper_funcs = {
112 .get_modes = rcar_lvds_connector_get_modes,
113 .atomic_check = rcar_lvds_connector_atomic_check,
114};
115
116static const struct drm_connector_funcs rcar_lvds_conn_funcs = {
117 .reset = drm_atomic_helper_connector_reset,
118 .fill_modes = drm_helper_probe_single_connector_modes,
119 .destroy = drm_connector_cleanup,
120 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
121 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
122};
123
124/* -----------------------------------------------------------------------------
125 * Bridge
126 */
127
128static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq)
129{
130 if (freq < 39000)
131 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
132 else if (freq < 61000)
133 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
134 else if (freq < 121000)
135 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
136 else
137 return LVDPLLCR_PLLDLYCNT_150M;
138}
139
140static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq)
141{
142 if (freq < 42000)
143 return LVDPLLCR_PLLDIVCNT_42M;
144 else if (freq < 85000)
145 return LVDPLLCR_PLLDIVCNT_85M;
146 else if (freq < 128000)
147 return LVDPLLCR_PLLDIVCNT_128M;
148 else
149 return LVDPLLCR_PLLDIVCNT_148M;
150}
151
152static void rcar_lvds_enable(struct drm_bridge *bridge)
153{
154 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
155 const struct drm_display_mode *mode = &lvds->display_mode;
156 /*
157 * FIXME: We should really retrieve the CRTC through the state, but how
158 * do we get a state pointer?
159 */
160 struct drm_crtc *crtc = lvds->bridge.encoder->crtc;
161 u32 lvdpllcr;
162 u32 lvdhcr;
163 u32 lvdcr0;
164 int ret;
165
166 WARN_ON(lvds->enabled);
167
168 ret = clk_prepare_enable(lvds->clock);
169 if (ret < 0)
170 return;
171
172 /*
173 * Hardcode the channels and control signals routing for now.
174 *
175 * HSYNC -> CTRL0
176 * VSYNC -> CTRL1
177 * DISP -> CTRL2
178 * 0 -> CTRL3
179 */
180 rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
181 LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
182 LVDCTRCR_CTR0SEL_HSYNC);
183
184 if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES)
185 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
186 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
187 else
188 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
189 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
190
191 rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
192
193 /* PLL clock configuration. */
194 if (lvds->info->gen < 3)
195 lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock);
196 else
197 lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock);
198 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
199
200 /* Set the LVDS mode and select the input. */
201 lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
202 if (drm_crtc_index(crtc) == 2)
203 lvdcr0 |= LVDCR0_DUSEL;
204 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
205
206 /* Turn all the channels on. */
207 rcar_lvds_write(lvds, LVDCR1,
208 LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
209 LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
210
211 if (lvds->info->gen < 3) {
212 /* Enable LVDS operation and turn the bias circuitry on. */
213 lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
214 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
215 }
216
217 /* Turn the PLL on. */
218 lvdcr0 |= LVDCR0_PLLON;
219 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
220
221 if (lvds->info->gen > 2) {
222 /* Set LVDS normal mode. */
223 lvdcr0 |= LVDCR0_PWD;
224 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
225 }
226
227 /* Wait for the startup delay. */
228 usleep_range(100, 150);
229
230 /* Turn the output on. */
231 lvdcr0 |= LVDCR0_LVRES;
232 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
233
234 if (lvds->panel) {
235 drm_panel_prepare(lvds->panel);
236 drm_panel_enable(lvds->panel);
237 }
238
239 lvds->enabled = true;
240}
241
242static void rcar_lvds_disable(struct drm_bridge *bridge)
243{
244 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
245
246 WARN_ON(!lvds->enabled);
247
248 if (lvds->panel) {
249 drm_panel_disable(lvds->panel);
250 drm_panel_unprepare(lvds->panel);
251 }
252
253 rcar_lvds_write(lvds, LVDCR0, 0);
254 rcar_lvds_write(lvds, LVDCR1, 0);
255
256 clk_disable_unprepare(lvds->clock);
257
258 lvds->enabled = false;
259}
260
261static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
262 const struct drm_display_mode *mode,
263 struct drm_display_mode *adjusted_mode)
264{
265 /*
266 * The internal LVDS encoder has a restricted clock frequency operating
267 * range (31MHz to 148.5MHz). Clamp the clock accordingly.
268 */
269 adjusted_mode->clock = clamp(adjusted_mode->clock, 31000, 148500);
270
271 return true;
272}
273
274static void rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds)
275{
276 struct drm_display_info *info = &lvds->connector.display_info;
277 enum rcar_lvds_mode mode;
278
279 /*
280 * There is no API yet to retrieve LVDS mode from a bridge, only panels
281 * are supported.
282 */
283 if (!lvds->panel)
284 return;
285
286 if (!info->num_bus_formats || !info->bus_formats) {
287 dev_err(lvds->dev, "no LVDS bus format reported\n");
288 return;
289 }
290
291 switch (info->bus_formats[0]) {
292 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
293 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
294 mode = RCAR_LVDS_MODE_JEIDA;
295 break;
296 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
297 mode = RCAR_LVDS_MODE_VESA;
298 break;
299 default:
300 dev_err(lvds->dev, "unsupported LVDS bus format 0x%04x\n",
301 info->bus_formats[0]);
302 return;
303 }
304
305 if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
306 mode |= RCAR_LVDS_MODE_MIRROR;
307
308 lvds->mode = mode;
309}
310
311static void rcar_lvds_mode_set(struct drm_bridge *bridge,
312 struct drm_display_mode *mode,
313 struct drm_display_mode *adjusted_mode)
314{
315 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
316
317 WARN_ON(lvds->enabled);
318
319 lvds->display_mode = *adjusted_mode;
320
321 rcar_lvds_get_lvds_mode(lvds);
322}
323
324static int rcar_lvds_attach(struct drm_bridge *bridge)
325{
326 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
327 struct drm_connector *connector = &lvds->connector;
328 struct drm_encoder *encoder = bridge->encoder;
329 int ret;
330
331 /* If we have a next bridge just attach it. */
332 if (lvds->next_bridge)
333 return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
334 bridge);
335
336 /* Otherwise we have a panel, create a connector. */
337 ret = drm_connector_init(bridge->dev, connector, &rcar_lvds_conn_funcs,
338 DRM_MODE_CONNECTOR_LVDS);
339 if (ret < 0)
340 return ret;
341
342 drm_connector_helper_add(connector, &rcar_lvds_conn_helper_funcs);
343
344 ret = drm_mode_connector_attach_encoder(connector, encoder);
345 if (ret < 0)
346 return ret;
347
348 return drm_panel_attach(lvds->panel, connector);
349}
350
351static void rcar_lvds_detach(struct drm_bridge *bridge)
352{
353 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
354
355 if (lvds->panel)
356 drm_panel_detach(lvds->panel);
357}
358
359static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
360 .attach = rcar_lvds_attach,
361 .detach = rcar_lvds_detach,
362 .enable = rcar_lvds_enable,
363 .disable = rcar_lvds_disable,
364 .mode_fixup = rcar_lvds_mode_fixup,
365 .mode_set = rcar_lvds_mode_set,
366};
367
368/* -----------------------------------------------------------------------------
369 * Probe & Remove
370 */
371
372static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
373{
374 struct device_node *local_output = NULL;
375 struct device_node *remote_input = NULL;
376 struct device_node *remote = NULL;
377 struct device_node *node;
378 bool is_bridge = false;
379 int ret = 0;
380
381 local_output = of_graph_get_endpoint_by_regs(lvds->dev->of_node, 1, 0);
382 if (!local_output) {
383 dev_dbg(lvds->dev, "unconnected port@1\n");
384 return -ENODEV;
385 }
386
387 /*
388 * Locate the connected entity and infer its type from the number of
389 * endpoints.
390 */
391 remote = of_graph_get_remote_port_parent(local_output);
392 if (!remote) {
393 dev_dbg(lvds->dev, "unconnected endpoint %pOF\n", local_output);
394 ret = -ENODEV;
395 goto done;
396 }
397
398 if (!of_device_is_available(remote)) {
399 dev_dbg(lvds->dev, "connected entity %pOF is disabled\n",
400 remote);
401 ret = -ENODEV;
402 goto done;
403 }
404
405 remote_input = of_graph_get_remote_endpoint(local_output);
406
407 for_each_endpoint_of_node(remote, node) {
408 if (node != remote_input) {
409 /*
410 * We've found one endpoint other than the input, this
411 * must be a bridge.
412 */
413 is_bridge = true;
414 of_node_put(node);
415 break;
416 }
417 }
418
419 if (is_bridge) {
420 lvds->next_bridge = of_drm_find_bridge(remote);
421 if (!lvds->next_bridge)
422 ret = -EPROBE_DEFER;
423 } else {
424 lvds->panel = of_drm_find_panel(remote);
425 if (!lvds->panel)
426 ret = -EPROBE_DEFER;
427 }
428
429done:
430 of_node_put(local_output);
431 of_node_put(remote_input);
432 of_node_put(remote);
433
434 return ret;
435}
436
437static int rcar_lvds_probe(struct platform_device *pdev)
438{
439 struct rcar_lvds *lvds;
440 struct resource *mem;
441 int ret;
442
443 lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
444 if (lvds == NULL)
445 return -ENOMEM;
446
447 platform_set_drvdata(pdev, lvds);
448
449 lvds->dev = &pdev->dev;
450 lvds->info = of_device_get_match_data(&pdev->dev);
451 lvds->enabled = false;
452
453 ret = rcar_lvds_parse_dt(lvds);
454 if (ret < 0)
455 return ret;
456
457 lvds->bridge.driver_private = lvds;
458 lvds->bridge.funcs = &rcar_lvds_bridge_ops;
459 lvds->bridge.of_node = pdev->dev.of_node;
460
461 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
462 lvds->mmio = devm_ioremap_resource(&pdev->dev, mem);
463 if (IS_ERR(lvds->mmio))
464 return PTR_ERR(lvds->mmio);
465
466 lvds->clock = devm_clk_get(&pdev->dev, NULL);
467 if (IS_ERR(lvds->clock)) {
468 dev_err(&pdev->dev, "failed to get clock\n");
469 return PTR_ERR(lvds->clock);
470 }
471
472 drm_bridge_add(&lvds->bridge);
473
474 return 0;
475}
476
477static int rcar_lvds_remove(struct platform_device *pdev)
478{
479 struct rcar_lvds *lvds = platform_get_drvdata(pdev);
480
481 drm_bridge_remove(&lvds->bridge);
482
483 return 0;
484}
485
486static const struct rcar_lvds_device_info rcar_lvds_gen2_info = {
487 .gen = 2,
488};
489
490static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = {
491 .gen = 2,
492 .quirks = RCAR_LVDS_QUIRK_LANES,
493};
494
495static const struct rcar_lvds_device_info rcar_lvds_gen3_info = {
496 .gen = 3,
497};
498
499static const struct of_device_id rcar_lvds_of_table[] = {
500 { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
501 { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_r8a7790_info },
502 { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
503 { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
504 { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info },
505 { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
506 { }
507};
508
509MODULE_DEVICE_TABLE(of, rcar_lvds_of_table);
510
511static struct platform_driver rcar_lvds_platform_driver = {
512 .probe = rcar_lvds_probe,
513 .remove = rcar_lvds_remove,
514 .driver = {
515 .name = "rcar-lvds",
516 .of_match_table = rcar_lvds_of_table,
517 },
518};
519
520module_platform_driver(rcar_lvds_platform_driver);
521
522MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
523MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver");
524MODULE_LICENSE("GPL");