blob: efff6dbbb86fe70494beaef60f19f15b8af8e3b0 [file] [log] [blame]
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +03001/*
2 * Generic MIPI DPI Panel Driver
3 *
Andrew F. Davisbb5cdf82017-12-05 14:29:31 -06004 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +03005 * 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
Tomi Valkeinend9e32ec2016-03-18 09:02:18 +020012#include <linux/gpio/consumer.h>
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030013#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
Tomi Valkeinen4843a052013-05-16 15:14:16 +030016#include <linux/of.h>
Uwe Kleine-König5379be22016-03-21 22:10:27 +010017#include <linux/regulator/consumer.h>
Peter Ujfalusi39135a32016-09-09 13:33:06 +030018#include <linux/backlight.h>
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030019
Tomi Valkeinen4843a052013-05-16 15:14:16 +030020#include <video/of_display_timing.h>
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030021
Peter Ujfalusi32043da2016-05-27 14:40:49 +030022#include "../dss/omapdss.h"
23
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030024struct panel_drv_data {
25 struct omap_dss_device dssdev;
26 struct omap_dss_device *in;
27
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +030028 struct videomode vm;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030029
Peter Ujfalusi39135a32016-09-09 13:33:06 +030030 struct backlight_device *backlight;
31
Tomi Valkeinen56610d92014-04-24 12:36:52 +030032 struct gpio_desc *enable_gpio;
Uwe Kleine-König5379be22016-03-21 22:10:27 +010033 struct regulator *vcc_supply;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030034};
35
36#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
37
38static 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
54static 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
65static 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
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +030077 in->ops.dpi->set_timings(in, &ddata->vm);
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030078
79 r = in->ops.dpi->enable(in);
80 if (r)
81 return r;
82
Uwe Kleine-König5379be22016-03-21 22:10:27 +010083 r = regulator_enable(ddata->vcc_supply);
84 if (r) {
85 in->ops.dpi->disable(in);
86 return r;
87 }
88
Uwe Kleine-Königef1ea032015-12-10 14:11:44 +010089 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030090
Peter Ujfalusi39135a32016-09-09 13:33:06 +030091 if (ddata->backlight) {
92 ddata->backlight->props.power = FB_BLANK_UNBLANK;
93 backlight_update_status(ddata->backlight);
94 }
95
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +030096 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
97
98 return 0;
99}
100
101static 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
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300109 if (ddata->backlight) {
110 ddata->backlight->props.power = FB_BLANK_POWERDOWN;
111 backlight_update_status(ddata->backlight);
112 }
113
Uwe Kleine-Königef1ea032015-12-10 14:11:44 +0100114 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
Uwe Kleine-König5379be22016-03-21 22:10:27 +0100115 regulator_disable(ddata->vcc_supply);
Uwe Kleine-König7f496cf2015-12-10 14:11:43 +0100116
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300117 in->ops.dpi->disable(in);
118
119 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
120}
121
122static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300123 struct videomode *vm)
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300124{
125 struct panel_drv_data *ddata = to_panel_data(dssdev);
126 struct omap_dss_device *in = ddata->in;
127
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300128 ddata->vm = *vm;
129 dssdev->panel.vm = *vm;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300130
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300131 in->ops.dpi->set_timings(in, vm);
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300132}
133
134static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300135 struct videomode *vm)
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300136{
137 struct panel_drv_data *ddata = to_panel_data(dssdev);
138
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300139 *vm = ddata->vm;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300140}
141
142static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300143 struct videomode *vm)
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300144{
145 struct panel_drv_data *ddata = to_panel_data(dssdev);
146 struct omap_dss_device *in = ddata->in;
147
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300148 return in->ops.dpi->check_timings(in, vm);
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300149}
150
151static 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,
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300161};
162
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300163static 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;
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300167 struct device_node *bl_node;
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300168 struct omap_dss_device *in;
169 int r;
170 struct display_timing timing;
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300171 struct gpio_desc *gpio;
172
Uwe Kleine-Königca8c67d2015-05-28 10:05:15 +0200173 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
174 if (IS_ERR(gpio))
175 return PTR_ERR(gpio);
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300176
Tomi Valkeinenc6e29d22014-05-15 16:19:44 +0300177 ddata->enable_gpio = gpio;
178
Uwe Kleine-Könige2513b32016-03-21 22:10:26 +0100179 /*
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
Uwe Kleine-König5379be22016-03-21 22:10:27 +0100189 ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
190 if (IS_ERR(ddata->vcc_supply))
191 return PTR_ERR(ddata->vcc_supply);
192
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300193 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
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300202 r = of_get_display_timing(node, "panel-timing", &timing);
203 if (r) {
204 dev_err(&pdev->dev, "failed to get video timing\n");
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300205 goto error_free_backlight;
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300206 }
207
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300208 videomode_from_timing(&timing, &ddata->vm);
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300209
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");
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300213 r = PTR_ERR(in);
214 goto error_free_backlight;
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300215 }
216
217 ddata->in = in;
218
219 return 0;
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300220
221error_free_backlight:
222 if (ddata->backlight)
223 put_device(&ddata->backlight->dev);
224
225 return r;
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300226}
227
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300228static 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
Laurent Pinchartafed19e2017-08-05 01:43:48 +0300234 if (!pdev->dev.of_node)
235 return -ENODEV;
236
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300237 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
238 if (ddata == NULL)
239 return -ENOMEM;
240
241 platform_set_drvdata(pdev, ddata);
242
Tomi Valkeinenbf31ce72017-05-12 12:36:18 +0300243 r = panel_dpi_probe_of(pdev);
244 if (r)
245 return r;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300246
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;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300252 dssdev->panel.vm = ddata->vm;
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300253
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
262err_reg:
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300263 omap_dss_put_device(ddata->in);
264 return r;
265}
266
267static 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
Peter Ujfalusi39135a32016-09-09 13:33:06 +0300280 if (ddata->backlight)
281 put_device(&ddata->backlight->dev);
282
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300283 return 0;
284}
285
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300286static const struct of_device_id panel_dpi_of_match[] = {
287 { .compatible = "omapdss,panel-dpi", },
288 {},
289};
290
291MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
292
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300293static struct platform_driver panel_dpi_driver = {
294 .probe = panel_dpi_probe,
295 .remove = __exit_p(panel_dpi_remove),
296 .driver = {
297 .name = "panel-dpi",
Tomi Valkeinen4843a052013-05-16 15:14:16 +0300298 .of_match_table = panel_dpi_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +0300299 .suppress_bind_attrs = true,
Tomi Valkeinen04f0ff02013-05-24 14:21:56 +0300300 },
301};
302
303module_platform_driver(panel_dpi_driver);
304
305MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
306MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
307MODULE_LICENSE("GPL");