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/module.h>
23 #include <linux/of.h>
24 #include <linux/regulator/consumer.h>
25 
26 #include <drm/drmP.h>
27 #include <drm/drm_crtc.h>
28 #include <drm/drm_mipi_dsi.h>
29 #include <drm/drm_panel.h>
30 
31 #include <video/mipi_display.h>
32 
33 /*
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
37  */
38 #define MIN_POFF_MS (500)
39 
40 struct wuxga_nt_panel {
41 	struct drm_panel base;
42 	struct mipi_dsi_device *dsi;
43 
44 	struct backlight_device *backlight;
45 	struct regulator *supply;
46 
47 	bool prepared;
48 	bool enabled;
49 
50 	ktime_t earliest_wake;
51 
52 	const struct drm_display_mode *mode;
53 };
54 
55 static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
56 {
57 	return container_of(panel, struct wuxga_nt_panel, base);
58 }
59 
60 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
61 {
62 	return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
63 }
64 
65 static int wuxga_nt_panel_disable(struct drm_panel *panel)
66 {
67 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
68 	int mipi_ret, bl_ret = 0;
69 
70 	if (!wuxga_nt->enabled)
71 		return 0;
72 
73 	mipi_ret = mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
74 
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);
79 	}
80 
81 	wuxga_nt->enabled = false;
82 
83 	return mipi_ret ? mipi_ret : bl_ret;
84 }
85 
86 static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
87 {
88 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
89 
90 	if (!wuxga_nt->prepared)
91 		return 0;
92 
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;
96 
97 	return 0;
98 }
99 
100 static int wuxga_nt_panel_prepare(struct drm_panel *panel)
101 {
102 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
103 	int ret;
104 	s64 enablewait;
105 
106 	if (wuxga_nt->prepared)
107 		return 0;
108 
109 	/*
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
112 	 */
113 	enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
114 
115 	/* Sanity check, this should never happen */
116 	if (enablewait > MIN_POFF_MS)
117 		enablewait = MIN_POFF_MS;
118 
119 	if (enablewait > 0)
120 		msleep(enablewait);
121 
122 	ret = regulator_enable(wuxga_nt->supply);
123 	if (ret < 0)
124 		return ret;
125 
126 	/*
127 	 * A minimum delay of 250ms is required after power-up until commands
128 	 * can be sent
129 	 */
130 	msleep(250);
131 
132 	ret = wuxga_nt_panel_on(wuxga_nt);
133 	if (ret < 0) {
134 		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
135 		goto poweroff;
136 	}
137 
138 	wuxga_nt->prepared = true;
139 
140 	return 0;
141 
142 poweroff:
143 	regulator_disable(wuxga_nt->supply);
144 
145 	return ret;
146 }
147 
148 static int wuxga_nt_panel_enable(struct drm_panel *panel)
149 {
150 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
151 
152 	if (wuxga_nt->enabled)
153 		return 0;
154 
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);
159 	}
160 
161 	wuxga_nt->enabled = true;
162 
163 	return 0;
164 }
165 
166 static const struct drm_display_mode default_mode = {
167 	.clock = 164402,
168 	.hdisplay = 1920,
169 	.hsync_start = 1920 + 152,
170 	.hsync_end = 1920 + 152 + 52,
171 	.htotal = 1920 + 152 + 52 + 20,
172 	.vdisplay = 1200,
173 	.vsync_start = 1200 + 24,
174 	.vsync_end = 1200 + 24 + 6,
175 	.vtotal = 1200 + 24 + 6 + 48,
176 	.vrefresh = 60,
177 };
178 
179 static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
180 {
181 	struct drm_display_mode *mode;
182 
183 	mode = drm_mode_duplicate(panel->drm, &default_mode);
184 	if (!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);
188 		return -ENOMEM;
189 	}
190 
191 	drm_mode_set_name(mode);
192 
193 	drm_mode_probed_add(panel->connector, mode);
194 
195 	panel->connector->display_info.width_mm = 217;
196 	panel->connector->display_info.height_mm = 136;
197 
198 	return 1;
199 }
200 
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,
207 };
208 
209 static const struct of_device_id wuxga_nt_of_match[] = {
210 	{ .compatible = "panasonic,vvx10f034n00", },
211 	{ }
212 };
213 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
214 
215 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
216 {
217 	struct device *dev = &wuxga_nt->dsi->dev;
218 	struct device_node *np;
219 	int ret;
220 
221 	wuxga_nt->mode = &default_mode;
222 
223 	wuxga_nt->supply = devm_regulator_get(dev, "power");
224 	if (IS_ERR(wuxga_nt->supply))
225 		return PTR_ERR(wuxga_nt->supply);
226 
227 	np = of_parse_phandle(dev->of_node, "backlight", 0);
228 	if (np) {
229 		wuxga_nt->backlight = of_find_backlight_by_node(np);
230 		of_node_put(np);
231 
232 		if (!wuxga_nt->backlight)
233 			return -EPROBE_DEFER;
234 	}
235 
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;
239 
240 	ret = drm_panel_add(&wuxga_nt->base);
241 	if (ret < 0)
242 		goto put_backlight;
243 
244 	return 0;
245 
246 put_backlight:
247 	if (wuxga_nt->backlight)
248 		put_device(&wuxga_nt->backlight->dev);
249 
250 	return ret;
251 }
252 
253 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
254 {
255 	if (wuxga_nt->base.dev)
256 		drm_panel_remove(&wuxga_nt->base);
257 
258 	if (wuxga_nt->backlight)
259 		put_device(&wuxga_nt->backlight->dev);
260 }
261 
262 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
263 {
264 	struct wuxga_nt_panel *wuxga_nt;
265 	int ret;
266 
267 	dsi->lanes = 4;
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 |
272 			MIPI_DSI_MODE_LPM;
273 
274 	wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
275 	if (!wuxga_nt)
276 		return -ENOMEM;
277 
278 	mipi_dsi_set_drvdata(dsi, wuxga_nt);
279 
280 	wuxga_nt->dsi = dsi;
281 
282 	ret = wuxga_nt_panel_add(wuxga_nt);
283 	if (ret < 0)
284 		return ret;
285 
286 	return mipi_dsi_attach(dsi);
287 }
288 
289 static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
290 {
291 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
292 	int ret;
293 
294 	ret = wuxga_nt_panel_disable(&wuxga_nt->base);
295 	if (ret < 0)
296 		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
297 
298 	ret = mipi_dsi_detach(dsi);
299 	if (ret < 0)
300 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
301 
302 	wuxga_nt_panel_del(wuxga_nt);
303 
304 	return 0;
305 }
306 
307 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
308 {
309 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
310 
311 	wuxga_nt_panel_disable(&wuxga_nt->base);
312 }
313 
314 static struct mipi_dsi_driver wuxga_nt_panel_driver = {
315 	.driver = {
316 		.name = "panel-panasonic-vvx10f034n00",
317 		.of_match_table = wuxga_nt_of_match,
318 	},
319 	.probe = wuxga_nt_panel_probe,
320 	.remove = wuxga_nt_panel_remove,
321 	.shutdown = wuxga_nt_panel_shutdown,
322 };
323 module_mipi_dsi_driver(wuxga_nt_panel_driver);
324 
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");
328