1 /*
2  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <linux/backlight.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/regulator/consumer.h>
15 
16 #include <drm/drmP.h>
17 #include <drm/drm_crtc.h>
18 #include <drm/drm_mipi_dsi.h>
19 #include <drm/drm_panel.h>
20 
21 #include <video/mipi_display.h>
22 
23 struct innolux_panel {
24 	struct drm_panel base;
25 	struct mipi_dsi_device *link;
26 
27 	struct backlight_device *backlight;
28 	struct regulator *supply;
29 	struct gpio_desc *enable_gpio;
30 
31 	bool prepared;
32 	bool enabled;
33 };
34 
35 static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
36 {
37 	return container_of(panel, struct innolux_panel, base);
38 }
39 
40 static int innolux_panel_disable(struct drm_panel *panel)
41 {
42 	struct innolux_panel *innolux = to_innolux_panel(panel);
43 	int err;
44 
45 	if (!innolux->enabled)
46 		return 0;
47 
48 	innolux->backlight->props.power = FB_BLANK_POWERDOWN;
49 	backlight_update_status(innolux->backlight);
50 
51 	err = mipi_dsi_dcs_set_display_off(innolux->link);
52 	if (err < 0)
53 		DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
54 			      err);
55 
56 	innolux->enabled = false;
57 
58 	return 0;
59 }
60 
61 static int innolux_panel_unprepare(struct drm_panel *panel)
62 {
63 	struct innolux_panel *innolux = to_innolux_panel(panel);
64 	int err;
65 
66 	if (!innolux->prepared)
67 		return 0;
68 
69 	err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
70 	if (err < 0) {
71 		DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
72 			      err);
73 		return err;
74 	}
75 
76 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
77 
78 	/* T8: 80ms - 1000ms */
79 	msleep(80);
80 
81 	err = regulator_disable(innolux->supply);
82 	if (err < 0)
83 		return err;
84 
85 	innolux->prepared = false;
86 
87 	return 0;
88 }
89 
90 static int innolux_panel_prepare(struct drm_panel *panel)
91 {
92 	struct innolux_panel *innolux = to_innolux_panel(panel);
93 	int err, regulator_err;
94 
95 	if (innolux->prepared)
96 		return 0;
97 
98 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
99 
100 	err = regulator_enable(innolux->supply);
101 	if (err < 0)
102 		return err;
103 
104 	/* T2: 15ms - 1000ms */
105 	usleep_range(15000, 16000);
106 
107 	gpiod_set_value_cansleep(innolux->enable_gpio, 1);
108 
109 	/* T4: 15ms - 1000ms */
110 	usleep_range(15000, 16000);
111 
112 	err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
113 	if (err < 0) {
114 		DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
115 			      err);
116 		goto poweroff;
117 	}
118 
119 	/* T6: 120ms - 1000ms*/
120 	msleep(120);
121 
122 	err = mipi_dsi_dcs_set_display_on(innolux->link);
123 	if (err < 0) {
124 		DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
125 			      err);
126 		goto poweroff;
127 	}
128 
129 	/* T7: 5ms */
130 	usleep_range(5000, 6000);
131 
132 	innolux->prepared = true;
133 
134 	return 0;
135 
136 poweroff:
137 	regulator_err = regulator_disable(innolux->supply);
138 	if (regulator_err)
139 		DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
140 			      regulator_err);
141 
142 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
143 	return err;
144 }
145 
146 static int innolux_panel_enable(struct drm_panel *panel)
147 {
148 	struct innolux_panel *innolux = to_innolux_panel(panel);
149 	int ret;
150 
151 	if (innolux->enabled)
152 		return 0;
153 
154 	innolux->backlight->props.power = FB_BLANK_UNBLANK;
155 	ret = backlight_update_status(innolux->backlight);
156 	if (ret) {
157 		DRM_DEV_ERROR(panel->drm->dev,
158 			      "Failed to enable backlight %d\n", ret);
159 		return ret;
160 	}
161 
162 	innolux->enabled = true;
163 
164 	return 0;
165 }
166 
167 static const struct drm_display_mode default_mode = {
168 	.clock = 56900,
169 	.hdisplay = 768,
170 	.hsync_start = 768 + 40,
171 	.hsync_end = 768 + 40 + 40,
172 	.htotal = 768 + 40 + 40 + 40,
173 	.vdisplay = 1024,
174 	.vsync_start = 1024 + 20,
175 	.vsync_end = 1024 + 20 + 4,
176 	.vtotal = 1024 + 20 + 4 + 20,
177 	.vrefresh = 60,
178 };
179 
180 static int innolux_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 		DRM_DEV_ERROR(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 = 120;
197 	panel->connector->display_info.height_mm = 160;
198 	panel->connector->display_info.bpc = 8;
199 
200 	return 1;
201 }
202 
203 static const struct drm_panel_funcs innolux_panel_funcs = {
204 	.disable = innolux_panel_disable,
205 	.unprepare = innolux_panel_unprepare,
206 	.prepare = innolux_panel_prepare,
207 	.enable = innolux_panel_enable,
208 	.get_modes = innolux_panel_get_modes,
209 };
210 
211 static const struct of_device_id innolux_of_match[] = {
212 	{ .compatible = "innolux,p079zca", },
213 	{ }
214 };
215 MODULE_DEVICE_TABLE(of, innolux_of_match);
216 
217 static int innolux_panel_add(struct innolux_panel *innolux)
218 {
219 	struct device *dev = &innolux->link->dev;
220 	struct device_node *np;
221 	int err;
222 
223 	innolux->supply = devm_regulator_get(dev, "power");
224 	if (IS_ERR(innolux->supply))
225 		return PTR_ERR(innolux->supply);
226 
227 	innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
228 						       GPIOD_OUT_HIGH);
229 	if (IS_ERR(innolux->enable_gpio)) {
230 		err = PTR_ERR(innolux->enable_gpio);
231 		dev_dbg(dev, "failed to get enable gpio: %d\n", err);
232 		innolux->enable_gpio = NULL;
233 	}
234 
235 	np = of_parse_phandle(dev->of_node, "backlight", 0);
236 	if (np) {
237 		innolux->backlight = of_find_backlight_by_node(np);
238 		of_node_put(np);
239 
240 		if (!innolux->backlight)
241 			return -EPROBE_DEFER;
242 	}
243 
244 	drm_panel_init(&innolux->base);
245 	innolux->base.funcs = &innolux_panel_funcs;
246 	innolux->base.dev = &innolux->link->dev;
247 
248 	err = drm_panel_add(&innolux->base);
249 	if (err < 0)
250 		goto put_backlight;
251 
252 	return 0;
253 
254 put_backlight:
255 	put_device(&innolux->backlight->dev);
256 
257 	return err;
258 }
259 
260 static void innolux_panel_del(struct innolux_panel *innolux)
261 {
262 	if (innolux->base.dev)
263 		drm_panel_remove(&innolux->base);
264 
265 	put_device(&innolux->backlight->dev);
266 }
267 
268 static int innolux_panel_probe(struct mipi_dsi_device *dsi)
269 {
270 	struct innolux_panel *innolux;
271 	int err;
272 
273 	dsi->lanes = 4;
274 	dsi->format = MIPI_DSI_FMT_RGB888;
275 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
276 			  MIPI_DSI_MODE_LPM;
277 
278 	innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
279 	if (!innolux)
280 		return -ENOMEM;
281 
282 	mipi_dsi_set_drvdata(dsi, innolux);
283 
284 	innolux->link = dsi;
285 
286 	err = innolux_panel_add(innolux);
287 	if (err < 0)
288 		return err;
289 
290 	err = mipi_dsi_attach(dsi);
291 	return err;
292 }
293 
294 static int innolux_panel_remove(struct mipi_dsi_device *dsi)
295 {
296 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
297 	int err;
298 
299 	err = innolux_panel_unprepare(&innolux->base);
300 	if (err < 0)
301 		DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
302 			      err);
303 
304 	err = innolux_panel_disable(&innolux->base);
305 	if (err < 0)
306 		DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
307 
308 	err = mipi_dsi_detach(dsi);
309 	if (err < 0)
310 		DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
311 			      err);
312 
313 	drm_panel_detach(&innolux->base);
314 	innolux_panel_del(innolux);
315 
316 	return 0;
317 }
318 
319 static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
320 {
321 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
322 
323 	innolux_panel_unprepare(&innolux->base);
324 	innolux_panel_disable(&innolux->base);
325 }
326 
327 static struct mipi_dsi_driver innolux_panel_driver = {
328 	.driver = {
329 		.name = "panel-innolux-p079zca",
330 		.of_match_table = innolux_of_match,
331 	},
332 	.probe = innolux_panel_probe,
333 	.remove = innolux_panel_remove,
334 	.shutdown = innolux_panel_shutdown,
335 };
336 module_mipi_dsi_driver(innolux_panel_driver);
337 
338 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
339 MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
340 MODULE_LICENSE("GPL v2");
341