1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Orisetech OTA5601A TFT LCD panel driver |
4 | * |
5 | * Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/bits.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/device.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/media-bus-format.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/regulator/consumer.h> |
17 | #include <linux/spi/spi.h> |
18 | |
19 | #include <drm/drm_modes.h> |
20 | #include <drm/drm_panel.h> |
21 | |
22 | #define OTA5601A_CTL 0x01 |
23 | #define OTA5601A_CTL_OFF 0x00 |
24 | #define OTA5601A_CTL_ON BIT(0) |
25 | |
26 | struct ota5601a_panel_info { |
27 | const struct drm_display_mode *display_modes; |
28 | unsigned int num_modes; |
29 | u16 width_mm, height_mm; |
30 | u32 bus_format, bus_flags; |
31 | }; |
32 | |
33 | struct ota5601a { |
34 | struct drm_panel drm_panel; |
35 | struct regmap *map; |
36 | struct regulator *supply; |
37 | const struct ota5601a_panel_info *panel_info; |
38 | |
39 | struct gpio_desc *reset_gpio; |
40 | }; |
41 | |
42 | static inline struct ota5601a *to_ota5601a(struct drm_panel *panel) |
43 | { |
44 | return container_of(panel, struct ota5601a, drm_panel); |
45 | } |
46 | |
47 | static const struct reg_sequence ota5601a_panel_regs[] = { |
48 | { 0xfd, 0x00 }, /* Page Shift */ |
49 | { 0x02, 0x00 }, /* Reset */ |
50 | |
51 | { 0x18, 0x00 }, /* Interface Sel: RGB 24 Bits */ |
52 | { 0x34, 0x20 }, /* Undocumented */ |
53 | |
54 | { 0x0c, 0x01 }, /* Contrast set by CMD1 == within page 0x00 */ |
55 | { 0x0d, 0x48 }, /* R Brightness */ |
56 | { 0x0e, 0x48 }, /* G Brightness */ |
57 | { 0x0f, 0x48 }, /* B Brightness */ |
58 | { 0x07, 0x40 }, /* R Contrast */ |
59 | { 0x08, 0x33 }, /* G Contrast */ |
60 | { 0x09, 0x3a }, /* B Contrast */ |
61 | |
62 | { 0x16, 0x01 }, /* NTSC Sel */ |
63 | { 0x19, 0x8d }, /* VBLK */ |
64 | { 0x1a, 0x28 }, /* HBLK */ |
65 | { 0x1c, 0x00 }, /* Scan Shift Dir. */ |
66 | |
67 | { 0xfd, 0xc5 }, /* Page Shift */ |
68 | { 0x82, 0x0c }, /* PWR_CTRL Pump */ |
69 | { 0xa2, 0xb4 }, /* PWR_CTRL VGH/VGL */ |
70 | |
71 | { 0xfd, 0xc4 }, /* Page Shift - What follows is listed as "RGB 24bit Timing Set" */ |
72 | { 0x82, 0x45 }, |
73 | |
74 | { 0xfd, 0xc1 }, |
75 | { 0x91, 0x02 }, |
76 | |
77 | { 0xfd, 0xc0 }, |
78 | { 0xa1, 0x01 }, |
79 | { 0xa2, 0x1f }, |
80 | { 0xa3, 0x0b }, |
81 | { 0xa4, 0x38 }, |
82 | { 0xa5, 0x00 }, |
83 | { 0xa6, 0x0a }, |
84 | { 0xa7, 0x38 }, |
85 | { 0xa8, 0x00 }, |
86 | { 0xa9, 0x0a }, |
87 | { 0xaa, 0x37 }, |
88 | |
89 | { 0xfd, 0xce }, |
90 | { 0x81, 0x18 }, |
91 | { 0x82, 0x43 }, |
92 | { 0x83, 0x43 }, |
93 | { 0x91, 0x06 }, |
94 | { 0x93, 0x38 }, |
95 | { 0x94, 0x02 }, |
96 | { 0x95, 0x06 }, |
97 | { 0x97, 0x38 }, |
98 | { 0x98, 0x02 }, |
99 | { 0x99, 0x06 }, |
100 | { 0x9b, 0x38 }, |
101 | { 0x9c, 0x02 }, |
102 | |
103 | { 0xfd, 0x00 }, /* Page Shift */ |
104 | }; |
105 | |
106 | static const struct regmap_config ota5601a_regmap_config = { |
107 | .reg_bits = 8, |
108 | .val_bits = 8, |
109 | }; |
110 | |
111 | static int ota5601a_prepare(struct drm_panel *drm_panel) |
112 | { |
113 | struct ota5601a *panel = to_ota5601a(panel: drm_panel); |
114 | int err; |
115 | |
116 | err = regulator_enable(regulator: panel->supply); |
117 | if (err) { |
118 | dev_err(drm_panel->dev, "Failed to enable power supply: %d\n" , err); |
119 | return err; |
120 | } |
121 | |
122 | /* Reset to be held low for 10us min according to the doc, 10ms before sending commands */ |
123 | gpiod_set_value_cansleep(desc: panel->reset_gpio, value: 1); |
124 | usleep_range(min: 10, max: 30); |
125 | gpiod_set_value_cansleep(desc: panel->reset_gpio, value: 0); |
126 | usleep_range(min: 10000, max: 20000); |
127 | |
128 | /* Init all registers. */ |
129 | err = regmap_multi_reg_write(map: panel->map, regs: ota5601a_panel_regs, |
130 | ARRAY_SIZE(ota5601a_panel_regs)); |
131 | if (err) { |
132 | dev_err(drm_panel->dev, "Failed to init registers: %d\n" , err); |
133 | goto err_disable_regulator; |
134 | } |
135 | |
136 | msleep(msecs: 120); |
137 | |
138 | return 0; |
139 | |
140 | err_disable_regulator: |
141 | regulator_disable(regulator: panel->supply); |
142 | return err; |
143 | } |
144 | |
145 | static int ota5601a_unprepare(struct drm_panel *drm_panel) |
146 | { |
147 | struct ota5601a *panel = to_ota5601a(panel: drm_panel); |
148 | |
149 | gpiod_set_value_cansleep(desc: panel->reset_gpio, value: 1); |
150 | |
151 | regulator_disable(regulator: panel->supply); |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static int ota5601a_enable(struct drm_panel *drm_panel) |
157 | { |
158 | struct ota5601a *panel = to_ota5601a(panel: drm_panel); |
159 | int err; |
160 | |
161 | err = regmap_write(map: panel->map, OTA5601A_CTL, OTA5601A_CTL_ON); |
162 | |
163 | if (err) { |
164 | dev_err(drm_panel->dev, "Unable to enable panel: %d\n" , err); |
165 | return err; |
166 | } |
167 | |
168 | if (drm_panel->backlight) { |
169 | /* Wait for the picture to be ready before enabling backlight */ |
170 | msleep(msecs: 120); |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | static int ota5601a_disable(struct drm_panel *drm_panel) |
177 | { |
178 | struct ota5601a *panel = to_ota5601a(panel: drm_panel); |
179 | int err; |
180 | |
181 | err = regmap_write(map: panel->map, OTA5601A_CTL, OTA5601A_CTL_OFF); |
182 | |
183 | if (err) { |
184 | dev_err(drm_panel->dev, "Unable to disable panel: %d\n" , err); |
185 | return err; |
186 | } |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | static int ota5601a_get_modes(struct drm_panel *drm_panel, |
192 | struct drm_connector *connector) |
193 | { |
194 | struct ota5601a *panel = to_ota5601a(panel: drm_panel); |
195 | const struct ota5601a_panel_info *panel_info = panel->panel_info; |
196 | struct drm_display_mode *mode; |
197 | unsigned int i; |
198 | |
199 | for (i = 0; i < panel_info->num_modes; i++) { |
200 | mode = drm_mode_duplicate(dev: connector->dev, |
201 | mode: &panel_info->display_modes[i]); |
202 | if (!mode) |
203 | return -ENOMEM; |
204 | |
205 | drm_mode_set_name(mode); |
206 | |
207 | mode->type = DRM_MODE_TYPE_DRIVER; |
208 | if (panel_info->num_modes == 1) |
209 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
210 | |
211 | drm_mode_probed_add(connector, mode); |
212 | } |
213 | |
214 | connector->display_info.bpc = 8; |
215 | connector->display_info.width_mm = panel_info->width_mm; |
216 | connector->display_info.height_mm = panel_info->height_mm; |
217 | |
218 | drm_display_info_set_bus_formats(info: &connector->display_info, |
219 | formats: &panel_info->bus_format, num_formats: 1); |
220 | connector->display_info.bus_flags = panel_info->bus_flags; |
221 | |
222 | return panel_info->num_modes; |
223 | } |
224 | |
225 | static const struct drm_panel_funcs ota5601a_funcs = { |
226 | .prepare = ota5601a_prepare, |
227 | .unprepare = ota5601a_unprepare, |
228 | .enable = ota5601a_enable, |
229 | .disable = ota5601a_disable, |
230 | .get_modes = ota5601a_get_modes, |
231 | }; |
232 | |
233 | static int ota5601a_probe(struct spi_device *spi) |
234 | { |
235 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
236 | struct device *dev = &spi->dev; |
237 | struct ota5601a *panel; |
238 | int err; |
239 | |
240 | panel = devm_kzalloc(dev, size: sizeof(*panel), GFP_KERNEL); |
241 | if (!panel) |
242 | return -ENOMEM; |
243 | |
244 | spi_set_drvdata(spi, data: panel); |
245 | |
246 | panel->panel_info = (const struct ota5601a_panel_info *)id->driver_data; |
247 | if (!panel->panel_info) |
248 | return -EINVAL; |
249 | |
250 | panel->supply = devm_regulator_get(dev, id: "power" ); |
251 | if (IS_ERR(ptr: panel->supply)) { |
252 | dev_err(dev, "Failed to get power supply\n" ); |
253 | return PTR_ERR(ptr: panel->supply); |
254 | } |
255 | |
256 | panel->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
257 | if (IS_ERR(ptr: panel->reset_gpio)) { |
258 | dev_err(dev, "Failed to get reset GPIO\n" ); |
259 | return PTR_ERR(ptr: panel->reset_gpio); |
260 | } |
261 | |
262 | spi->bits_per_word = 8; |
263 | spi->mode = SPI_MODE_3 | SPI_3WIRE; |
264 | err = spi_setup(spi); |
265 | if (err) { |
266 | dev_err(dev, "Failed to setup SPI\n" ); |
267 | return err; |
268 | } |
269 | |
270 | panel->map = devm_regmap_init_spi(spi, &ota5601a_regmap_config); |
271 | if (IS_ERR(ptr: panel->map)) { |
272 | dev_err(dev, "Failed to init regmap\n" ); |
273 | return PTR_ERR(ptr: panel->map); |
274 | } |
275 | |
276 | drm_panel_init(panel: &panel->drm_panel, dev, funcs: &ota5601a_funcs, |
277 | DRM_MODE_CONNECTOR_DPI); |
278 | |
279 | err = drm_panel_of_backlight(panel: &panel->drm_panel); |
280 | if (err) { |
281 | if (err != -EPROBE_DEFER) |
282 | dev_err(dev, "Failed to get backlight handle\n" ); |
283 | return err; |
284 | } |
285 | |
286 | drm_panel_add(panel: &panel->drm_panel); |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | static void ota5601a_remove(struct spi_device *spi) |
292 | { |
293 | struct ota5601a *panel = spi_get_drvdata(spi); |
294 | |
295 | drm_panel_remove(panel: &panel->drm_panel); |
296 | |
297 | ota5601a_disable(drm_panel: &panel->drm_panel); |
298 | ota5601a_unprepare(drm_panel: &panel->drm_panel); |
299 | } |
300 | |
301 | static const struct drm_display_mode gpt3_display_modes[] = { |
302 | { /* 60 Hz */ |
303 | .clock = 27000, |
304 | .hdisplay = 640, |
305 | .hsync_start = 640 + 220, |
306 | .hsync_end = 640 + 220 + 20, |
307 | .htotal = 640 + 220 + 20 + 20, |
308 | .vdisplay = 480, |
309 | .vsync_start = 480 + 7, |
310 | .vsync_end = 480 + 7 + 6, |
311 | .vtotal = 480 + 7 + 6 + 7, |
312 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
313 | }, |
314 | |
315 | { /* 50 Hz */ |
316 | .clock = 24000, |
317 | .hdisplay = 640, |
318 | .hsync_start = 640 + 280, |
319 | .hsync_end = 640 + 280 + 20, |
320 | .htotal = 640 + 280 + 20 + 20, |
321 | .vdisplay = 480, |
322 | .vsync_start = 480 + 7, |
323 | .vsync_end = 480 + 7 + 6, |
324 | .vtotal = 480 + 7 + 6 + 7, |
325 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
326 | }, |
327 | }; |
328 | |
329 | static const struct ota5601a_panel_info gpt3_info = { |
330 | .display_modes = gpt3_display_modes, |
331 | .num_modes = ARRAY_SIZE(gpt3_display_modes), |
332 | .width_mm = 71, |
333 | .height_mm = 51, |
334 | .bus_format = MEDIA_BUS_FMT_RGB888_1X24, |
335 | .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, |
336 | }; |
337 | |
338 | static const struct spi_device_id gpt3_id[] = { |
339 | { "gpt3" , (kernel_ulong_t)&gpt3_info }, |
340 | { /* sentinel */ } |
341 | }; |
342 | MODULE_DEVICE_TABLE(spi, gpt3_id); |
343 | |
344 | static const struct of_device_id ota5601a_of_match[] = { |
345 | { .compatible = "focaltech,gpt3" }, |
346 | { /* sentinel */ } |
347 | }; |
348 | MODULE_DEVICE_TABLE(of, ota5601a_of_match); |
349 | |
350 | static struct spi_driver ota5601a_driver = { |
351 | .driver = { |
352 | .name = "ota5601a" , |
353 | .of_match_table = ota5601a_of_match, |
354 | }, |
355 | .id_table = gpt3_id, |
356 | .probe = ota5601a_probe, |
357 | .remove = ota5601a_remove, |
358 | }; |
359 | |
360 | module_spi_driver(ota5601a_driver); |
361 | |
362 | MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>" ); |
363 | MODULE_LICENSE("GPL" ); |
364 | |