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
,
46 enum drm_bridge_attach_flags flags
)
48 struct thc63_dev
*thc63
= to_thc63(bridge
);
50 return drm_bridge_attach(bridge
->encoder
, thc63
->next
, bridge
, flags
);
53 static enum drm_mode_status
thc63_mode_valid(struct drm_bridge
*bridge
,
54 const struct drm_display_info
*info
,
55 const struct drm_display_mode
*mode
)
57 struct thc63_dev
*thc63
= to_thc63(bridge
);
58 unsigned int min_freq
;
59 unsigned int max_freq
;
62 * The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but
63 * dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out
64 * isn't supported by the driver yet, simply derive the limits from the
67 if (thc63
->timings
.dual_link
) {
75 if (mode
->clock
< min_freq
)
76 return MODE_CLOCK_LOW
;
78 if (mode
->clock
> max_freq
)
79 return MODE_CLOCK_HIGH
;
84 static void thc63_enable(struct drm_bridge
*bridge
)
86 struct thc63_dev
*thc63
= to_thc63(bridge
);
89 ret
= regulator_enable(thc63
->vcc
);
92 "Failed to enable regulator \"vcc\": %d\n", ret
);
96 gpiod_set_value(thc63
->pdwn
, 0);
97 gpiod_set_value(thc63
->oe
, 1);
100 static void thc63_disable(struct drm_bridge
*bridge
)
102 struct thc63_dev
*thc63
= to_thc63(bridge
);
105 gpiod_set_value(thc63
->oe
, 0);
106 gpiod_set_value(thc63
->pdwn
, 1);
108 ret
= regulator_disable(thc63
->vcc
);
111 "Failed to disable regulator \"vcc\": %d\n", ret
);
114 static const struct drm_bridge_funcs thc63_bridge_func
= {
115 .attach
= thc63_attach
,
116 .mode_valid
= thc63_mode_valid
,
117 .enable
= thc63_enable
,
118 .disable
= thc63_disable
,
121 static int thc63_parse_dt(struct thc63_dev
*thc63
)
123 struct device_node
*endpoint
;
124 struct device_node
*remote
;
126 endpoint
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
129 dev_err(thc63
->dev
, "Missing endpoint in port@%u\n",
134 remote
= of_graph_get_remote_port_parent(endpoint
);
135 of_node_put(endpoint
);
137 dev_err(thc63
->dev
, "Endpoint in port@%u unconnected\n",
142 if (!of_device_is_available(remote
)) {
143 dev_err(thc63
->dev
, "port@%u remote endpoint is disabled\n",
149 thc63
->next
= of_drm_find_bridge(remote
);
152 return -EPROBE_DEFER
;
154 endpoint
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
157 remote
= of_graph_get_remote_port_parent(endpoint
);
158 of_node_put(endpoint
);
161 if (of_device_is_available(remote
))
162 thc63
->timings
.dual_link
= true;
167 dev_dbg(thc63
->dev
, "operating in %s-link mode\n",
168 thc63
->timings
.dual_link
? "dual" : "single");
173 static int thc63_gpio_init(struct thc63_dev
*thc63
)
175 thc63
->oe
= devm_gpiod_get_optional(thc63
->dev
, "oe", GPIOD_OUT_LOW
);
176 if (IS_ERR(thc63
->oe
)) {
177 dev_err(thc63
->dev
, "Unable to get \"oe-gpios\": %ld\n",
179 return PTR_ERR(thc63
->oe
);
182 thc63
->pdwn
= devm_gpiod_get_optional(thc63
->dev
, "powerdown",
184 if (IS_ERR(thc63
->pdwn
)) {
185 dev_err(thc63
->dev
, "Unable to get \"powerdown-gpios\": %ld\n",
186 PTR_ERR(thc63
->pdwn
));
187 return PTR_ERR(thc63
->pdwn
);
193 static int thc63_probe(struct platform_device
*pdev
)
195 struct thc63_dev
*thc63
;
198 thc63
= devm_kzalloc(&pdev
->dev
, sizeof(*thc63
), GFP_KERNEL
);
202 thc63
->dev
= &pdev
->dev
;
203 platform_set_drvdata(pdev
, thc63
);
205 thc63
->vcc
= devm_regulator_get_optional(thc63
->dev
, "vcc");
206 if (IS_ERR(thc63
->vcc
)) {
207 if (PTR_ERR(thc63
->vcc
) == -EPROBE_DEFER
)
208 return -EPROBE_DEFER
;
210 dev_err(thc63
->dev
, "Unable to get \"vcc\" supply: %ld\n",
211 PTR_ERR(thc63
->vcc
));
212 return PTR_ERR(thc63
->vcc
);
215 ret
= thc63_gpio_init(thc63
);
219 ret
= thc63_parse_dt(thc63
);
223 thc63
->bridge
.driver_private
= thc63
;
224 thc63
->bridge
.of_node
= pdev
->dev
.of_node
;
225 thc63
->bridge
.funcs
= &thc63_bridge_func
;
226 thc63
->bridge
.timings
= &thc63
->timings
;
228 drm_bridge_add(&thc63
->bridge
);
233 static int thc63_remove(struct platform_device
*pdev
)
235 struct thc63_dev
*thc63
= platform_get_drvdata(pdev
);
237 drm_bridge_remove(&thc63
->bridge
);
242 static const struct of_device_id thc63_match
[] = {
243 { .compatible
= "thine,thc63lvd1024", },
246 MODULE_DEVICE_TABLE(of
, thc63_match
);
248 static struct platform_driver thc63_driver
= {
249 .probe
= thc63_probe
,
250 .remove
= thc63_remove
,
252 .name
= "thc63lvd1024",
253 .of_match_table
= thc63_match
,
256 module_platform_driver(thc63_driver
);
258 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
259 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
260 MODULE_LICENSE("GPL v2");