2 * Generic MIPI DPI Panel Driver
4 * Copyright (C) 2013 Texas Instruments
5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
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.
12 #include <linux/gpio/consumer.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
17 #include <linux/of_gpio.h>
18 #include <linux/regulator/consumer.h>
20 #include <video/omap-panel-data.h>
21 #include <video/of_display_timing.h>
23 #include "../dss/omapdss.h"
25 struct panel_drv_data
{
26 struct omap_dss_device dssdev
;
27 struct omap_dss_device
*in
;
31 struct omap_video_timings videomode
;
33 /* used for non-DT boot, to be removed */
36 struct gpio_desc
*enable_gpio
;
37 struct regulator
*vcc_supply
;
40 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
42 static int panel_dpi_connect(struct omap_dss_device
*dssdev
)
44 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
45 struct omap_dss_device
*in
= ddata
->in
;
48 if (omapdss_device_is_connected(dssdev
))
51 r
= in
->ops
.dpi
->connect(in
, dssdev
);
58 static void panel_dpi_disconnect(struct omap_dss_device
*dssdev
)
60 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
61 struct omap_dss_device
*in
= ddata
->in
;
63 if (!omapdss_device_is_connected(dssdev
))
66 in
->ops
.dpi
->disconnect(in
, dssdev
);
69 static int panel_dpi_enable(struct omap_dss_device
*dssdev
)
71 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
72 struct omap_dss_device
*in
= ddata
->in
;
75 if (!omapdss_device_is_connected(dssdev
))
78 if (omapdss_device_is_enabled(dssdev
))
81 if (ddata
->data_lines
)
82 in
->ops
.dpi
->set_data_lines(in
, ddata
->data_lines
);
83 in
->ops
.dpi
->set_timings(in
, &ddata
->videomode
);
85 r
= in
->ops
.dpi
->enable(in
);
89 r
= regulator_enable(ddata
->vcc_supply
);
91 in
->ops
.dpi
->disable(in
);
95 gpiod_set_value_cansleep(ddata
->enable_gpio
, 1);
97 if (gpio_is_valid(ddata
->backlight_gpio
))
98 gpio_set_value_cansleep(ddata
->backlight_gpio
, 1);
100 dssdev
->state
= OMAP_DSS_DISPLAY_ACTIVE
;
105 static void panel_dpi_disable(struct omap_dss_device
*dssdev
)
107 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
108 struct omap_dss_device
*in
= ddata
->in
;
110 if (!omapdss_device_is_enabled(dssdev
))
113 if (gpio_is_valid(ddata
->backlight_gpio
))
114 gpio_set_value_cansleep(ddata
->backlight_gpio
, 0);
116 gpiod_set_value_cansleep(ddata
->enable_gpio
, 0);
117 regulator_disable(ddata
->vcc_supply
);
119 in
->ops
.dpi
->disable(in
);
121 dssdev
->state
= OMAP_DSS_DISPLAY_DISABLED
;
124 static void panel_dpi_set_timings(struct omap_dss_device
*dssdev
,
125 struct omap_video_timings
*timings
)
127 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
128 struct omap_dss_device
*in
= ddata
->in
;
130 ddata
->videomode
= *timings
;
131 dssdev
->panel
.timings
= *timings
;
133 in
->ops
.dpi
->set_timings(in
, timings
);
136 static void panel_dpi_get_timings(struct omap_dss_device
*dssdev
,
137 struct omap_video_timings
*timings
)
139 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
141 *timings
= ddata
->videomode
;
144 static int panel_dpi_check_timings(struct omap_dss_device
*dssdev
,
145 struct omap_video_timings
*timings
)
147 struct panel_drv_data
*ddata
= to_panel_data(dssdev
);
148 struct omap_dss_device
*in
= ddata
->in
;
150 return in
->ops
.dpi
->check_timings(in
, timings
);
153 static struct omap_dss_driver panel_dpi_ops
= {
154 .connect
= panel_dpi_connect
,
155 .disconnect
= panel_dpi_disconnect
,
157 .enable
= panel_dpi_enable
,
158 .disable
= panel_dpi_disable
,
160 .set_timings
= panel_dpi_set_timings
,
161 .get_timings
= panel_dpi_get_timings
,
162 .check_timings
= panel_dpi_check_timings
,
164 .get_resolution
= omapdss_default_get_resolution
,
167 static int panel_dpi_probe_pdata(struct platform_device
*pdev
)
169 const struct panel_dpi_platform_data
*pdata
;
170 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
171 struct omap_dss_device
*dssdev
, *in
;
175 pdata
= dev_get_platdata(&pdev
->dev
);
177 in
= omap_dss_find_output(pdata
->source
);
179 dev_err(&pdev
->dev
, "failed to find video source '%s'\n",
181 return -EPROBE_DEFER
;
186 ddata
->data_lines
= pdata
->data_lines
;
188 videomode_from_timing(pdata
->display_timing
, &vm
);
189 videomode_to_omap_video_timings(&vm
, &ddata
->videomode
);
191 dssdev
= &ddata
->dssdev
;
192 dssdev
->name
= pdata
->name
;
194 r
= devm_gpio_request_one(&pdev
->dev
, pdata
->enable_gpio
,
195 GPIOF_OUT_INIT_LOW
, "panel enable");
199 ddata
->enable_gpio
= gpio_to_desc(pdata
->enable_gpio
);
201 ddata
->backlight_gpio
= pdata
->backlight_gpio
;
206 omap_dss_put_device(ddata
->in
);
210 static int panel_dpi_probe_of(struct platform_device
*pdev
)
212 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
213 struct device_node
*node
= pdev
->dev
.of_node
;
214 struct omap_dss_device
*in
;
216 struct display_timing timing
;
218 struct gpio_desc
*gpio
;
220 gpio
= devm_gpiod_get_optional(&pdev
->dev
, "enable", GPIOD_OUT_LOW
);
222 return PTR_ERR(gpio
);
224 ddata
->enable_gpio
= gpio
;
227 * Many different panels are supported by this driver and there are
228 * probably very different needs for their reset pins in regards to
229 * timing and order relative to the enable gpio. So for now it's just
230 * ensured that the reset line isn't active.
232 gpio
= devm_gpiod_get_optional(&pdev
->dev
, "reset", GPIOD_OUT_LOW
);
234 return PTR_ERR(gpio
);
236 ddata
->vcc_supply
= devm_regulator_get(&pdev
->dev
, "vcc");
237 if (IS_ERR(ddata
->vcc_supply
))
238 return PTR_ERR(ddata
->vcc_supply
);
240 ddata
->backlight_gpio
= -ENOENT
;
242 r
= of_get_display_timing(node
, "panel-timing", &timing
);
244 dev_err(&pdev
->dev
, "failed to get video timing\n");
248 videomode_from_timing(&timing
, &vm
);
249 videomode_to_omap_video_timings(&vm
, &ddata
->videomode
);
251 in
= omapdss_of_find_source_for_first_ep(node
);
253 dev_err(&pdev
->dev
, "failed to find video source\n");
262 static int panel_dpi_probe(struct platform_device
*pdev
)
264 struct panel_drv_data
*ddata
;
265 struct omap_dss_device
*dssdev
;
268 ddata
= devm_kzalloc(&pdev
->dev
, sizeof(*ddata
), GFP_KERNEL
);
272 platform_set_drvdata(pdev
, ddata
);
274 if (dev_get_platdata(&pdev
->dev
)) {
275 r
= panel_dpi_probe_pdata(pdev
);
278 } else if (pdev
->dev
.of_node
) {
279 r
= panel_dpi_probe_of(pdev
);
286 if (gpio_is_valid(ddata
->backlight_gpio
)) {
287 r
= devm_gpio_request_one(&pdev
->dev
, ddata
->backlight_gpio
,
288 GPIOF_OUT_INIT_LOW
, "panel backlight");
293 dssdev
= &ddata
->dssdev
;
294 dssdev
->dev
= &pdev
->dev
;
295 dssdev
->driver
= &panel_dpi_ops
;
296 dssdev
->type
= OMAP_DISPLAY_TYPE_DPI
;
297 dssdev
->owner
= THIS_MODULE
;
298 dssdev
->panel
.timings
= ddata
->videomode
;
299 dssdev
->phy
.dpi
.data_lines
= ddata
->data_lines
;
301 r
= omapdss_register_display(dssdev
);
303 dev_err(&pdev
->dev
, "Failed to register panel\n");
311 omap_dss_put_device(ddata
->in
);
315 static int __exit
panel_dpi_remove(struct platform_device
*pdev
)
317 struct panel_drv_data
*ddata
= platform_get_drvdata(pdev
);
318 struct omap_dss_device
*dssdev
= &ddata
->dssdev
;
319 struct omap_dss_device
*in
= ddata
->in
;
321 omapdss_unregister_display(dssdev
);
323 panel_dpi_disable(dssdev
);
324 panel_dpi_disconnect(dssdev
);
326 omap_dss_put_device(in
);
331 static const struct of_device_id panel_dpi_of_match
[] = {
332 { .compatible
= "omapdss,panel-dpi", },
336 MODULE_DEVICE_TABLE(of
, panel_dpi_of_match
);
338 static struct platform_driver panel_dpi_driver
= {
339 .probe
= panel_dpi_probe
,
340 .remove
= __exit_p(panel_dpi_remove
),
343 .of_match_table
= panel_dpi_of_match
,
344 .suppress_bind_attrs
= true,
348 module_platform_driver(panel_dpi_driver
);
350 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
351 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
352 MODULE_LICENSE("GPL");