1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ee017238SWerner Johansson /*
3ee017238SWerner Johansson  * Copyright (C) 2015 Red Hat
4ee017238SWerner Johansson  * Copyright (C) 2015 Sony Mobile Communications Inc.
5ee017238SWerner Johansson  * Author: Werner Johansson <werner.johansson@sonymobile.com>
6ee017238SWerner Johansson  *
7ee017238SWerner Johansson  * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
8ee017238SWerner Johansson  */
9ee017238SWerner Johansson 
10cb23eae3SSam Ravnborg #include <linux/delay.h>
11ee017238SWerner Johansson #include <linux/gpio/consumer.h>
12ee017238SWerner Johansson #include <linux/module.h>
13ee017238SWerner Johansson #include <linux/of.h>
14ee017238SWerner Johansson #include <linux/regulator/consumer.h>
15ee017238SWerner Johansson 
16cb23eae3SSam Ravnborg #include <video/mipi_display.h>
17cb23eae3SSam Ravnborg 
18ee017238SWerner Johansson #include <drm/drm_crtc.h>
19cb23eae3SSam Ravnborg #include <drm/drm_device.h>
20ee017238SWerner Johansson #include <drm/drm_mipi_dsi.h>
21ee017238SWerner Johansson #include <drm/drm_panel.h>
22ee017238SWerner Johansson 
23ee017238SWerner Johansson struct sharp_nt_panel {
24ee017238SWerner Johansson 	struct drm_panel base;
25ee017238SWerner Johansson 	struct mipi_dsi_device *dsi;
26ee017238SWerner Johansson 
27ee017238SWerner Johansson 	struct regulator *supply;
28ee017238SWerner Johansson 	struct gpio_desc *reset_gpio;
29ee017238SWerner Johansson 
30ee017238SWerner Johansson 	bool prepared;
31ee017238SWerner Johansson };
32ee017238SWerner Johansson 
to_sharp_nt_panel(struct drm_panel * panel)33ee017238SWerner Johansson static inline struct sharp_nt_panel *to_sharp_nt_panel(struct drm_panel *panel)
34ee017238SWerner Johansson {
35ee017238SWerner Johansson 	return container_of(panel, struct sharp_nt_panel, base);
36ee017238SWerner Johansson }
37ee017238SWerner Johansson 
sharp_nt_panel_init(struct sharp_nt_panel * sharp_nt)38ee017238SWerner Johansson static int sharp_nt_panel_init(struct sharp_nt_panel *sharp_nt)
39ee017238SWerner Johansson {
40ee017238SWerner Johansson 	struct mipi_dsi_device *dsi = sharp_nt->dsi;
41ee017238SWerner Johansson 	int ret;
42ee017238SWerner Johansson 
43ee017238SWerner Johansson 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
44ee017238SWerner Johansson 
45ee017238SWerner Johansson 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
46ee017238SWerner Johansson 	if (ret < 0)
47ee017238SWerner Johansson 		return ret;
48ee017238SWerner Johansson 
49ee017238SWerner Johansson 	msleep(120);
50ee017238SWerner Johansson 
51ee017238SWerner Johansson 	/* Novatek two-lane operation */
52ee017238SWerner Johansson 	ret = mipi_dsi_dcs_write(dsi, 0xae, (u8[]){ 0x03 }, 1);
53ee017238SWerner Johansson 	if (ret < 0)
54ee017238SWerner Johansson 		return ret;
55ee017238SWerner Johansson 
56ee017238SWerner Johansson 	/* Set both MCU and RGB I/F to 24bpp */
57ee017238SWerner Johansson 	ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
58ee017238SWerner Johansson 					(MIPI_DCS_PIXEL_FMT_24BIT << 4));
59ee017238SWerner Johansson 	if (ret < 0)
60ee017238SWerner Johansson 		return ret;
61ee017238SWerner Johansson 
62ee017238SWerner Johansson 	return 0;
63ee017238SWerner Johansson }
64ee017238SWerner Johansson 
sharp_nt_panel_on(struct sharp_nt_panel * sharp_nt)65ee017238SWerner Johansson static int sharp_nt_panel_on(struct sharp_nt_panel *sharp_nt)
66ee017238SWerner Johansson {
67ee017238SWerner Johansson 	struct mipi_dsi_device *dsi = sharp_nt->dsi;
68ee017238SWerner Johansson 	int ret;
69ee017238SWerner Johansson 
70ee017238SWerner Johansson 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
71ee017238SWerner Johansson 
72ee017238SWerner Johansson 	ret = mipi_dsi_dcs_set_display_on(dsi);
73ee017238SWerner Johansson 	if (ret < 0)
74ee017238SWerner Johansson 		return ret;
75ee017238SWerner Johansson 
76ee017238SWerner Johansson 	return 0;
77ee017238SWerner Johansson }
78ee017238SWerner Johansson 
sharp_nt_panel_off(struct sharp_nt_panel * sharp_nt)79ee017238SWerner Johansson static int sharp_nt_panel_off(struct sharp_nt_panel *sharp_nt)
80ee017238SWerner Johansson {
81ee017238SWerner Johansson 	struct mipi_dsi_device *dsi = sharp_nt->dsi;
82ee017238SWerner Johansson 	int ret;
83ee017238SWerner Johansson 
84ee017238SWerner Johansson 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
85ee017238SWerner Johansson 
86ee017238SWerner Johansson 	ret = mipi_dsi_dcs_set_display_off(dsi);
87ee017238SWerner Johansson 	if (ret < 0)
88ee017238SWerner Johansson 		return ret;
89ee017238SWerner Johansson 
90ee017238SWerner Johansson 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
91ee017238SWerner Johansson 	if (ret < 0)
92ee017238SWerner Johansson 		return ret;
93ee017238SWerner Johansson 
94ee017238SWerner Johansson 	return 0;
95ee017238SWerner Johansson }
96ee017238SWerner Johansson 
sharp_nt_panel_unprepare(struct drm_panel * panel)97ee017238SWerner Johansson static int sharp_nt_panel_unprepare(struct drm_panel *panel)
98ee017238SWerner Johansson {
99ee017238SWerner Johansson 	struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
100ee017238SWerner Johansson 	int ret;
101ee017238SWerner Johansson 
102ee017238SWerner Johansson 	if (!sharp_nt->prepared)
103ee017238SWerner Johansson 		return 0;
104ee017238SWerner Johansson 
105ee017238SWerner Johansson 	ret = sharp_nt_panel_off(sharp_nt);
106ee017238SWerner Johansson 	if (ret < 0) {
107ee017238SWerner Johansson 		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
108ee017238SWerner Johansson 		return ret;
109ee017238SWerner Johansson 	}
110ee017238SWerner Johansson 
111ee017238SWerner Johansson 	regulator_disable(sharp_nt->supply);
112ee017238SWerner Johansson 	if (sharp_nt->reset_gpio)
113ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 0);
114ee017238SWerner Johansson 
115ee017238SWerner Johansson 	sharp_nt->prepared = false;
116ee017238SWerner Johansson 
117ee017238SWerner Johansson 	return 0;
118ee017238SWerner Johansson }
119ee017238SWerner Johansson 
sharp_nt_panel_prepare(struct drm_panel * panel)120ee017238SWerner Johansson static int sharp_nt_panel_prepare(struct drm_panel *panel)
121ee017238SWerner Johansson {
122ee017238SWerner Johansson 	struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
123ee017238SWerner Johansson 	int ret;
124ee017238SWerner Johansson 
125ee017238SWerner Johansson 	if (sharp_nt->prepared)
126ee017238SWerner Johansson 		return 0;
127ee017238SWerner Johansson 
128ee017238SWerner Johansson 	ret = regulator_enable(sharp_nt->supply);
129ee017238SWerner Johansson 	if (ret < 0)
130ee017238SWerner Johansson 		return ret;
131ee017238SWerner Johansson 
132ee017238SWerner Johansson 	msleep(20);
133ee017238SWerner Johansson 
134ee017238SWerner Johansson 	if (sharp_nt->reset_gpio) {
135ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 1);
136ee017238SWerner Johansson 		msleep(1);
137ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 0);
138ee017238SWerner Johansson 		msleep(1);
139ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 1);
140ee017238SWerner Johansson 		msleep(10);
141ee017238SWerner Johansson 	}
142ee017238SWerner Johansson 
143ee017238SWerner Johansson 	ret = sharp_nt_panel_init(sharp_nt);
144ee017238SWerner Johansson 	if (ret < 0) {
145ee017238SWerner Johansson 		dev_err(panel->dev, "failed to init panel: %d\n", ret);
146ee017238SWerner Johansson 		goto poweroff;
147ee017238SWerner Johansson 	}
148ee017238SWerner Johansson 
149ee017238SWerner Johansson 	ret = sharp_nt_panel_on(sharp_nt);
150ee017238SWerner Johansson 	if (ret < 0) {
151ee017238SWerner Johansson 		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
152ee017238SWerner Johansson 		goto poweroff;
153ee017238SWerner Johansson 	}
154ee017238SWerner Johansson 
155ee017238SWerner Johansson 	sharp_nt->prepared = true;
156ee017238SWerner Johansson 
157ee017238SWerner Johansson 	return 0;
158ee017238SWerner Johansson 
159ee017238SWerner Johansson poweroff:
160ee017238SWerner Johansson 	regulator_disable(sharp_nt->supply);
161ee017238SWerner Johansson 	if (sharp_nt->reset_gpio)
162ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 0);
163ee017238SWerner Johansson 	return ret;
164ee017238SWerner Johansson }
165ee017238SWerner Johansson 
166ee017238SWerner Johansson static const struct drm_display_mode default_mode = {
167*dee23b2cSDmitry Baryshkov 	.clock = (540 + 48 + 32 + 80) * (960 + 3 + 10 + 15) * 60 / 1000,
168ee017238SWerner Johansson 	.hdisplay = 540,
169ee017238SWerner Johansson 	.hsync_start = 540 + 48,
170*dee23b2cSDmitry Baryshkov 	.hsync_end = 540 + 48 + 32,
171*dee23b2cSDmitry Baryshkov 	.htotal = 540 + 48 + 32 + 80,
172ee017238SWerner Johansson 	.vdisplay = 960,
173ee017238SWerner Johansson 	.vsync_start = 960 + 3,
174*dee23b2cSDmitry Baryshkov 	.vsync_end = 960 + 3 + 10,
175*dee23b2cSDmitry Baryshkov 	.vtotal = 960 + 3 + 10 + 15,
176ee017238SWerner Johansson };
177ee017238SWerner Johansson 
sharp_nt_panel_get_modes(struct drm_panel * panel,struct drm_connector * connector)1780ce8ddd8SSam Ravnborg static int sharp_nt_panel_get_modes(struct drm_panel *panel,
1790ce8ddd8SSam Ravnborg 				    struct drm_connector *connector)
180ee017238SWerner Johansson {
181ee017238SWerner Johansson 	struct drm_display_mode *mode;
182ee017238SWerner Johansson 
183aa6c4364SSam Ravnborg 	mode = drm_mode_duplicate(connector->dev, &default_mode);
184ee017238SWerner Johansson 	if (!mode) {
185aa6c4364SSam Ravnborg 		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
186ee017238SWerner Johansson 			default_mode.hdisplay, default_mode.vdisplay,
1870425662fSVille Syrjälä 			drm_mode_vrefresh(&default_mode));
188ee017238SWerner Johansson 		return -ENOMEM;
189ee017238SWerner Johansson 	}
190ee017238SWerner Johansson 
191ee017238SWerner Johansson 	drm_mode_set_name(mode);
192ee017238SWerner Johansson 
1930ce8ddd8SSam Ravnborg 	drm_mode_probed_add(connector, mode);
194ee017238SWerner Johansson 
1950ce8ddd8SSam Ravnborg 	connector->display_info.width_mm = 54;
1960ce8ddd8SSam Ravnborg 	connector->display_info.height_mm = 95;
197ee017238SWerner Johansson 
198ee017238SWerner Johansson 	return 1;
199ee017238SWerner Johansson }
200ee017238SWerner Johansson 
201ee017238SWerner Johansson static const struct drm_panel_funcs sharp_nt_panel_funcs = {
202ee017238SWerner Johansson 	.unprepare = sharp_nt_panel_unprepare,
203ee017238SWerner Johansson 	.prepare = sharp_nt_panel_prepare,
204ee017238SWerner Johansson 	.get_modes = sharp_nt_panel_get_modes,
205ee017238SWerner Johansson };
206ee017238SWerner Johansson 
sharp_nt_panel_add(struct sharp_nt_panel * sharp_nt)207ee017238SWerner Johansson static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
208ee017238SWerner Johansson {
209ee017238SWerner Johansson 	struct device *dev = &sharp_nt->dsi->dev;
2108cc85177SSam Ravnborg 	int ret;
211ee017238SWerner Johansson 
212ee017238SWerner Johansson 	sharp_nt->supply = devm_regulator_get(dev, "avdd");
213ee017238SWerner Johansson 	if (IS_ERR(sharp_nt->supply))
214ee017238SWerner Johansson 		return PTR_ERR(sharp_nt->supply);
215ee017238SWerner Johansson 
216ee017238SWerner Johansson 	sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
217ee017238SWerner Johansson 	if (IS_ERR(sharp_nt->reset_gpio)) {
218ee017238SWerner Johansson 		dev_err(dev, "cannot get reset-gpios %ld\n",
219ee017238SWerner Johansson 			PTR_ERR(sharp_nt->reset_gpio));
220ee017238SWerner Johansson 		sharp_nt->reset_gpio = NULL;
221ee017238SWerner Johansson 	} else {
222ee017238SWerner Johansson 		gpiod_set_value(sharp_nt->reset_gpio, 0);
223ee017238SWerner Johansson 	}
224ee017238SWerner Johansson 
2256dbe0c4bSLaurent Pinchart 	drm_panel_init(&sharp_nt->base, &sharp_nt->dsi->dev,
2269a2654c0SLaurent Pinchart 		       &sharp_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
227ee017238SWerner Johansson 
2288cc85177SSam Ravnborg 	ret = drm_panel_of_backlight(&sharp_nt->base);
2298cc85177SSam Ravnborg 	if (ret)
2308cc85177SSam Ravnborg 		return ret;
2318cc85177SSam Ravnborg 
232c3ee8c65SBernard Zhao 	drm_panel_add(&sharp_nt->base);
233c3ee8c65SBernard Zhao 
234c3ee8c65SBernard Zhao 	return 0;
235ee017238SWerner Johansson }
236ee017238SWerner Johansson 
sharp_nt_panel_del(struct sharp_nt_panel * sharp_nt)237ee017238SWerner Johansson static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
238ee017238SWerner Johansson {
239ee017238SWerner Johansson 	if (sharp_nt->base.dev)
240ee017238SWerner Johansson 		drm_panel_remove(&sharp_nt->base);
241ee017238SWerner Johansson }
242ee017238SWerner Johansson 
sharp_nt_panel_probe(struct mipi_dsi_device * dsi)243ee017238SWerner Johansson static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
244ee017238SWerner Johansson {
245ee017238SWerner Johansson 	struct sharp_nt_panel *sharp_nt;
246ee017238SWerner Johansson 	int ret;
247ee017238SWerner Johansson 
248ee017238SWerner Johansson 	dsi->lanes = 2;
249ee017238SWerner Johansson 	dsi->format = MIPI_DSI_FMT_RGB888;
250ee017238SWerner Johansson 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
251*dee23b2cSDmitry Baryshkov 			MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
252ee017238SWerner Johansson 			MIPI_DSI_MODE_VIDEO_HSE |
253ee017238SWerner Johansson 			MIPI_DSI_CLOCK_NON_CONTINUOUS |
2540f3b68b6SNicolas Boichat 			MIPI_DSI_MODE_NO_EOT_PACKET;
255ee017238SWerner Johansson 
256ee017238SWerner Johansson 	sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
257ee017238SWerner Johansson 	if (!sharp_nt)
258ee017238SWerner Johansson 		return -ENOMEM;
259ee017238SWerner Johansson 
260ee017238SWerner Johansson 	mipi_dsi_set_drvdata(dsi, sharp_nt);
261ee017238SWerner Johansson 
262ee017238SWerner Johansson 	sharp_nt->dsi = dsi;
263ee017238SWerner Johansson 
264ee017238SWerner Johansson 	ret = sharp_nt_panel_add(sharp_nt);
265ee017238SWerner Johansson 	if (ret < 0)
266ee017238SWerner Johansson 		return ret;
267ee017238SWerner Johansson 
2689bf7123bSBrian Norris 	ret = mipi_dsi_attach(dsi);
2699bf7123bSBrian Norris 	if (ret < 0) {
2709bf7123bSBrian Norris 		sharp_nt_panel_del(sharp_nt);
2719bf7123bSBrian Norris 		return ret;
2729bf7123bSBrian Norris 	}
2739bf7123bSBrian Norris 
2749bf7123bSBrian Norris 	return 0;
275ee017238SWerner Johansson }
276ee017238SWerner Johansson 
sharp_nt_panel_remove(struct mipi_dsi_device * dsi)27779abca2bSUwe Kleine-König static void sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
278ee017238SWerner Johansson {
279ee017238SWerner Johansson 	struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
280ee017238SWerner Johansson 	int ret;
281ee017238SWerner Johansson 
2828cc85177SSam Ravnborg 	ret = drm_panel_disable(&sharp_nt->base);
283ee017238SWerner Johansson 	if (ret < 0)
284ee017238SWerner Johansson 		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
285ee017238SWerner Johansson 
286ee017238SWerner Johansson 	ret = mipi_dsi_detach(dsi);
287ee017238SWerner Johansson 	if (ret < 0)
288ee017238SWerner Johansson 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
289ee017238SWerner Johansson 
290ee017238SWerner Johansson 	sharp_nt_panel_del(sharp_nt);
291ee017238SWerner Johansson }
292ee017238SWerner Johansson 
sharp_nt_panel_shutdown(struct mipi_dsi_device * dsi)293ee017238SWerner Johansson static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
294ee017238SWerner Johansson {
295ee017238SWerner Johansson 	struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
296ee017238SWerner Johansson 
2978cc85177SSam Ravnborg 	drm_panel_disable(&sharp_nt->base);
298ee017238SWerner Johansson }
299ee017238SWerner Johansson 
300ee017238SWerner Johansson static const struct of_device_id sharp_nt_of_match[] = {
301ee017238SWerner Johansson 	{ .compatible = "sharp,ls043t1le01-qhd", },
302ee017238SWerner Johansson 	{ }
303ee017238SWerner Johansson };
304ee017238SWerner Johansson MODULE_DEVICE_TABLE(of, sharp_nt_of_match);
305ee017238SWerner Johansson 
306ee017238SWerner Johansson static struct mipi_dsi_driver sharp_nt_panel_driver = {
307ee017238SWerner Johansson 	.driver = {
308ee017238SWerner Johansson 		.name = "panel-sharp-ls043t1le01-qhd",
309ee017238SWerner Johansson 		.of_match_table = sharp_nt_of_match,
310ee017238SWerner Johansson 	},
311ee017238SWerner Johansson 	.probe = sharp_nt_panel_probe,
312ee017238SWerner Johansson 	.remove = sharp_nt_panel_remove,
313ee017238SWerner Johansson 	.shutdown = sharp_nt_panel_shutdown,
314ee017238SWerner Johansson };
315ee017238SWerner Johansson module_mipi_dsi_driver(sharp_nt_panel_driver);
316ee017238SWerner Johansson 
317ee017238SWerner Johansson MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
318ee017238SWerner Johansson MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");
319ee017238SWerner Johansson MODULE_LICENSE("GPL v2");
320