1 // SPDX-License-Identifier: GPL-2.0
3 * THC63LVD1024 LVDS to parallel data DRM bridge driver.
5 * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
8 #include <linux/gpio/consumer.h>
9 #include <linux/module.h>
11 #include <linux/of_graph.h>
12 #include <linux/platform_device.h>
13 #include <linux/regulator/consumer.h>
14 #include <linux/slab.h>
16 #include <drm/drm_bridge.h>
17 #include <drm/drm_panel.h>
29 struct regulator
*vcc
;
31 struct gpio_desc
*pdwn
;
34 struct drm_bridge bridge
;
35 struct drm_bridge
*next
;
37 struct drm_bridge_timings timings
;
40 static inline struct thc63_dev
*to_thc63(struct drm_bridge
*bridge
)
42 return container_of(bridge
, struct thc63_dev
, bridge
);
45 static int thc63_attach(struct drm_bridge
*bridge
)
47 struct thc63_dev
*thc63
= to_thc63(bridge
);
49 return drm_bridge_attach(bridge
->encoder
, thc63
->next
, bridge
);
52 static enum drm_mode_status
thc63_mode_valid(struct drm_bridge
*bridge
,
53 const struct drm_display_mode
*mode
)
55 struct thc63_dev
*thc63
= to_thc63(bridge
);
56 unsigned int min_freq
;
57 unsigned int max_freq
;
60 * The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but
61 * dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out
62 * isn't supported by the driver yet, simply derive the limits from the
65 if (thc63
->timings
.dual_link
) {
73 if (mode
->clock
< min_freq
)
74 return MODE_CLOCK_LOW
;
76 if (mode
->clock
> max_freq
)
77 return MODE_CLOCK_HIGH
;
82 static void thc63_enable(struct drm_bridge
*bridge
)
84 struct thc63_dev
*thc63
= to_thc63(bridge
);
87 ret
= regulator_enable(thc63
->vcc
);
90 "Failed to enable regulator \"vcc\": %d\n", ret
);
94 gpiod_set_value(thc63
->pdwn
, 0);
95 gpiod_set_value(thc63
->oe
, 1);
98 static void thc63_disable(struct drm_bridge
*bridge
)
100 struct thc63_dev
*thc63
= to_thc63(bridge
);
103 gpiod_set_value(thc63
->oe
, 0);
104 gpiod_set_value(thc63
->pdwn
, 1);
106 ret
= regulator_disable(thc63
->vcc
);
109 "Failed to disable regulator \"vcc\": %d\n", ret
);
112 static const struct drm_bridge_funcs thc63_bridge_func
= {
113 .attach
= thc63_attach
,
114 .mode_valid
= thc63_mode_valid
,
115 .enable
= thc63_enable
,
116 .disable
= thc63_disable
,
119 static int thc63_parse_dt(struct thc63_dev
*thc63
)
121 struct device_node
*endpoint
;
122 struct device_node
*remote
;
124 endpoint
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
127 dev_err(thc63
->dev
, "Missing endpoint in port@%u\n",
132 remote
= of_graph_get_remote_port_parent(endpoint
);
133 of_node_put(endpoint
);
135 dev_err(thc63
->dev
, "Endpoint in port@%u unconnected\n",
140 if (!of_device_is_available(remote
)) {
141 dev_err(thc63
->dev
, "port@%u remote endpoint is disabled\n",
147 thc63
->next
= of_drm_find_bridge(remote
);
150 return -EPROBE_DEFER
;
152 endpoint
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
155 remote
= of_graph_get_remote_port_parent(endpoint
);
156 of_node_put(endpoint
);
159 if (of_device_is_available(remote
))
160 thc63
->timings
.dual_link
= true;
165 dev_dbg(thc63
->dev
, "operating in %s-link mode\n",
166 thc63
->timings
.dual_link
? "dual" : "single");
171 static int thc63_gpio_init(struct thc63_dev
*thc63
)
173 thc63
->oe
= devm_gpiod_get_optional(thc63
->dev
, "oe", GPIOD_OUT_LOW
);
174 if (IS_ERR(thc63
->oe
)) {
175 dev_err(thc63
->dev
, "Unable to get \"oe-gpios\": %ld\n",
177 return PTR_ERR(thc63
->oe
);
180 thc63
->pdwn
= devm_gpiod_get_optional(thc63
->dev
, "powerdown",
182 if (IS_ERR(thc63
->pdwn
)) {
183 dev_err(thc63
->dev
, "Unable to get \"powerdown-gpios\": %ld\n",
184 PTR_ERR(thc63
->pdwn
));
185 return PTR_ERR(thc63
->pdwn
);
191 static int thc63_probe(struct platform_device
*pdev
)
193 struct thc63_dev
*thc63
;
196 thc63
= devm_kzalloc(&pdev
->dev
, sizeof(*thc63
), GFP_KERNEL
);
200 thc63
->dev
= &pdev
->dev
;
201 platform_set_drvdata(pdev
, thc63
);
203 thc63
->vcc
= devm_regulator_get_optional(thc63
->dev
, "vcc");
204 if (IS_ERR(thc63
->vcc
)) {
205 if (PTR_ERR(thc63
->vcc
) == -EPROBE_DEFER
)
206 return -EPROBE_DEFER
;
208 dev_err(thc63
->dev
, "Unable to get \"vcc\" supply: %ld\n",
209 PTR_ERR(thc63
->vcc
));
210 return PTR_ERR(thc63
->vcc
);
213 ret
= thc63_gpio_init(thc63
);
217 ret
= thc63_parse_dt(thc63
);
221 thc63
->bridge
.driver_private
= thc63
;
222 thc63
->bridge
.of_node
= pdev
->dev
.of_node
;
223 thc63
->bridge
.funcs
= &thc63_bridge_func
;
224 thc63
->bridge
.timings
= &thc63
->timings
;
226 drm_bridge_add(&thc63
->bridge
);
231 static int thc63_remove(struct platform_device
*pdev
)
233 struct thc63_dev
*thc63
= platform_get_drvdata(pdev
);
235 drm_bridge_remove(&thc63
->bridge
);
240 static const struct of_device_id thc63_match
[] = {
241 { .compatible
= "thine,thc63lvd1024", },
244 MODULE_DEVICE_TABLE(of
, thc63_match
);
246 static struct platform_driver thc63_driver
= {
247 .probe
= thc63_probe
,
248 .remove
= thc63_remove
,
250 .name
= "thc63lvd1024",
251 .of_match_table
= thc63_match
,
254 module_platform_driver(thc63_driver
);
256 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
257 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
258 MODULE_LICENSE("GPL v2");