xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Toppoly TD043MTEA1 Panel Driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2019 Texas Instruments Incorporated
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Based on the omapdrm-specific panel-tpo-td043mtea1 driver
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Author: Gražvydas Ignotas <notasas@gmail.com>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
15*4882a593Smuzhiyun #include <linux/spi/spi.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <drm/drm_connector.h>
18*4882a593Smuzhiyun #include <drm/drm_modes.h>
19*4882a593Smuzhiyun #include <drm/drm_panel.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define TPO_R02_MODE(x)			((x) & 7)
22*4882a593Smuzhiyun #define TPO_R02_MODE_800x480		7
23*4882a593Smuzhiyun #define TPO_R02_NCLK_RISING		BIT(3)
24*4882a593Smuzhiyun #define TPO_R02_HSYNC_HIGH		BIT(4)
25*4882a593Smuzhiyun #define TPO_R02_VSYNC_HIGH		BIT(5)
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define TPO_R03_NSTANDBY		BIT(0)
28*4882a593Smuzhiyun #define TPO_R03_EN_CP_CLK		BIT(1)
29*4882a593Smuzhiyun #define TPO_R03_EN_VGL_PUMP		BIT(2)
30*4882a593Smuzhiyun #define TPO_R03_EN_PWM			BIT(3)
31*4882a593Smuzhiyun #define TPO_R03_DRIVING_CAP_100		BIT(4)
32*4882a593Smuzhiyun #define TPO_R03_EN_PRE_CHARGE		BIT(6)
33*4882a593Smuzhiyun #define TPO_R03_SOFTWARE_CTL		BIT(7)
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define TPO_R04_NFLIP_H			BIT(0)
36*4882a593Smuzhiyun #define TPO_R04_NFLIP_V			BIT(1)
37*4882a593Smuzhiyun #define TPO_R04_CP_CLK_FREQ_1H		BIT(2)
38*4882a593Smuzhiyun #define TPO_R04_VGL_FREQ_1H		BIT(4)
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define TPO_R03_VAL_NORMAL \
41*4882a593Smuzhiyun 	(TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | TPO_R03_EN_VGL_PUMP | \
42*4882a593Smuzhiyun 	 TPO_R03_EN_PWM | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
43*4882a593Smuzhiyun 	 TPO_R03_SOFTWARE_CTL)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define TPO_R03_VAL_STANDBY \
46*4882a593Smuzhiyun 	(TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
47*4882a593Smuzhiyun 	 TPO_R03_SOFTWARE_CTL)
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun static const u16 td043mtea1_def_gamma[12] = {
50*4882a593Smuzhiyun 	105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun struct td043mtea1_panel {
54*4882a593Smuzhiyun 	struct drm_panel panel;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	struct spi_device *spi;
57*4882a593Smuzhiyun 	struct regulator *vcc_reg;
58*4882a593Smuzhiyun 	struct gpio_desc *reset_gpio;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	unsigned int mode;
61*4882a593Smuzhiyun 	u16 gamma[12];
62*4882a593Smuzhiyun 	bool vmirror;
63*4882a593Smuzhiyun 	bool powered_on;
64*4882a593Smuzhiyun 	bool spi_suspended;
65*4882a593Smuzhiyun 	bool power_on_resume;
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun #define to_td043mtea1_device(p) container_of(p, struct td043mtea1_panel, panel)
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
71*4882a593Smuzhiyun  * Hardware Access
72*4882a593Smuzhiyun  */
73*4882a593Smuzhiyun 
td043mtea1_write(struct td043mtea1_panel * lcd,u8 addr,u8 value)74*4882a593Smuzhiyun static int td043mtea1_write(struct td043mtea1_panel *lcd, u8 addr, u8 value)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct spi_message msg;
77*4882a593Smuzhiyun 	struct spi_transfer xfer;
78*4882a593Smuzhiyun 	u16 data;
79*4882a593Smuzhiyun 	int ret;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	spi_message_init(&msg);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	memset(&xfer, 0, sizeof(xfer));
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	data = ((u16)addr << 10) | (1 << 8) | value;
86*4882a593Smuzhiyun 	xfer.tx_buf = &data;
87*4882a593Smuzhiyun 	xfer.bits_per_word = 16;
88*4882a593Smuzhiyun 	xfer.len = 2;
89*4882a593Smuzhiyun 	spi_message_add_tail(&xfer, &msg);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	ret = spi_sync(lcd->spi, &msg);
92*4882a593Smuzhiyun 	if (ret < 0)
93*4882a593Smuzhiyun 		dev_warn(&lcd->spi->dev, "failed to write to LCD reg (%d)\n",
94*4882a593Smuzhiyun 			 ret);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	return ret;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
td043mtea1_write_gamma(struct td043mtea1_panel * lcd)99*4882a593Smuzhiyun static void td043mtea1_write_gamma(struct td043mtea1_panel *lcd)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	const u16 *gamma = lcd->gamma;
102*4882a593Smuzhiyun 	unsigned int i;
103*4882a593Smuzhiyun 	u8 val;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	/* gamma bits [9:8] */
106*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
107*4882a593Smuzhiyun 		val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
108*4882a593Smuzhiyun 	td043mtea1_write(lcd, 0x11, val);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
111*4882a593Smuzhiyun 		val |= (gamma[i + 4] & 0x300) >> ((i + 1) * 2);
112*4882a593Smuzhiyun 	td043mtea1_write(lcd, 0x12, val);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
115*4882a593Smuzhiyun 		val |= (gamma[i + 8] & 0x300) >> ((i + 1) * 2);
116*4882a593Smuzhiyun 	td043mtea1_write(lcd, 0x13, val);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	/* gamma bits [7:0] */
119*4882a593Smuzhiyun 	for (i = 0; i < 12; i++)
120*4882a593Smuzhiyun 		td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
td043mtea1_write_mirror(struct td043mtea1_panel * lcd)123*4882a593Smuzhiyun static int td043mtea1_write_mirror(struct td043mtea1_panel *lcd)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
126*4882a593Smuzhiyun 		TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
127*4882a593Smuzhiyun 	if (lcd->vmirror)
128*4882a593Smuzhiyun 		reg4 &= ~TPO_R04_NFLIP_V;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	return td043mtea1_write(lcd, 4, reg4);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
td043mtea1_power_on(struct td043mtea1_panel * lcd)133*4882a593Smuzhiyun static int td043mtea1_power_on(struct td043mtea1_panel *lcd)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	int ret;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (lcd->powered_on)
138*4882a593Smuzhiyun 		return 0;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	ret = regulator_enable(lcd->vcc_reg);
141*4882a593Smuzhiyun 	if (ret < 0)
142*4882a593Smuzhiyun 		return ret;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	/* Wait for the panel to stabilize. */
145*4882a593Smuzhiyun 	msleep(160);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	gpiod_set_value(lcd->reset_gpio, 0);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	td043mtea1_write(lcd, 2, TPO_R02_MODE(lcd->mode) | TPO_R02_NCLK_RISING);
150*4882a593Smuzhiyun 	td043mtea1_write(lcd, 3, TPO_R03_VAL_NORMAL);
151*4882a593Smuzhiyun 	td043mtea1_write(lcd, 0x20, 0xf0);
152*4882a593Smuzhiyun 	td043mtea1_write(lcd, 0x21, 0xf0);
153*4882a593Smuzhiyun 	td043mtea1_write_mirror(lcd);
154*4882a593Smuzhiyun 	td043mtea1_write_gamma(lcd);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	lcd->powered_on = true;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	return 0;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
td043mtea1_power_off(struct td043mtea1_panel * lcd)161*4882a593Smuzhiyun static void td043mtea1_power_off(struct td043mtea1_panel *lcd)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	if (!lcd->powered_on)
164*4882a593Smuzhiyun 		return;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	gpiod_set_value(lcd->reset_gpio, 1);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* wait for at least 2 vsyncs before cutting off power */
171*4882a593Smuzhiyun 	msleep(50);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	regulator_disable(lcd->vcc_reg);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	lcd->powered_on = false;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
181*4882a593Smuzhiyun  * sysfs
182*4882a593Smuzhiyun  */
183*4882a593Smuzhiyun 
vmirror_show(struct device * dev,struct device_attribute * attr,char * buf)184*4882a593Smuzhiyun static ssize_t vmirror_show(struct device *dev, struct device_attribute *attr,
185*4882a593Smuzhiyun 			    char *buf)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%d\n", lcd->vmirror);
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
vmirror_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)192*4882a593Smuzhiyun static ssize_t vmirror_store(struct device *dev, struct device_attribute *attr,
193*4882a593Smuzhiyun 			     const char *buf, size_t count)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
196*4882a593Smuzhiyun 	int val;
197*4882a593Smuzhiyun 	int ret;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	ret = kstrtoint(buf, 0, &val);
200*4882a593Smuzhiyun 	if (ret < 0)
201*4882a593Smuzhiyun 		return ret;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	lcd->vmirror = !!val;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	ret = td043mtea1_write_mirror(lcd);
206*4882a593Smuzhiyun 	if (ret < 0)
207*4882a593Smuzhiyun 		return ret;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return count;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
mode_show(struct device * dev,struct device_attribute * attr,char * buf)212*4882a593Smuzhiyun static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
213*4882a593Smuzhiyun 			 char *buf)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%d\n", lcd->mode);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)220*4882a593Smuzhiyun static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
221*4882a593Smuzhiyun 			  const char *buf, size_t count)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
224*4882a593Smuzhiyun 	long val;
225*4882a593Smuzhiyun 	int ret;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	ret = kstrtol(buf, 0, &val);
228*4882a593Smuzhiyun 	if (ret != 0 || val & ~7)
229*4882a593Smuzhiyun 		return -EINVAL;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	lcd->mode = val;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	val |= TPO_R02_NCLK_RISING;
234*4882a593Smuzhiyun 	td043mtea1_write(lcd, 2, val);
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	return count;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun 
gamma_show(struct device * dev,struct device_attribute * attr,char * buf)239*4882a593Smuzhiyun static ssize_t gamma_show(struct device *dev, struct device_attribute *attr,
240*4882a593Smuzhiyun 			  char *buf)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
243*4882a593Smuzhiyun 	ssize_t len = 0;
244*4882a593Smuzhiyun 	unsigned int i;
245*4882a593Smuzhiyun 	int ret;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(lcd->gamma); i++) {
248*4882a593Smuzhiyun 		ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
249*4882a593Smuzhiyun 			       lcd->gamma[i]);
250*4882a593Smuzhiyun 		if (ret < 0)
251*4882a593Smuzhiyun 			return ret;
252*4882a593Smuzhiyun 		len += ret;
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 	buf[len - 1] = '\n';
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	return len;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
gamma_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)259*4882a593Smuzhiyun static ssize_t gamma_store(struct device *dev, struct device_attribute *attr,
260*4882a593Smuzhiyun 			   const char *buf, size_t count)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
263*4882a593Smuzhiyun 	unsigned int g[12];
264*4882a593Smuzhiyun 	unsigned int i;
265*4882a593Smuzhiyun 	int ret;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
268*4882a593Smuzhiyun 		     &g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
269*4882a593Smuzhiyun 		     &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
270*4882a593Smuzhiyun 	if (ret != 12)
271*4882a593Smuzhiyun 		return -EINVAL;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	for (i = 0; i < 12; i++)
274*4882a593Smuzhiyun 		lcd->gamma[i] = g[i];
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	td043mtea1_write_gamma(lcd);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	return count;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun static DEVICE_ATTR_RW(vmirror);
282*4882a593Smuzhiyun static DEVICE_ATTR_RW(mode);
283*4882a593Smuzhiyun static DEVICE_ATTR_RW(gamma);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun static struct attribute *td043mtea1_attrs[] = {
286*4882a593Smuzhiyun 	&dev_attr_vmirror.attr,
287*4882a593Smuzhiyun 	&dev_attr_mode.attr,
288*4882a593Smuzhiyun 	&dev_attr_gamma.attr,
289*4882a593Smuzhiyun 	NULL,
290*4882a593Smuzhiyun };
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun static const struct attribute_group td043mtea1_attr_group = {
293*4882a593Smuzhiyun 	.attrs = td043mtea1_attrs,
294*4882a593Smuzhiyun };
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
297*4882a593Smuzhiyun  * Panel Operations
298*4882a593Smuzhiyun  */
299*4882a593Smuzhiyun 
td043mtea1_unprepare(struct drm_panel * panel)300*4882a593Smuzhiyun static int td043mtea1_unprepare(struct drm_panel *panel)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	if (!lcd->spi_suspended)
305*4882a593Smuzhiyun 		td043mtea1_power_off(lcd);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	return 0;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun 
td043mtea1_prepare(struct drm_panel * panel)310*4882a593Smuzhiyun static int td043mtea1_prepare(struct drm_panel *panel)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
313*4882a593Smuzhiyun 	int ret;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	/*
316*4882a593Smuzhiyun 	 * If we are resuming from system suspend, SPI might not be enabled
317*4882a593Smuzhiyun 	 * yet, so we'll program the LCD from SPI PM resume callback.
318*4882a593Smuzhiyun 	 */
319*4882a593Smuzhiyun 	if (lcd->spi_suspended)
320*4882a593Smuzhiyun 		return 0;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	ret = td043mtea1_power_on(lcd);
323*4882a593Smuzhiyun 	if (ret) {
324*4882a593Smuzhiyun 		dev_err(&lcd->spi->dev, "%s: power on failed (%d)\n",
325*4882a593Smuzhiyun 			__func__, ret);
326*4882a593Smuzhiyun 		return ret;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	return 0;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun static const struct drm_display_mode td043mtea1_mode = {
333*4882a593Smuzhiyun 	.clock = 36000,
334*4882a593Smuzhiyun 	.hdisplay = 800,
335*4882a593Smuzhiyun 	.hsync_start = 800 + 68,
336*4882a593Smuzhiyun 	.hsync_end = 800 + 68 + 1,
337*4882a593Smuzhiyun 	.htotal = 800 + 68 + 1 + 214,
338*4882a593Smuzhiyun 	.vdisplay = 480,
339*4882a593Smuzhiyun 	.vsync_start = 480 + 39,
340*4882a593Smuzhiyun 	.vsync_end = 480 + 39 + 1,
341*4882a593Smuzhiyun 	.vtotal = 480 + 39 + 1 + 34,
342*4882a593Smuzhiyun 	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
343*4882a593Smuzhiyun 	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
344*4882a593Smuzhiyun 	.width_mm = 94,
345*4882a593Smuzhiyun 	.height_mm = 56,
346*4882a593Smuzhiyun };
347*4882a593Smuzhiyun 
td043mtea1_get_modes(struct drm_panel * panel,struct drm_connector * connector)348*4882a593Smuzhiyun static int td043mtea1_get_modes(struct drm_panel *panel,
349*4882a593Smuzhiyun 				struct drm_connector *connector)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun 	struct drm_display_mode *mode;
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	mode = drm_mode_duplicate(connector->dev, &td043mtea1_mode);
354*4882a593Smuzhiyun 	if (!mode)
355*4882a593Smuzhiyun 		return -ENOMEM;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	drm_mode_set_name(mode);
358*4882a593Smuzhiyun 	drm_mode_probed_add(connector, mode);
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	connector->display_info.width_mm = td043mtea1_mode.width_mm;
361*4882a593Smuzhiyun 	connector->display_info.height_mm = td043mtea1_mode.height_mm;
362*4882a593Smuzhiyun 	/*
363*4882a593Smuzhiyun 	 * FIXME: According to the datasheet sync signals are sampled on the
364*4882a593Smuzhiyun 	 * rising edge of the clock, but the code running on the OMAP3 Pandora
365*4882a593Smuzhiyun 	 * indicates sampling on the falling edge. This should be tested on a
366*4882a593Smuzhiyun 	 * real device.
367*4882a593Smuzhiyun 	 */
368*4882a593Smuzhiyun 	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
369*4882a593Smuzhiyun 					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
370*4882a593Smuzhiyun 					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return 1;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun static const struct drm_panel_funcs td043mtea1_funcs = {
376*4882a593Smuzhiyun 	.unprepare = td043mtea1_unprepare,
377*4882a593Smuzhiyun 	.prepare = td043mtea1_prepare,
378*4882a593Smuzhiyun 	.get_modes = td043mtea1_get_modes,
379*4882a593Smuzhiyun };
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
382*4882a593Smuzhiyun  * Power Management, Probe and Remove
383*4882a593Smuzhiyun  */
384*4882a593Smuzhiyun 
td043mtea1_suspend(struct device * dev)385*4882a593Smuzhiyun static int __maybe_unused td043mtea1_suspend(struct device *dev)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	if (lcd->powered_on) {
390*4882a593Smuzhiyun 		td043mtea1_power_off(lcd);
391*4882a593Smuzhiyun 		lcd->powered_on = true;
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	lcd->spi_suspended = true;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	return 0;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun 
td043mtea1_resume(struct device * dev)399*4882a593Smuzhiyun static int __maybe_unused td043mtea1_resume(struct device *dev)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
402*4882a593Smuzhiyun 	int ret;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	lcd->spi_suspended = false;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	if (lcd->powered_on) {
407*4882a593Smuzhiyun 		lcd->powered_on = false;
408*4882a593Smuzhiyun 		ret = td043mtea1_power_on(lcd);
409*4882a593Smuzhiyun 		if (ret)
410*4882a593Smuzhiyun 			return ret;
411*4882a593Smuzhiyun 	}
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	return 0;
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend,
417*4882a593Smuzhiyun 			 td043mtea1_resume);
418*4882a593Smuzhiyun 
td043mtea1_probe(struct spi_device * spi)419*4882a593Smuzhiyun static int td043mtea1_probe(struct spi_device *spi)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd;
422*4882a593Smuzhiyun 	int ret;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
425*4882a593Smuzhiyun 	if (lcd == NULL)
426*4882a593Smuzhiyun 		return -ENOMEM;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	spi_set_drvdata(spi, lcd);
429*4882a593Smuzhiyun 	lcd->spi = spi;
430*4882a593Smuzhiyun 	lcd->mode = TPO_R02_MODE_800x480;
431*4882a593Smuzhiyun 	memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma));
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
434*4882a593Smuzhiyun 	if (IS_ERR(lcd->vcc_reg)) {
435*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to get VCC regulator\n");
436*4882a593Smuzhiyun 		return PTR_ERR(lcd->vcc_reg);
437*4882a593Smuzhiyun 	}
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
440*4882a593Smuzhiyun 	if (IS_ERR(lcd->reset_gpio)) {
441*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to get reset GPIO\n");
442*4882a593Smuzhiyun 		return PTR_ERR(lcd->reset_gpio);
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	spi->bits_per_word = 16;
446*4882a593Smuzhiyun 	spi->mode = SPI_MODE_0;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	ret = spi_setup(spi);
449*4882a593Smuzhiyun 	if (ret < 0) {
450*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
451*4882a593Smuzhiyun 		return ret;
452*4882a593Smuzhiyun 	}
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group);
455*4882a593Smuzhiyun 	if (ret < 0) {
456*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to create sysfs files\n");
457*4882a593Smuzhiyun 		return ret;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	drm_panel_init(&lcd->panel, &lcd->spi->dev, &td043mtea1_funcs,
461*4882a593Smuzhiyun 		       DRM_MODE_CONNECTOR_DPI);
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	drm_panel_add(&lcd->panel);
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	return 0;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
td043mtea1_remove(struct spi_device * spi)468*4882a593Smuzhiyun static int td043mtea1_remove(struct spi_device *spi)
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun 	struct td043mtea1_panel *lcd = spi_get_drvdata(spi);
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	drm_panel_remove(&lcd->panel);
473*4882a593Smuzhiyun 	drm_panel_disable(&lcd->panel);
474*4882a593Smuzhiyun 	drm_panel_unprepare(&lcd->panel);
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	return 0;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun static const struct of_device_id td043mtea1_of_match[] = {
482*4882a593Smuzhiyun 	{ .compatible = "tpo,td043mtea1", },
483*4882a593Smuzhiyun 	{ /* sentinel */ },
484*4882a593Smuzhiyun };
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, td043mtea1_of_match);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun static const struct spi_device_id td043mtea1_ids[] = {
489*4882a593Smuzhiyun 	{ "td043mtea1", 0 },
490*4882a593Smuzhiyun 	{ /* sentinel */ }
491*4882a593Smuzhiyun };
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun MODULE_DEVICE_TABLE(spi, td043mtea1_ids);
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun static struct spi_driver td043mtea1_driver = {
496*4882a593Smuzhiyun 	.probe		= td043mtea1_probe,
497*4882a593Smuzhiyun 	.remove		= td043mtea1_remove,
498*4882a593Smuzhiyun 	.id_table	= td043mtea1_ids,
499*4882a593Smuzhiyun 	.driver		= {
500*4882a593Smuzhiyun 		.name	= "panel-tpo-td043mtea1",
501*4882a593Smuzhiyun 		.pm	= &td043mtea1_pm_ops,
502*4882a593Smuzhiyun 		.of_match_table = td043mtea1_of_match,
503*4882a593Smuzhiyun 	},
504*4882a593Smuzhiyun };
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun module_spi_driver(td043mtea1_driver);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
509*4882a593Smuzhiyun MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver");
510*4882a593Smuzhiyun MODULE_LICENSE("GPL");
511