18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2016-2017 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 58c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Portions of this file (derived from panel-simple.c) are: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2013, NVIDIA Corporation. All rights reserved. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 138c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 148c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 158c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sub license, 168c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 178c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the 208c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 218c2ecf20Sopenharmony_ci * of the Software. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 248c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 258c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 268c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 278c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 288c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 298c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * Raspberry Pi 7" touchscreen panel driver. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * The 7" touchscreen consists of a DPI LCD panel, a Toshiba 368c2ecf20Sopenharmony_ci * TC358762XBG DSI-DPI bridge, and an I2C-connected Atmel ATTINY88-MUR 378c2ecf20Sopenharmony_ci * controlling power management, the LCD PWM, and initial register 388c2ecf20Sopenharmony_ci * setup of the Tohsiba. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * This driver controls the TC358762 and ATTINY88, presenting a DSI 418c2ecf20Sopenharmony_ci * device with a drm_panel. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/delay.h> 458c2ecf20Sopenharmony_ci#include <linux/err.h> 468c2ecf20Sopenharmony_ci#include <linux/fb.h> 478c2ecf20Sopenharmony_ci#include <linux/i2c.h> 488c2ecf20Sopenharmony_ci#include <linux/module.h> 498c2ecf20Sopenharmony_ci#include <linux/of.h> 508c2ecf20Sopenharmony_ci#include <linux/of_device.h> 518c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 528c2ecf20Sopenharmony_ci#include <linux/pm.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 558c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 568c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 578c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define RPI_DSI_DRIVER_NAME "rpi-ts-dsi" 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* I2C registers of the Atmel microcontroller. */ 628c2ecf20Sopenharmony_cienum REG_ADDR { 638c2ecf20Sopenharmony_ci REG_ID = 0x80, 648c2ecf20Sopenharmony_ci REG_PORTA, /* BIT(2) for horizontal flip, BIT(3) for vertical flip */ 658c2ecf20Sopenharmony_ci REG_PORTB, 668c2ecf20Sopenharmony_ci REG_PORTC, 678c2ecf20Sopenharmony_ci REG_PORTD, 688c2ecf20Sopenharmony_ci REG_POWERON, 698c2ecf20Sopenharmony_ci REG_PWM, 708c2ecf20Sopenharmony_ci REG_DDRA, 718c2ecf20Sopenharmony_ci REG_DDRB, 728c2ecf20Sopenharmony_ci REG_DDRC, 738c2ecf20Sopenharmony_ci REG_DDRD, 748c2ecf20Sopenharmony_ci REG_TEST, 758c2ecf20Sopenharmony_ci REG_WR_ADDRL, 768c2ecf20Sopenharmony_ci REG_WR_ADDRH, 778c2ecf20Sopenharmony_ci REG_READH, 788c2ecf20Sopenharmony_ci REG_READL, 798c2ecf20Sopenharmony_ci REG_WRITEH, 808c2ecf20Sopenharmony_ci REG_WRITEL, 818c2ecf20Sopenharmony_ci REG_ID2, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* DSI D-PHY Layer Registers */ 858c2ecf20Sopenharmony_ci#define D0W_DPHYCONTTX 0x0004 868c2ecf20Sopenharmony_ci#define CLW_DPHYCONTRX 0x0020 878c2ecf20Sopenharmony_ci#define D0W_DPHYCONTRX 0x0024 888c2ecf20Sopenharmony_ci#define D1W_DPHYCONTRX 0x0028 898c2ecf20Sopenharmony_ci#define COM_DPHYCONTRX 0x0038 908c2ecf20Sopenharmony_ci#define CLW_CNTRL 0x0040 918c2ecf20Sopenharmony_ci#define D0W_CNTRL 0x0044 928c2ecf20Sopenharmony_ci#define D1W_CNTRL 0x0048 938c2ecf20Sopenharmony_ci#define DFTMODE_CNTRL 0x0054 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* DSI PPI Layer Registers */ 968c2ecf20Sopenharmony_ci#define PPI_STARTPPI 0x0104 978c2ecf20Sopenharmony_ci#define PPI_BUSYPPI 0x0108 988c2ecf20Sopenharmony_ci#define PPI_LINEINITCNT 0x0110 998c2ecf20Sopenharmony_ci#define PPI_LPTXTIMECNT 0x0114 1008c2ecf20Sopenharmony_ci#define PPI_CLS_ATMR 0x0140 1018c2ecf20Sopenharmony_ci#define PPI_D0S_ATMR 0x0144 1028c2ecf20Sopenharmony_ci#define PPI_D1S_ATMR 0x0148 1038c2ecf20Sopenharmony_ci#define PPI_D0S_CLRSIPOCOUNT 0x0164 1048c2ecf20Sopenharmony_ci#define PPI_D1S_CLRSIPOCOUNT 0x0168 1058c2ecf20Sopenharmony_ci#define CLS_PRE 0x0180 1068c2ecf20Sopenharmony_ci#define D0S_PRE 0x0184 1078c2ecf20Sopenharmony_ci#define D1S_PRE 0x0188 1088c2ecf20Sopenharmony_ci#define CLS_PREP 0x01A0 1098c2ecf20Sopenharmony_ci#define D0S_PREP 0x01A4 1108c2ecf20Sopenharmony_ci#define D1S_PREP 0x01A8 1118c2ecf20Sopenharmony_ci#define CLS_ZERO 0x01C0 1128c2ecf20Sopenharmony_ci#define D0S_ZERO 0x01C4 1138c2ecf20Sopenharmony_ci#define D1S_ZERO 0x01C8 1148c2ecf20Sopenharmony_ci#define PPI_CLRFLG 0x01E0 1158c2ecf20Sopenharmony_ci#define PPI_CLRSIPO 0x01E4 1168c2ecf20Sopenharmony_ci#define HSTIMEOUT 0x01F0 1178c2ecf20Sopenharmony_ci#define HSTIMEOUTENABLE 0x01F4 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* DSI Protocol Layer Registers */ 1208c2ecf20Sopenharmony_ci#define DSI_STARTDSI 0x0204 1218c2ecf20Sopenharmony_ci#define DSI_BUSYDSI 0x0208 1228c2ecf20Sopenharmony_ci#define DSI_LANEENABLE 0x0210 1238c2ecf20Sopenharmony_ci# define DSI_LANEENABLE_CLOCK BIT(0) 1248c2ecf20Sopenharmony_ci# define DSI_LANEENABLE_D0 BIT(1) 1258c2ecf20Sopenharmony_ci# define DSI_LANEENABLE_D1 BIT(2) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define DSI_LANESTATUS0 0x0214 1288c2ecf20Sopenharmony_ci#define DSI_LANESTATUS1 0x0218 1298c2ecf20Sopenharmony_ci#define DSI_INTSTATUS 0x0220 1308c2ecf20Sopenharmony_ci#define DSI_INTMASK 0x0224 1318c2ecf20Sopenharmony_ci#define DSI_INTCLR 0x0228 1328c2ecf20Sopenharmony_ci#define DSI_LPTXTO 0x0230 1338c2ecf20Sopenharmony_ci#define DSI_MODE 0x0260 1348c2ecf20Sopenharmony_ci#define DSI_PAYLOAD0 0x0268 1358c2ecf20Sopenharmony_ci#define DSI_PAYLOAD1 0x026C 1368c2ecf20Sopenharmony_ci#define DSI_SHORTPKTDAT 0x0270 1378c2ecf20Sopenharmony_ci#define DSI_SHORTPKTREQ 0x0274 1388c2ecf20Sopenharmony_ci#define DSI_BTASTA 0x0278 1398c2ecf20Sopenharmony_ci#define DSI_BTACLR 0x027C 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* DSI General Registers */ 1428c2ecf20Sopenharmony_ci#define DSIERRCNT 0x0300 1438c2ecf20Sopenharmony_ci#define DSISIGMOD 0x0304 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* DSI Application Layer Registers */ 1468c2ecf20Sopenharmony_ci#define APLCTRL 0x0400 1478c2ecf20Sopenharmony_ci#define APLSTAT 0x0404 1488c2ecf20Sopenharmony_ci#define APLERR 0x0408 1498c2ecf20Sopenharmony_ci#define PWRMOD 0x040C 1508c2ecf20Sopenharmony_ci#define RDPKTLN 0x0410 1518c2ecf20Sopenharmony_ci#define PXLFMT 0x0414 1528c2ecf20Sopenharmony_ci#define MEMWRCMD 0x0418 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* LCDC/DPI Host Registers */ 1558c2ecf20Sopenharmony_ci#define LCDCTRL 0x0420 1568c2ecf20Sopenharmony_ci#define HSR 0x0424 1578c2ecf20Sopenharmony_ci#define HDISPR 0x0428 1588c2ecf20Sopenharmony_ci#define VSR 0x042C 1598c2ecf20Sopenharmony_ci#define VDISPR 0x0430 1608c2ecf20Sopenharmony_ci#define VFUEN 0x0434 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* DBI-B Host Registers */ 1638c2ecf20Sopenharmony_ci#define DBIBCTRL 0x0440 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* SPI Master Registers */ 1668c2ecf20Sopenharmony_ci#define SPICMR 0x0450 1678c2ecf20Sopenharmony_ci#define SPITCR 0x0454 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* System Controller Registers */ 1708c2ecf20Sopenharmony_ci#define SYSSTAT 0x0460 1718c2ecf20Sopenharmony_ci#define SYSCTRL 0x0464 1728c2ecf20Sopenharmony_ci#define SYSPLL1 0x0468 1738c2ecf20Sopenharmony_ci#define SYSPLL2 0x046C 1748c2ecf20Sopenharmony_ci#define SYSPLL3 0x0470 1758c2ecf20Sopenharmony_ci#define SYSPMCTRL 0x047C 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* GPIO Registers */ 1788c2ecf20Sopenharmony_ci#define GPIOC 0x0480 1798c2ecf20Sopenharmony_ci#define GPIOO 0x0484 1808c2ecf20Sopenharmony_ci#define GPIOI 0x0488 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* I2C Registers */ 1838c2ecf20Sopenharmony_ci#define I2CCLKCTRL 0x0490 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* Chip/Rev Registers */ 1868c2ecf20Sopenharmony_ci#define IDREG 0x04A0 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* Debug Registers */ 1898c2ecf20Sopenharmony_ci#define WCMDQUEUE 0x0500 1908c2ecf20Sopenharmony_ci#define RCMDQUEUE 0x0504 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistruct rpi_touchscreen { 1938c2ecf20Sopenharmony_ci struct drm_panel base; 1948c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 1958c2ecf20Sopenharmony_ci struct i2c_client *i2c; 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct drm_display_mode rpi_touchscreen_modes[] = { 1998c2ecf20Sopenharmony_ci { 2008c2ecf20Sopenharmony_ci /* Modeline comes from the Raspberry Pi firmware, with HFP=1 2018c2ecf20Sopenharmony_ci * plugged in and clock re-computed from that. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci .clock = 25979400 / 1000, 2048c2ecf20Sopenharmony_ci .hdisplay = 800, 2058c2ecf20Sopenharmony_ci .hsync_start = 800 + 1, 2068c2ecf20Sopenharmony_ci .hsync_end = 800 + 1 + 2, 2078c2ecf20Sopenharmony_ci .htotal = 800 + 1 + 2 + 46, 2088c2ecf20Sopenharmony_ci .vdisplay = 480, 2098c2ecf20Sopenharmony_ci .vsync_start = 480 + 7, 2108c2ecf20Sopenharmony_ci .vsync_end = 480 + 7 + 2, 2118c2ecf20Sopenharmony_ci .vtotal = 480 + 7 + 2 + 21, 2128c2ecf20Sopenharmony_ci }, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic struct rpi_touchscreen *panel_to_ts(struct drm_panel *panel) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci return container_of(panel, struct rpi_touchscreen, base); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int rpi_touchscreen_i2c_read(struct rpi_touchscreen *ts, u8 reg) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(ts->i2c, reg); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void rpi_touchscreen_i2c_write(struct rpi_touchscreen *ts, 2268c2ecf20Sopenharmony_ci u8 reg, u8 val) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(ts->i2c, reg, val); 2318c2ecf20Sopenharmony_ci if (ret) 2328c2ecf20Sopenharmony_ci dev_err(&ts->i2c->dev, "I2C write failed: %d\n", ret); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int rpi_touchscreen_write(struct rpi_touchscreen *ts, u16 reg, u32 val) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci u8 msg[] = { 2388c2ecf20Sopenharmony_ci reg, 2398c2ecf20Sopenharmony_ci reg >> 8, 2408c2ecf20Sopenharmony_ci val, 2418c2ecf20Sopenharmony_ci val >> 8, 2428c2ecf20Sopenharmony_ci val >> 16, 2438c2ecf20Sopenharmony_ci val >> 24, 2448c2ecf20Sopenharmony_ci }; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mipi_dsi_generic_write(ts->dsi, msg, sizeof(msg)); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int rpi_touchscreen_disable(struct drm_panel *panel) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct rpi_touchscreen *ts = panel_to_ts(panel); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_PWM, 0); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_POWERON, 0); 2588c2ecf20Sopenharmony_ci udelay(1); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int rpi_touchscreen_noop(struct drm_panel *panel) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int rpi_touchscreen_prepare(struct drm_panel *panel) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct rpi_touchscreen *ts = panel_to_ts(panel); 2718c2ecf20Sopenharmony_ci int i; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_POWERON, 1); 2748c2ecf20Sopenharmony_ci /* Wait for nPWRDWN to go low to indicate poweron is done. */ 2758c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 2768c2ecf20Sopenharmony_ci if (rpi_touchscreen_i2c_read(ts, REG_PORTB) & 1) 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, DSI_LANEENABLE, 2818c2ecf20Sopenharmony_ci DSI_LANEENABLE_CLOCK | 2828c2ecf20Sopenharmony_ci DSI_LANEENABLE_D0); 2838c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_D0S_CLRSIPOCOUNT, 0x05); 2848c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_D1S_CLRSIPOCOUNT, 0x05); 2858c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_D0S_ATMR, 0x00); 2868c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_D1S_ATMR, 0x00); 2878c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_LPTXTIMECNT, 0x03); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, SPICMR, 0x00); 2908c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, LCDCTRL, 0x00100150); 2918c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, SYSCTRL, 0x040f); 2928c2ecf20Sopenharmony_ci msleep(100); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, PPI_STARTPPI, 0x01); 2958c2ecf20Sopenharmony_ci rpi_touchscreen_write(ts, DSI_STARTDSI, 0x01); 2968c2ecf20Sopenharmony_ci msleep(100); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int rpi_touchscreen_enable(struct drm_panel *panel) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct rpi_touchscreen *ts = panel_to_ts(panel); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Turn on the backlight. */ 3068c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_PWM, 255); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Default to the same orientation as the closed source 3098c2ecf20Sopenharmony_ci * firmware used for the panel. Runtime rotation 3108c2ecf20Sopenharmony_ci * configuration will be supported using VC4's plane 3118c2ecf20Sopenharmony_ci * orientation bits. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_PORTA, BIT(2)); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int rpi_touchscreen_get_modes(struct drm_panel *panel, 3198c2ecf20Sopenharmony_ci struct drm_connector *connector) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci unsigned int i, num = 0; 3228c2ecf20Sopenharmony_ci static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rpi_touchscreen_modes); i++) { 3258c2ecf20Sopenharmony_ci const struct drm_display_mode *m = &rpi_touchscreen_modes[i]; 3268c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, m); 3298c2ecf20Sopenharmony_ci if (!mode) { 3308c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 3318c2ecf20Sopenharmony_ci m->hdisplay, m->vdisplay, 3328c2ecf20Sopenharmony_ci drm_mode_vrefresh(m)); 3338c2ecf20Sopenharmony_ci continue; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mode->type |= DRM_MODE_TYPE_DRIVER; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (i == 0) 3398c2ecf20Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 3448c2ecf20Sopenharmony_ci num++; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci connector->display_info.bpc = 8; 3488c2ecf20Sopenharmony_ci connector->display_info.width_mm = 154; 3498c2ecf20Sopenharmony_ci connector->display_info.height_mm = 86; 3508c2ecf20Sopenharmony_ci drm_display_info_set_bus_formats(&connector->display_info, 3518c2ecf20Sopenharmony_ci &bus_format, 1); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return num; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs rpi_touchscreen_funcs = { 3578c2ecf20Sopenharmony_ci .disable = rpi_touchscreen_disable, 3588c2ecf20Sopenharmony_ci .unprepare = rpi_touchscreen_noop, 3598c2ecf20Sopenharmony_ci .prepare = rpi_touchscreen_prepare, 3608c2ecf20Sopenharmony_ci .enable = rpi_touchscreen_enable, 3618c2ecf20Sopenharmony_ci .get_modes = rpi_touchscreen_get_modes, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int rpi_touchscreen_probe(struct i2c_client *i2c, 3658c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct device *dev = &i2c->dev; 3688c2ecf20Sopenharmony_ci struct rpi_touchscreen *ts; 3698c2ecf20Sopenharmony_ci struct device_node *endpoint, *dsi_host_node; 3708c2ecf20Sopenharmony_ci struct mipi_dsi_host *host; 3718c2ecf20Sopenharmony_ci int ver; 3728c2ecf20Sopenharmony_ci struct mipi_dsi_device_info info = { 3738c2ecf20Sopenharmony_ci .type = RPI_DSI_DRIVER_NAME, 3748c2ecf20Sopenharmony_ci .channel = 0, 3758c2ecf20Sopenharmony_ci .node = NULL, 3768c2ecf20Sopenharmony_ci }; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); 3798c2ecf20Sopenharmony_ci if (!ts) 3808c2ecf20Sopenharmony_ci return -ENOMEM; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci i2c_set_clientdata(i2c, ts); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ts->i2c = i2c; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ver = rpi_touchscreen_i2c_read(ts, REG_ID); 3878c2ecf20Sopenharmony_ci if (ver < 0) { 3888c2ecf20Sopenharmony_ci dev_err(dev, "Atmel I2C read failed: %d\n", ver); 3898c2ecf20Sopenharmony_ci return -ENODEV; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (ver) { 3938c2ecf20Sopenharmony_ci case 0xde: /* ver 1 */ 3948c2ecf20Sopenharmony_ci case 0xc3: /* ver 2 */ 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci dev_err(dev, "Unknown Atmel firmware revision: 0x%02x\n", ver); 3988c2ecf20Sopenharmony_ci return -ENODEV; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Turn off at boot, so we can cleanly sequence powering on. */ 4028c2ecf20Sopenharmony_ci rpi_touchscreen_i2c_write(ts, REG_POWERON, 0); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Look up the DSI host. It needs to probe before we do. */ 4058c2ecf20Sopenharmony_ci endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); 4068c2ecf20Sopenharmony_ci if (!endpoint) 4078c2ecf20Sopenharmony_ci return -ENODEV; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci dsi_host_node = of_graph_get_remote_port_parent(endpoint); 4108c2ecf20Sopenharmony_ci if (!dsi_host_node) 4118c2ecf20Sopenharmony_ci goto error; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci host = of_find_mipi_dsi_host_by_node(dsi_host_node); 4148c2ecf20Sopenharmony_ci of_node_put(dsi_host_node); 4158c2ecf20Sopenharmony_ci if (!host) { 4168c2ecf20Sopenharmony_ci of_node_put(endpoint); 4178c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci info.node = of_graph_get_remote_port(endpoint); 4218c2ecf20Sopenharmony_ci if (!info.node) 4228c2ecf20Sopenharmony_ci goto error; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci of_node_put(endpoint); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ts->dsi = mipi_dsi_device_register_full(host, &info); 4278c2ecf20Sopenharmony_ci if (IS_ERR(ts->dsi)) { 4288c2ecf20Sopenharmony_ci dev_err(dev, "DSI device registration failed: %ld\n", 4298c2ecf20Sopenharmony_ci PTR_ERR(ts->dsi)); 4308c2ecf20Sopenharmony_ci return PTR_ERR(ts->dsi); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci drm_panel_init(&ts->base, dev, &rpi_touchscreen_funcs, 4348c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* This appears last, as it's what will unblock the DSI host 4378c2ecf20Sopenharmony_ci * driver's component bind function. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci drm_panel_add(&ts->base); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cierror: 4448c2ecf20Sopenharmony_ci of_node_put(endpoint); 4458c2ecf20Sopenharmony_ci return -ENODEV; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int rpi_touchscreen_remove(struct i2c_client *i2c) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct rpi_touchscreen *ts = i2c_get_clientdata(i2c); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci mipi_dsi_detach(ts->dsi); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci drm_panel_remove(&ts->base); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci mipi_dsi_device_unregister(ts->dsi); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int rpi_touchscreen_dsi_probe(struct mipi_dsi_device *dsi) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci int ret; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | 4668c2ecf20Sopenharmony_ci MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 4678c2ecf20Sopenharmony_ci MIPI_DSI_MODE_LPM); 4688c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 4698c2ecf20Sopenharmony_ci dsi->lanes = 1; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "failed to attach dsi to host: %d\n", ret); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver rpi_touchscreen_dsi_driver = { 4808c2ecf20Sopenharmony_ci .driver.name = RPI_DSI_DRIVER_NAME, 4818c2ecf20Sopenharmony_ci .probe = rpi_touchscreen_dsi_probe, 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic const struct of_device_id rpi_touchscreen_of_ids[] = { 4858c2ecf20Sopenharmony_ci { .compatible = "raspberrypi,7inch-touchscreen-panel" }, 4868c2ecf20Sopenharmony_ci { } /* sentinel */ 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rpi_touchscreen_of_ids); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic struct i2c_driver rpi_touchscreen_driver = { 4918c2ecf20Sopenharmony_ci .driver = { 4928c2ecf20Sopenharmony_ci .name = "rpi_touchscreen", 4938c2ecf20Sopenharmony_ci .of_match_table = rpi_touchscreen_of_ids, 4948c2ecf20Sopenharmony_ci }, 4958c2ecf20Sopenharmony_ci .probe = rpi_touchscreen_probe, 4968c2ecf20Sopenharmony_ci .remove = rpi_touchscreen_remove, 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int __init rpi_touchscreen_init(void) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci mipi_dsi_driver_register(&rpi_touchscreen_dsi_driver); 5028c2ecf20Sopenharmony_ci return i2c_add_driver(&rpi_touchscreen_driver); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_cimodule_init(rpi_touchscreen_init); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void __exit rpi_touchscreen_exit(void) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci i2c_del_driver(&rpi_touchscreen_driver); 5098c2ecf20Sopenharmony_ci mipi_dsi_driver_unregister(&rpi_touchscreen_dsi_driver); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_cimodule_exit(rpi_touchscreen_exit); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); 5148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Raspberry Pi 7-inch touchscreen driver"); 5158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 516