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>
9 #include <drm/drm_bridge.h>
10 #include <drm/drm_panel.h>
12 #include <linux/gpio/consumer.h>
13 #include <linux/of_graph.h>
14 #include <linux/regulator/consumer.h>
15 #include <linux/slab.h>
27 struct regulator
*vcc
;
29 struct gpio_desc
*pdwn
;
32 struct drm_bridge bridge
;
33 struct drm_bridge
*next
;
36 static inline struct thc63_dev
*to_thc63(struct drm_bridge
*bridge
)
38 return container_of(bridge
, struct thc63_dev
, bridge
);
41 static int thc63_attach(struct drm_bridge
*bridge
)
43 struct thc63_dev
*thc63
= to_thc63(bridge
);
45 return drm_bridge_attach(bridge
->encoder
, thc63
->next
, bridge
);
48 static enum drm_mode_status
thc63_mode_valid(struct drm_bridge
*bridge
,
49 const struct drm_display_mode
*mode
)
52 * The THC63LVD1024 clock frequency range is 8 to 135 MHz in single-in
53 * mode. Note that the limits are different in dual-in, single-out mode,
54 * and will need to be adjusted accordingly.
56 if (mode
->clock
< 8000)
57 return MODE_CLOCK_LOW
;
59 if (mode
->clock
> 135000)
60 return MODE_CLOCK_HIGH
;
65 static void thc63_enable(struct drm_bridge
*bridge
)
67 struct thc63_dev
*thc63
= to_thc63(bridge
);
70 ret
= regulator_enable(thc63
->vcc
);
73 "Failed to enable regulator \"vcc\": %d\n", ret
);
77 gpiod_set_value(thc63
->pdwn
, 0);
78 gpiod_set_value(thc63
->oe
, 1);
81 static void thc63_disable(struct drm_bridge
*bridge
)
83 struct thc63_dev
*thc63
= to_thc63(bridge
);
86 gpiod_set_value(thc63
->oe
, 0);
87 gpiod_set_value(thc63
->pdwn
, 1);
89 ret
= regulator_disable(thc63
->vcc
);
92 "Failed to disable regulator \"vcc\": %d\n", ret
);
95 static const struct drm_bridge_funcs thc63_bridge_func
= {
96 .attach
= thc63_attach
,
97 .mode_valid
= thc63_mode_valid
,
98 .enable
= thc63_enable
,
99 .disable
= thc63_disable
,
102 static int thc63_parse_dt(struct thc63_dev
*thc63
)
104 struct device_node
*thc63_out
;
105 struct device_node
*remote
;
107 thc63_out
= of_graph_get_endpoint_by_regs(thc63
->dev
->of_node
,
110 dev_err(thc63
->dev
, "Missing endpoint in port@%u\n",
115 remote
= of_graph_get_remote_port_parent(thc63_out
);
116 of_node_put(thc63_out
);
118 dev_err(thc63
->dev
, "Endpoint in port@%u unconnected\n",
123 if (!of_device_is_available(remote
)) {
124 dev_err(thc63
->dev
, "port@%u remote endpoint is disabled\n",
130 thc63
->next
= of_drm_find_bridge(remote
);
133 return -EPROBE_DEFER
;
138 static int thc63_gpio_init(struct thc63_dev
*thc63
)
140 thc63
->oe
= devm_gpiod_get_optional(thc63
->dev
, "oe", GPIOD_OUT_LOW
);
141 if (IS_ERR(thc63
->oe
)) {
142 dev_err(thc63
->dev
, "Unable to get \"oe-gpios\": %ld\n",
144 return PTR_ERR(thc63
->oe
);
147 thc63
->pdwn
= devm_gpiod_get_optional(thc63
->dev
, "powerdown",
149 if (IS_ERR(thc63
->pdwn
)) {
150 dev_err(thc63
->dev
, "Unable to get \"powerdown-gpios\": %ld\n",
151 PTR_ERR(thc63
->pdwn
));
152 return PTR_ERR(thc63
->pdwn
);
158 static int thc63_probe(struct platform_device
*pdev
)
160 struct thc63_dev
*thc63
;
163 thc63
= devm_kzalloc(&pdev
->dev
, sizeof(*thc63
), GFP_KERNEL
);
167 thc63
->dev
= &pdev
->dev
;
168 platform_set_drvdata(pdev
, thc63
);
170 thc63
->vcc
= devm_regulator_get_optional(thc63
->dev
, "vcc");
171 if (IS_ERR(thc63
->vcc
)) {
172 if (PTR_ERR(thc63
->vcc
) == -EPROBE_DEFER
)
173 return -EPROBE_DEFER
;
175 dev_err(thc63
->dev
, "Unable to get \"vcc\" supply: %ld\n",
176 PTR_ERR(thc63
->vcc
));
177 return PTR_ERR(thc63
->vcc
);
180 ret
= thc63_gpio_init(thc63
);
184 ret
= thc63_parse_dt(thc63
);
188 thc63
->bridge
.driver_private
= thc63
;
189 thc63
->bridge
.of_node
= pdev
->dev
.of_node
;
190 thc63
->bridge
.funcs
= &thc63_bridge_func
;
192 drm_bridge_add(&thc63
->bridge
);
197 static int thc63_remove(struct platform_device
*pdev
)
199 struct thc63_dev
*thc63
= platform_get_drvdata(pdev
);
201 drm_bridge_remove(&thc63
->bridge
);
206 static const struct of_device_id thc63_match
[] = {
207 { .compatible
= "thine,thc63lvd1024", },
210 MODULE_DEVICE_TABLE(of
, thc63_match
);
212 static struct platform_driver thc63_driver
= {
213 .probe
= thc63_probe
,
214 .remove
= thc63_remove
,
216 .name
= "thc63lvd1024",
217 .of_match_table
= thc63_match
,
220 module_platform_driver(thc63_driver
);
222 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
223 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
224 MODULE_LICENSE("GPL v2");