Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/bridge/thc63lvd1024.c
51176 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* THC63LVD1024 LVDS to parallel data DRM bridge driver.
4
*
5
* Copyright (C) 2018 Jacopo Mondi <[email protected]>
6
*/
7
8
#include <linux/gpio/consumer.h>
9
#include <linux/module.h>
10
#include <linux/of.h>
11
#include <linux/of_graph.h>
12
#include <linux/platform_device.h>
13
#include <linux/regulator/consumer.h>
14
#include <linux/slab.h>
15
16
#include <drm/drm_bridge.h>
17
#include <drm/drm_panel.h>
18
19
enum thc63_ports {
20
THC63_LVDS_IN0,
21
THC63_LVDS_IN1,
22
THC63_RGB_OUT0,
23
THC63_RGB_OUT1,
24
};
25
26
struct thc63_dev {
27
struct device *dev;
28
29
struct regulator *vcc;
30
31
struct gpio_desc *pdwn;
32
struct gpio_desc *oe;
33
34
struct drm_bridge bridge;
35
36
struct drm_bridge_timings timings;
37
};
38
39
static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
40
{
41
return container_of(bridge, struct thc63_dev, bridge);
42
}
43
44
static int thc63_attach(struct drm_bridge *bridge,
45
struct drm_encoder *encoder,
46
enum drm_bridge_attach_flags flags)
47
{
48
struct thc63_dev *thc63 = to_thc63(bridge);
49
50
return drm_bridge_attach(encoder, thc63->bridge.next_bridge, bridge, flags);
51
}
52
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)
56
{
57
struct thc63_dev *thc63 = to_thc63(bridge);
58
unsigned int min_freq;
59
unsigned int max_freq;
60
61
/*
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
65
* input mode.
66
*/
67
if (thc63->timings.dual_link) {
68
min_freq = 40000;
69
max_freq = 150000;
70
} else {
71
min_freq = 8000;
72
max_freq = 135000;
73
}
74
75
if (mode->clock < min_freq)
76
return MODE_CLOCK_LOW;
77
78
if (mode->clock > max_freq)
79
return MODE_CLOCK_HIGH;
80
81
return MODE_OK;
82
}
83
84
static void thc63_enable(struct drm_bridge *bridge)
85
{
86
struct thc63_dev *thc63 = to_thc63(bridge);
87
int ret;
88
89
ret = regulator_enable(thc63->vcc);
90
if (ret) {
91
dev_err(thc63->dev,
92
"Failed to enable regulator \"vcc\": %d\n", ret);
93
return;
94
}
95
96
gpiod_set_value(thc63->pdwn, 0);
97
gpiod_set_value(thc63->oe, 1);
98
}
99
100
static void thc63_disable(struct drm_bridge *bridge)
101
{
102
struct thc63_dev *thc63 = to_thc63(bridge);
103
int ret;
104
105
gpiod_set_value(thc63->oe, 0);
106
gpiod_set_value(thc63->pdwn, 1);
107
108
ret = regulator_disable(thc63->vcc);
109
if (ret)
110
dev_err(thc63->dev,
111
"Failed to disable regulator \"vcc\": %d\n", ret);
112
}
113
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,
119
};
120
121
static int thc63_parse_dt(struct thc63_dev *thc63)
122
{
123
struct device_node *endpoint;
124
struct device_node *remote;
125
126
remote = of_graph_get_remote_node(thc63->dev->of_node,
127
THC63_RGB_OUT0, -1);
128
if (!remote) {
129
dev_err(thc63->dev, "No remote endpoint for port@%u\n",
130
THC63_RGB_OUT0);
131
return -ENODEV;
132
}
133
134
thc63->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
135
of_node_put(remote);
136
if (!thc63->bridge.next_bridge)
137
return -EPROBE_DEFER;
138
139
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
140
THC63_LVDS_IN1, -1);
141
if (endpoint) {
142
remote = of_graph_get_remote_port_parent(endpoint);
143
of_node_put(endpoint);
144
145
if (remote) {
146
if (of_device_is_available(remote))
147
thc63->timings.dual_link = true;
148
of_node_put(remote);
149
}
150
}
151
152
dev_dbg(thc63->dev, "operating in %s-link mode\n",
153
thc63->timings.dual_link ? "dual" : "single");
154
155
return 0;
156
}
157
158
static int thc63_gpio_init(struct thc63_dev *thc63)
159
{
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",
163
PTR_ERR(thc63->oe));
164
return PTR_ERR(thc63->oe);
165
}
166
167
thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown",
168
GPIOD_OUT_HIGH);
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);
173
}
174
175
return 0;
176
}
177
178
static int thc63_probe(struct platform_device *pdev)
179
{
180
struct thc63_dev *thc63;
181
int ret;
182
183
thc63 = devm_drm_bridge_alloc(&pdev->dev, struct thc63_dev, bridge,
184
&thc63_bridge_func);
185
if (IS_ERR(thc63))
186
return PTR_ERR(thc63);
187
188
thc63->dev = &pdev->dev;
189
platform_set_drvdata(pdev, thc63);
190
191
thc63->vcc = devm_regulator_get(thc63->dev, "vcc");
192
if (IS_ERR(thc63->vcc)) {
193
if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER)
194
return -EPROBE_DEFER;
195
196
dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n",
197
PTR_ERR(thc63->vcc));
198
return PTR_ERR(thc63->vcc);
199
}
200
201
ret = thc63_gpio_init(thc63);
202
if (ret)
203
return ret;
204
205
ret = thc63_parse_dt(thc63);
206
if (ret)
207
return ret;
208
209
thc63->bridge.driver_private = thc63;
210
thc63->bridge.of_node = pdev->dev.of_node;
211
thc63->bridge.timings = &thc63->timings;
212
213
drm_bridge_add(&thc63->bridge);
214
215
return 0;
216
}
217
218
static void thc63_remove(struct platform_device *pdev)
219
{
220
struct thc63_dev *thc63 = platform_get_drvdata(pdev);
221
222
drm_bridge_remove(&thc63->bridge);
223
}
224
225
static const struct of_device_id thc63_match[] = {
226
{ .compatible = "thine,thc63lvd1024", },
227
{ },
228
};
229
MODULE_DEVICE_TABLE(of, thc63_match);
230
231
static struct platform_driver thc63_driver = {
232
.probe = thc63_probe,
233
.remove = thc63_remove,
234
.driver = {
235
.name = "thc63lvd1024",
236
.of_match_table = thc63_match,
237
},
238
};
239
module_platform_driver(thc63_driver);
240
241
MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
242
MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
243
MODULE_LICENSE("GPL v2");
244
245