2 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
10 #include <linux/backlight.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/module.h>
14 #include <linux/regulator/consumer.h>
17 #include <drm/drm_crtc.h>
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_panel.h>
21 #include <video/mipi_display.h>
23 struct innolux_panel
{
24 struct drm_panel base
;
25 struct mipi_dsi_device
*link
;
27 struct backlight_device
*backlight
;
28 struct regulator
*supply
;
29 struct gpio_desc
*enable_gpio
;
35 static inline struct innolux_panel
*to_innolux_panel(struct drm_panel
*panel
)
37 return container_of(panel
, struct innolux_panel
, base
);
40 static int innolux_panel_disable(struct drm_panel
*panel
)
42 struct innolux_panel
*innolux
= to_innolux_panel(panel
);
45 if (!innolux
->enabled
)
48 innolux
->backlight
->props
.power
= FB_BLANK_POWERDOWN
;
49 backlight_update_status(innolux
->backlight
);
51 err
= mipi_dsi_dcs_set_display_off(innolux
->link
);
53 DRM_DEV_ERROR(panel
->dev
, "failed to set display off: %d\n",
56 innolux
->enabled
= false;
61 static int innolux_panel_unprepare(struct drm_panel
*panel
)
63 struct innolux_panel
*innolux
= to_innolux_panel(panel
);
66 if (!innolux
->prepared
)
69 err
= mipi_dsi_dcs_enter_sleep_mode(innolux
->link
);
71 DRM_DEV_ERROR(panel
->dev
, "failed to enter sleep mode: %d\n",
76 gpiod_set_value_cansleep(innolux
->enable_gpio
, 0);
78 /* T8: 80ms - 1000ms */
81 err
= regulator_disable(innolux
->supply
);
85 innolux
->prepared
= false;
90 static int innolux_panel_prepare(struct drm_panel
*panel
)
92 struct innolux_panel
*innolux
= to_innolux_panel(panel
);
93 int err
, regulator_err
;
95 if (innolux
->prepared
)
98 gpiod_set_value_cansleep(innolux
->enable_gpio
, 0);
100 err
= regulator_enable(innolux
->supply
);
104 /* T2: 15ms - 1000ms */
105 usleep_range(15000, 16000);
107 gpiod_set_value_cansleep(innolux
->enable_gpio
, 1);
109 /* T4: 15ms - 1000ms */
110 usleep_range(15000, 16000);
112 err
= mipi_dsi_dcs_exit_sleep_mode(innolux
->link
);
114 DRM_DEV_ERROR(panel
->dev
, "failed to exit sleep mode: %d\n",
119 /* T6: 120ms - 1000ms*/
122 err
= mipi_dsi_dcs_set_display_on(innolux
->link
);
124 DRM_DEV_ERROR(panel
->dev
, "failed to set display on: %d\n",
130 usleep_range(5000, 6000);
132 innolux
->prepared
= true;
137 regulator_err
= regulator_disable(innolux
->supply
);
139 DRM_DEV_ERROR(panel
->dev
, "failed to disable regulator: %d\n",
142 gpiod_set_value_cansleep(innolux
->enable_gpio
, 0);
146 static int innolux_panel_enable(struct drm_panel
*panel
)
148 struct innolux_panel
*innolux
= to_innolux_panel(panel
);
151 if (innolux
->enabled
)
154 innolux
->backlight
->props
.power
= FB_BLANK_UNBLANK
;
155 ret
= backlight_update_status(innolux
->backlight
);
157 DRM_DEV_ERROR(panel
->drm
->dev
,
158 "Failed to enable backlight %d\n", ret
);
162 innolux
->enabled
= true;
167 static const struct drm_display_mode default_mode
= {
170 .hsync_start
= 768 + 40,
171 .hsync_end
= 768 + 40 + 40,
172 .htotal
= 768 + 40 + 40 + 40,
174 .vsync_start
= 1024 + 20,
175 .vsync_end
= 1024 + 20 + 4,
176 .vtotal
= 1024 + 20 + 4 + 20,
180 static int innolux_panel_get_modes(struct drm_panel
*panel
)
182 struct drm_display_mode
*mode
;
184 mode
= drm_mode_duplicate(panel
->drm
, &default_mode
);
186 DRM_DEV_ERROR(panel
->drm
->dev
, "failed to add mode %ux%ux@%u\n",
187 default_mode
.hdisplay
, default_mode
.vdisplay
,
188 default_mode
.vrefresh
);
192 drm_mode_set_name(mode
);
194 drm_mode_probed_add(panel
->connector
, mode
);
196 panel
->connector
->display_info
.width_mm
= 120;
197 panel
->connector
->display_info
.height_mm
= 160;
198 panel
->connector
->display_info
.bpc
= 8;
203 static const struct drm_panel_funcs innolux_panel_funcs
= {
204 .disable
= innolux_panel_disable
,
205 .unprepare
= innolux_panel_unprepare
,
206 .prepare
= innolux_panel_prepare
,
207 .enable
= innolux_panel_enable
,
208 .get_modes
= innolux_panel_get_modes
,
211 static const struct of_device_id innolux_of_match
[] = {
212 { .compatible
= "innolux,p079zca", },
215 MODULE_DEVICE_TABLE(of
, innolux_of_match
);
217 static int innolux_panel_add(struct innolux_panel
*innolux
)
219 struct device
*dev
= &innolux
->link
->dev
;
220 struct device_node
*np
;
223 innolux
->supply
= devm_regulator_get(dev
, "power");
224 if (IS_ERR(innolux
->supply
))
225 return PTR_ERR(innolux
->supply
);
227 innolux
->enable_gpio
= devm_gpiod_get_optional(dev
, "enable",
229 if (IS_ERR(innolux
->enable_gpio
)) {
230 err
= PTR_ERR(innolux
->enable_gpio
);
231 dev_dbg(dev
, "failed to get enable gpio: %d\n", err
);
232 innolux
->enable_gpio
= NULL
;
235 np
= of_parse_phandle(dev
->of_node
, "backlight", 0);
237 innolux
->backlight
= of_find_backlight_by_node(np
);
240 if (!innolux
->backlight
)
241 return -EPROBE_DEFER
;
244 drm_panel_init(&innolux
->base
);
245 innolux
->base
.funcs
= &innolux_panel_funcs
;
246 innolux
->base
.dev
= &innolux
->link
->dev
;
248 err
= drm_panel_add(&innolux
->base
);
255 put_device(&innolux
->backlight
->dev
);
260 static void innolux_panel_del(struct innolux_panel
*innolux
)
262 if (innolux
->base
.dev
)
263 drm_panel_remove(&innolux
->base
);
265 put_device(&innolux
->backlight
->dev
);
268 static int innolux_panel_probe(struct mipi_dsi_device
*dsi
)
270 struct innolux_panel
*innolux
;
274 dsi
->format
= MIPI_DSI_FMT_RGB888
;
275 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
| MIPI_DSI_MODE_VIDEO_SYNC_PULSE
|
278 innolux
= devm_kzalloc(&dsi
->dev
, sizeof(*innolux
), GFP_KERNEL
);
282 mipi_dsi_set_drvdata(dsi
, innolux
);
286 err
= innolux_panel_add(innolux
);
290 err
= mipi_dsi_attach(dsi
);
294 static int innolux_panel_remove(struct mipi_dsi_device
*dsi
)
296 struct innolux_panel
*innolux
= mipi_dsi_get_drvdata(dsi
);
299 err
= innolux_panel_unprepare(&innolux
->base
);
301 DRM_DEV_ERROR(&dsi
->dev
, "failed to unprepare panel: %d\n",
304 err
= innolux_panel_disable(&innolux
->base
);
306 DRM_DEV_ERROR(&dsi
->dev
, "failed to disable panel: %d\n", err
);
308 err
= mipi_dsi_detach(dsi
);
310 DRM_DEV_ERROR(&dsi
->dev
, "failed to detach from DSI host: %d\n",
313 drm_panel_detach(&innolux
->base
);
314 innolux_panel_del(innolux
);
319 static void innolux_panel_shutdown(struct mipi_dsi_device
*dsi
)
321 struct innolux_panel
*innolux
= mipi_dsi_get_drvdata(dsi
);
323 innolux_panel_unprepare(&innolux
->base
);
324 innolux_panel_disable(&innolux
->base
);
327 static struct mipi_dsi_driver innolux_panel_driver
= {
329 .name
= "panel-innolux-p079zca",
330 .of_match_table
= innolux_of_match
,
332 .probe
= innolux_panel_probe
,
333 .remove
= innolux_panel_remove
,
334 .shutdown
= innolux_panel_shutdown
,
336 module_mipi_dsi_driver(innolux_panel_driver
);
338 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
339 MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
340 MODULE_LICENSE("GPL v2");