• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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