]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/gpu/drm/omapdrm/displays/panel-dpi.c
48a03f55610aeb58b115590dd76057d98367f585
[mirror_ubuntu-hirsute-kernel.git] / drivers / gpu / drm / omapdrm / displays / panel-dpi.c
1 /*
2 * Generic MIPI DPI Panel Driver
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 */
11
12 #include <linux/gpio/consumer.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/of.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/backlight.h>
19
20 #include <video/of_display_timing.h>
21
22 #include "../dss/omapdss.h"
23
24 struct panel_drv_data {
25 struct omap_dss_device dssdev;
26 struct omap_dss_device *in;
27
28 struct videomode vm;
29
30 struct backlight_device *backlight;
31
32 struct gpio_desc *enable_gpio;
33 struct regulator *vcc_supply;
34 };
35
36 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
37
38 static int panel_dpi_connect(struct omap_dss_device *dssdev)
39 {
40 struct panel_drv_data *ddata = to_panel_data(dssdev);
41 struct omap_dss_device *in = ddata->in;
42 int r;
43
44 if (omapdss_device_is_connected(dssdev))
45 return 0;
46
47 r = in->ops.dpi->connect(in, dssdev);
48 if (r)
49 return r;
50
51 return 0;
52 }
53
54 static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
55 {
56 struct panel_drv_data *ddata = to_panel_data(dssdev);
57 struct omap_dss_device *in = ddata->in;
58
59 if (!omapdss_device_is_connected(dssdev))
60 return;
61
62 in->ops.dpi->disconnect(in, dssdev);
63 }
64
65 static int panel_dpi_enable(struct omap_dss_device *dssdev)
66 {
67 struct panel_drv_data *ddata = to_panel_data(dssdev);
68 struct omap_dss_device *in = ddata->in;
69 int r;
70
71 if (!omapdss_device_is_connected(dssdev))
72 return -ENODEV;
73
74 if (omapdss_device_is_enabled(dssdev))
75 return 0;
76
77 in->ops.dpi->set_timings(in, &ddata->vm);
78
79 r = in->ops.dpi->enable(in);
80 if (r)
81 return r;
82
83 r = regulator_enable(ddata->vcc_supply);
84 if (r) {
85 in->ops.dpi->disable(in);
86 return r;
87 }
88
89 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
90 backlight_enable(ddata->backlight);
91
92 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
93
94 return 0;
95 }
96
97 static void panel_dpi_disable(struct omap_dss_device *dssdev)
98 {
99 struct panel_drv_data *ddata = to_panel_data(dssdev);
100 struct omap_dss_device *in = ddata->in;
101
102 if (!omapdss_device_is_enabled(dssdev))
103 return;
104
105 backlight_disable(ddata->backlight);
106
107 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
108 regulator_disable(ddata->vcc_supply);
109
110 in->ops.dpi->disable(in);
111
112 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
113 }
114
115 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
116 struct videomode *vm)
117 {
118 struct panel_drv_data *ddata = to_panel_data(dssdev);
119 struct omap_dss_device *in = ddata->in;
120
121 ddata->vm = *vm;
122 dssdev->panel.vm = *vm;
123
124 in->ops.dpi->set_timings(in, vm);
125 }
126
127 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
128 struct videomode *vm)
129 {
130 struct panel_drv_data *ddata = to_panel_data(dssdev);
131
132 *vm = ddata->vm;
133 }
134
135 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
136 struct videomode *vm)
137 {
138 struct panel_drv_data *ddata = to_panel_data(dssdev);
139 struct omap_dss_device *in = ddata->in;
140
141 return in->ops.dpi->check_timings(in, vm);
142 }
143
144 static struct omap_dss_driver panel_dpi_ops = {
145 .connect = panel_dpi_connect,
146 .disconnect = panel_dpi_disconnect,
147
148 .enable = panel_dpi_enable,
149 .disable = panel_dpi_disable,
150
151 .set_timings = panel_dpi_set_timings,
152 .get_timings = panel_dpi_get_timings,
153 .check_timings = panel_dpi_check_timings,
154 };
155
156 static int panel_dpi_probe_of(struct platform_device *pdev)
157 {
158 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
159 struct device_node *node = pdev->dev.of_node;
160 struct omap_dss_device *in;
161 int r;
162 struct display_timing timing;
163 struct gpio_desc *gpio;
164
165 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
166 if (IS_ERR(gpio))
167 return PTR_ERR(gpio);
168
169 ddata->enable_gpio = gpio;
170
171 /*
172 * Many different panels are supported by this driver and there are
173 * probably very different needs for their reset pins in regards to
174 * timing and order relative to the enable gpio. So for now it's just
175 * ensured that the reset line isn't active.
176 */
177 gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
178 if (IS_ERR(gpio))
179 return PTR_ERR(gpio);
180
181 ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
182 if (IS_ERR(ddata->vcc_supply))
183 return PTR_ERR(ddata->vcc_supply);
184
185 ddata->backlight = devm_of_find_backlight(&pdev->dev);
186
187 if (IS_ERR(ddata->backlight))
188 return PTR_ERR(ddata->backlight);
189
190 r = of_get_display_timing(node, "panel-timing", &timing);
191 if (r) {
192 dev_err(&pdev->dev, "failed to get video timing\n");
193 return r;
194 }
195
196 videomode_from_timing(&timing, &ddata->vm);
197
198 in = omapdss_of_find_source_for_first_ep(node);
199 if (IS_ERR(in)) {
200 dev_err(&pdev->dev, "failed to find video source\n");
201 return PTR_ERR(in);
202 }
203
204 ddata->in = in;
205
206 return 0;
207 }
208
209 static int panel_dpi_probe(struct platform_device *pdev)
210 {
211 struct panel_drv_data *ddata;
212 struct omap_dss_device *dssdev;
213 int r;
214
215 if (!pdev->dev.of_node)
216 return -ENODEV;
217
218 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
219 if (ddata == NULL)
220 return -ENOMEM;
221
222 platform_set_drvdata(pdev, ddata);
223
224 r = panel_dpi_probe_of(pdev);
225 if (r)
226 return r;
227
228 dssdev = &ddata->dssdev;
229 dssdev->dev = &pdev->dev;
230 dssdev->driver = &panel_dpi_ops;
231 dssdev->type = OMAP_DISPLAY_TYPE_DPI;
232 dssdev->owner = THIS_MODULE;
233 dssdev->panel.vm = ddata->vm;
234
235 r = omapdss_register_display(dssdev);
236 if (r) {
237 dev_err(&pdev->dev, "Failed to register panel\n");
238 goto err_reg;
239 }
240
241 return 0;
242
243 err_reg:
244 omap_dss_put_device(ddata->in);
245 return r;
246 }
247
248 static int __exit panel_dpi_remove(struct platform_device *pdev)
249 {
250 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
251 struct omap_dss_device *dssdev = &ddata->dssdev;
252 struct omap_dss_device *in = ddata->in;
253
254 omapdss_unregister_display(dssdev);
255
256 panel_dpi_disable(dssdev);
257 panel_dpi_disconnect(dssdev);
258
259 omap_dss_put_device(in);
260
261 return 0;
262 }
263
264 static const struct of_device_id panel_dpi_of_match[] = {
265 { .compatible = "omapdss,panel-dpi", },
266 {},
267 };
268
269 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
270
271 static struct platform_driver panel_dpi_driver = {
272 .probe = panel_dpi_probe,
273 .remove = __exit_p(panel_dpi_remove),
274 .driver = {
275 .name = "panel-dpi",
276 .of_match_table = panel_dpi_of_match,
277 .suppress_bind_attrs = true,
278 },
279 };
280
281 module_platform_driver(panel_dpi_driver);
282
283 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
284 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
285 MODULE_LICENSE("GPL");