1 // SPDX-License-Identifier: GPL-2.0+
3 * Generic LVDS panel driver
5 * Copyright (C) 2016 Laurent Pinchart
6 * Copyright (C) 2016 Renesas Electronics Corporation
8 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
11 #include <linux/gpio/consumer.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/regulator/consumer.h>
16 #include <linux/slab.h>
18 #include <video/display_timing.h>
19 #include <video/of_display_timing.h>
20 #include <video/videomode.h>
22 #include <drm/drm_crtc.h>
23 #include <drm/drm_of.h>
24 #include <drm/drm_panel.h>
27 struct drm_panel panel
;
33 struct drm_display_mode dmode
;
35 unsigned int bus_format
;
37 struct regulator
*supply
;
39 struct gpio_desc
*enable_gpio
;
40 struct gpio_desc
*reset_gpio
;
42 enum drm_panel_orientation orientation
;
45 static inline struct panel_lvds
*to_panel_lvds(struct drm_panel
*panel
)
47 return container_of(panel
, struct panel_lvds
, panel
);
50 static int panel_lvds_unprepare(struct drm_panel
*panel
)
52 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
54 if (lvds
->enable_gpio
)
55 gpiod_set_value_cansleep(lvds
->enable_gpio
, 0);
58 regulator_disable(lvds
->supply
);
63 static int panel_lvds_prepare(struct drm_panel
*panel
)
65 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
70 err
= regulator_enable(lvds
->supply
);
72 dev_err(lvds
->dev
, "failed to enable supply: %d\n",
78 if (lvds
->enable_gpio
)
79 gpiod_set_value_cansleep(lvds
->enable_gpio
, 1);
84 static int panel_lvds_get_modes(struct drm_panel
*panel
,
85 struct drm_connector
*connector
)
87 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
88 struct drm_display_mode
*mode
;
90 mode
= drm_mode_duplicate(connector
->dev
, &lvds
->dmode
);
94 mode
->type
|= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
95 drm_mode_probed_add(connector
, mode
);
97 connector
->display_info
.width_mm
= lvds
->dmode
.width_mm
;
98 connector
->display_info
.height_mm
= lvds
->dmode
.height_mm
;
99 drm_display_info_set_bus_formats(&connector
->display_info
,
100 &lvds
->bus_format
, 1);
101 connector
->display_info
.bus_flags
= lvds
->bus_flags
;
104 * TODO: Remove once all drm drivers call
105 * drm_connector_set_orientation_from_panel()
107 drm_connector_set_panel_orientation(connector
, lvds
->orientation
);
112 static enum drm_panel_orientation
panel_lvds_get_orientation(struct drm_panel
*panel
)
114 struct panel_lvds
*lvds
= to_panel_lvds(panel
);
116 return lvds
->orientation
;
119 static const struct drm_panel_funcs panel_lvds_funcs
= {
120 .unprepare
= panel_lvds_unprepare
,
121 .prepare
= panel_lvds_prepare
,
122 .get_modes
= panel_lvds_get_modes
,
123 .get_orientation
= panel_lvds_get_orientation
,
126 static int panel_lvds_parse_dt(struct panel_lvds
*lvds
)
128 struct device_node
*np
= lvds
->dev
->of_node
;
131 ret
= of_drm_get_panel_orientation(np
, &lvds
->orientation
);
133 dev_err(lvds
->dev
, "%pOF: failed to get orientation %d\n", np
, ret
);
137 ret
= of_get_drm_panel_display_mode(np
, &lvds
->dmode
, &lvds
->bus_flags
);
139 dev_err(lvds
->dev
, "%pOF: problems parsing panel-timing (%d)\n",
144 of_property_read_string(np
, "label", &lvds
->label
);
146 ret
= drm_of_lvds_get_data_mapping(np
);
148 dev_err(lvds
->dev
, "%pOF: invalid or missing %s DT property\n",
153 lvds
->bus_format
= ret
;
155 lvds
->bus_flags
|= of_property_read_bool(np
, "data-mirror") ?
156 DRM_BUS_FLAG_DATA_LSB_TO_MSB
:
157 DRM_BUS_FLAG_DATA_MSB_TO_LSB
;
162 static int panel_lvds_probe(struct platform_device
*pdev
)
164 struct panel_lvds
*lvds
;
167 lvds
= devm_kzalloc(&pdev
->dev
, sizeof(*lvds
), GFP_KERNEL
);
171 lvds
->dev
= &pdev
->dev
;
173 ret
= panel_lvds_parse_dt(lvds
);
177 lvds
->supply
= devm_regulator_get_optional(lvds
->dev
, "power");
178 if (IS_ERR(lvds
->supply
)) {
179 ret
= PTR_ERR(lvds
->supply
);
181 if (ret
!= -ENODEV
) {
182 if (ret
!= -EPROBE_DEFER
)
183 dev_err(lvds
->dev
, "failed to request regulator: %d\n",
191 /* Get GPIOs and backlight controller. */
192 lvds
->enable_gpio
= devm_gpiod_get_optional(lvds
->dev
, "enable",
194 if (IS_ERR(lvds
->enable_gpio
)) {
195 ret
= PTR_ERR(lvds
->enable_gpio
);
196 dev_err(lvds
->dev
, "failed to request %s GPIO: %d\n",
201 lvds
->reset_gpio
= devm_gpiod_get_optional(lvds
->dev
, "reset",
203 if (IS_ERR(lvds
->reset_gpio
)) {
204 ret
= PTR_ERR(lvds
->reset_gpio
);
205 dev_err(lvds
->dev
, "failed to request %s GPIO: %d\n",
211 * TODO: Handle all power supplies specified in the DT node in a generic
212 * way for panels that don't care about power supply ordering. LVDS
213 * panels that require a specific power sequence will need a dedicated
217 /* Register the panel. */
218 drm_panel_init(&lvds
->panel
, lvds
->dev
, &panel_lvds_funcs
,
219 DRM_MODE_CONNECTOR_LVDS
);
221 ret
= drm_panel_of_backlight(&lvds
->panel
);
225 drm_panel_add(&lvds
->panel
);
227 dev_set_drvdata(lvds
->dev
, lvds
);
231 static void panel_lvds_remove(struct platform_device
*pdev
)
233 struct panel_lvds
*lvds
= platform_get_drvdata(pdev
);
235 drm_panel_remove(&lvds
->panel
);
237 drm_panel_disable(&lvds
->panel
);
240 static const struct of_device_id panel_lvds_of_table
[] = {
241 { .compatible
= "panel-lvds", },
245 MODULE_DEVICE_TABLE(of
, panel_lvds_of_table
);
247 static struct platform_driver panel_lvds_driver
= {
248 .probe
= panel_lvds_probe
,
249 .remove
= panel_lvds_remove
,
251 .name
= "panel-lvds",
252 .of_match_table
= panel_lvds_of_table
,
256 module_platform_driver(panel_lvds_driver
);
258 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
259 MODULE_DESCRIPTION("LVDS Panel Driver");
260 MODULE_LICENSE("GPL");