aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/panel/panel-simple.c
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2013-08-30 09:10:14 -0400
committerThierry Reding <treding@nvidia.com>2013-12-17 12:09:51 -0500
commit280921de7241ee63184c8baa89ec3fe0122dedb3 (patch)
tree10f9170b4018bfa46431a39336b4ce3782dd3de1 /drivers/gpu/drm/panel/panel-simple.c
parentaead40ea0b53a0e28d34adf7bb923ecb2968c04a (diff)
drm/panel: Add simple panel support
Add a driver for simple panels. Such panels can have a regulator that provides the supply voltage and a separate GPIO to enable the panel. Optionally the panels can have a backlight associated with them so it can be enabled or disabled according to the panel's power management mode. Support is added for two panels: An AU Optronics 10.1" WSVGA and a Chunghwa Picture Tubes 10.1" WXGA panel. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/panel/panel-simple.c')
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
new file mode 100644
index 000000000000..767b7bef199f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -0,0 +1,417 @@
1/*
2 * Copyright (C) 2013, NVIDIA Corporation. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include <linux/backlight.h>
25#include <linux/gpio.h>
26#include <linux/module.h>
27#include <linux/of_gpio.h>
28#include <linux/of_platform.h>
29#include <linux/platform_device.h>
30#include <linux/regulator/consumer.h>
31
32#include <drm/drmP.h>
33#include <drm/drm_crtc.h>
34#include <drm/drm_panel.h>
35
36struct panel_desc {
37 const struct drm_display_mode *modes;
38 unsigned int num_modes;
39
40 struct {
41 unsigned int width;
42 unsigned int height;
43 } size;
44};
45
46/* TODO: convert to gpiod_*() API once it's been merged */
47#define GPIO_ACTIVE_LOW (1 << 0)
48
49struct panel_simple {
50 struct drm_panel base;
51 bool enabled;
52
53 const struct panel_desc *desc;
54
55 struct backlight_device *backlight;
56 struct regulator *supply;
57 struct i2c_adapter *ddc;
58
59 unsigned long enable_gpio_flags;
60 int enable_gpio;
61};
62
63static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
64{
65 return container_of(panel, struct panel_simple, base);
66}
67
68static int panel_simple_get_fixed_modes(struct panel_simple *panel)
69{
70 struct drm_connector *connector = panel->base.connector;
71 struct drm_device *drm = panel->base.drm;
72 struct drm_display_mode *mode;
73 unsigned int i, num = 0;
74
75 if (!panel->desc)
76 return 0;
77
78 for (i = 0; i < panel->desc->num_modes; i++) {
79 const struct drm_display_mode *m = &panel->desc->modes[i];
80
81 mode = drm_mode_duplicate(drm, m);
82 if (!mode) {
83 dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
84 m->hdisplay, m->vdisplay, m->vrefresh);
85 continue;
86 }
87
88 drm_mode_set_name(mode);
89
90 drm_mode_probed_add(connector, mode);
91 num++;
92 }
93
94 connector->display_info.width_mm = panel->desc->size.width;
95 connector->display_info.height_mm = panel->desc->size.height;
96
97 return num;
98}
99
100static int panel_simple_disable(struct drm_panel *panel)
101{
102 struct panel_simple *p = to_panel_simple(panel);
103
104 if (!p->enabled)
105 return 0;
106
107 if (p->backlight) {
108 p->backlight->props.power = FB_BLANK_POWERDOWN;
109 backlight_update_status(p->backlight);
110 }
111
112 if (gpio_is_valid(p->enable_gpio)) {
113 if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
114 gpio_set_value(p->enable_gpio, 1);
115 else
116 gpio_set_value(p->enable_gpio, 0);
117 }
118
119 regulator_disable(p->supply);
120 p->enabled = false;
121
122 return 0;
123}
124
125static int panel_simple_enable(struct drm_panel *panel)
126{
127 struct panel_simple *p = to_panel_simple(panel);
128 int err;
129
130 if (p->enabled)
131 return 0;
132
133 err = regulator_enable(p->supply);
134 if (err < 0) {
135 dev_err(panel->dev, "failed to enable supply: %d\n", err);
136 return err;
137 }
138
139 if (gpio_is_valid(p->enable_gpio)) {
140 if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
141 gpio_set_value(p->enable_gpio, 0);
142 else
143 gpio_set_value(p->enable_gpio, 1);
144 }
145
146 if (p->backlight) {
147 p->backlight->props.power = FB_BLANK_UNBLANK;
148 backlight_update_status(p->backlight);
149 }
150
151 p->enabled = true;
152
153 return 0;
154}
155
156static int panel_simple_get_modes(struct drm_panel *panel)
157{
158 struct panel_simple *p = to_panel_simple(panel);
159 int num = 0;
160
161 /* probe EDID if a DDC bus is available */
162 if (p->ddc) {
163 struct edid *edid = drm_get_edid(panel->connector, p->ddc);
164 if (edid) {
165 num += drm_add_edid_modes(panel->connector, edid);
166 kfree(edid);
167 }
168 }
169
170 /* add hard-coded panel modes */
171 num += panel_simple_get_fixed_modes(p);
172
173 return num;
174}
175
176static const struct drm_panel_funcs panel_simple_funcs = {
177 .disable = panel_simple_disable,
178 .enable = panel_simple_enable,
179 .get_modes = panel_simple_get_modes,
180};
181
182static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
183{
184 struct device_node *backlight, *ddc;
185 struct panel_simple *panel;
186 enum of_gpio_flags flags;
187 int err;
188
189 panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
190 if (!panel)
191 return -ENOMEM;
192
193 panel->enabled = false;
194 panel->desc = desc;
195
196 panel->supply = devm_regulator_get(dev, "power");
197 if (IS_ERR(panel->supply))
198 return PTR_ERR(panel->supply);
199
200 panel->enable_gpio = of_get_named_gpio_flags(dev->of_node,
201 "enable-gpios", 0,
202 &flags);
203 if (gpio_is_valid(panel->enable_gpio)) {
204 unsigned int value;
205
206 if (flags & OF_GPIO_ACTIVE_LOW)
207 panel->enable_gpio_flags |= GPIO_ACTIVE_LOW;
208
209 err = gpio_request(panel->enable_gpio, "enable");
210 if (err < 0) {
211 dev_err(dev, "failed to request GPIO#%u: %d\n",
212 panel->enable_gpio, err);
213 return err;
214 }
215
216 value = (panel->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
217
218 err = gpio_direction_output(panel->enable_gpio, value);
219 if (err < 0) {
220 dev_err(dev, "failed to setup GPIO%u: %d\n",
221 panel->enable_gpio, err);
222 goto free_gpio;
223 }
224 }
225
226 backlight = of_parse_phandle(dev->of_node, "backlight", 0);
227 if (backlight) {
228 panel->backlight = of_find_backlight_by_node(backlight);
229 of_node_put(backlight);
230
231 if (!panel->backlight) {
232 err = -EPROBE_DEFER;
233 goto free_gpio;
234 }
235 }
236
237 ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
238 if (ddc) {
239 panel->ddc = of_find_i2c_adapter_by_node(ddc);
240 of_node_put(ddc);
241
242 if (!panel->ddc) {
243 err = -EPROBE_DEFER;
244 goto free_backlight;
245 }
246 }
247
248 drm_panel_init(&panel->base);
249 panel->base.dev = dev;
250 panel->base.funcs = &panel_simple_funcs;
251
252 err = drm_panel_add(&panel->base);
253 if (err < 0)
254 goto free_ddc;
255
256 dev_set_drvdata(dev, panel);
257
258 return 0;
259
260free_ddc:
261 if (panel->ddc)
262 put_device(&panel->ddc->dev);
263free_backlight:
264 if (panel->backlight)
265 put_device(&panel->backlight->dev);
266free_gpio:
267 if (gpio_is_valid(panel->enable_gpio))
268 gpio_free(panel->enable_gpio);
269
270 return err;
271}
272
273static int panel_simple_remove(struct device *dev)
274{
275 struct panel_simple *panel = dev_get_drvdata(dev);
276
277 drm_panel_detach(&panel->base);
278 drm_panel_remove(&panel->base);
279
280 panel_simple_disable(&panel->base);
281
282 if (panel->ddc)
283 put_device(&panel->ddc->dev);
284
285 if (panel->backlight)
286 put_device(&panel->backlight->dev);
287
288 if (gpio_is_valid(panel->enable_gpio))
289 gpio_free(panel->enable_gpio);
290
291 regulator_disable(panel->supply);
292
293 return 0;
294}
295
296static const struct drm_display_mode auo_b101aw03_mode = {
297 .clock = 51450,
298 .hdisplay = 1024,
299 .hsync_start = 1024 + 156,
300 .hsync_end = 1024 + 156 + 8,
301 .htotal = 1024 + 156 + 8 + 156,
302 .vdisplay = 600,
303 .vsync_start = 600 + 16,
304 .vsync_end = 600 + 16 + 6,
305 .vtotal = 600 + 16 + 6 + 16,
306 .vrefresh = 60,
307};
308
309static const struct panel_desc auo_b101aw03 = {
310 .modes = &auo_b101aw03_mode,
311 .num_modes = 1,
312 .size = {
313 .width = 223,
314 .height = 125,
315 },
316};
317
318static const struct drm_display_mode chunghwa_claa101wb01_mode = {
319 .clock = 69300,
320 .hdisplay = 1366,
321 .hsync_start = 1366 + 48,
322 .hsync_end = 1366 + 48 + 32,
323 .htotal = 1366 + 48 + 32 + 20,
324 .vdisplay = 768,
325 .vsync_start = 768 + 16,
326 .vsync_end = 768 + 16 + 8,
327 .vtotal = 768 + 16 + 8 + 16,
328 .vrefresh = 60,
329};
330
331static const struct panel_desc chunghwa_claa101wb01 = {
332 .modes = &chunghwa_claa101wb01_mode,
333 .num_modes = 1,
334 .size = {
335 .width = 223,
336 .height = 125,
337 },
338};
339
340static const struct of_device_id platform_of_match[] = {
341 {
342 .compatible = "auo,b101aw03",
343 .data = &auo_b101aw03,
344 }, {
345 .compatible = "chunghwa,claa101wb01",
346 .data = &chunghwa_claa101wb01
347 }, {
348 .compatible = "simple-panel",
349 }, {
350 /* sentinel */
351 }
352};
353MODULE_DEVICE_TABLE(of, platform_of_match);
354
355static int panel_simple_platform_probe(struct platform_device *pdev)
356{
357 const struct of_device_id *id;
358
359 id = of_match_node(platform_of_match, pdev->dev.of_node);
360 if (!id)
361 return -ENODEV;
362
363 return panel_simple_probe(&pdev->dev, id->data);
364}
365
366static int panel_simple_platform_remove(struct platform_device *pdev)
367{
368 return panel_simple_remove(&pdev->dev);
369}
370
371static struct platform_driver panel_simple_platform_driver = {
372 .driver = {
373 .name = "panel-simple",
374 .owner = THIS_MODULE,
375 .of_match_table = platform_of_match,
376 },
377 .probe = panel_simple_platform_probe,
378 .remove = panel_simple_platform_remove,
379};
380
381static const struct drm_display_mode panasonic_vvx10f004b00_mode = {
382 .clock = 157200,
383 .hdisplay = 1920,
384 .hsync_start = 1920 + 154,
385 .hsync_end = 1920 + 154 + 16,
386 .htotal = 1920 + 154 + 16 + 32,
387 .vdisplay = 1200,
388 .vsync_start = 1200 + 17,
389 .vsync_end = 1200 + 17 + 2,
390 .vtotal = 1200 + 17 + 2 + 16,
391 .vrefresh = 60,
392};
393
394static const struct panel_desc panasonic_vvx10f004b00 = {
395 .modes = &panasonic_vvx10f004b00_mode,
396 .num_modes = 1,
397 .size = {
398 .width = 217,
399 .height = 136,
400 },
401};
402
403static int __init panel_simple_init(void)
404{
405 return platform_driver_register(&panel_simple_platform_driver);
406}
407module_init(panel_simple_init);
408
409static void __exit panel_simple_exit(void)
410{
411 platform_driver_unregister(&panel_simple_platform_driver);
412}
413module_exit(panel_simple_exit);
414
415MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
416MODULE_DESCRIPTION("DRM Driver for Simple Panels");
417MODULE_LICENSE("GPL and additional rights");