1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2019 Renesas Electronics Corporation
4 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 #include <linux/gpio/consumer.h>
8 #include <linux/media-bus-format.h>
9 #include <linux/module.h>
11 #include <linux/of_graph.h>
12 #include <linux/platform_device.h>
13 #include <linux/regulator/consumer.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_bridge.h>
17 #include <drm/drm_of.h>
18 #include <drm/drm_panel.h>
22 struct drm_bridge bridge
;
23 struct drm_bridge
*panel_bridge
;
24 struct drm_bridge_timings timings
;
25 struct regulator
*vcc
;
26 struct gpio_desc
*powerdown_gpio
;
28 unsigned int bus_format
;
31 static inline struct lvds_codec
*to_lvds_codec(struct drm_bridge
*bridge
)
33 return container_of(bridge
, struct lvds_codec
, bridge
);
36 static int lvds_codec_attach(struct drm_bridge
*bridge
,
37 enum drm_bridge_attach_flags flags
)
39 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
41 return drm_bridge_attach(bridge
->encoder
, lvds_codec
->panel_bridge
,
45 static void lvds_codec_enable(struct drm_bridge
*bridge
)
47 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
50 ret
= regulator_enable(lvds_codec
->vcc
);
52 dev_err(lvds_codec
->dev
,
53 "Failed to enable regulator \"vcc\": %d\n", ret
);
57 if (lvds_codec
->powerdown_gpio
)
58 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 0);
61 static void lvds_codec_disable(struct drm_bridge
*bridge
)
63 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
66 if (lvds_codec
->powerdown_gpio
)
67 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 1);
69 ret
= regulator_disable(lvds_codec
->vcc
);
71 dev_err(lvds_codec
->dev
,
72 "Failed to disable regulator \"vcc\": %d\n", ret
);
75 #define MAX_INPUT_SEL_FORMATS 1
77 lvds_codec_atomic_get_input_bus_fmts(struct drm_bridge
*bridge
,
78 struct drm_bridge_state
*bridge_state
,
79 struct drm_crtc_state
*crtc_state
,
80 struct drm_connector_state
*conn_state
,
82 unsigned int *num_input_fmts
)
84 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
89 input_fmts
= kcalloc(MAX_INPUT_SEL_FORMATS
, sizeof(*input_fmts
),
94 input_fmts
[0] = lvds_codec
->bus_format
;
95 *num_input_fmts
= MAX_INPUT_SEL_FORMATS
;
100 static const struct drm_bridge_funcs funcs
= {
101 .attach
= lvds_codec_attach
,
102 .enable
= lvds_codec_enable
,
103 .disable
= lvds_codec_disable
,
104 .atomic_duplicate_state
= drm_atomic_helper_bridge_duplicate_state
,
105 .atomic_destroy_state
= drm_atomic_helper_bridge_destroy_state
,
106 .atomic_reset
= drm_atomic_helper_bridge_reset
,
107 .atomic_get_input_bus_fmts
= lvds_codec_atomic_get_input_bus_fmts
,
110 static int lvds_codec_probe(struct platform_device
*pdev
)
112 struct device
*dev
= &pdev
->dev
;
113 struct device_node
*panel_node
;
114 struct device_node
*bus_node
;
115 struct drm_panel
*panel
;
116 struct lvds_codec
*lvds_codec
;
120 lvds_codec
= devm_kzalloc(dev
, sizeof(*lvds_codec
), GFP_KERNEL
);
124 lvds_codec
->dev
= &pdev
->dev
;
125 lvds_codec
->connector_type
= (uintptr_t)of_device_get_match_data(dev
);
127 lvds_codec
->vcc
= devm_regulator_get(lvds_codec
->dev
, "power");
128 if (IS_ERR(lvds_codec
->vcc
))
129 return dev_err_probe(dev
, PTR_ERR(lvds_codec
->vcc
),
130 "Unable to get \"vcc\" supply\n");
132 lvds_codec
->powerdown_gpio
= devm_gpiod_get_optional(dev
, "powerdown",
134 if (IS_ERR(lvds_codec
->powerdown_gpio
))
135 return dev_err_probe(dev
, PTR_ERR(lvds_codec
->powerdown_gpio
),
136 "powerdown GPIO failure\n");
138 /* Locate the panel DT node. */
139 panel_node
= of_graph_get_remote_node(dev
->of_node
, 1, 0);
141 dev_dbg(dev
, "panel DT node not found\n");
145 panel
= of_drm_find_panel(panel_node
);
146 of_node_put(panel_node
);
148 dev_dbg(dev
, "panel not found, deferring probe\n");
149 return PTR_ERR(panel
);
152 lvds_codec
->panel_bridge
=
153 devm_drm_panel_bridge_add_typed(dev
, panel
,
154 lvds_codec
->connector_type
);
155 if (IS_ERR(lvds_codec
->panel_bridge
))
156 return PTR_ERR(lvds_codec
->panel_bridge
);
158 lvds_codec
->bridge
.funcs
= &funcs
;
161 * Decoder input LVDS format is a property of the decoder chip or even
162 * its strapping. Handle data-mapping the same way lvds-panel does. In
163 * case data-mapping is not present, do nothing, since there are still
164 * legacy bindings which do not specify this property.
166 if (lvds_codec
->connector_type
!= DRM_MODE_CONNECTOR_LVDS
) {
167 bus_node
= of_graph_get_endpoint_by_regs(dev
->of_node
, 0, 0);
169 dev_dbg(dev
, "bus DT node not found\n");
173 ret
= drm_of_lvds_get_data_mapping(bus_node
);
174 of_node_put(bus_node
);
175 if (ret
== -ENODEV
) {
176 dev_warn(dev
, "missing 'data-mapping' DT property\n");
177 } else if (ret
< 0) {
178 dev_err(dev
, "invalid 'data-mapping' DT property\n");
181 lvds_codec
->bus_format
= ret
;
184 lvds_codec
->bus_format
= MEDIA_BUS_FMT_RGB888_1X24
;
188 * Encoder might sample data on different clock edge than the display,
189 * for example OnSemi FIN3385 has a dedicated strapping pin to select
192 if (lvds_codec
->connector_type
== DRM_MODE_CONNECTOR_LVDS
&&
193 !of_property_read_u32(dev
->of_node
, "pclk-sample", &val
)) {
194 lvds_codec
->timings
.input_bus_flags
= val
?
195 DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
:
196 DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
;
200 * The panel_bridge bridge is attached to the panel's of_node,
201 * but we need a bridge attached to our of_node for our user
204 lvds_codec
->bridge
.of_node
= dev
->of_node
;
205 lvds_codec
->bridge
.timings
= &lvds_codec
->timings
;
206 drm_bridge_add(&lvds_codec
->bridge
);
208 platform_set_drvdata(pdev
, lvds_codec
);
213 static void lvds_codec_remove(struct platform_device
*pdev
)
215 struct lvds_codec
*lvds_codec
= platform_get_drvdata(pdev
);
217 drm_bridge_remove(&lvds_codec
->bridge
);
220 static const struct of_device_id lvds_codec_match
[] = {
222 .compatible
= "lvds-decoder",
223 .data
= (void *)DRM_MODE_CONNECTOR_DPI
,
226 .compatible
= "lvds-encoder",
227 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
230 .compatible
= "thine,thc63lvdm83d",
231 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
235 MODULE_DEVICE_TABLE(of
, lvds_codec_match
);
237 static struct platform_driver lvds_codec_driver
= {
238 .probe
= lvds_codec_probe
,
239 .remove
= lvds_codec_remove
,
241 .name
= "lvds-codec",
242 .of_match_table
= lvds_codec_match
,
245 module_platform_driver(lvds_codec_driver
);
247 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
248 MODULE_DESCRIPTION("LVDS encoders and decoders");
249 MODULE_LICENSE("GPL");