1 | /* |
2 | * Copyright © 2016-2017 Broadcom |
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 version 2 as |
6 | * published by the Free Software Foundation. |
7 | * |
8 | * Portions of this file (derived from panel-simple.c) are: |
9 | * |
10 | * Copyright (C) 2013, NVIDIA Corporation. All rights reserved. |
11 | * |
12 | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | * copy of this software and associated documentation files (the "Software"), |
14 | * to deal in the Software without restriction, including without limitation |
15 | * the rights to use, copy, modify, merge, publish, distribute, sub license, |
16 | * and/or sell copies of the Software, and to permit persons to whom the |
17 | * Software is furnished to do so, subject to the following conditions: |
18 | * |
19 | * The above copyright notice and this permission notice (including the |
20 | * next paragraph) shall be included in all copies or substantial portions |
21 | * of the Software. |
22 | * |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
26 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
28 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
29 | * DEALINGS IN THE SOFTWARE. |
30 | */ |
31 | |
32 | /* |
33 | * Raspberry Pi 7" touchscreen panel driver. |
34 | * |
35 | * The 7" touchscreen consists of a DPI LCD panel, a Toshiba |
36 | * TC358762XBG DSI-DPI bridge, and an I2C-connected Atmel ATTINY88-MUR |
37 | * controlling power management, the LCD PWM, and initial register |
38 | * setup of the Tohsiba. |
39 | * |
40 | * This driver controls the TC358762 and ATTINY88, presenting a DSI |
41 | * device with a drm_panel. |
42 | */ |
43 | |
44 | #include <linux/delay.h> |
45 | #include <linux/err.h> |
46 | #include <linux/i2c.h> |
47 | #include <linux/media-bus-format.h> |
48 | #include <linux/module.h> |
49 | #include <linux/of.h> |
50 | #include <linux/of_graph.h> |
51 | #include <linux/pm.h> |
52 | |
53 | #include <drm/drm_crtc.h> |
54 | #include <drm/drm_device.h> |
55 | #include <drm/drm_mipi_dsi.h> |
56 | #include <drm/drm_panel.h> |
57 | |
58 | #define RPI_DSI_DRIVER_NAME "rpi-ts-dsi" |
59 | |
60 | /* I2C registers of the Atmel microcontroller. */ |
61 | enum REG_ADDR { |
62 | REG_ID = 0x80, |
63 | REG_PORTA, /* BIT(2) for horizontal flip, BIT(3) for vertical flip */ |
64 | REG_PORTB, |
65 | REG_PORTC, |
66 | REG_PORTD, |
67 | REG_POWERON, |
68 | REG_PWM, |
69 | REG_DDRA, |
70 | REG_DDRB, |
71 | REG_DDRC, |
72 | REG_DDRD, |
73 | REG_TEST, |
74 | REG_WR_ADDRL, |
75 | REG_WR_ADDRH, |
76 | REG_READH, |
77 | REG_READL, |
78 | REG_WRITEH, |
79 | REG_WRITEL, |
80 | REG_ID2, |
81 | }; |
82 | |
83 | /* DSI D-PHY Layer Registers */ |
84 | #define D0W_DPHYCONTTX 0x0004 |
85 | #define CLW_DPHYCONTRX 0x0020 |
86 | #define D0W_DPHYCONTRX 0x0024 |
87 | #define D1W_DPHYCONTRX 0x0028 |
88 | #define COM_DPHYCONTRX 0x0038 |
89 | #define CLW_CNTRL 0x0040 |
90 | #define D0W_CNTRL 0x0044 |
91 | #define D1W_CNTRL 0x0048 |
92 | #define DFTMODE_CNTRL 0x0054 |
93 | |
94 | /* DSI PPI Layer Registers */ |
95 | #define PPI_STARTPPI 0x0104 |
96 | #define PPI_BUSYPPI 0x0108 |
97 | #define PPI_LINEINITCNT 0x0110 |
98 | #define PPI_LPTXTIMECNT 0x0114 |
99 | #define PPI_CLS_ATMR 0x0140 |
100 | #define PPI_D0S_ATMR 0x0144 |
101 | #define PPI_D1S_ATMR 0x0148 |
102 | #define PPI_D0S_CLRSIPOCOUNT 0x0164 |
103 | #define PPI_D1S_CLRSIPOCOUNT 0x0168 |
104 | #define CLS_PRE 0x0180 |
105 | #define D0S_PRE 0x0184 |
106 | #define D1S_PRE 0x0188 |
107 | #define CLS_PREP 0x01A0 |
108 | #define D0S_PREP 0x01A4 |
109 | #define D1S_PREP 0x01A8 |
110 | #define CLS_ZERO 0x01C0 |
111 | #define D0S_ZERO 0x01C4 |
112 | #define D1S_ZERO 0x01C8 |
113 | #define PPI_CLRFLG 0x01E0 |
114 | #define PPI_CLRSIPO 0x01E4 |
115 | #define HSTIMEOUT 0x01F0 |
116 | #define HSTIMEOUTENABLE 0x01F4 |
117 | |
118 | /* DSI Protocol Layer Registers */ |
119 | #define DSI_STARTDSI 0x0204 |
120 | #define DSI_BUSYDSI 0x0208 |
121 | #define DSI_LANEENABLE 0x0210 |
122 | # define DSI_LANEENABLE_CLOCK BIT(0) |
123 | # define DSI_LANEENABLE_D0 BIT(1) |
124 | # define DSI_LANEENABLE_D1 BIT(2) |
125 | |
126 | #define DSI_LANESTATUS0 0x0214 |
127 | #define DSI_LANESTATUS1 0x0218 |
128 | #define DSI_INTSTATUS 0x0220 |
129 | #define DSI_INTMASK 0x0224 |
130 | #define DSI_INTCLR 0x0228 |
131 | #define DSI_LPTXTO 0x0230 |
132 | #define DSI_MODE 0x0260 |
133 | #define DSI_PAYLOAD0 0x0268 |
134 | #define DSI_PAYLOAD1 0x026C |
135 | #define DSI_SHORTPKTDAT 0x0270 |
136 | #define DSI_SHORTPKTREQ 0x0274 |
137 | #define DSI_BTASTA 0x0278 |
138 | #define DSI_BTACLR 0x027C |
139 | |
140 | /* DSI General Registers */ |
141 | #define DSIERRCNT 0x0300 |
142 | #define DSISIGMOD 0x0304 |
143 | |
144 | /* DSI Application Layer Registers */ |
145 | #define APLCTRL 0x0400 |
146 | #define APLSTAT 0x0404 |
147 | #define APLERR 0x0408 |
148 | #define PWRMOD 0x040C |
149 | #define RDPKTLN 0x0410 |
150 | #define PXLFMT 0x0414 |
151 | #define MEMWRCMD 0x0418 |
152 | |
153 | /* LCDC/DPI Host Registers */ |
154 | #define LCDCTRL 0x0420 |
155 | #define HSR 0x0424 |
156 | #define HDISPR 0x0428 |
157 | #define VSR 0x042C |
158 | #define VDISPR 0x0430 |
159 | #define VFUEN 0x0434 |
160 | |
161 | /* DBI-B Host Registers */ |
162 | #define DBIBCTRL 0x0440 |
163 | |
164 | /* SPI Master Registers */ |
165 | #define SPICMR 0x0450 |
166 | #define SPITCR 0x0454 |
167 | |
168 | /* System Controller Registers */ |
169 | #define SYSSTAT 0x0460 |
170 | #define SYSCTRL 0x0464 |
171 | #define SYSPLL1 0x0468 |
172 | #define SYSPLL2 0x046C |
173 | #define SYSPLL3 0x0470 |
174 | #define SYSPMCTRL 0x047C |
175 | |
176 | /* GPIO Registers */ |
177 | #define GPIOC 0x0480 |
178 | #define GPIOO 0x0484 |
179 | #define GPIOI 0x0488 |
180 | |
181 | /* I2C Registers */ |
182 | #define I2CCLKCTRL 0x0490 |
183 | |
184 | /* Chip/Rev Registers */ |
185 | #define IDREG 0x04A0 |
186 | |
187 | /* Debug Registers */ |
188 | #define WCMDQUEUE 0x0500 |
189 | #define RCMDQUEUE 0x0504 |
190 | |
191 | struct rpi_touchscreen { |
192 | struct drm_panel base; |
193 | struct mipi_dsi_device *dsi; |
194 | struct i2c_client *i2c; |
195 | }; |
196 | |
197 | static const struct drm_display_mode rpi_touchscreen_modes[] = { |
198 | { |
199 | /* Modeline comes from the Raspberry Pi firmware, with HFP=1 |
200 | * plugged in and clock re-computed from that. |
201 | */ |
202 | .clock = 25979400 / 1000, |
203 | .hdisplay = 800, |
204 | .hsync_start = 800 + 1, |
205 | .hsync_end = 800 + 1 + 2, |
206 | .htotal = 800 + 1 + 2 + 46, |
207 | .vdisplay = 480, |
208 | .vsync_start = 480 + 7, |
209 | .vsync_end = 480 + 7 + 2, |
210 | .vtotal = 480 + 7 + 2 + 21, |
211 | }, |
212 | }; |
213 | |
214 | static struct rpi_touchscreen *panel_to_ts(struct drm_panel *panel) |
215 | { |
216 | return container_of(panel, struct rpi_touchscreen, base); |
217 | } |
218 | |
219 | static int rpi_touchscreen_i2c_read(struct rpi_touchscreen *ts, u8 reg) |
220 | { |
221 | return i2c_smbus_read_byte_data(client: ts->i2c, command: reg); |
222 | } |
223 | |
224 | static void rpi_touchscreen_i2c_write(struct rpi_touchscreen *ts, |
225 | u8 reg, u8 val) |
226 | { |
227 | int ret; |
228 | |
229 | ret = i2c_smbus_write_byte_data(client: ts->i2c, command: reg, value: val); |
230 | if (ret) |
231 | dev_err(&ts->i2c->dev, "I2C write failed: %d\n" , ret); |
232 | } |
233 | |
234 | static int rpi_touchscreen_write(struct rpi_touchscreen *ts, u16 reg, u32 val) |
235 | { |
236 | u8 msg[] = { |
237 | reg, |
238 | reg >> 8, |
239 | val, |
240 | val >> 8, |
241 | val >> 16, |
242 | val >> 24, |
243 | }; |
244 | |
245 | mipi_dsi_generic_write(dsi: ts->dsi, payload: msg, size: sizeof(msg)); |
246 | |
247 | return 0; |
248 | } |
249 | |
250 | static int rpi_touchscreen_disable(struct drm_panel *panel) |
251 | { |
252 | struct rpi_touchscreen *ts = panel_to_ts(panel); |
253 | |
254 | rpi_touchscreen_i2c_write(ts, reg: REG_PWM, val: 0); |
255 | |
256 | rpi_touchscreen_i2c_write(ts, reg: REG_POWERON, val: 0); |
257 | udelay(1); |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | static int rpi_touchscreen_noop(struct drm_panel *panel) |
263 | { |
264 | return 0; |
265 | } |
266 | |
267 | static int rpi_touchscreen_prepare(struct drm_panel *panel) |
268 | { |
269 | struct rpi_touchscreen *ts = panel_to_ts(panel); |
270 | int i; |
271 | |
272 | rpi_touchscreen_i2c_write(ts, reg: REG_POWERON, val: 1); |
273 | /* Wait for nPWRDWN to go low to indicate poweron is done. */ |
274 | for (i = 0; i < 100; i++) { |
275 | if (rpi_touchscreen_i2c_read(ts, reg: REG_PORTB) & 1) |
276 | break; |
277 | } |
278 | |
279 | rpi_touchscreen_write(ts, DSI_LANEENABLE, |
280 | DSI_LANEENABLE_CLOCK | |
281 | DSI_LANEENABLE_D0); |
282 | rpi_touchscreen_write(ts, PPI_D0S_CLRSIPOCOUNT, val: 0x05); |
283 | rpi_touchscreen_write(ts, PPI_D1S_CLRSIPOCOUNT, val: 0x05); |
284 | rpi_touchscreen_write(ts, PPI_D0S_ATMR, val: 0x00); |
285 | rpi_touchscreen_write(ts, PPI_D1S_ATMR, val: 0x00); |
286 | rpi_touchscreen_write(ts, PPI_LPTXTIMECNT, val: 0x03); |
287 | |
288 | rpi_touchscreen_write(ts, SPICMR, val: 0x00); |
289 | rpi_touchscreen_write(ts, LCDCTRL, val: 0x00100150); |
290 | rpi_touchscreen_write(ts, SYSCTRL, val: 0x040f); |
291 | msleep(msecs: 100); |
292 | |
293 | rpi_touchscreen_write(ts, PPI_STARTPPI, val: 0x01); |
294 | rpi_touchscreen_write(ts, DSI_STARTDSI, val: 0x01); |
295 | msleep(msecs: 100); |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static int rpi_touchscreen_enable(struct drm_panel *panel) |
301 | { |
302 | struct rpi_touchscreen *ts = panel_to_ts(panel); |
303 | |
304 | /* Turn on the backlight. */ |
305 | rpi_touchscreen_i2c_write(ts, reg: REG_PWM, val: 255); |
306 | |
307 | /* Default to the same orientation as the closed source |
308 | * firmware used for the panel. Runtime rotation |
309 | * configuration will be supported using VC4's plane |
310 | * orientation bits. |
311 | */ |
312 | rpi_touchscreen_i2c_write(ts, reg: REG_PORTA, BIT(2)); |
313 | |
314 | return 0; |
315 | } |
316 | |
317 | static int rpi_touchscreen_get_modes(struct drm_panel *panel, |
318 | struct drm_connector *connector) |
319 | { |
320 | unsigned int i, num = 0; |
321 | static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
322 | |
323 | for (i = 0; i < ARRAY_SIZE(rpi_touchscreen_modes); i++) { |
324 | const struct drm_display_mode *m = &rpi_touchscreen_modes[i]; |
325 | struct drm_display_mode *mode; |
326 | |
327 | mode = drm_mode_duplicate(dev: connector->dev, mode: m); |
328 | if (!mode) { |
329 | dev_err(panel->dev, "failed to add mode %ux%u@%u\n" , |
330 | m->hdisplay, m->vdisplay, |
331 | drm_mode_vrefresh(m)); |
332 | continue; |
333 | } |
334 | |
335 | mode->type |= DRM_MODE_TYPE_DRIVER; |
336 | |
337 | if (i == 0) |
338 | mode->type |= DRM_MODE_TYPE_PREFERRED; |
339 | |
340 | drm_mode_set_name(mode); |
341 | |
342 | drm_mode_probed_add(connector, mode); |
343 | num++; |
344 | } |
345 | |
346 | connector->display_info.bpc = 8; |
347 | connector->display_info.width_mm = 154; |
348 | connector->display_info.height_mm = 86; |
349 | drm_display_info_set_bus_formats(info: &connector->display_info, |
350 | formats: &bus_format, num_formats: 1); |
351 | |
352 | return num; |
353 | } |
354 | |
355 | static const struct drm_panel_funcs rpi_touchscreen_funcs = { |
356 | .disable = rpi_touchscreen_disable, |
357 | .unprepare = rpi_touchscreen_noop, |
358 | .prepare = rpi_touchscreen_prepare, |
359 | .enable = rpi_touchscreen_enable, |
360 | .get_modes = rpi_touchscreen_get_modes, |
361 | }; |
362 | |
363 | static int rpi_touchscreen_probe(struct i2c_client *i2c) |
364 | { |
365 | struct device *dev = &i2c->dev; |
366 | struct rpi_touchscreen *ts; |
367 | struct device_node *endpoint, *dsi_host_node; |
368 | struct mipi_dsi_host *host; |
369 | int ver; |
370 | struct mipi_dsi_device_info info = { |
371 | .type = RPI_DSI_DRIVER_NAME, |
372 | .channel = 0, |
373 | .node = NULL, |
374 | }; |
375 | |
376 | ts = devm_kzalloc(dev, size: sizeof(*ts), GFP_KERNEL); |
377 | if (!ts) |
378 | return -ENOMEM; |
379 | |
380 | i2c_set_clientdata(client: i2c, data: ts); |
381 | |
382 | ts->i2c = i2c; |
383 | |
384 | ver = rpi_touchscreen_i2c_read(ts, reg: REG_ID); |
385 | if (ver < 0) { |
386 | dev_err(dev, "Atmel I2C read failed: %d\n" , ver); |
387 | return -ENODEV; |
388 | } |
389 | |
390 | switch (ver) { |
391 | case 0xde: /* ver 1 */ |
392 | case 0xc3: /* ver 2 */ |
393 | break; |
394 | default: |
395 | dev_err(dev, "Unknown Atmel firmware revision: 0x%02x\n" , ver); |
396 | return -ENODEV; |
397 | } |
398 | |
399 | /* Turn off at boot, so we can cleanly sequence powering on. */ |
400 | rpi_touchscreen_i2c_write(ts, reg: REG_POWERON, val: 0); |
401 | |
402 | /* Look up the DSI host. It needs to probe before we do. */ |
403 | endpoint = of_graph_get_next_endpoint(parent: dev->of_node, NULL); |
404 | if (!endpoint) |
405 | return -ENODEV; |
406 | |
407 | dsi_host_node = of_graph_get_remote_port_parent(node: endpoint); |
408 | if (!dsi_host_node) |
409 | goto error; |
410 | |
411 | host = of_find_mipi_dsi_host_by_node(node: dsi_host_node); |
412 | of_node_put(node: dsi_host_node); |
413 | if (!host) { |
414 | of_node_put(node: endpoint); |
415 | return -EPROBE_DEFER; |
416 | } |
417 | |
418 | info.node = of_graph_get_remote_port(node: endpoint); |
419 | if (!info.node) |
420 | goto error; |
421 | |
422 | of_node_put(node: endpoint); |
423 | |
424 | ts->dsi = mipi_dsi_device_register_full(host, info: &info); |
425 | if (IS_ERR(ptr: ts->dsi)) { |
426 | dev_err(dev, "DSI device registration failed: %ld\n" , |
427 | PTR_ERR(ts->dsi)); |
428 | return PTR_ERR(ptr: ts->dsi); |
429 | } |
430 | |
431 | drm_panel_init(panel: &ts->base, dev, funcs: &rpi_touchscreen_funcs, |
432 | DRM_MODE_CONNECTOR_DSI); |
433 | |
434 | /* This appears last, as it's what will unblock the DSI host |
435 | * driver's component bind function. |
436 | */ |
437 | drm_panel_add(panel: &ts->base); |
438 | |
439 | return 0; |
440 | |
441 | error: |
442 | of_node_put(node: endpoint); |
443 | return -ENODEV; |
444 | } |
445 | |
446 | static void rpi_touchscreen_remove(struct i2c_client *i2c) |
447 | { |
448 | struct rpi_touchscreen *ts = i2c_get_clientdata(client: i2c); |
449 | |
450 | mipi_dsi_detach(dsi: ts->dsi); |
451 | |
452 | drm_panel_remove(panel: &ts->base); |
453 | |
454 | mipi_dsi_device_unregister(dsi: ts->dsi); |
455 | } |
456 | |
457 | static int rpi_touchscreen_dsi_probe(struct mipi_dsi_device *dsi) |
458 | { |
459 | int ret; |
460 | |
461 | dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | |
462 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | |
463 | MIPI_DSI_MODE_LPM); |
464 | dsi->format = MIPI_DSI_FMT_RGB888; |
465 | dsi->lanes = 1; |
466 | |
467 | ret = mipi_dsi_attach(dsi); |
468 | |
469 | if (ret) |
470 | dev_err(&dsi->dev, "failed to attach dsi to host: %d\n" , ret); |
471 | |
472 | return ret; |
473 | } |
474 | |
475 | static struct mipi_dsi_driver rpi_touchscreen_dsi_driver = { |
476 | .driver.name = RPI_DSI_DRIVER_NAME, |
477 | .probe = rpi_touchscreen_dsi_probe, |
478 | }; |
479 | |
480 | static const struct of_device_id rpi_touchscreen_of_ids[] = { |
481 | { .compatible = "raspberrypi,7inch-touchscreen-panel" }, |
482 | { } /* sentinel */ |
483 | }; |
484 | MODULE_DEVICE_TABLE(of, rpi_touchscreen_of_ids); |
485 | |
486 | static struct i2c_driver rpi_touchscreen_driver = { |
487 | .driver = { |
488 | .name = "rpi_touchscreen" , |
489 | .of_match_table = rpi_touchscreen_of_ids, |
490 | }, |
491 | .probe = rpi_touchscreen_probe, |
492 | .remove = rpi_touchscreen_remove, |
493 | }; |
494 | |
495 | static int __init rpi_touchscreen_init(void) |
496 | { |
497 | mipi_dsi_driver_register(&rpi_touchscreen_dsi_driver); |
498 | return i2c_add_driver(&rpi_touchscreen_driver); |
499 | } |
500 | module_init(rpi_touchscreen_init); |
501 | |
502 | static void __exit rpi_touchscreen_exit(void) |
503 | { |
504 | i2c_del_driver(driver: &rpi_touchscreen_driver); |
505 | mipi_dsi_driver_unregister(driver: &rpi_touchscreen_dsi_driver); |
506 | } |
507 | module_exit(rpi_touchscreen_exit); |
508 | |
509 | MODULE_AUTHOR("Eric Anholt <eric@anholt.net>" ); |
510 | MODULE_DESCRIPTION("Raspberry Pi 7-inch touchscreen driver" ); |
511 | MODULE_LICENSE("GPL v2" ); |
512 | |