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