1 /*
2 * Generic MIPI DPI Panel Driver
3 *
4 * Copyright (C) 2013 Texas Instruments
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
panel_dpi_connect(struct omap_dss_device * dssdev)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
panel_dpi_disconnect(struct omap_dss_device * dssdev)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
panel_dpi_enable(struct omap_dss_device * dssdev)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
91 if (ddata->backlight) {
92 ddata->backlight->props.power = FB_BLANK_UNBLANK;
93 backlight_update_status(ddata->backlight);
94 }
95
96 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
97
98 return 0;
99 }
100
panel_dpi_disable(struct omap_dss_device * dssdev)101 static void panel_dpi_disable(struct omap_dss_device *dssdev)
102 {
103 struct panel_drv_data *ddata = to_panel_data(dssdev);
104 struct omap_dss_device *in = ddata->in;
105
106 if (!omapdss_device_is_enabled(dssdev))
107 return;
108
109 if (ddata->backlight) {
110 ddata->backlight->props.power = FB_BLANK_POWERDOWN;
111 backlight_update_status(ddata->backlight);
112 }
113
114 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
115 regulator_disable(ddata->vcc_supply);
116
117 in->ops.dpi->disable(in);
118
119 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
120 }
121
panel_dpi_set_timings(struct omap_dss_device * dssdev,struct videomode * vm)122 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
123 struct videomode *vm)
124 {
125 struct panel_drv_data *ddata = to_panel_data(dssdev);
126 struct omap_dss_device *in = ddata->in;
127
128 ddata->vm = *vm;
129 dssdev->panel.vm = *vm;
130
131 in->ops.dpi->set_timings(in, vm);
132 }
133
panel_dpi_get_timings(struct omap_dss_device * dssdev,struct videomode * vm)134 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
135 struct videomode *vm)
136 {
137 struct panel_drv_data *ddata = to_panel_data(dssdev);
138
139 *vm = ddata->vm;
140 }
141
panel_dpi_check_timings(struct omap_dss_device * dssdev,struct videomode * vm)142 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
143 struct videomode *vm)
144 {
145 struct panel_drv_data *ddata = to_panel_data(dssdev);
146 struct omap_dss_device *in = ddata->in;
147
148 return in->ops.dpi->check_timings(in, vm);
149 }
150
151 static struct omap_dss_driver panel_dpi_ops = {
152 .connect = panel_dpi_connect,
153 .disconnect = panel_dpi_disconnect,
154
155 .enable = panel_dpi_enable,
156 .disable = panel_dpi_disable,
157
158 .set_timings = panel_dpi_set_timings,
159 .get_timings = panel_dpi_get_timings,
160 .check_timings = panel_dpi_check_timings,
161 };
162
panel_dpi_probe_of(struct platform_device * pdev)163 static int panel_dpi_probe_of(struct platform_device *pdev)
164 {
165 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
166 struct device_node *node = pdev->dev.of_node;
167 struct device_node *bl_node;
168 struct omap_dss_device *in;
169 int r;
170 struct display_timing timing;
171 struct gpio_desc *gpio;
172
173 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
174 if (IS_ERR(gpio))
175 return PTR_ERR(gpio);
176
177 ddata->enable_gpio = gpio;
178
179 /*
180 * Many different panels are supported by this driver and there are
181 * probably very different needs for their reset pins in regards to
182 * timing and order relative to the enable gpio. So for now it's just
183 * ensured that the reset line isn't active.
184 */
185 gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
186 if (IS_ERR(gpio))
187 return PTR_ERR(gpio);
188
189 ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
190 if (IS_ERR(ddata->vcc_supply))
191 return PTR_ERR(ddata->vcc_supply);
192
193 bl_node = of_parse_phandle(node, "backlight", 0);
194 if (bl_node) {
195 ddata->backlight = of_find_backlight_by_node(bl_node);
196 of_node_put(bl_node);
197
198 if (!ddata->backlight)
199 return -EPROBE_DEFER;
200 }
201
202 r = of_get_display_timing(node, "panel-timing", &timing);
203 if (r) {
204 dev_err(&pdev->dev, "failed to get video timing\n");
205 goto error_free_backlight;
206 }
207
208 videomode_from_timing(&timing, &ddata->vm);
209
210 in = omapdss_of_find_source_for_first_ep(node);
211 if (IS_ERR(in)) {
212 dev_err(&pdev->dev, "failed to find video source\n");
213 r = PTR_ERR(in);
214 goto error_free_backlight;
215 }
216
217 ddata->in = in;
218
219 return 0;
220
221 error_free_backlight:
222 if (ddata->backlight)
223 put_device(&ddata->backlight->dev);
224
225 return r;
226 }
227
panel_dpi_probe(struct platform_device * pdev)228 static int panel_dpi_probe(struct platform_device *pdev)
229 {
230 struct panel_drv_data *ddata;
231 struct omap_dss_device *dssdev;
232 int r;
233
234 if (!pdev->dev.of_node)
235 return -ENODEV;
236
237 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
238 if (ddata == NULL)
239 return -ENOMEM;
240
241 platform_set_drvdata(pdev, ddata);
242
243 r = panel_dpi_probe_of(pdev);
244 if (r)
245 return r;
246
247 dssdev = &ddata->dssdev;
248 dssdev->dev = &pdev->dev;
249 dssdev->driver = &panel_dpi_ops;
250 dssdev->type = OMAP_DISPLAY_TYPE_DPI;
251 dssdev->owner = THIS_MODULE;
252 dssdev->panel.vm = ddata->vm;
253
254 r = omapdss_register_display(dssdev);
255 if (r) {
256 dev_err(&pdev->dev, "Failed to register panel\n");
257 goto err_reg;
258 }
259
260 return 0;
261
262 err_reg:
263 omap_dss_put_device(ddata->in);
264 return r;
265 }
266
panel_dpi_remove(struct platform_device * pdev)267 static int __exit panel_dpi_remove(struct platform_device *pdev)
268 {
269 struct panel_drv_data *ddata = platform_get_drvdata(pdev);
270 struct omap_dss_device *dssdev = &ddata->dssdev;
271 struct omap_dss_device *in = ddata->in;
272
273 omapdss_unregister_display(dssdev);
274
275 panel_dpi_disable(dssdev);
276 panel_dpi_disconnect(dssdev);
277
278 omap_dss_put_device(in);
279
280 if (ddata->backlight)
281 put_device(&ddata->backlight->dev);
282
283 return 0;
284 }
285
286 static const struct of_device_id panel_dpi_of_match[] = {
287 { .compatible = "omapdss,panel-dpi", },
288 {},
289 };
290
291 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
292
293 static struct platform_driver panel_dpi_driver = {
294 .probe = panel_dpi_probe,
295 .remove = __exit_p(panel_dpi_remove),
296 .driver = {
297 .name = "panel-dpi",
298 .of_match_table = panel_dpi_of_match,
299 .suppress_bind_attrs = true,
300 },
301 };
302
303 module_platform_driver(panel_dpi_driver);
304
305 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
306 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
307 MODULE_LICENSE("GPL");
308