• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
to_wuxga_nt_panel(struct drm_panel * panel)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 
wuxga_nt_panel_on(struct wuxga_nt_panel * wuxga_nt)60 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
61 {
62 	struct mipi_dsi_device *dsi = wuxga_nt->dsi;
63 	int ret;
64 
65 	ret = mipi_dsi_turn_on_peripheral(dsi);
66 	if (ret < 0)
67 		return ret;
68 
69 	return 0;
70 }
71 
wuxga_nt_panel_disable(struct drm_panel * panel)72 static int wuxga_nt_panel_disable(struct drm_panel *panel)
73 {
74 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
75 
76 	if (!wuxga_nt->enabled)
77 		return 0;
78 
79 	mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
80 
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);
85 	}
86 
87 	wuxga_nt->enabled = false;
88 
89 	return 0;
90 }
91 
wuxga_nt_panel_unprepare(struct drm_panel * panel)92 static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
93 {
94 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
95 
96 	if (!wuxga_nt->prepared)
97 		return 0;
98 
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;
102 
103 	return 0;
104 }
105 
wuxga_nt_panel_prepare(struct drm_panel * panel)106 static int wuxga_nt_panel_prepare(struct drm_panel *panel)
107 {
108 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
109 	int ret;
110 	s64 enablewait;
111 
112 	if (wuxga_nt->prepared)
113 		return 0;
114 
115 	/*
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
118 	 */
119 	enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
120 
121 	/* Sanity check, this should never happen */
122 	if (enablewait > MIN_POFF_MS)
123 		enablewait = MIN_POFF_MS;
124 
125 	if (enablewait > 0)
126 		msleep(enablewait);
127 
128 	ret = regulator_enable(wuxga_nt->supply);
129 	if (ret < 0)
130 		return ret;
131 
132 	/*
133 	 * A minimum delay of 250ms is required after power-up until commands
134 	 * can be sent
135 	 */
136 	msleep(250);
137 
138 	ret = wuxga_nt_panel_on(wuxga_nt);
139 	if (ret < 0) {
140 		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
141 		goto poweroff;
142 	}
143 
144 	wuxga_nt->prepared = true;
145 
146 	return 0;
147 
148 poweroff:
149 	regulator_disable(wuxga_nt->supply);
150 
151 	return ret;
152 }
153 
wuxga_nt_panel_enable(struct drm_panel * panel)154 static int wuxga_nt_panel_enable(struct drm_panel *panel)
155 {
156 	struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
157 
158 	if (wuxga_nt->enabled)
159 		return 0;
160 
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);
165 	}
166 
167 	wuxga_nt->enabled = true;
168 
169 	return 0;
170 }
171 
172 static const struct drm_display_mode default_mode = {
173 	.clock = 164402,
174 	.hdisplay = 1920,
175 	.hsync_start = 1920 + 152,
176 	.hsync_end = 1920 + 152 + 52,
177 	.htotal = 1920 + 152 + 52 + 20,
178 	.vdisplay = 1200,
179 	.vsync_start = 1200 + 24,
180 	.vsync_end = 1200 + 24 + 6,
181 	.vtotal = 1200 + 24 + 6 + 48,
182 	.vrefresh = 60,
183 };
184 
wuxga_nt_panel_get_modes(struct drm_panel * panel)185 static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
186 {
187 	struct drm_display_mode *mode;
188 
189 	mode = drm_mode_duplicate(panel->drm, &default_mode);
190 	if (!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);
194 		return -ENOMEM;
195 	}
196 
197 	drm_mode_set_name(mode);
198 
199 	drm_mode_probed_add(panel->connector, mode);
200 
201 	panel->connector->display_info.width_mm = 217;
202 	panel->connector->display_info.height_mm = 136;
203 
204 	return 1;
205 }
206 
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,
213 };
214 
215 static const struct of_device_id wuxga_nt_of_match[] = {
216 	{ .compatible = "panasonic,vvx10f034n00", },
217 	{ }
218 };
219 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
220 
wuxga_nt_panel_add(struct wuxga_nt_panel * wuxga_nt)221 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
222 {
223 	struct device *dev = &wuxga_nt->dsi->dev;
224 	struct device_node *np;
225 	int ret;
226 
227 	wuxga_nt->mode = &default_mode;
228 
229 	wuxga_nt->supply = devm_regulator_get(dev, "power");
230 	if (IS_ERR(wuxga_nt->supply))
231 		return PTR_ERR(wuxga_nt->supply);
232 
233 	np = of_parse_phandle(dev->of_node, "backlight", 0);
234 	if (np) {
235 		wuxga_nt->backlight = of_find_backlight_by_node(np);
236 		of_node_put(np);
237 
238 		if (!wuxga_nt->backlight)
239 			return -EPROBE_DEFER;
240 	}
241 
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;
245 
246 	ret = drm_panel_add(&wuxga_nt->base);
247 	if (ret < 0)
248 		goto put_backlight;
249 
250 	return 0;
251 
252 put_backlight:
253 	if (wuxga_nt->backlight)
254 		put_device(&wuxga_nt->backlight->dev);
255 
256 	return ret;
257 }
258 
wuxga_nt_panel_del(struct wuxga_nt_panel * wuxga_nt)259 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
260 {
261 	if (wuxga_nt->base.dev)
262 		drm_panel_remove(&wuxga_nt->base);
263 
264 	if (wuxga_nt->backlight)
265 		put_device(&wuxga_nt->backlight->dev);
266 }
267 
wuxga_nt_panel_probe(struct mipi_dsi_device * dsi)268 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
269 {
270 	struct wuxga_nt_panel *wuxga_nt;
271 	int ret;
272 
273 	dsi->lanes = 4;
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 |
278 			MIPI_DSI_MODE_LPM;
279 
280 	wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
281 	if (!wuxga_nt)
282 		return -ENOMEM;
283 
284 	mipi_dsi_set_drvdata(dsi, wuxga_nt);
285 
286 	wuxga_nt->dsi = dsi;
287 
288 	ret = wuxga_nt_panel_add(wuxga_nt);
289 	if (ret < 0)
290 		return ret;
291 
292 	return mipi_dsi_attach(dsi);
293 }
294 
wuxga_nt_panel_remove(struct mipi_dsi_device * dsi)295 static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
296 {
297 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
298 	int ret;
299 
300 	ret = wuxga_nt_panel_disable(&wuxga_nt->base);
301 	if (ret < 0)
302 		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
303 
304 	ret = mipi_dsi_detach(dsi);
305 	if (ret < 0)
306 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
307 
308 	drm_panel_detach(&wuxga_nt->base);
309 	wuxga_nt_panel_del(wuxga_nt);
310 
311 	return 0;
312 }
313 
wuxga_nt_panel_shutdown(struct mipi_dsi_device * dsi)314 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
315 {
316 	struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
317 
318 	wuxga_nt_panel_disable(&wuxga_nt->base);
319 }
320 
321 static struct mipi_dsi_driver wuxga_nt_panel_driver = {
322 	.driver = {
323 		.name = "panel-panasonic-vvx10f034n00",
324 		.of_match_table = wuxga_nt_of_match,
325 	},
326 	.probe = wuxga_nt_panel_probe,
327 	.remove = wuxga_nt_panel_remove,
328 	.shutdown = wuxga_nt_panel_shutdown,
329 };
330 module_mipi_dsi_driver(wuxga_nt_panel_driver);
331 
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");
335