Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/bridge/thc63lvd1024.c
26494 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
struct drm_bridge *next;
36
37
struct drm_bridge_timings timings;
38
};
39
40
static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
41
{
42
return container_of(bridge, struct thc63_dev, bridge);
43
}
44
45
static int thc63_attach(struct drm_bridge *bridge,
46
struct drm_encoder *encoder,
47
enum drm_bridge_attach_flags flags)
48
{
49
struct thc63_dev *thc63 = to_thc63(bridge);
50
51
return drm_bridge_attach(encoder, thc63->next, bridge, flags);
52
}
53
54
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
55
const struct drm_display_info *info,
56
const struct drm_display_mode *mode)
57
{
58
struct thc63_dev *thc63 = to_thc63(bridge);
59
unsigned int min_freq;
60
unsigned int max_freq;
61
62
/*
63
* The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but
64
* dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out
65
* isn't supported by the driver yet, simply derive the limits from the
66
* input mode.
67
*/
68
if (thc63->timings.dual_link) {
69
min_freq = 40000;
70
max_freq = 150000;
71
} else {
72
min_freq = 8000;
73
max_freq = 135000;
74
}
75
76
if (mode->clock < min_freq)
77
return MODE_CLOCK_LOW;
78
79
if (mode->clock > max_freq)
80
return MODE_CLOCK_HIGH;
81
82
return MODE_OK;
83
}
84
85
static void thc63_enable(struct drm_bridge *bridge)
86
{
87
struct thc63_dev *thc63 = to_thc63(bridge);
88
int ret;
89
90
ret = regulator_enable(thc63->vcc);
91
if (ret) {
92
dev_err(thc63->dev,
93
"Failed to enable regulator \"vcc\": %d\n", ret);
94
return;
95
}
96
97
gpiod_set_value(thc63->pdwn, 0);
98
gpiod_set_value(thc63->oe, 1);
99
}
100
101
static void thc63_disable(struct drm_bridge *bridge)
102
{
103
struct thc63_dev *thc63 = to_thc63(bridge);
104
int ret;
105
106
gpiod_set_value(thc63->oe, 0);
107
gpiod_set_value(thc63->pdwn, 1);
108
109
ret = regulator_disable(thc63->vcc);
110
if (ret)
111
dev_err(thc63->dev,
112
"Failed to disable regulator \"vcc\": %d\n", ret);
113
}
114
115
static const struct drm_bridge_funcs thc63_bridge_func = {
116
.attach = thc63_attach,
117
.mode_valid = thc63_mode_valid,
118
.enable = thc63_enable,
119
.disable = thc63_disable,
120
};
121
122
static int thc63_parse_dt(struct thc63_dev *thc63)
123
{
124
struct device_node *endpoint;
125
struct device_node *remote;
126
127
remote = of_graph_get_remote_node(thc63->dev->of_node,
128
THC63_RGB_OUT0, -1);
129
if (!remote) {
130
dev_err(thc63->dev, "No remote endpoint for port@%u\n",
131
THC63_RGB_OUT0);
132
return -ENODEV;
133
}
134
135
thc63->next = of_drm_find_bridge(remote);
136
of_node_put(remote);
137
if (!thc63->next)
138
return -EPROBE_DEFER;
139
140
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
141
THC63_LVDS_IN1, -1);
142
if (endpoint) {
143
remote = of_graph_get_remote_port_parent(endpoint);
144
of_node_put(endpoint);
145
146
if (remote) {
147
if (of_device_is_available(remote))
148
thc63->timings.dual_link = true;
149
of_node_put(remote);
150
}
151
}
152
153
dev_dbg(thc63->dev, "operating in %s-link mode\n",
154
thc63->timings.dual_link ? "dual" : "single");
155
156
return 0;
157
}
158
159
static int thc63_gpio_init(struct thc63_dev *thc63)
160
{
161
thc63->oe = devm_gpiod_get_optional(thc63->dev, "oe", GPIOD_OUT_LOW);
162
if (IS_ERR(thc63->oe)) {
163
dev_err(thc63->dev, "Unable to get \"oe-gpios\": %ld\n",
164
PTR_ERR(thc63->oe));
165
return PTR_ERR(thc63->oe);
166
}
167
168
thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown",
169
GPIOD_OUT_HIGH);
170
if (IS_ERR(thc63->pdwn)) {
171
dev_err(thc63->dev, "Unable to get \"powerdown-gpios\": %ld\n",
172
PTR_ERR(thc63->pdwn));
173
return PTR_ERR(thc63->pdwn);
174
}
175
176
return 0;
177
}
178
179
static int thc63_probe(struct platform_device *pdev)
180
{
181
struct thc63_dev *thc63;
182
int ret;
183
184
thc63 = devm_drm_bridge_alloc(&pdev->dev, struct thc63_dev, bridge,
185
&thc63_bridge_func);
186
if (IS_ERR(thc63))
187
return PTR_ERR(thc63);
188
189
thc63->dev = &pdev->dev;
190
platform_set_drvdata(pdev, thc63);
191
192
thc63->vcc = devm_regulator_get(thc63->dev, "vcc");
193
if (IS_ERR(thc63->vcc)) {
194
if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER)
195
return -EPROBE_DEFER;
196
197
dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n",
198
PTR_ERR(thc63->vcc));
199
return PTR_ERR(thc63->vcc);
200
}
201
202
ret = thc63_gpio_init(thc63);
203
if (ret)
204
return ret;
205
206
ret = thc63_parse_dt(thc63);
207
if (ret)
208
return ret;
209
210
thc63->bridge.driver_private = thc63;
211
thc63->bridge.of_node = pdev->dev.of_node;
212
thc63->bridge.timings = &thc63->timings;
213
214
drm_bridge_add(&thc63->bridge);
215
216
return 0;
217
}
218
219
static void thc63_remove(struct platform_device *pdev)
220
{
221
struct thc63_dev *thc63 = platform_get_drvdata(pdev);
222
223
drm_bridge_remove(&thc63->bridge);
224
}
225
226
static const struct of_device_id thc63_match[] = {
227
{ .compatible = "thine,thc63lvd1024", },
228
{ },
229
};
230
MODULE_DEVICE_TABLE(of, thc63_match);
231
232
static struct platform_driver thc63_driver = {
233
.probe = thc63_probe,
234
.remove = thc63_remove,
235
.driver = {
236
.name = "thc63lvd1024",
237
.of_match_table = thc63_match,
238
},
239
};
240
module_platform_driver(thc63_driver);
241
242
MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
243
MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
244
MODULE_LICENSE("GPL v2");
245
246