xref: /openbmc/linux/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c (revision abade675e02e1b73da0c20ffaf08fbe309038298)
1 /*
2  * Copyright (C) 2015 Red Hat
3  * Copyright (C) 2015 Sony Mobile Communications Inc.
4  * Author: Werner Johansson <werner.johansson@sonymobile.com>
5  *
6  * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
7  *
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.
11  *
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
15  * more details.
16  *
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/>.
19  */
20 
21 #include <linux/backlight.h>
22 #include <linux/delay.h>
23 #include <linux/module.h>
24 #include <linux/of.h>
25 #include <linux/regulator/consumer.h>
26 
27 #include <video/mipi_display.h>
28 
29 #include <drm/drm_crtc.h>
30 #include <drm/drm_device.h>
31 #include <drm/drm_mipi_dsi.h>
32 #include <drm/drm_panel.h>
33 
34 /*
35  * When power is turned off to this panel a minimum off time of 500ms has to be
36  * observed before powering back on as there's no external reset pin. Keep
37  * track of earliest wakeup time and delay subsequent prepare call accordingly
38  */
39 #define MIN_POFF_MS (500)
40 
41 struct wuxga_nt_panel {
42 	struct drm_panel base;
43 	struct mipi_dsi_device *dsi;
44 
45 	struct backlight_device *backlight;
46 	struct regulator *supply;
47 
48 	bool prepared;
49 	bool enabled;
50 
51 	ktime_t earliest_wake;
52 
53 	const struct drm_display_mode *mode;
54 };
55 
56 static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
57 {
58 	return container_of(panel, struct wuxga_nt_panel, base);
59 }
60 
61 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
62 {
63 	return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
64 }
65 
66 static int wuxga_nt_panel_disable(struct drm_panel *panel)
67 {
68 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
69 	int mipi_ret, bl_ret = 0;
70 
71 	if (!wuxga_nt->enabled)
72 		return 0;
73 
74 	mipi_ret = mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
75 
76 	if (wuxga_nt->backlight) {
77 		wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN;
78 		wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK;
79 		bl_ret = backlight_update_status(wuxga_nt->backlight);
80 	}
81 
82 	wuxga_nt->enabled = false;
83 
84 	return mipi_ret ? mipi_ret : bl_ret;
85 }
86 
87 static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
88 {
89 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
90 
91 	if (!wuxga_nt->prepared)
92 		return 0;
93 
94 	regulator_disable(wuxga_nt->supply);
95 	wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS);
96 	wuxga_nt->prepared = false;
97 
98 	return 0;
99 }
100 
101 static int wuxga_nt_panel_prepare(struct drm_panel *panel)
102 {
103 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
104 	int ret;
105 	s64 enablewait;
106 
107 	if (wuxga_nt->prepared)
108 		return 0;
109 
110 	/*
111 	 * If the user re-enabled the panel before the required off-time then
112 	 * we need to wait the remaining period before re-enabling regulator
113 	 */
114 	enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
115 
116 	/* Sanity check, this should never happen */
117 	if (enablewait > MIN_POFF_MS)
118 		enablewait = MIN_POFF_MS;
119 
120 	if (enablewait > 0)
121 		msleep(enablewait);
122 
123 	ret = regulator_enable(wuxga_nt->supply);
124 	if (ret < 0)
125 		return ret;
126 
127 	/*
128 	 * A minimum delay of 250ms is required after power-up until commands
129 	 * can be sent
130 	 */
131 	msleep(250);
132 
133 	ret = wuxga_nt_panel_on(wuxga_nt);
134 	if (ret < 0) {
135 		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
136 		goto poweroff;
137 	}
138 
139 	wuxga_nt->prepared = true;
140 
141 	return 0;
142 
143 poweroff:
144 	regulator_disable(wuxga_nt->supply);
145 
146 	return ret;
147 }
148 
149 static int wuxga_nt_panel_enable(struct drm_panel *panel)
150 {
151 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
152 
153 	if (wuxga_nt->enabled)
154 		return 0;
155 
156 	if (wuxga_nt->backlight) {
157 		wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK;
158 		wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK;
159 		backlight_update_status(wuxga_nt->backlight);
160 	}
161 
162 	wuxga_nt->enabled = true;
163 
164 	return 0;
165 }
166 
167 static const struct drm_display_mode default_mode = {
168 	.clock = 164402,
169 	.hdisplay = 1920,
170 	.hsync_start = 1920 + 152,
171 	.hsync_end = 1920 + 152 + 52,
172 	.htotal = 1920 + 152 + 52 + 20,
173 	.vdisplay = 1200,
174 	.vsync_start = 1200 + 24,
175 	.vsync_end = 1200 + 24 + 6,
176 	.vtotal = 1200 + 24 + 6 + 48,
177 	.vrefresh = 60,
178 };
179 
180 static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
181 {
182 	struct drm_display_mode *mode;
183 
184 	mode = drm_mode_duplicate(panel->drm, &default_mode);
185 	if (!mode) {
186 		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
187 				default_mode.hdisplay, default_mode.vdisplay,
188 				default_mode.vrefresh);
189 		return -ENOMEM;
190 	}
191 
192 	drm_mode_set_name(mode);
193 
194 	drm_mode_probed_add(panel->connector, mode);
195 
196 	panel->connector->display_info.width_mm = 217;
197 	panel->connector->display_info.height_mm = 136;
198 
199 	return 1;
200 }
201 
202 static const struct drm_panel_funcs wuxga_nt_panel_funcs = {
203 	.disable = wuxga_nt_panel_disable,
204 	.unprepare = wuxga_nt_panel_unprepare,
205 	.prepare = wuxga_nt_panel_prepare,
206 	.enable = wuxga_nt_panel_enable,
207 	.get_modes = wuxga_nt_panel_get_modes,
208 };
209 
210 static const struct of_device_id wuxga_nt_of_match[] = {
211 	{ .compatible = "panasonic,vvx10f034n00", },
212 	{ }
213 };
214 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
215 
216 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
217 {
218 	struct device *dev = &wuxga_nt->dsi->dev;
219 	struct device_node *np;
220 	int ret;
221 
222 	wuxga_nt->mode = &default_mode;
223 
224 	wuxga_nt->supply = devm_regulator_get(dev, "power");
225 	if (IS_ERR(wuxga_nt->supply))
226 		return PTR_ERR(wuxga_nt->supply);
227 
228 	np = of_parse_phandle(dev->of_node, "backlight", 0);
229 	if (np) {
230 		wuxga_nt->backlight = of_find_backlight_by_node(np);
231 		of_node_put(np);
232 
233 		if (!wuxga_nt->backlight)
234 			return -EPROBE_DEFER;
235 	}
236 
237 	drm_panel_init(&wuxga_nt->base);
238 	wuxga_nt->base.funcs = &wuxga_nt_panel_funcs;
239 	wuxga_nt->base.dev = &wuxga_nt->dsi->dev;
240 
241 	ret = drm_panel_add(&wuxga_nt->base);
242 	if (ret < 0)
243 		goto put_backlight;
244 
245 	return 0;
246 
247 put_backlight:
248 	if (wuxga_nt->backlight)
249 		put_device(&wuxga_nt->backlight->dev);
250 
251 	return ret;
252 }
253 
254 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
255 {
256 	if (wuxga_nt->base.dev)
257 		drm_panel_remove(&wuxga_nt->base);
258 
259 	if (wuxga_nt->backlight)
260 		put_device(&wuxga_nt->backlight->dev);
261 }
262 
263 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
264 {
265 	struct wuxga_nt_panel *wuxga_nt;
266 	int ret;
267 
268 	dsi->lanes = 4;
269 	dsi->format = MIPI_DSI_FMT_RGB888;
270 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
271 			MIPI_DSI_MODE_VIDEO_HSE |
272 			MIPI_DSI_CLOCK_NON_CONTINUOUS |
273 			MIPI_DSI_MODE_LPM;
274 
275 	wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
276 	if (!wuxga_nt)
277 		return -ENOMEM;
278 
279 	mipi_dsi_set_drvdata(dsi, wuxga_nt);
280 
281 	wuxga_nt->dsi = dsi;
282 
283 	ret = wuxga_nt_panel_add(wuxga_nt);
284 	if (ret < 0)
285 		return ret;
286 
287 	return mipi_dsi_attach(dsi);
288 }
289 
290 static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
291 {
292 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
293 	int ret;
294 
295 	ret = wuxga_nt_panel_disable(&wuxga_nt->base);
296 	if (ret < 0)
297 		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
298 
299 	ret = mipi_dsi_detach(dsi);
300 	if (ret < 0)
301 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
302 
303 	wuxga_nt_panel_del(wuxga_nt);
304 
305 	return 0;
306 }
307 
308 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
309 {
310 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
311 
312 	wuxga_nt_panel_disable(&wuxga_nt->base);
313 }
314 
315 static struct mipi_dsi_driver wuxga_nt_panel_driver = {
316 	.driver = {
317 		.name = "panel-panasonic-vvx10f034n00",
318 		.of_match_table = wuxga_nt_of_match,
319 	},
320 	.probe = wuxga_nt_panel_probe,
321 	.remove = wuxga_nt_panel_remove,
322 	.shutdown = wuxga_nt_panel_shutdown,
323 };
324 module_mipi_dsi_driver(wuxga_nt_panel_driver);
325 
326 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
327 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
328 MODULE_LICENSE("GPL v2");
329