• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Generic MIPI DPI Panel Driver
3  *
4  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
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;
42 	int r;
43 
44 	if (omapdss_device_is_connected(dssdev))
45 		return 0;
46 
47 	in = omapdss_of_find_source_for_first_ep(dssdev->dev->of_node);
48 	if (IS_ERR(in)) {
49 		dev_err(dssdev->dev, "failed to find video source\n");
50 		return PTR_ERR(in);
51 	}
52 
53 	r = in->ops.dpi->connect(in, dssdev);
54 	if (r) {
55 		omap_dss_put_device(in);
56 		return r;
57 	}
58 
59 	ddata->in = in;
60 	return 0;
61 }
62 
panel_dpi_disconnect(struct omap_dss_device * dssdev)63 static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
64 {
65 	struct panel_drv_data *ddata = to_panel_data(dssdev);
66 	struct omap_dss_device *in = ddata->in;
67 
68 	if (!omapdss_device_is_connected(dssdev))
69 		return;
70 
71 	in->ops.dpi->disconnect(in, dssdev);
72 
73 	omap_dss_put_device(in);
74 	ddata->in = NULL;
75 }
76 
panel_dpi_enable(struct omap_dss_device * dssdev)77 static int panel_dpi_enable(struct omap_dss_device *dssdev)
78 {
79 	struct panel_drv_data *ddata = to_panel_data(dssdev);
80 	struct omap_dss_device *in = ddata->in;
81 	int r;
82 
83 	if (!omapdss_device_is_connected(dssdev))
84 		return -ENODEV;
85 
86 	if (omapdss_device_is_enabled(dssdev))
87 		return 0;
88 
89 	in->ops.dpi->set_timings(in, &ddata->vm);
90 
91 	r = in->ops.dpi->enable(in);
92 	if (r)
93 		return r;
94 
95 	r = regulator_enable(ddata->vcc_supply);
96 	if (r) {
97 		in->ops.dpi->disable(in);
98 		return r;
99 	}
100 
101 	gpiod_set_value_cansleep(ddata->enable_gpio, 1);
102 	backlight_enable(ddata->backlight);
103 
104 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
105 
106 	return 0;
107 }
108 
panel_dpi_disable(struct omap_dss_device * dssdev)109 static void panel_dpi_disable(struct omap_dss_device *dssdev)
110 {
111 	struct panel_drv_data *ddata = to_panel_data(dssdev);
112 	struct omap_dss_device *in = ddata->in;
113 
114 	if (!omapdss_device_is_enabled(dssdev))
115 		return;
116 
117 	backlight_disable(ddata->backlight);
118 
119 	gpiod_set_value_cansleep(ddata->enable_gpio, 0);
120 	regulator_disable(ddata->vcc_supply);
121 
122 	in->ops.dpi->disable(in);
123 
124 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
125 }
126 
panel_dpi_set_timings(struct omap_dss_device * dssdev,struct videomode * vm)127 static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
128 				  struct videomode *vm)
129 {
130 	struct panel_drv_data *ddata = to_panel_data(dssdev);
131 	struct omap_dss_device *in = ddata->in;
132 
133 	ddata->vm = *vm;
134 	dssdev->panel.vm = *vm;
135 
136 	in->ops.dpi->set_timings(in, vm);
137 }
138 
panel_dpi_get_timings(struct omap_dss_device * dssdev,struct videomode * vm)139 static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
140 				  struct videomode *vm)
141 {
142 	struct panel_drv_data *ddata = to_panel_data(dssdev);
143 
144 	*vm = ddata->vm;
145 }
146 
panel_dpi_check_timings(struct omap_dss_device * dssdev,struct videomode * vm)147 static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
148 				   struct videomode *vm)
149 {
150 	struct panel_drv_data *ddata = to_panel_data(dssdev);
151 	struct omap_dss_device *in = ddata->in;
152 
153 	return in->ops.dpi->check_timings(in, vm);
154 }
155 
156 static struct omap_dss_driver panel_dpi_ops = {
157 	.connect	= panel_dpi_connect,
158 	.disconnect	= panel_dpi_disconnect,
159 
160 	.enable		= panel_dpi_enable,
161 	.disable	= panel_dpi_disable,
162 
163 	.set_timings	= panel_dpi_set_timings,
164 	.get_timings	= panel_dpi_get_timings,
165 	.check_timings	= panel_dpi_check_timings,
166 };
167 
panel_dpi_probe_of(struct platform_device * pdev)168 static int panel_dpi_probe_of(struct platform_device *pdev)
169 {
170 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
171 	struct device_node *node = pdev->dev.of_node;
172 	int r;
173 	struct display_timing timing;
174 	struct gpio_desc *gpio;
175 
176 	gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
177 	if (IS_ERR(gpio))
178 		return PTR_ERR(gpio);
179 
180 	ddata->enable_gpio = gpio;
181 
182 	/*
183 	 * Many different panels are supported by this driver and there are
184 	 * probably very different needs for their reset pins in regards to
185 	 * timing and order relative to the enable gpio. So for now it's just
186 	 * ensured that the reset line isn't active.
187 	 */
188 	gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
189 	if (IS_ERR(gpio))
190 		return PTR_ERR(gpio);
191 
192 	ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
193 	if (IS_ERR(ddata->vcc_supply))
194 		return PTR_ERR(ddata->vcc_supply);
195 
196 	ddata->backlight = devm_of_find_backlight(&pdev->dev);
197 
198 	if (IS_ERR(ddata->backlight))
199 		return PTR_ERR(ddata->backlight);
200 
201 	r = of_get_display_timing(node, "panel-timing", &timing);
202 	if (r) {
203 		dev_err(&pdev->dev, "failed to get video timing\n");
204 		return r;
205 	}
206 
207 	videomode_from_timing(&timing, &ddata->vm);
208 
209 	return 0;
210 }
211 
panel_dpi_probe(struct platform_device * pdev)212 static int panel_dpi_probe(struct platform_device *pdev)
213 {
214 	struct panel_drv_data *ddata;
215 	struct omap_dss_device *dssdev;
216 	int r;
217 
218 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
219 	if (ddata == NULL)
220 		return -ENOMEM;
221 
222 	platform_set_drvdata(pdev, ddata);
223 
224 	r = panel_dpi_probe_of(pdev);
225 	if (r)
226 		return r;
227 
228 	dssdev = &ddata->dssdev;
229 	dssdev->dev = &pdev->dev;
230 	dssdev->driver = &panel_dpi_ops;
231 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
232 	dssdev->owner = THIS_MODULE;
233 	dssdev->panel.vm = ddata->vm;
234 
235 	r = omapdss_register_display(dssdev);
236 	if (r) {
237 		dev_err(&pdev->dev, "Failed to register panel\n");
238 		return r;
239 	}
240 
241 	return 0;
242 }
243 
panel_dpi_remove(struct platform_device * pdev)244 static int __exit panel_dpi_remove(struct platform_device *pdev)
245 {
246 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
247 	struct omap_dss_device *dssdev = &ddata->dssdev;
248 
249 	omapdss_unregister_display(dssdev);
250 
251 	panel_dpi_disable(dssdev);
252 	panel_dpi_disconnect(dssdev);
253 
254 	return 0;
255 }
256 
257 static const struct of_device_id panel_dpi_of_match[] = {
258 	{ .compatible = "omapdss,panel-dpi", },
259 	{},
260 };
261 
262 MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
263 
264 static struct platform_driver panel_dpi_driver = {
265 	.probe = panel_dpi_probe,
266 	.remove = __exit_p(panel_dpi_remove),
267 	.driver = {
268 		.name = "panel-dpi",
269 		.of_match_table = panel_dpi_of_match,
270 		.suppress_bind_attrs = true,
271 	},
272 };
273 
274 module_platform_driver(panel_dpi_driver);
275 
276 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
277 MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
278 MODULE_LICENSE("GPL");
279