2 * Generic LVDS panel driver
4 * Copyright (C) 2016 Laurent Pinchart
5 * Copyright (C) 2016 Renesas Electronics Corporation
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
15 #include <linux/backlight.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/module.h>
18 #include <linux/of_platform.h>
19 #include <linux/platform_device.h>
20 #include <linux/regulator/consumer.h>
21 #include <linux/slab.h>
24 #include <drm/drm_crtc.h>
25 #include <drm/drm_panel.h>
27 #include <video/display_timing.h>
28 #include <video/of_display_timing.h>
29 #include <video/videomode.h>
32 struct drm_panel panel
;
38 struct videomode video_mode
;
39 unsigned int bus_format
;
42 struct backlight_device
*backlight
;
43 struct regulator
*supply
;
45 struct gpio_desc
*enable_gpio
;
46 struct gpio_desc
*reset_gpio
;
49 static inline struct panel_lvds
*to_panel_lvds(struct drm_panel
*panel
)
51 return container_of(panel
, struct panel_lvds
, panel
);
54 static int panel_lvds_disable(struct drm_panel
*panel
)
56 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
58 if (lvds
->backlight
) {
59 lvds
->backlight
->props
.power
= FB_BLANK_POWERDOWN
;
60 lvds
->backlight
->props
.state
|= BL_CORE_FBBLANK
;
61 backlight_update_status(lvds
->backlight
);
67 static int panel_lvds_unprepare(struct drm_panel
*panel
)
69 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
71 if (lvds
->enable_gpio
)
72 gpiod_set_value_cansleep(lvds
->enable_gpio
, 0);
75 regulator_disable(lvds
->supply
);
80 static int panel_lvds_prepare(struct drm_panel
*panel
)
82 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
87 err
= regulator_enable(lvds
->supply
);
89 dev_err(lvds
->dev
, "failed to enable supply: %d\n",
95 if (lvds
->enable_gpio
)
96 gpiod_set_value_cansleep(lvds
->enable_gpio
, 1);
101 static int panel_lvds_enable(struct drm_panel
*panel
)
103 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
105 if (lvds
->backlight
) {
106 lvds
->backlight
->props
.state
&= ~BL_CORE_FBBLANK
;
107 lvds
->backlight
->props
.power
= FB_BLANK_UNBLANK
;
108 backlight_update_status(lvds
->backlight
);
114 static int panel_lvds_get_modes(struct drm_panel
*panel
)
116 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
117 struct drm_connector
*connector
= lvds
->panel
.connector
;
118 struct drm_display_mode
*mode
;
120 mode
= drm_mode_create(lvds
->panel
.drm
);
124 drm_display_mode_from_videomode(&lvds
->video_mode
, mode
);
125 mode
->type
|= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
126 drm_mode_probed_add(connector
, mode
);
128 connector
->display_info
.width_mm
= lvds
->width
;
129 connector
->display_info
.height_mm
= lvds
->height
;
130 drm_display_info_set_bus_formats(&connector
->display_info
,
131 &lvds
->bus_format
, 1);
132 connector
->display_info
.bus_flags
= lvds
->data_mirror
133 ? DRM_BUS_FLAG_DATA_LSB_TO_MSB
134 : DRM_BUS_FLAG_DATA_MSB_TO_LSB
;
139 static const struct drm_panel_funcs panel_lvds_funcs
= {
140 .disable
= panel_lvds_disable
,
141 .unprepare
= panel_lvds_unprepare
,
142 .prepare
= panel_lvds_prepare
,
143 .enable
= panel_lvds_enable
,
144 .get_modes
= panel_lvds_get_modes
,
147 static int panel_lvds_parse_dt(struct panel_lvds
*lvds
)
149 struct device_node
*np
= lvds
->dev
->of_node
;
150 struct display_timing timing
;
154 ret
= of_get_display_timing(np
, "panel-timing", &timing
);
158 videomode_from_timing(&timing
, &lvds
->video_mode
);
160 ret
= of_property_read_u32(np
, "width-mm", &lvds
->width
);
162 dev_err(lvds
->dev
, "%pOF: invalid or missing %s DT property\n",
166 ret
= of_property_read_u32(np
, "height-mm", &lvds
->height
);
168 dev_err(lvds
->dev
, "%pOF: invalid or missing %s DT property\n",
173 of_property_read_string(np
, "label", &lvds
->label
);
175 ret
= of_property_read_string(np
, "data-mapping", &mapping
);
177 dev_err(lvds
->dev
, "%pOF: invalid or missing %s DT property\n",
182 if (!strcmp(mapping
, "jeida-18")) {
183 lvds
->bus_format
= MEDIA_BUS_FMT_RGB666_1X7X3_SPWG
;
184 } else if (!strcmp(mapping
, "jeida-24")) {
185 lvds
->bus_format
= MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA
;
186 } else if (!strcmp(mapping
, "vesa-24")) {
187 lvds
->bus_format
= MEDIA_BUS_FMT_RGB888_1X7X4_SPWG
;
189 dev_err(lvds
->dev
, "%pOF: invalid or missing %s DT property\n",
194 lvds
->data_mirror
= of_property_read_bool(np
, "data-mirror");
199 static int panel_lvds_probe(struct platform_device
*pdev
)
201 struct panel_lvds
*lvds
;
202 struct device_node
*np
;
205 lvds
= devm_kzalloc(&pdev
->dev
, sizeof(*lvds
), GFP_KERNEL
);
209 lvds
->dev
= &pdev
->dev
;
211 ret
= panel_lvds_parse_dt(lvds
);
215 lvds
->supply
= devm_regulator_get_optional(lvds
->dev
, "power");
216 if (IS_ERR(lvds
->supply
)) {
217 ret
= PTR_ERR(lvds
->supply
);
219 if (ret
!= -ENODEV
) {
220 if (ret
!= -EPROBE_DEFER
)
221 dev_err(lvds
->dev
, "failed to request regulator: %d\n",
229 /* Get GPIOs and backlight controller. */
230 lvds
->enable_gpio
= devm_gpiod_get_optional(lvds
->dev
, "enable",
232 if (IS_ERR(lvds
->enable_gpio
)) {
233 ret
= PTR_ERR(lvds
->enable_gpio
);
234 dev_err(lvds
->dev
, "failed to request %s GPIO: %d\n",
239 lvds
->reset_gpio
= devm_gpiod_get_optional(lvds
->dev
, "reset",
241 if (IS_ERR(lvds
->reset_gpio
)) {
242 ret
= PTR_ERR(lvds
->reset_gpio
);
243 dev_err(lvds
->dev
, "failed to request %s GPIO: %d\n",
248 np
= of_parse_phandle(lvds
->dev
->of_node
, "backlight", 0);
250 lvds
->backlight
= of_find_backlight_by_node(np
);
253 if (!lvds
->backlight
)
254 return -EPROBE_DEFER
;
258 * TODO: Handle all power supplies specified in the DT node in a generic
259 * way for panels that don't care about power supply ordering. LVDS
260 * panels that require a specific power sequence will need a dedicated
264 /* Register the panel. */
265 drm_panel_init(&lvds
->panel
);
266 lvds
->panel
.dev
= lvds
->dev
;
267 lvds
->panel
.funcs
= &panel_lvds_funcs
;
269 ret
= drm_panel_add(&lvds
->panel
);
273 dev_set_drvdata(lvds
->dev
, lvds
);
277 put_device(&lvds
->backlight
->dev
);
281 static int panel_lvds_remove(struct platform_device
*pdev
)
283 struct panel_lvds
*lvds
= dev_get_drvdata(&pdev
->dev
);
285 drm_panel_remove(&lvds
->panel
);
287 panel_lvds_disable(&lvds
->panel
);
290 put_device(&lvds
->backlight
->dev
);
295 static const struct of_device_id panel_lvds_of_table
[] = {
296 { .compatible
= "panel-lvds", },
300 MODULE_DEVICE_TABLE(of
, panel_lvds_of_table
);
302 static struct platform_driver panel_lvds_driver
= {
303 .probe
= panel_lvds_probe
,
304 .remove
= panel_lvds_remove
,
306 .name
= "panel-lvds",
307 .of_match_table
= panel_lvds_of_table
,
311 module_platform_driver(panel_lvds_driver
);
313 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
314 MODULE_DESCRIPTION("LVDS Panel Driver");
315 MODULE_LICENSE("GPL");