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 	backlight_disable(innolux->backlight);
49 
50 	err = mipi_dsi_dcs_set_display_off(innolux->link);
51 	if (err < 0)
52 		DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
53 			      err);
54 
55 	innolux->enabled = false;
56 
57 	return 0;
58 }
59 
60 static int innolux_panel_unprepare(struct drm_panel *panel)
61 {
62 	struct innolux_panel *innolux = to_innolux_panel(panel);
63 	int err;
64 
65 	if (!innolux->prepared)
66 		return 0;
67 
68 	err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
69 	if (err < 0) {
70 		DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
71 			      err);
72 		return err;
73 	}
74 
75 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
76 
77 	/* T8: 80ms - 1000ms */
78 	msleep(80);
79 
80 	err = regulator_disable(innolux->supply);
81 	if (err < 0)
82 		return err;
83 
84 	innolux->prepared = false;
85 
86 	return 0;
87 }
88 
89 static int innolux_panel_prepare(struct drm_panel *panel)
90 {
91 	struct innolux_panel *innolux = to_innolux_panel(panel);
92 	int err, regulator_err;
93 
94 	if (innolux->prepared)
95 		return 0;
96 
97 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
98 
99 	err = regulator_enable(innolux->supply);
100 	if (err < 0)
101 		return err;
102 
103 	/* T2: 15ms - 1000ms */
104 	usleep_range(15000, 16000);
105 
106 	gpiod_set_value_cansleep(innolux->enable_gpio, 1);
107 
108 	/* T4: 15ms - 1000ms */
109 	usleep_range(15000, 16000);
110 
111 	err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
112 	if (err < 0) {
113 		DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
114 			      err);
115 		goto poweroff;
116 	}
117 
118 	/* T6: 120ms - 1000ms*/
119 	msleep(120);
120 
121 	err = mipi_dsi_dcs_set_display_on(innolux->link);
122 	if (err < 0) {
123 		DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
124 			      err);
125 		goto poweroff;
126 	}
127 
128 	/* T7: 5ms */
129 	usleep_range(5000, 6000);
130 
131 	innolux->prepared = true;
132 
133 	return 0;
134 
135 poweroff:
136 	regulator_err = regulator_disable(innolux->supply);
137 	if (regulator_err)
138 		DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
139 			      regulator_err);
140 
141 	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
142 	return err;
143 }
144 
145 static int innolux_panel_enable(struct drm_panel *panel)
146 {
147 	struct innolux_panel *innolux = to_innolux_panel(panel);
148 	int ret;
149 
150 	if (innolux->enabled)
151 		return 0;
152 
153 	ret = backlight_enable(innolux->backlight);
154 	if (ret) {
155 		DRM_DEV_ERROR(panel->drm->dev,
156 			      "Failed to enable backlight %d\n", ret);
157 		return ret;
158 	}
159 
160 	innolux->enabled = true;
161 
162 	return 0;
163 }
164 
165 static const struct drm_display_mode default_mode = {
166 	.clock = 56900,
167 	.hdisplay = 768,
168 	.hsync_start = 768 + 40,
169 	.hsync_end = 768 + 40 + 40,
170 	.htotal = 768 + 40 + 40 + 40,
171 	.vdisplay = 1024,
172 	.vsync_start = 1024 + 20,
173 	.vsync_end = 1024 + 20 + 4,
174 	.vtotal = 1024 + 20 + 4 + 20,
175 	.vrefresh = 60,
176 };
177 
178 static int innolux_panel_get_modes(struct drm_panel *panel)
179 {
180 	struct drm_display_mode *mode;
181 
182 	mode = drm_mode_duplicate(panel->drm, &default_mode);
183 	if (!mode) {
184 		DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
185 			      default_mode.hdisplay, default_mode.vdisplay,
186 			      default_mode.vrefresh);
187 		return -ENOMEM;
188 	}
189 
190 	drm_mode_set_name(mode);
191 
192 	drm_mode_probed_add(panel->connector, mode);
193 
194 	panel->connector->display_info.width_mm = 120;
195 	panel->connector->display_info.height_mm = 160;
196 	panel->connector->display_info.bpc = 8;
197 
198 	return 1;
199 }
200 
201 static const struct drm_panel_funcs innolux_panel_funcs = {
202 	.disable = innolux_panel_disable,
203 	.unprepare = innolux_panel_unprepare,
204 	.prepare = innolux_panel_prepare,
205 	.enable = innolux_panel_enable,
206 	.get_modes = innolux_panel_get_modes,
207 };
208 
209 static const struct of_device_id innolux_of_match[] = {
210 	{ .compatible = "innolux,p079zca", },
211 	{ }
212 };
213 MODULE_DEVICE_TABLE(of, innolux_of_match);
214 
215 static int innolux_panel_add(struct innolux_panel *innolux)
216 {
217 	struct device *dev = &innolux->link->dev;
218 	int err;
219 
220 	innolux->supply = devm_regulator_get(dev, "power");
221 	if (IS_ERR(innolux->supply))
222 		return PTR_ERR(innolux->supply);
223 
224 	innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
225 						       GPIOD_OUT_HIGH);
226 	if (IS_ERR(innolux->enable_gpio)) {
227 		err = PTR_ERR(innolux->enable_gpio);
228 		dev_dbg(dev, "failed to get enable gpio: %d\n", err);
229 		innolux->enable_gpio = NULL;
230 	}
231 
232 	innolux->backlight = devm_of_find_backlight(dev);
233 
234 	if (IS_ERR(innolux->backlight))
235 		return PTR_ERR(innolux->backlight);
236 
237 	drm_panel_init(&innolux->base);
238 	innolux->base.funcs = &innolux_panel_funcs;
239 	innolux->base.dev = &innolux->link->dev;
240 
241 	return drm_panel_add(&innolux->base);
242 }
243 
244 static void innolux_panel_del(struct innolux_panel *innolux)
245 {
246 	if (innolux->base.dev)
247 		drm_panel_remove(&innolux->base);
248 }
249 
250 static int innolux_panel_probe(struct mipi_dsi_device *dsi)
251 {
252 	struct innolux_panel *innolux;
253 	int err;
254 
255 	dsi->lanes = 4;
256 	dsi->format = MIPI_DSI_FMT_RGB888;
257 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
258 			  MIPI_DSI_MODE_LPM;
259 
260 	innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
261 	if (!innolux)
262 		return -ENOMEM;
263 
264 	mipi_dsi_set_drvdata(dsi, innolux);
265 
266 	innolux->link = dsi;
267 
268 	err = innolux_panel_add(innolux);
269 	if (err < 0)
270 		return err;
271 
272 	err = mipi_dsi_attach(dsi);
273 	return err;
274 }
275 
276 static int innolux_panel_remove(struct mipi_dsi_device *dsi)
277 {
278 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
279 	int err;
280 
281 	err = innolux_panel_unprepare(&innolux->base);
282 	if (err < 0)
283 		DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
284 			      err);
285 
286 	err = innolux_panel_disable(&innolux->base);
287 	if (err < 0)
288 		DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
289 
290 	err = mipi_dsi_detach(dsi);
291 	if (err < 0)
292 		DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
293 			      err);
294 
295 	innolux_panel_del(innolux);
296 
297 	return 0;
298 }
299 
300 static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
301 {
302 	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
303 
304 	innolux_panel_unprepare(&innolux->base);
305 	innolux_panel_disable(&innolux->base);
306 }
307 
308 static struct mipi_dsi_driver innolux_panel_driver = {
309 	.driver = {
310 		.name = "panel-innolux-p079zca",
311 		.of_match_table = innolux_of_match,
312 	},
313 	.probe = innolux_panel_probe,
314 	.remove = innolux_panel_remove,
315 	.shutdown = innolux_panel_shutdown,
316 };
317 module_mipi_dsi_driver(innolux_panel_driver);
318 
319 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
320 MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
321 MODULE_LICENSE("GPL v2");
322