aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig4
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile3
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c21
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h5
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.c175
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.h12
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c14
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c93
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h24
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c238
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h64
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.c524
12 files changed, 561 insertions, 616 deletions
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 3f83352a7313..edde8d4b87a3 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -19,8 +19,8 @@ config DRM_RCAR_DW_HDMI
19 Enable support for R-Car Gen3 internal HDMI encoder. 19 Enable support for R-Car Gen3 internal HDMI encoder.
20 20
21config DRM_RCAR_LVDS 21config DRM_RCAR_LVDS
22 bool "R-Car DU LVDS Encoder Support" 22 tristate "R-Car DU LVDS Encoder Support"
23 depends on DRM_RCAR_DU 23 depends on DRM && DRM_BRIDGE && OF
24 select DRM_PANEL 24 select DRM_PANEL
25 select OF_FLATTREE 25 select OF_FLATTREE
26 select OF_OVERLAY 26 select OF_OVERLAY
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 86b337b4be5d..3e58ed93d5b1 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -4,10 +4,8 @@ rcar-du-drm-y := rcar_du_crtc.o \
4 rcar_du_encoder.o \ 4 rcar_du_encoder.o \
5 rcar_du_group.o \ 5 rcar_du_group.o \
6 rcar_du_kms.o \ 6 rcar_du_kms.o \
7 rcar_du_lvdscon.o \
8 rcar_du_plane.o 7 rcar_du_plane.o
9 8
10rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
11rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \ 9rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
12 rcar_du_of_lvds_r8a7790.dtb.o \ 10 rcar_du_of_lvds_r8a7790.dtb.o \
13 rcar_du_of_lvds_r8a7791.dtb.o \ 11 rcar_du_of_lvds_r8a7791.dtb.o \
@@ -18,3 +16,4 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
18 16
19obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 17obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
20obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o 18obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
19obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 6e02c762a557..06a3fbdd728a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -29,6 +29,7 @@
29 29
30#include "rcar_du_drv.h" 30#include "rcar_du_drv.h"
31#include "rcar_du_kms.h" 31#include "rcar_du_kms.h"
32#include "rcar_du_of.h"
32#include "rcar_du_regs.h" 33#include "rcar_du_regs.h"
33 34
34/* ----------------------------------------------------------------------------- 35/* -----------------------------------------------------------------------------
@@ -74,7 +75,6 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
74 .port = 1, 75 .port = 1,
75 }, 76 },
76 }, 77 },
77 .num_lvds = 0,
78}; 78};
79 79
80static const struct rcar_du_device_info rcar_du_r8a7779_info = { 80static const struct rcar_du_device_info rcar_du_r8a7779_info = {
@@ -95,14 +95,13 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = {
95 .port = 1, 95 .port = 1,
96 }, 96 },
97 }, 97 },
98 .num_lvds = 0,
99}; 98};
100 99
101static const struct rcar_du_device_info rcar_du_r8a7790_info = { 100static const struct rcar_du_device_info rcar_du_r8a7790_info = {
102 .gen = 2, 101 .gen = 2,
103 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 102 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
104 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 103 | RCAR_DU_FEATURE_EXT_CTRL_REGS,
105 .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, 104 .quirks = RCAR_DU_QUIRK_ALIGN_128B,
106 .num_crtcs = 3, 105 .num_crtcs = 3,
107 .routes = { 106 .routes = {
108 /* 107 /*
@@ -164,7 +163,6 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
164 .port = 1, 163 .port = 1,
165 }, 164 },
166 }, 165 },
167 .num_lvds = 0,
168}; 166};
169 167
170static const struct rcar_du_device_info rcar_du_r8a7794_info = { 168static const struct rcar_du_device_info rcar_du_r8a7794_info = {
@@ -186,7 +184,6 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
186 .port = 1, 184 .port = 1,
187 }, 185 },
188 }, 186 },
189 .num_lvds = 0,
190}; 187};
191 188
192static const struct rcar_du_device_info rcar_du_r8a7795_info = { 189static const struct rcar_du_device_info rcar_du_r8a7795_info = {
@@ -434,7 +431,19 @@ static struct platform_driver rcar_du_platform_driver = {
434 }, 431 },
435}; 432};
436 433
437module_platform_driver(rcar_du_platform_driver); 434static int __init rcar_du_init(void)
435{
436 rcar_du_of_init(rcar_du_of_table);
437
438 return platform_driver_register(&rcar_du_platform_driver);
439}
440module_init(rcar_du_init);
441
442static void __exit rcar_du_exit(void)
443{
444 platform_driver_unregister(&rcar_du_platform_driver);
445}
446module_exit(rcar_du_exit);
438 447
439MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 448MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
440MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); 449MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index f400fde65a0c..5c7ec15818c7 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -26,14 +26,12 @@ struct device;
26struct drm_device; 26struct drm_device;
27struct drm_fbdev_cma; 27struct drm_fbdev_cma;
28struct rcar_du_device; 28struct rcar_du_device;
29struct rcar_du_lvdsenc;
30 29
31#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ 30#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */
32#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */ 31#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */
33#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */ 32#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */
34 33
35#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */ 34#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
36#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
37 35
38/* 36/*
39 * struct rcar_du_output_routing - Output routing specification 37 * struct rcar_du_output_routing - Output routing specification
@@ -70,7 +68,6 @@ struct rcar_du_device_info {
70 68
71#define RCAR_DU_MAX_CRTCS 4 69#define RCAR_DU_MAX_CRTCS 4
72#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2) 70#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
73#define RCAR_DU_MAX_LVDS 2
74#define RCAR_DU_MAX_VSPS 4 71#define RCAR_DU_MAX_VSPS 4
75 72
76struct rcar_du_device { 73struct rcar_du_device {
@@ -96,8 +93,6 @@ struct rcar_du_device {
96 93
97 unsigned int dpad0_source; 94 unsigned int dpad0_source;
98 unsigned int vspd1_sink; 95 unsigned int vspd1_sink;
99
100 struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
101}; 96};
102 97
103static inline bool rcar_du_has(struct rcar_du_device *rcdu, 98static inline bool rcar_du_has(struct rcar_du_device *rcdu,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index ba8d2804c1d1..f9c933d3bae6 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -21,134 +21,22 @@
21#include "rcar_du_drv.h" 21#include "rcar_du_drv.h"
22#include "rcar_du_encoder.h" 22#include "rcar_du_encoder.h"
23#include "rcar_du_kms.h" 23#include "rcar_du_kms.h"
24#include "rcar_du_lvdscon.h"
25#include "rcar_du_lvdsenc.h"
26 24
27/* ----------------------------------------------------------------------------- 25/* -----------------------------------------------------------------------------
28 * Encoder 26 * Encoder
29 */ 27 */
30 28
31static void rcar_du_encoder_disable(struct drm_encoder *encoder)
32{
33 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
34
35 if (renc->connector && renc->connector->panel) {
36 drm_panel_disable(renc->connector->panel);
37 drm_panel_unprepare(renc->connector->panel);
38 }
39
40 if (renc->lvds)
41 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
42}
43
44static void rcar_du_encoder_enable(struct drm_encoder *encoder)
45{
46 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
47
48 if (renc->lvds)
49 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
50
51 if (renc->connector && renc->connector->panel) {
52 drm_panel_prepare(renc->connector->panel);
53 drm_panel_enable(renc->connector->panel);
54 }
55}
56
57static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
58 struct drm_crtc_state *crtc_state,
59 struct drm_connector_state *conn_state)
60{
61 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
62 struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
63 const struct drm_display_mode *mode = &crtc_state->mode;
64 struct drm_connector *connector = conn_state->connector;
65 struct drm_device *dev = encoder->dev;
66
67 /*
68 * Only panel-related encoder types require validation here, everything
69 * else is handled by the bridge drivers.
70 */
71 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
72 const struct drm_display_mode *panel_mode;
73
74 if (list_empty(&connector->modes)) {
75 dev_dbg(dev->dev, "encoder: empty modes list\n");
76 return -EINVAL;
77 }
78
79 panel_mode = list_first_entry(&connector->modes,
80 struct drm_display_mode, head);
81
82 /* We're not allowed to modify the resolution. */
83 if (mode->hdisplay != panel_mode->hdisplay ||
84 mode->vdisplay != panel_mode->vdisplay)
85 return -EINVAL;
86
87 /*
88 * The flat panel mode is fixed, just copy it to the adjusted
89 * mode.
90 */
91 drm_mode_copy(adjusted_mode, panel_mode);
92 }
93
94 if (renc->lvds)
95 rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode);
96
97 return 0;
98}
99
100static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, 29static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
101 struct drm_crtc_state *crtc_state, 30 struct drm_crtc_state *crtc_state,
102 struct drm_connector_state *conn_state) 31 struct drm_connector_state *conn_state)
103{ 32{
104 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 33 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
105 struct drm_display_info *info = &conn_state->connector->display_info;
106 enum rcar_lvds_mode mode;
107 34
108 rcar_du_crtc_route_output(crtc_state->crtc, renc->output); 35 rcar_du_crtc_route_output(crtc_state->crtc, renc->output);
109
110 if (!renc->lvds) {
111 /*
112 * The DU driver creates connectors only for the outputs of the
113 * internal LVDS encoders.
114 */
115 renc->connector = NULL;
116 return;
117 }
118
119 renc->connector = to_rcar_connector(conn_state->connector);
120
121 if (!info->num_bus_formats || !info->bus_formats) {
122 dev_err(encoder->dev->dev, "no LVDS bus format reported\n");
123 return;
124 }
125
126 switch (info->bus_formats[0]) {
127 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
128 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
129 mode = RCAR_LVDS_MODE_JEIDA;
130 break;
131 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
132 mode = RCAR_LVDS_MODE_VESA;
133 break;
134 default:
135 dev_err(encoder->dev->dev,
136 "unsupported LVDS bus format 0x%04x\n",
137 info->bus_formats[0]);
138 return;
139 }
140
141 if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
142 mode |= RCAR_LVDS_MODE_MIRROR;
143
144 rcar_du_lvdsenc_set_mode(renc->lvds, mode);
145} 36}
146 37
147static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 38static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
148 .atomic_mode_set = rcar_du_encoder_mode_set, 39 .atomic_mode_set = rcar_du_encoder_mode_set,
149 .disable = rcar_du_encoder_disable,
150 .enable = rcar_du_encoder_enable,
151 .atomic_check = rcar_du_encoder_atomic_check,
152}; 40};
153 41
154static const struct drm_encoder_funcs encoder_funcs = { 42static const struct drm_encoder_funcs encoder_funcs = {
@@ -172,33 +60,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
172 renc->output = output; 60 renc->output = output;
173 encoder = rcar_encoder_to_drm_encoder(renc); 61 encoder = rcar_encoder_to_drm_encoder(renc);
174 62
175 switch (output) { 63 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
176 case RCAR_DU_OUTPUT_LVDS0: 64 enc_node, output);
177 renc->lvds = rcdu->lvds[0];
178 break;
179 65
180 case RCAR_DU_OUTPUT_LVDS1: 66 /* Locate the DRM bridge from the encoder DT node. */
181 renc->lvds = rcdu->lvds[1]; 67 bridge = of_drm_find_bridge(enc_node);
182 break; 68 if (!bridge) {
183 69 ret = -EPROBE_DEFER;
184 default: 70 goto done;
185 break;
186 }
187
188 if (enc_node) {
189 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
190 enc_node, output);
191
192 /* Locate the DRM bridge from the encoder DT node. */
193 bridge = of_drm_find_bridge(enc_node);
194 if (!bridge) {
195 ret = -EPROBE_DEFER;
196 goto done;
197 }
198 } else {
199 dev_dbg(rcdu->dev,
200 "initializing internal encoder for output %u\n",
201 output);
202 } 71 }
203 72
204 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 73 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
@@ -208,28 +77,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
208 77
209 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 78 drm_encoder_helper_add(encoder, &encoder_helper_funcs);
210 79
211 if (bridge) { 80 /*
212 /* 81 * Attach the bridge to the encoder. The bridge will create the
213 * Attach the bridge to the encoder. The bridge will create the 82 * connector.
214 * connector. 83 */
215 */ 84 ret = drm_bridge_attach(encoder, bridge, NULL);
216 ret = drm_bridge_attach(encoder, bridge, NULL); 85 if (ret) {
217 if (ret) { 86 drm_encoder_cleanup(encoder);
218 drm_encoder_cleanup(encoder); 87 return ret;
219 return ret;
220 }
221 } else {
222 /* There's no bridge, create the connector manually. */
223 switch (output) {
224 case RCAR_DU_OUTPUT_LVDS0:
225 case RCAR_DU_OUTPUT_LVDS1:
226 ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
227 break;
228
229 default:
230 ret = -EINVAL;
231 break;
232 }
233 } 88 }
234 89
235done: 90done:
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 5422fa4df272..2d2abcacd169 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -19,13 +19,10 @@
19 19
20struct drm_panel; 20struct drm_panel;
21struct rcar_du_device; 21struct rcar_du_device;
22struct rcar_du_lvdsenc;
23 22
24struct rcar_du_encoder { 23struct rcar_du_encoder {
25 struct drm_encoder base; 24 struct drm_encoder base;
26 enum rcar_du_output output; 25 enum rcar_du_output output;
27 struct rcar_du_connector *connector;
28 struct rcar_du_lvdsenc *lvds;
29}; 26};
30 27
31#define to_rcar_encoder(e) \ 28#define to_rcar_encoder(e) \
@@ -33,15 +30,6 @@ struct rcar_du_encoder {
33 30
34#define rcar_encoder_to_drm_encoder(e) (&(e)->base) 31#define rcar_encoder_to_drm_encoder(e) (&(e)->base)
35 32
36struct rcar_du_connector {
37 struct drm_connector connector;
38 struct rcar_du_encoder *encoder;
39 struct drm_panel *panel;
40};
41
42#define to_rcar_connector(c) \
43 container_of(c, struct rcar_du_connector, connector)
44
45int rcar_du_encoder_init(struct rcar_du_device *rcdu, 33int rcar_du_encoder_init(struct rcar_du_device *rcdu,
46 enum rcar_du_output output, 34 enum rcar_du_output output,
47 struct device_node *enc_node, 35 struct device_node *enc_node,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 566d1a948c8f..0329b354bfa0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -27,7 +27,6 @@
27#include "rcar_du_drv.h" 27#include "rcar_du_drv.h"
28#include "rcar_du_encoder.h" 28#include "rcar_du_encoder.h"
29#include "rcar_du_kms.h" 29#include "rcar_du_kms.h"
30#include "rcar_du_lvdsenc.h"
31#include "rcar_du_regs.h" 30#include "rcar_du_regs.h"
32#include "rcar_du_vsp.h" 31#include "rcar_du_vsp.h"
33 32
@@ -341,11 +340,10 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
341 of_node_put(entity_ep_node); 340 of_node_put(entity_ep_node);
342 341
343 if (!encoder) { 342 if (!encoder) {
344 /* 343 dev_warn(rcdu->dev,
345 * If no encoder has been found the entity must be the 344 "no encoder found for endpoint %pOF, skipping\n",
346 * connector. 345 ep->local_node);
347 */ 346 return -ENODEV;
348 connector = entity;
349 } 347 }
350 348
351 ret = rcar_du_encoder_init(rcdu, output, encoder, connector); 349 ret = rcar_du_encoder_init(rcdu, output, encoder, connector);
@@ -595,10 +593,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
595 } 593 }
596 594
597 /* Initialize the encoders. */ 595 /* Initialize the encoders. */
598 ret = rcar_du_lvdsenc_init(rcdu);
599 if (ret < 0)
600 return ret;
601
602 ret = rcar_du_encoders_init(rcdu); 596 ret = rcar_du_encoders_init(rcdu);
603 if (ret < 0) 597 if (ret < 0)
604 return ret; 598 return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
deleted file mode 100644
index e96f2df0c305..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ /dev/null
@@ -1,93 +0,0 @@
1/*
2 * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_atomic_helper.h>
16#include <drm/drm_crtc.h>
17#include <drm/drm_crtc_helper.h>
18#include <drm/drm_panel.h>
19
20#include <video/display_timing.h>
21#include <video/of_display_timing.h>
22#include <video/videomode.h>
23
24#include "rcar_du_drv.h"
25#include "rcar_du_encoder.h"
26#include "rcar_du_kms.h"
27#include "rcar_du_lvdscon.h"
28
29static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
30{
31 struct rcar_du_connector *rcon = to_rcar_connector(connector);
32
33 return drm_panel_get_modes(rcon->panel);
34}
35
36static const struct drm_connector_helper_funcs connector_helper_funcs = {
37 .get_modes = rcar_du_lvds_connector_get_modes,
38};
39
40static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
41{
42 struct rcar_du_connector *rcon = to_rcar_connector(connector);
43
44 drm_panel_detach(rcon->panel);
45 drm_connector_cleanup(connector);
46}
47
48static const struct drm_connector_funcs connector_funcs = {
49 .reset = drm_atomic_helper_connector_reset,
50 .fill_modes = drm_helper_probe_single_connector_modes,
51 .destroy = rcar_du_lvds_connector_destroy,
52 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
53 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
54};
55
56int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
57 struct rcar_du_encoder *renc,
58 const struct device_node *np)
59{
60 struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
61 struct rcar_du_connector *rcon;
62 struct drm_connector *connector;
63 int ret;
64
65 rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
66 if (rcon == NULL)
67 return -ENOMEM;
68
69 connector = &rcon->connector;
70
71 rcon->panel = of_drm_find_panel(np);
72 if (!rcon->panel)
73 return -EPROBE_DEFER;
74
75 ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
76 DRM_MODE_CONNECTOR_LVDS);
77 if (ret < 0)
78 return ret;
79
80 drm_connector_helper_add(connector, &connector_helper_funcs);
81
82 ret = drm_mode_connector_attach_encoder(connector, encoder);
83 if (ret < 0)
84 return ret;
85
86 ret = drm_panel_attach(rcon->panel, connector);
87 if (ret < 0)
88 return ret;
89
90 rcon->encoder = renc;
91
92 return 0;
93}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
deleted file mode 100644
index 639071dd235c..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ /dev/null
@@ -1,24 +0,0 @@
1/*
2 * rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_LVDSCON_H__
15#define __RCAR_DU_LVDSCON_H__
16
17struct rcar_du_device;
18struct rcar_du_encoder;
19
20int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
21 struct rcar_du_encoder *renc,
22 const struct device_node *np);
23
24#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
deleted file mode 100644
index 4defa8123eb2..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ /dev/null
@@ -1,238 +0,0 @@
1/*
2 * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19
20#include "rcar_du_drv.h"
21#include "rcar_du_encoder.h"
22#include "rcar_du_lvdsenc.h"
23#include "rcar_lvds_regs.h"
24
25struct rcar_du_lvdsenc {
26 struct rcar_du_device *dev;
27
28 unsigned int index;
29 void __iomem *mmio;
30 struct clk *clock;
31 bool enabled;
32
33 enum rcar_lvds_input input;
34 enum rcar_lvds_mode mode;
35};
36
37static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
38{
39 iowrite32(data, lvds->mmio + reg);
40}
41
42static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq)
43{
44 if (freq < 39000)
45 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
46 else if (freq < 61000)
47 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
48 else if (freq < 121000)
49 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
50 else
51 return LVDPLLCR_PLLDLYCNT_150M;
52}
53
54static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq)
55{
56 if (freq < 42000)
57 return LVDPLLCR_PLLDIVCNT_42M;
58 else if (freq < 85000)
59 return LVDPLLCR_PLLDIVCNT_85M;
60 else if (freq < 128000)
61 return LVDPLLCR_PLLDIVCNT_128M;
62 else
63 return LVDPLLCR_PLLDIVCNT_148M;
64}
65
66static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
67 struct rcar_du_crtc *rcrtc)
68{
69 const struct drm_display_mode *mode = &rcrtc->crtc.mode;
70 u32 lvdpllcr;
71 u32 lvdhcr;
72 u32 lvdcr0;
73 int ret;
74
75 if (lvds->enabled)
76 return 0;
77
78 ret = clk_prepare_enable(lvds->clock);
79 if (ret < 0)
80 return ret;
81
82 /*
83 * Hardcode the channels and control signals routing for now.
84 *
85 * HSYNC -> CTRL0
86 * VSYNC -> CTRL1
87 * DISP -> CTRL2
88 * 0 -> CTRL3
89 */
90 rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
91 LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
92 LVDCTRCR_CTR0SEL_HSYNC);
93
94 if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
95 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
96 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
97 else
98 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
99 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
100
101 rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
102
103 /* PLL clock configuration. */
104 if (lvds->dev->info->gen < 3)
105 lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock);
106 else
107 lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock);
108 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
109
110 /* Set the LVDS mode and select the input. */
111 lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
112 if (rcrtc->index == 2)
113 lvdcr0 |= LVDCR0_DUSEL;
114 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
115
116 /* Turn all the channels on. */
117 rcar_lvds_write(lvds, LVDCR1,
118 LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
119 LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
120
121 if (lvds->dev->info->gen < 3) {
122 /* Enable LVDS operation and turn the bias circuitry on. */
123 lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
124 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
125 }
126
127 /* Turn the PLL on. */
128 lvdcr0 |= LVDCR0_PLLON;
129 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
130
131 if (lvds->dev->info->gen > 2) {
132 /* Set LVDS normal mode. */
133 lvdcr0 |= LVDCR0_PWD;
134 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
135 }
136
137 /* Wait for the startup delay. */
138 usleep_range(100, 150);
139
140 /* Turn the output on. */
141 lvdcr0 |= LVDCR0_LVRES;
142 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
143
144 lvds->enabled = true;
145
146 return 0;
147}
148
149static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
150{
151 if (!lvds->enabled)
152 return;
153
154 rcar_lvds_write(lvds, LVDCR0, 0);
155 rcar_lvds_write(lvds, LVDCR1, 0);
156
157 clk_disable_unprepare(lvds->clock);
158
159 lvds->enabled = false;
160}
161
162int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
163 bool enable)
164{
165 if (!enable) {
166 rcar_du_lvdsenc_stop(lvds);
167 return 0;
168 } else if (crtc) {
169 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
170 return rcar_du_lvdsenc_start(lvds, rcrtc);
171 } else
172 return -EINVAL;
173}
174
175void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
176 struct drm_display_mode *mode)
177{
178 /*
179 * The internal LVDS encoder has a restricted clock frequency operating
180 * range (31MHz to 148.5MHz). Clamp the clock accordingly.
181 */
182 mode->clock = clamp(mode->clock, 31000, 148500);
183}
184
185void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
186 enum rcar_lvds_mode mode)
187{
188 lvds->mode = mode;
189}
190
191static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
192 struct platform_device *pdev)
193{
194 struct resource *mem;
195 char name[7];
196
197 sprintf(name, "lvds.%u", lvds->index);
198
199 mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
200 lvds->mmio = devm_ioremap_resource(&pdev->dev, mem);
201 if (IS_ERR(lvds->mmio))
202 return PTR_ERR(lvds->mmio);
203
204 lvds->clock = devm_clk_get(&pdev->dev, name);
205 if (IS_ERR(lvds->clock)) {
206 dev_err(&pdev->dev, "failed to get clock for %s\n", name);
207 return PTR_ERR(lvds->clock);
208 }
209
210 return 0;
211}
212
213int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
214{
215 struct platform_device *pdev = to_platform_device(rcdu->dev);
216 struct rcar_du_lvdsenc *lvds;
217 unsigned int i;
218 int ret;
219
220 for (i = 0; i < rcdu->info->num_lvds; ++i) {
221 lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
222 if (lvds == NULL)
223 return -ENOMEM;
224
225 lvds->dev = rcdu;
226 lvds->index = i;
227 lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
228 lvds->enabled = false;
229
230 ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
231 if (ret < 0)
232 return ret;
233
234 rcdu->lvds[i] = lvds;
235 }
236
237 return 0;
238}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
deleted file mode 100644
index 7218ac89333e..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
+++ /dev/null
@@ -1,64 +0,0 @@
1/*
2 * rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_LVDSENC_H__
15#define __RCAR_DU_LVDSENC_H__
16
17#include <linux/io.h>
18#include <linux/module.h>
19
20struct rcar_drm_crtc;
21struct rcar_du_lvdsenc;
22
23enum rcar_lvds_input {
24 RCAR_LVDS_INPUT_DU0,
25 RCAR_LVDS_INPUT_DU1,
26 RCAR_LVDS_INPUT_DU2,
27};
28
29/* Keep in sync with the LVDCR0.LVMD hardware register values. */
30enum rcar_lvds_mode {
31 RCAR_LVDS_MODE_JEIDA = 0,
32 RCAR_LVDS_MODE_MIRROR = 1,
33 RCAR_LVDS_MODE_VESA = 4,
34};
35
36#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
37int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
38void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
39 enum rcar_lvds_mode mode);
40int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
41 struct drm_crtc *crtc, bool enable);
42void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
43 struct drm_display_mode *mode);
44#else
45static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
46{
47 return 0;
48}
49static inline void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
50 enum rcar_lvds_mode mode)
51{
52}
53static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
54 struct drm_crtc *crtc, bool enable)
55{
56 return 0;
57}
58static inline void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
59 struct drm_display_mode *mode)
60{
61}
62#endif
63
64#endif /* __RCAR_DU_LVDSENC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
new file mode 100644
index 000000000000..1247e26a0559
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
@@ -0,0 +1,524 @@
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>
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");