2 * Copyright (C) 2015 Red Hat
3 * Copyright (C) 2015 Sony Mobile Communications Inc.
4 * Author: Werner Johansson <werner.johansson@sonymobile.com>
6 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published by
10 * the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <linux/backlight.h>
22 #include <linux/module.h>
24 #include <linux/regulator/consumer.h>
27 #include <drm/drm_crtc.h>
28 #include <drm/drm_mipi_dsi.h>
29 #include <drm/drm_panel.h>
31 #include <video/mipi_display.h>
34 * When power is turned off to this panel a minimum off time of 500ms has to be
35 * observed before powering back on as there's no external reset pin. Keep
36 * track of earliest wakeup time and delay subsequent prepare call accordingly
38 #define MIN_POFF_MS (500)
40 struct wuxga_nt_panel
{
41 struct drm_panel base
;
42 struct mipi_dsi_device
*dsi
;
44 struct backlight_device
*backlight
;
45 struct regulator
*supply
;
50 ktime_t earliest_wake
;
52 const struct drm_display_mode
*mode
;
55 static inline struct wuxga_nt_panel
*to_wuxga_nt_panel(struct drm_panel
*panel
)
57 return container_of(panel
, struct wuxga_nt_panel
, base
);
60 static int wuxga_nt_panel_on(struct wuxga_nt_panel
*wuxga_nt
)
62 return mipi_dsi_turn_on_peripheral(wuxga_nt
->dsi
);
65 static int wuxga_nt_panel_disable(struct drm_panel
*panel
)
67 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
68 int mipi_ret
, bl_ret
= 0;
70 if (!wuxga_nt
->enabled
)
73 mipi_ret
= mipi_dsi_shutdown_peripheral(wuxga_nt
->dsi
);
75 if (wuxga_nt
->backlight
) {
76 wuxga_nt
->backlight
->props
.power
= FB_BLANK_POWERDOWN
;
77 wuxga_nt
->backlight
->props
.state
|= BL_CORE_FBBLANK
;
78 bl_ret
= backlight_update_status(wuxga_nt
->backlight
);
81 wuxga_nt
->enabled
= false;
83 return mipi_ret
? mipi_ret
: bl_ret
;
86 static int wuxga_nt_panel_unprepare(struct drm_panel
*panel
)
88 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
90 if (!wuxga_nt
->prepared
)
93 regulator_disable(wuxga_nt
->supply
);
94 wuxga_nt
->earliest_wake
= ktime_add_ms(ktime_get_real(), MIN_POFF_MS
);
95 wuxga_nt
->prepared
= false;
100 static int wuxga_nt_panel_prepare(struct drm_panel
*panel
)
102 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
106 if (wuxga_nt
->prepared
)
110 * If the user re-enabled the panel before the required off-time then
111 * we need to wait the remaining period before re-enabling regulator
113 enablewait
= ktime_ms_delta(wuxga_nt
->earliest_wake
, ktime_get_real());
115 /* Sanity check, this should never happen */
116 if (enablewait
> MIN_POFF_MS
)
117 enablewait
= MIN_POFF_MS
;
122 ret
= regulator_enable(wuxga_nt
->supply
);
127 * A minimum delay of 250ms is required after power-up until commands
132 ret
= wuxga_nt_panel_on(wuxga_nt
);
134 dev_err(panel
->dev
, "failed to set panel on: %d\n", ret
);
138 wuxga_nt
->prepared
= true;
143 regulator_disable(wuxga_nt
->supply
);
148 static int wuxga_nt_panel_enable(struct drm_panel
*panel
)
150 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
152 if (wuxga_nt
->enabled
)
155 if (wuxga_nt
->backlight
) {
156 wuxga_nt
->backlight
->props
.power
= FB_BLANK_UNBLANK
;
157 wuxga_nt
->backlight
->props
.state
&= ~BL_CORE_FBBLANK
;
158 backlight_update_status(wuxga_nt
->backlight
);
161 wuxga_nt
->enabled
= true;
166 static const struct drm_display_mode default_mode
= {
169 .hsync_start
= 1920 + 152,
170 .hsync_end
= 1920 + 152 + 52,
171 .htotal
= 1920 + 152 + 52 + 20,
173 .vsync_start
= 1200 + 24,
174 .vsync_end
= 1200 + 24 + 6,
175 .vtotal
= 1200 + 24 + 6 + 48,
179 static int wuxga_nt_panel_get_modes(struct drm_panel
*panel
)
181 struct drm_display_mode
*mode
;
183 mode
= drm_mode_duplicate(panel
->drm
, &default_mode
);
185 dev_err(panel
->drm
->dev
, "failed to add mode %ux%ux@%u\n",
186 default_mode
.hdisplay
, default_mode
.vdisplay
,
187 default_mode
.vrefresh
);
191 drm_mode_set_name(mode
);
193 drm_mode_probed_add(panel
->connector
, mode
);
195 panel
->connector
->display_info
.width_mm
= 217;
196 panel
->connector
->display_info
.height_mm
= 136;
201 static const struct drm_panel_funcs wuxga_nt_panel_funcs
= {
202 .disable
= wuxga_nt_panel_disable
,
203 .unprepare
= wuxga_nt_panel_unprepare
,
204 .prepare
= wuxga_nt_panel_prepare
,
205 .enable
= wuxga_nt_panel_enable
,
206 .get_modes
= wuxga_nt_panel_get_modes
,
209 static const struct of_device_id wuxga_nt_of_match
[] = {
210 { .compatible
= "panasonic,vvx10f034n00", },
213 MODULE_DEVICE_TABLE(of
, wuxga_nt_of_match
);
215 static int wuxga_nt_panel_add(struct wuxga_nt_panel
*wuxga_nt
)
217 struct device
*dev
= &wuxga_nt
->dsi
->dev
;
218 struct device_node
*np
;
221 wuxga_nt
->mode
= &default_mode
;
223 wuxga_nt
->supply
= devm_regulator_get(dev
, "power");
224 if (IS_ERR(wuxga_nt
->supply
))
225 return PTR_ERR(wuxga_nt
->supply
);
227 np
= of_parse_phandle(dev
->of_node
, "backlight", 0);
229 wuxga_nt
->backlight
= of_find_backlight_by_node(np
);
232 if (!wuxga_nt
->backlight
)
233 return -EPROBE_DEFER
;
236 drm_panel_init(&wuxga_nt
->base
);
237 wuxga_nt
->base
.funcs
= &wuxga_nt_panel_funcs
;
238 wuxga_nt
->base
.dev
= &wuxga_nt
->dsi
->dev
;
240 ret
= drm_panel_add(&wuxga_nt
->base
);
247 if (wuxga_nt
->backlight
)
248 put_device(&wuxga_nt
->backlight
->dev
);
253 static void wuxga_nt_panel_del(struct wuxga_nt_panel
*wuxga_nt
)
255 if (wuxga_nt
->base
.dev
)
256 drm_panel_remove(&wuxga_nt
->base
);
258 if (wuxga_nt
->backlight
)
259 put_device(&wuxga_nt
->backlight
->dev
);
262 static int wuxga_nt_panel_probe(struct mipi_dsi_device
*dsi
)
264 struct wuxga_nt_panel
*wuxga_nt
;
268 dsi
->format
= MIPI_DSI_FMT_RGB888
;
269 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
|
270 MIPI_DSI_MODE_VIDEO_HSE
|
271 MIPI_DSI_CLOCK_NON_CONTINUOUS
|
274 wuxga_nt
= devm_kzalloc(&dsi
->dev
, sizeof(*wuxga_nt
), GFP_KERNEL
);
278 mipi_dsi_set_drvdata(dsi
, wuxga_nt
);
282 ret
= wuxga_nt_panel_add(wuxga_nt
);
286 return mipi_dsi_attach(dsi
);
289 static int wuxga_nt_panel_remove(struct mipi_dsi_device
*dsi
)
291 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
294 ret
= wuxga_nt_panel_disable(&wuxga_nt
->base
);
296 dev_err(&dsi
->dev
, "failed to disable panel: %d\n", ret
);
298 ret
= mipi_dsi_detach(dsi
);
300 dev_err(&dsi
->dev
, "failed to detach from DSI host: %d\n", ret
);
302 wuxga_nt_panel_del(wuxga_nt
);
307 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device
*dsi
)
309 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
311 wuxga_nt_panel_disable(&wuxga_nt
->base
);
314 static struct mipi_dsi_driver wuxga_nt_panel_driver
= {
316 .name
= "panel-panasonic-vvx10f034n00",
317 .of_match_table
= wuxga_nt_of_match
,
319 .probe
= wuxga_nt_panel_probe
,
320 .remove
= wuxga_nt_panel_remove
,
321 .shutdown
= wuxga_nt_panel_shutdown
,
323 module_mipi_dsi_driver(wuxga_nt_panel_driver
);
325 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
326 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
327 MODULE_LICENSE("GPL v2");