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 struct mipi_dsi_device
*dsi
= wuxga_nt
->dsi
;
65 ret
= mipi_dsi_turn_on_peripheral(dsi
);
72 static int wuxga_nt_panel_disable(struct drm_panel
*panel
)
74 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
76 if (!wuxga_nt
->enabled
)
79 mipi_dsi_shutdown_peripheral(wuxga_nt
->dsi
);
81 if (wuxga_nt
->backlight
) {
82 wuxga_nt
->backlight
->props
.power
= FB_BLANK_POWERDOWN
;
83 wuxga_nt
->backlight
->props
.state
|= BL_CORE_FBBLANK
;
84 backlight_update_status(wuxga_nt
->backlight
);
87 wuxga_nt
->enabled
= false;
92 static int wuxga_nt_panel_unprepare(struct drm_panel
*panel
)
94 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
96 if (!wuxga_nt
->prepared
)
99 regulator_disable(wuxga_nt
->supply
);
100 wuxga_nt
->earliest_wake
= ktime_add_ms(ktime_get_real(), MIN_POFF_MS
);
101 wuxga_nt
->prepared
= false;
106 static int wuxga_nt_panel_prepare(struct drm_panel
*panel
)
108 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
112 if (wuxga_nt
->prepared
)
116 * If the user re-enabled the panel before the required off-time then
117 * we need to wait the remaining period before re-enabling regulator
119 enablewait
= ktime_ms_delta(wuxga_nt
->earliest_wake
, ktime_get_real());
121 /* Sanity check, this should never happen */
122 if (enablewait
> MIN_POFF_MS
)
123 enablewait
= MIN_POFF_MS
;
128 ret
= regulator_enable(wuxga_nt
->supply
);
133 * A minimum delay of 250ms is required after power-up until commands
138 ret
= wuxga_nt_panel_on(wuxga_nt
);
140 dev_err(panel
->dev
, "failed to set panel on: %d\n", ret
);
144 wuxga_nt
->prepared
= true;
149 regulator_disable(wuxga_nt
->supply
);
154 static int wuxga_nt_panel_enable(struct drm_panel
*panel
)
156 struct wuxga_nt_panel
*wuxga_nt
= to_wuxga_nt_panel(panel
);
158 if (wuxga_nt
->enabled
)
161 if (wuxga_nt
->backlight
) {
162 wuxga_nt
->backlight
->props
.power
= FB_BLANK_UNBLANK
;
163 wuxga_nt
->backlight
->props
.state
&= ~BL_CORE_FBBLANK
;
164 backlight_update_status(wuxga_nt
->backlight
);
167 wuxga_nt
->enabled
= true;
172 static const struct drm_display_mode default_mode
= {
175 .hsync_start
= 1920 + 152,
176 .hsync_end
= 1920 + 152 + 52,
177 .htotal
= 1920 + 152 + 52 + 20,
179 .vsync_start
= 1200 + 24,
180 .vsync_end
= 1200 + 24 + 6,
181 .vtotal
= 1200 + 24 + 6 + 48,
185 static int wuxga_nt_panel_get_modes(struct drm_panel
*panel
)
187 struct drm_display_mode
*mode
;
189 mode
= drm_mode_duplicate(panel
->drm
, &default_mode
);
191 dev_err(panel
->drm
->dev
, "failed to add mode %ux%ux@%u\n",
192 default_mode
.hdisplay
, default_mode
.vdisplay
,
193 default_mode
.vrefresh
);
197 drm_mode_set_name(mode
);
199 drm_mode_probed_add(panel
->connector
, mode
);
201 panel
->connector
->display_info
.width_mm
= 217;
202 panel
->connector
->display_info
.height_mm
= 136;
207 static const struct drm_panel_funcs wuxga_nt_panel_funcs
= {
208 .disable
= wuxga_nt_panel_disable
,
209 .unprepare
= wuxga_nt_panel_unprepare
,
210 .prepare
= wuxga_nt_panel_prepare
,
211 .enable
= wuxga_nt_panel_enable
,
212 .get_modes
= wuxga_nt_panel_get_modes
,
215 static const struct of_device_id wuxga_nt_of_match
[] = {
216 { .compatible
= "panasonic,vvx10f034n00", },
219 MODULE_DEVICE_TABLE(of
, wuxga_nt_of_match
);
221 static int wuxga_nt_panel_add(struct wuxga_nt_panel
*wuxga_nt
)
223 struct device
*dev
= &wuxga_nt
->dsi
->dev
;
224 struct device_node
*np
;
227 wuxga_nt
->mode
= &default_mode
;
229 wuxga_nt
->supply
= devm_regulator_get(dev
, "power");
230 if (IS_ERR(wuxga_nt
->supply
))
231 return PTR_ERR(wuxga_nt
->supply
);
233 np
= of_parse_phandle(dev
->of_node
, "backlight", 0);
235 wuxga_nt
->backlight
= of_find_backlight_by_node(np
);
238 if (!wuxga_nt
->backlight
)
239 return -EPROBE_DEFER
;
242 drm_panel_init(&wuxga_nt
->base
);
243 wuxga_nt
->base
.funcs
= &wuxga_nt_panel_funcs
;
244 wuxga_nt
->base
.dev
= &wuxga_nt
->dsi
->dev
;
246 ret
= drm_panel_add(&wuxga_nt
->base
);
253 if (wuxga_nt
->backlight
)
254 put_device(&wuxga_nt
->backlight
->dev
);
259 static void wuxga_nt_panel_del(struct wuxga_nt_panel
*wuxga_nt
)
261 if (wuxga_nt
->base
.dev
)
262 drm_panel_remove(&wuxga_nt
->base
);
264 if (wuxga_nt
->backlight
)
265 put_device(&wuxga_nt
->backlight
->dev
);
268 static int wuxga_nt_panel_probe(struct mipi_dsi_device
*dsi
)
270 struct wuxga_nt_panel
*wuxga_nt
;
274 dsi
->format
= MIPI_DSI_FMT_RGB888
;
275 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
|
276 MIPI_DSI_MODE_VIDEO_HSE
|
277 MIPI_DSI_CLOCK_NON_CONTINUOUS
|
280 wuxga_nt
= devm_kzalloc(&dsi
->dev
, sizeof(*wuxga_nt
), GFP_KERNEL
);
284 mipi_dsi_set_drvdata(dsi
, wuxga_nt
);
288 ret
= wuxga_nt_panel_add(wuxga_nt
);
292 return mipi_dsi_attach(dsi
);
295 static int wuxga_nt_panel_remove(struct mipi_dsi_device
*dsi
)
297 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
300 ret
= wuxga_nt_panel_disable(&wuxga_nt
->base
);
302 dev_err(&dsi
->dev
, "failed to disable panel: %d\n", ret
);
304 ret
= mipi_dsi_detach(dsi
);
306 dev_err(&dsi
->dev
, "failed to detach from DSI host: %d\n", ret
);
308 drm_panel_detach(&wuxga_nt
->base
);
309 wuxga_nt_panel_del(wuxga_nt
);
314 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device
*dsi
)
316 struct wuxga_nt_panel
*wuxga_nt
= mipi_dsi_get_drvdata(dsi
);
318 wuxga_nt_panel_disable(&wuxga_nt
->base
);
321 static struct mipi_dsi_driver wuxga_nt_panel_driver
= {
323 .name
= "panel-panasonic-vvx10f034n00",
324 .of_match_table
= wuxga_nt_of_match
,
326 .probe
= wuxga_nt_panel_probe
,
327 .remove
= wuxga_nt_panel_remove
,
328 .shutdown
= wuxga_nt_panel_shutdown
,
330 module_mipi_dsi_driver(wuxga_nt_panel_driver
);
332 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
333 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
334 MODULE_LICENSE("GPL v2");