1c9cf4c2aSLaurent Pinchart // SPDX-License-Identifier: GPL-2.0
2c9cf4c2aSLaurent Pinchart /*
3c9cf4c2aSLaurent Pinchart * Sharp LS037V7DW01 LCD Panel Driver
4c9cf4c2aSLaurent Pinchart *
5c9cf4c2aSLaurent Pinchart * Copyright (C) 2019 Texas Instruments Incorporated
6c9cf4c2aSLaurent Pinchart *
7c9cf4c2aSLaurent Pinchart * Based on the omapdrm-specific panel-sharp-ls037v7dw01 driver
8c9cf4c2aSLaurent Pinchart *
9c9cf4c2aSLaurent Pinchart * Copyright (C) 2013 Texas Instruments Incorporated
10c9cf4c2aSLaurent Pinchart * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
11c9cf4c2aSLaurent Pinchart */
12c9cf4c2aSLaurent Pinchart
13c9cf4c2aSLaurent Pinchart #include <linux/delay.h>
14c9cf4c2aSLaurent Pinchart #include <linux/gpio/consumer.h>
15c9cf4c2aSLaurent Pinchart #include <linux/module.h>
16c9cf4c2aSLaurent Pinchart #include <linux/of.h>
17c9cf4c2aSLaurent Pinchart #include <linux/platform_device.h>
18c9cf4c2aSLaurent Pinchart #include <linux/regulator/consumer.h>
19c9cf4c2aSLaurent Pinchart
20c9cf4c2aSLaurent Pinchart #include <drm/drm_connector.h>
21c9cf4c2aSLaurent Pinchart #include <drm/drm_modes.h>
22c9cf4c2aSLaurent Pinchart #include <drm/drm_panel.h>
23c9cf4c2aSLaurent Pinchart
24c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel {
25c9cf4c2aSLaurent Pinchart struct drm_panel panel;
26c9cf4c2aSLaurent Pinchart struct platform_device *pdev;
27c9cf4c2aSLaurent Pinchart
28c9cf4c2aSLaurent Pinchart struct regulator *vdd;
29c9cf4c2aSLaurent Pinchart struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
30c9cf4c2aSLaurent Pinchart struct gpio_desc *ini_gpio; /* high = power on */
31c9cf4c2aSLaurent Pinchart struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
32c9cf4c2aSLaurent Pinchart struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
33c9cf4c2aSLaurent Pinchart struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
34c9cf4c2aSLaurent Pinchart };
35c9cf4c2aSLaurent Pinchart
36c9cf4c2aSLaurent Pinchart #define to_ls037v7dw01_device(p) \
37c9cf4c2aSLaurent Pinchart container_of(p, struct ls037v7dw01_panel, panel)
38c9cf4c2aSLaurent Pinchart
ls037v7dw01_disable(struct drm_panel * panel)39c9cf4c2aSLaurent Pinchart static int ls037v7dw01_disable(struct drm_panel *panel)
40c9cf4c2aSLaurent Pinchart {
41c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
42c9cf4c2aSLaurent Pinchart
43c9cf4c2aSLaurent Pinchart gpiod_set_value_cansleep(lcd->ini_gpio, 0);
44c9cf4c2aSLaurent Pinchart gpiod_set_value_cansleep(lcd->resb_gpio, 0);
45c9cf4c2aSLaurent Pinchart
46c9cf4c2aSLaurent Pinchart /* Wait at least 5 vsyncs after disabling the LCD. */
47c9cf4c2aSLaurent Pinchart msleep(100);
48c9cf4c2aSLaurent Pinchart
49c9cf4c2aSLaurent Pinchart return 0;
50c9cf4c2aSLaurent Pinchart }
51c9cf4c2aSLaurent Pinchart
ls037v7dw01_unprepare(struct drm_panel * panel)52c9cf4c2aSLaurent Pinchart static int ls037v7dw01_unprepare(struct drm_panel *panel)
53c9cf4c2aSLaurent Pinchart {
54c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
55c9cf4c2aSLaurent Pinchart
56c9cf4c2aSLaurent Pinchart regulator_disable(lcd->vdd);
57c9cf4c2aSLaurent Pinchart return 0;
58c9cf4c2aSLaurent Pinchart }
59c9cf4c2aSLaurent Pinchart
ls037v7dw01_prepare(struct drm_panel * panel)60c9cf4c2aSLaurent Pinchart static int ls037v7dw01_prepare(struct drm_panel *panel)
61c9cf4c2aSLaurent Pinchart {
62c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
63c9cf4c2aSLaurent Pinchart int ret;
64c9cf4c2aSLaurent Pinchart
65c9cf4c2aSLaurent Pinchart ret = regulator_enable(lcd->vdd);
66c9cf4c2aSLaurent Pinchart if (ret < 0)
67c9cf4c2aSLaurent Pinchart dev_err(&lcd->pdev->dev, "%s: failed to enable regulator\n",
68c9cf4c2aSLaurent Pinchart __func__);
69c9cf4c2aSLaurent Pinchart
70c9cf4c2aSLaurent Pinchart return ret;
71c9cf4c2aSLaurent Pinchart }
72c9cf4c2aSLaurent Pinchart
ls037v7dw01_enable(struct drm_panel * panel)73c9cf4c2aSLaurent Pinchart static int ls037v7dw01_enable(struct drm_panel *panel)
74c9cf4c2aSLaurent Pinchart {
75c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
76c9cf4c2aSLaurent Pinchart
77c9cf4c2aSLaurent Pinchart /* Wait couple of vsyncs before enabling the LCD. */
78c9cf4c2aSLaurent Pinchart msleep(50);
79c9cf4c2aSLaurent Pinchart
80c9cf4c2aSLaurent Pinchart gpiod_set_value_cansleep(lcd->resb_gpio, 1);
81c9cf4c2aSLaurent Pinchart gpiod_set_value_cansleep(lcd->ini_gpio, 1);
82c9cf4c2aSLaurent Pinchart
83c9cf4c2aSLaurent Pinchart return 0;
84c9cf4c2aSLaurent Pinchart }
85c9cf4c2aSLaurent Pinchart
86c9cf4c2aSLaurent Pinchart static const struct drm_display_mode ls037v7dw01_mode = {
87c9cf4c2aSLaurent Pinchart .clock = 19200,
88c9cf4c2aSLaurent Pinchart .hdisplay = 480,
89c9cf4c2aSLaurent Pinchart .hsync_start = 480 + 1,
90c9cf4c2aSLaurent Pinchart .hsync_end = 480 + 1 + 2,
91c9cf4c2aSLaurent Pinchart .htotal = 480 + 1 + 2 + 28,
92c9cf4c2aSLaurent Pinchart .vdisplay = 640,
93c9cf4c2aSLaurent Pinchart .vsync_start = 640 + 1,
94c9cf4c2aSLaurent Pinchart .vsync_end = 640 + 1 + 1,
95c9cf4c2aSLaurent Pinchart .vtotal = 640 + 1 + 1 + 1,
96c9cf4c2aSLaurent Pinchart .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
97c9cf4c2aSLaurent Pinchart .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
98c9cf4c2aSLaurent Pinchart .width_mm = 56,
99c9cf4c2aSLaurent Pinchart .height_mm = 75,
100c9cf4c2aSLaurent Pinchart };
101c9cf4c2aSLaurent Pinchart
ls037v7dw01_get_modes(struct drm_panel * panel,struct drm_connector * connector)1020ce8ddd8SSam Ravnborg static int ls037v7dw01_get_modes(struct drm_panel *panel,
1030ce8ddd8SSam Ravnborg struct drm_connector *connector)
104c9cf4c2aSLaurent Pinchart {
105c9cf4c2aSLaurent Pinchart struct drm_display_mode *mode;
106c9cf4c2aSLaurent Pinchart
107aa6c4364SSam Ravnborg mode = drm_mode_duplicate(connector->dev, &ls037v7dw01_mode);
108c9cf4c2aSLaurent Pinchart if (!mode)
109c9cf4c2aSLaurent Pinchart return -ENOMEM;
110c9cf4c2aSLaurent Pinchart
111c9cf4c2aSLaurent Pinchart drm_mode_set_name(mode);
112c9cf4c2aSLaurent Pinchart drm_mode_probed_add(connector, mode);
113c9cf4c2aSLaurent Pinchart
114c9cf4c2aSLaurent Pinchart connector->display_info.width_mm = ls037v7dw01_mode.width_mm;
115c9cf4c2aSLaurent Pinchart connector->display_info.height_mm = ls037v7dw01_mode.height_mm;
116c9cf4c2aSLaurent Pinchart /*
117c9cf4c2aSLaurent Pinchart * FIXME: According to the datasheet pixel data is sampled on the
118c9cf4c2aSLaurent Pinchart * rising edge of the clock, but the code running on the SDP3430
119c9cf4c2aSLaurent Pinchart * indicates sampling on the negative edge. This should be tested on a
120c9cf4c2aSLaurent Pinchart * real device.
121c9cf4c2aSLaurent Pinchart */
122c9cf4c2aSLaurent Pinchart connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
123c9cf4c2aSLaurent Pinchart | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
124c9cf4c2aSLaurent Pinchart | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
125c9cf4c2aSLaurent Pinchart
126c9cf4c2aSLaurent Pinchart return 1;
127c9cf4c2aSLaurent Pinchart }
128c9cf4c2aSLaurent Pinchart
129c9cf4c2aSLaurent Pinchart static const struct drm_panel_funcs ls037v7dw01_funcs = {
130c9cf4c2aSLaurent Pinchart .disable = ls037v7dw01_disable,
131c9cf4c2aSLaurent Pinchart .unprepare = ls037v7dw01_unprepare,
132c9cf4c2aSLaurent Pinchart .prepare = ls037v7dw01_prepare,
133c9cf4c2aSLaurent Pinchart .enable = ls037v7dw01_enable,
134c9cf4c2aSLaurent Pinchart .get_modes = ls037v7dw01_get_modes,
135c9cf4c2aSLaurent Pinchart };
136c9cf4c2aSLaurent Pinchart
ls037v7dw01_probe(struct platform_device * pdev)137c9cf4c2aSLaurent Pinchart static int ls037v7dw01_probe(struct platform_device *pdev)
138c9cf4c2aSLaurent Pinchart {
139c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd;
140c9cf4c2aSLaurent Pinchart
141c9cf4c2aSLaurent Pinchart lcd = devm_kzalloc(&pdev->dev, sizeof(*lcd), GFP_KERNEL);
1421c8fc3f0SLaurent Pinchart if (!lcd)
143c9cf4c2aSLaurent Pinchart return -ENOMEM;
144c9cf4c2aSLaurent Pinchart
145c9cf4c2aSLaurent Pinchart platform_set_drvdata(pdev, lcd);
146c9cf4c2aSLaurent Pinchart lcd->pdev = pdev;
147c9cf4c2aSLaurent Pinchart
148c9cf4c2aSLaurent Pinchart lcd->vdd = devm_regulator_get(&pdev->dev, "envdd");
149ef41af47SCai Huoqing if (IS_ERR(lcd->vdd))
150ef41af47SCai Huoqing return dev_err_probe(&pdev->dev, PTR_ERR(lcd->vdd),
151ef41af47SCai Huoqing "failed to get regulator\n");
152c9cf4c2aSLaurent Pinchart
153c9cf4c2aSLaurent Pinchart lcd->ini_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
154ef41af47SCai Huoqing if (IS_ERR(lcd->ini_gpio))
155ef41af47SCai Huoqing return dev_err_probe(&pdev->dev, PTR_ERR(lcd->ini_gpio),
156ef41af47SCai Huoqing "failed to get enable gpio\n");
157c9cf4c2aSLaurent Pinchart
158c9cf4c2aSLaurent Pinchart lcd->resb_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
159ef41af47SCai Huoqing if (IS_ERR(lcd->resb_gpio))
160ef41af47SCai Huoqing return dev_err_probe(&pdev->dev, PTR_ERR(lcd->resb_gpio),
161ef41af47SCai Huoqing "failed to get reset gpio\n");
162c9cf4c2aSLaurent Pinchart
163c9cf4c2aSLaurent Pinchart lcd->mo_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 0,
164c9cf4c2aSLaurent Pinchart GPIOD_OUT_LOW);
165c9cf4c2aSLaurent Pinchart if (IS_ERR(lcd->mo_gpio)) {
166c9cf4c2aSLaurent Pinchart dev_err(&pdev->dev, "failed to get mode[0] gpio\n");
167c9cf4c2aSLaurent Pinchart return PTR_ERR(lcd->mo_gpio);
168c9cf4c2aSLaurent Pinchart }
169c9cf4c2aSLaurent Pinchart
170c9cf4c2aSLaurent Pinchart lcd->lr_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 1,
171c9cf4c2aSLaurent Pinchart GPIOD_OUT_LOW);
172c9cf4c2aSLaurent Pinchart if (IS_ERR(lcd->lr_gpio)) {
173c9cf4c2aSLaurent Pinchart dev_err(&pdev->dev, "failed to get mode[1] gpio\n");
174c9cf4c2aSLaurent Pinchart return PTR_ERR(lcd->lr_gpio);
175c9cf4c2aSLaurent Pinchart }
176c9cf4c2aSLaurent Pinchart
177c9cf4c2aSLaurent Pinchart lcd->ud_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 2,
178c9cf4c2aSLaurent Pinchart GPIOD_OUT_LOW);
179c9cf4c2aSLaurent Pinchart if (IS_ERR(lcd->ud_gpio)) {
180c9cf4c2aSLaurent Pinchart dev_err(&pdev->dev, "failed to get mode[2] gpio\n");
181c9cf4c2aSLaurent Pinchart return PTR_ERR(lcd->ud_gpio);
182c9cf4c2aSLaurent Pinchart }
183c9cf4c2aSLaurent Pinchart
1849a2654c0SLaurent Pinchart drm_panel_init(&lcd->panel, &pdev->dev, &ls037v7dw01_funcs,
1859a2654c0SLaurent Pinchart DRM_MODE_CONNECTOR_DPI);
186c9cf4c2aSLaurent Pinchart
187c3ee8c65SBernard Zhao drm_panel_add(&lcd->panel);
188c3ee8c65SBernard Zhao
189c3ee8c65SBernard Zhao return 0;
190c9cf4c2aSLaurent Pinchart }
191c9cf4c2aSLaurent Pinchart
ls037v7dw01_remove(struct platform_device * pdev)192*cef3776dSUwe Kleine-König static void ls037v7dw01_remove(struct platform_device *pdev)
193c9cf4c2aSLaurent Pinchart {
194c9cf4c2aSLaurent Pinchart struct ls037v7dw01_panel *lcd = platform_get_drvdata(pdev);
195c9cf4c2aSLaurent Pinchart
196c9cf4c2aSLaurent Pinchart drm_panel_remove(&lcd->panel);
197c9cf4c2aSLaurent Pinchart drm_panel_disable(&lcd->panel);
198c9cf4c2aSLaurent Pinchart drm_panel_unprepare(&lcd->panel);
199c9cf4c2aSLaurent Pinchart }
200c9cf4c2aSLaurent Pinchart
201c9cf4c2aSLaurent Pinchart static const struct of_device_id ls037v7dw01_of_match[] = {
202c9cf4c2aSLaurent Pinchart { .compatible = "sharp,ls037v7dw01", },
203c9cf4c2aSLaurent Pinchart { /* sentinel */ },
204c9cf4c2aSLaurent Pinchart };
205c9cf4c2aSLaurent Pinchart
206c9cf4c2aSLaurent Pinchart MODULE_DEVICE_TABLE(of, ls037v7dw01_of_match);
207c9cf4c2aSLaurent Pinchart
208c9cf4c2aSLaurent Pinchart static struct platform_driver ls037v7dw01_driver = {
209c9cf4c2aSLaurent Pinchart .probe = ls037v7dw01_probe,
210*cef3776dSUwe Kleine-König .remove_new = ls037v7dw01_remove,
211c9cf4c2aSLaurent Pinchart .driver = {
212c9cf4c2aSLaurent Pinchart .name = "panel-sharp-ls037v7dw01",
213c9cf4c2aSLaurent Pinchart .of_match_table = ls037v7dw01_of_match,
214c9cf4c2aSLaurent Pinchart },
215c9cf4c2aSLaurent Pinchart };
216c9cf4c2aSLaurent Pinchart
217c9cf4c2aSLaurent Pinchart module_platform_driver(ls037v7dw01_driver);
218c9cf4c2aSLaurent Pinchart
219c9cf4c2aSLaurent Pinchart MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
220c9cf4c2aSLaurent Pinchart MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
221c9cf4c2aSLaurent Pinchart MODULE_LICENSE("GPL");
222