Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cris-mirror.git] / drivers / gpu / drm / omapdrm / displays / panel-dpi.c
blobefff6dbbb86fe70494beaef60f19f15b8af8e3b0
1 /*
2 * Generic MIPI DPI Panel Driver
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
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>
16 #include <linux/of.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/backlight.h>
20 #include <video/of_display_timing.h>
22 #include "../dss/omapdss.h"
24 struct panel_drv_data {
25 struct omap_dss_device dssdev;
26 struct omap_dss_device *in;
28 struct videomode vm;
30 struct backlight_device *backlight;
32 struct gpio_desc *enable_gpio;
33 struct regulator *vcc_supply;
36 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
38 static int panel_dpi_connect(struct omap_dss_device *dssdev)
40 struct panel_drv_data *ddata = to_panel_data(dssdev);
41 struct omap_dss_device *in = ddata->in;
42 int r;
44 if (omapdss_device_is_connected(dssdev))
45 return 0;
47 r = in->ops.dpi->connect(in, dssdev);
48 if (r)
49 return r;
51 return 0;
54 static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
56 struct panel_drv_data *ddata = to_panel_data(dssdev);
57 struct omap_dss_device *in = ddata->in;
59 if (!omapdss_device_is_connected(dssdev))
60 return;
62 in->ops.dpi->disconnect(in, dssdev);
65 static int panel_dpi_enable(struct omap_dss_device *dssdev)
67 struct panel_drv_data *ddata = to_panel_data(dssdev);
68 struct omap_dss_device *in = ddata->in;
69 int r;
71 if (!omapdss_device_is_connected(dssdev))
72 return -ENODEV;
74 if (omapdss_device_is_enabled(dssdev))
75 return 0;
77 in->ops.dpi->set_timings(in, &ddata->vm);
79 r = in->ops.dpi->enable(in);
80 if (r)
81 return r;
83 r = regulator_enable(ddata->vcc_supply);
84 if (r) {
85 in->ops.dpi->disable(in);
86 return r;
89 gpiod_set_value_cansleep(ddata->enable_gpio, 1);
91 if (ddata->backlight) {
92 ddata->backlight->props.power = FB_BLANK_UNBLANK;
93 backlight_update_status(ddata->backlight);
96 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
98 return 0;
101 static void panel_dpi_disable(struct omap_dss_device *dssdev)
103 struct panel_drv_data *ddata = to_panel_data(dssdev);
104 struct omap_dss_device *in = ddata->in;
106 if (!omapdss_device_is_enabled(dssdev))
107 return;
109 if (ddata->backlight) {
110 ddata->backlight->props.power = FB_BLANK_POWERDOWN;
111 backlight_update_status(ddata->backlight);
114 gpiod_set_value_cansleep(ddata->enable_gpio, 0);
115 regulator_disable(ddata->vcc_supply);
117 in->ops.dpi->disable(in);
119 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
122 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
123 struct videomode *vm)
125 struct panel_drv_data *ddata = to_panel_data(dssdev);
126 struct omap_dss_device *in = ddata->in;
128 ddata->vm = *vm;
129 dssdev->panel.vm = *vm;
131 in->ops.dpi->set_timings(in, vm);
134 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
135 struct videomode *vm)
137 struct panel_drv_data *ddata = to_panel_data(dssdev);
139 *vm = ddata->vm;
142 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
143 struct videomode *vm)
145 struct panel_drv_data *ddata = to_panel_data(dssdev);
146 struct omap_dss_device *in = ddata->in;
148 return in->ops.dpi->check_timings(in, vm);
151 static struct omap_dss_driver panel_dpi_ops = {
152 .connect = panel_dpi_connect,
153 .disconnect = panel_dpi_disconnect,
155 .enable = panel_dpi_enable,
156 .disable = panel_dpi_disable,
158 .set_timings = panel_dpi_set_timings,
159 .get_timings = panel_dpi_get_timings,
160 .check_timings = panel_dpi_check_timings,
163 static int panel_dpi_probe_of(struct platform_device *pdev)
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;
173 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
174 if (IS_ERR(gpio))
175 return PTR_ERR(gpio);
177 ddata->enable_gpio = gpio;
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.
185 gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
186 if (IS_ERR(gpio))
187 return PTR_ERR(gpio);
189 ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
190 if (IS_ERR(ddata->vcc_supply))
191 return PTR_ERR(ddata->vcc_supply);
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);
198 if (!ddata->backlight)
199 return -EPROBE_DEFER;
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;
208 videomode_from_timing(&timing, &ddata->vm);
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;
217 ddata->in = in;
219 return 0;
221 error_free_backlight:
222 if (ddata->backlight)
223 put_device(&ddata->backlight->dev);
225 return r;
228 static int panel_dpi_probe(struct platform_device *pdev)
230 struct panel_drv_data *ddata;
231 struct omap_dss_device *dssdev;
232 int r;
234 if (!pdev->dev.of_node)
235 return -ENODEV;
237 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
238 if (ddata == NULL)
239 return -ENOMEM;
241 platform_set_drvdata(pdev, ddata);
243 r = panel_dpi_probe_of(pdev);
244 if (r)
245 return r;
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;
254 r = omapdss_register_display(dssdev);
255 if (r) {
256 dev_err(&pdev->dev, "Failed to register panel\n");
257 goto err_reg;
260 return 0;
262 err_reg:
263 omap_dss_put_device(ddata->in);
264 return r;
267 static int __exit panel_dpi_remove(struct platform_device *pdev)
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;
273 omapdss_unregister_display(dssdev);
275 panel_dpi_disable(dssdev);
276 panel_dpi_disconnect(dssdev);
278 omap_dss_put_device(in);
280 if (ddata->backlight)
281 put_device(&ddata->backlight->dev);
283 return 0;
286 static const struct of_device_id panel_dpi_of_match[] = {
287 { .compatible = "omapdss,panel-dpi", },
291 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
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,
303 module_platform_driver(panel_dpi_driver);
305 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
306 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
307 MODULE_LICENSE("GPL");