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 remote
= of_graph_get_remote_node(thc63
->dev
->of_node
,
129 dev_err(thc63
->dev
, "No remote endpoint for port@%u\n",
134 thc63
->next
= of_drm_find_bridge(remote
);
137 return -EPROBE_DEFER
;
139 endpoint
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
142 remote
= of_graph_get_remote_port_parent(endpoint
);
143 of_node_put(endpoint
);
146 if (of_device_is_available(remote
))
147 thc63
->timings
.dual_link
= true;
152 dev_dbg(thc63
->dev
, "operating in %s-link mode\n",
153 thc63
->timings
.dual_link
? "dual" : "single");
158 static int thc63_gpio_init(struct thc63_dev
*thc63
)
160 thc63
->oe
= devm_gpiod_get_optional(thc63
->dev
, "oe", GPIOD_OUT_LOW
);
161 if (IS_ERR(thc63
->oe
)) {
162 dev_err(thc63
->dev
, "Unable to get \"oe-gpios\": %ld\n",
164 return PTR_ERR(thc63
->oe
);
167 thc63
->pdwn
= devm_gpiod_get_optional(thc63
->dev
, "powerdown",
169 if (IS_ERR(thc63
->pdwn
)) {
170 dev_err(thc63
->dev
, "Unable to get \"powerdown-gpios\": %ld\n",
171 PTR_ERR(thc63
->pdwn
));
172 return PTR_ERR(thc63
->pdwn
);
178 static int thc63_probe(struct platform_device
*pdev
)
180 struct thc63_dev
*thc63
;
183 thc63
= devm_kzalloc(&pdev
->dev
, sizeof(*thc63
), GFP_KERNEL
);
187 thc63
->dev
= &pdev
->dev
;
188 platform_set_drvdata(pdev
, thc63
);
190 thc63
->vcc
= devm_regulator_get(thc63
->dev
, "vcc");
191 if (IS_ERR(thc63
->vcc
)) {
192 if (PTR_ERR(thc63
->vcc
) == -EPROBE_DEFER
)
193 return -EPROBE_DEFER
;
195 dev_err(thc63
->dev
, "Unable to get \"vcc\" supply: %ld\n",
196 PTR_ERR(thc63
->vcc
));
197 return PTR_ERR(thc63
->vcc
);
200 ret
= thc63_gpio_init(thc63
);
204 ret
= thc63_parse_dt(thc63
);
208 thc63
->bridge
.driver_private
= thc63
;
209 thc63
->bridge
.of_node
= pdev
->dev
.of_node
;
210 thc63
->bridge
.funcs
= &thc63_bridge_func
;
211 thc63
->bridge
.timings
= &thc63
->timings
;
213 drm_bridge_add(&thc63
->bridge
);
218 static void thc63_remove(struct platform_device
*pdev
)
220 struct thc63_dev
*thc63
= platform_get_drvdata(pdev
);
222 drm_bridge_remove(&thc63
->bridge
);
225 static const struct of_device_id thc63_match
[] = {
226 { .compatible
= "thine,thc63lvd1024", },
229 MODULE_DEVICE_TABLE(of
, thc63_match
);
231 static struct platform_driver thc63_driver
= {
232 .probe
= thc63_probe
,
233 .remove
= thc63_remove
,
235 .name
= "thc63lvd1024",
236 .of_match_table
= thc63_match
,
239 module_platform_driver(thc63_driver
);
241 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
242 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
243 MODULE_LICENSE("GPL v2");