1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Red Hat
4 * Copyright (C) 2015 Sony Mobile Communications Inc.
5 * Author: Werner Johansson <werner.johansson@sonymobile.com>
7 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
10 #include <linux/backlight.h>
11 #include <linux/module.h>
13 #include <linux/regulator/consumer.h>
16 #include <drm/drm_crtc.h>
17 #include <drm/drm_mipi_dsi.h>
18 #include <drm/drm_panel.h>
20 #include <video/mipi_display.h>
23 * When power is turned off to this panel a minimum off time of 500ms has to be
24 * observed before powering back on as there's no external reset pin. Keep
25 * track of earliest wakeup time and delay subsequent prepare call accordingly
27 #define MIN_POFF_MS (500)
29 struct wuxga_nt_panel
{
30 struct drm_panel base
;
31 struct mipi_dsi_device
*dsi
;
33 struct backlight_device
*backlight
;
34 struct regulator
*supply
;
39 ktime_t earliest_wake
;
41 const struct drm_display_mode
*mode
;
44 static inline struct wuxga_nt_panel
*to_wuxga_nt_panel(struct drm_panel
*panel
)
46 return container_of(panel
, struct wuxga_nt_panel
, base
);
49 static int wuxga_nt_panel_on(struct wuxga_nt_panel
*wuxga_nt
)
51 return mipi_dsi_turn_on_peripheral(wuxga_nt
->dsi
);
54 static int wuxga_nt_panel_disable(struct drm_panel
*panel
)
56 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
57 int mipi_ret
, bl_ret
= 0;
59 if (!wuxga_nt
->enabled
)
62 mipi_ret
= mipi_dsi_shutdown_peripheral(wuxga_nt
->dsi
);
64 if (wuxga_nt
->backlight
) {
65 wuxga_nt
->backlight
->props
.power
= FB_BLANK_POWERDOWN
;
66 wuxga_nt
->backlight
->props
.state
|= BL_CORE_FBBLANK
;
67 bl_ret
= backlight_update_status(wuxga_nt
->backlight
);
70 wuxga_nt
->enabled
= false;
72 return mipi_ret
? mipi_ret
: bl_ret
;
75 static int wuxga_nt_panel_unprepare(struct drm_panel
*panel
)
77 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
79 if (!wuxga_nt
->prepared
)
82 regulator_disable(wuxga_nt
->supply
);
83 wuxga_nt
->earliest_wake
= ktime_add_ms(ktime_get_real(), MIN_POFF_MS
);
84 wuxga_nt
->prepared
= false;
89 static int wuxga_nt_panel_prepare(struct drm_panel
*panel
)
91 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
95 if (wuxga_nt
->prepared
)
99 * If the user re-enabled the panel before the required off-time then
100 * we need to wait the remaining period before re-enabling regulator
102 enablewait
= ktime_ms_delta(wuxga_nt
->earliest_wake
, ktime_get_real());
104 /* Sanity check, this should never happen */
105 if (enablewait
> MIN_POFF_MS
)
106 enablewait
= MIN_POFF_MS
;
111 ret
= regulator_enable(wuxga_nt
->supply
);
116 * A minimum delay of 250ms is required after power-up until commands
121 ret
= wuxga_nt_panel_on(wuxga_nt
);
123 dev_err(panel
->dev
, "failed to set panel on: %d\n", ret
);
127 wuxga_nt
->prepared
= true;
132 regulator_disable(wuxga_nt
->supply
);
137 static int wuxga_nt_panel_enable(struct drm_panel
*panel
)
139 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
141 if (wuxga_nt
->enabled
)
144 if (wuxga_nt
->backlight
) {
145 wuxga_nt
->backlight
->props
.power
= FB_BLANK_UNBLANK
;
146 wuxga_nt
->backlight
->props
.state
&= ~BL_CORE_FBBLANK
;
147 backlight_update_status(wuxga_nt
->backlight
);
150 wuxga_nt
->enabled
= true;
155 static const struct drm_display_mode default_mode
= {
158 .hsync_start
= 1920 + 152,
159 .hsync_end
= 1920 + 152 + 52,
160 .htotal
= 1920 + 152 + 52 + 20,
162 .vsync_start
= 1200 + 24,
163 .vsync_end
= 1200 + 24 + 6,
164 .vtotal
= 1200 + 24 + 6 + 48,
168 static int wuxga_nt_panel_get_modes(struct drm_panel
*panel
)
170 struct drm_display_mode
*mode
;
172 mode
= drm_mode_duplicate(panel
->drm
, &default_mode
);
174 dev_err(panel
->drm
->dev
, "failed to add mode %ux%ux@%u\n",
175 default_mode
.hdisplay
, default_mode
.vdisplay
,
176 default_mode
.vrefresh
);
180 drm_mode_set_name(mode
);
182 drm_mode_probed_add(panel
->connector
, mode
);
184 panel
->connector
->display_info
.width_mm
= 217;
185 panel
->connector
->display_info
.height_mm
= 136;
190 static const struct drm_panel_funcs wuxga_nt_panel_funcs
= {
191 .disable
= wuxga_nt_panel_disable
,
192 .unprepare
= wuxga_nt_panel_unprepare
,
193 .prepare
= wuxga_nt_panel_prepare
,
194 .enable
= wuxga_nt_panel_enable
,
195 .get_modes
= wuxga_nt_panel_get_modes
,
198 static const struct of_device_id wuxga_nt_of_match
[] = {
199 { .compatible
= "panasonic,vvx10f034n00", },
202 MODULE_DEVICE_TABLE(of
, wuxga_nt_of_match
);
204 static int wuxga_nt_panel_add(struct wuxga_nt_panel
*wuxga_nt
)
206 struct device
*dev
= &wuxga_nt
->dsi
->dev
;
207 struct device_node
*np
;
210 wuxga_nt
->mode
= &default_mode
;
212 wuxga_nt
->supply
= devm_regulator_get(dev
, "power");
213 if (IS_ERR(wuxga_nt
->supply
))
214 return PTR_ERR(wuxga_nt
->supply
);
216 np
= of_parse_phandle(dev
->of_node
, "backlight", 0);
218 wuxga_nt
->backlight
= of_find_backlight_by_node(np
);
221 if (!wuxga_nt
->backlight
)
222 return -EPROBE_DEFER
;
225 drm_panel_init(&wuxga_nt
->base
);
226 wuxga_nt
->base
.funcs
= &wuxga_nt_panel_funcs
;
227 wuxga_nt
->base
.dev
= &wuxga_nt
->dsi
->dev
;
229 ret
= drm_panel_add(&wuxga_nt
->base
);
236 if (wuxga_nt
->backlight
)
237 put_device(&wuxga_nt
->backlight
->dev
);
242 static void wuxga_nt_panel_del(struct wuxga_nt_panel
*wuxga_nt
)
244 if (wuxga_nt
->base
.dev
)
245 drm_panel_remove(&wuxga_nt
->base
);
247 if (wuxga_nt
->backlight
)
248 put_device(&wuxga_nt
->backlight
->dev
);
251 static int wuxga_nt_panel_probe(struct mipi_dsi_device
*dsi
)
253 struct wuxga_nt_panel
*wuxga_nt
;
257 dsi
->format
= MIPI_DSI_FMT_RGB888
;
258 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
|
259 MIPI_DSI_MODE_VIDEO_HSE
|
260 MIPI_DSI_CLOCK_NON_CONTINUOUS
|
263 wuxga_nt
= devm_kzalloc(&dsi
->dev
, sizeof(*wuxga_nt
), GFP_KERNEL
);
267 mipi_dsi_set_drvdata(dsi
, wuxga_nt
);
271 ret
= wuxga_nt_panel_add(wuxga_nt
);
275 return mipi_dsi_attach(dsi
);
278 static int wuxga_nt_panel_remove(struct mipi_dsi_device
*dsi
)
280 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
283 ret
= wuxga_nt_panel_disable(&wuxga_nt
->base
);
285 dev_err(&dsi
->dev
, "failed to disable panel: %d\n", ret
);
287 ret
= mipi_dsi_detach(dsi
);
289 dev_err(&dsi
->dev
, "failed to detach from DSI host: %d\n", ret
);
291 wuxga_nt_panel_del(wuxga_nt
);
296 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device
*dsi
)
298 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
300 wuxga_nt_panel_disable(&wuxga_nt
->base
);
303 static struct mipi_dsi_driver wuxga_nt_panel_driver
= {
305 .name
= "panel-panasonic-vvx10f034n00",
306 .of_match_table
= wuxga_nt_of_match
,
308 .probe
= wuxga_nt_panel_probe
,
309 .remove
= wuxga_nt_panel_remove
,
310 .shutdown
= wuxga_nt_panel_shutdown
,
312 module_mipi_dsi_driver(wuxga_nt_panel_driver
);
314 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
315 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
316 MODULE_LICENSE("GPL v2");