xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * TPO TD043MTEA1 Panel driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Author: Gražvydas Ignotas <notasas@gmail.com>
6*4882a593Smuzhiyun  * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/spi/spi.h>
12*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
13*4882a593Smuzhiyun #include <linux/gpio.h>
14*4882a593Smuzhiyun #include <linux/err.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/of_gpio.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <video/omapfb_dss.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define TPO_R02_MODE(x)		((x) & 7)
21*4882a593Smuzhiyun #define TPO_R02_MODE_800x480	7
22*4882a593Smuzhiyun #define TPO_R02_NCLK_RISING	BIT(3)
23*4882a593Smuzhiyun #define TPO_R02_HSYNC_HIGH	BIT(4)
24*4882a593Smuzhiyun #define TPO_R02_VSYNC_HIGH	BIT(5)
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define TPO_R03_NSTANDBY	BIT(0)
27*4882a593Smuzhiyun #define TPO_R03_EN_CP_CLK	BIT(1)
28*4882a593Smuzhiyun #define TPO_R03_EN_VGL_PUMP	BIT(2)
29*4882a593Smuzhiyun #define TPO_R03_EN_PWM		BIT(3)
30*4882a593Smuzhiyun #define TPO_R03_DRIVING_CAP_100	BIT(4)
31*4882a593Smuzhiyun #define TPO_R03_EN_PRE_CHARGE	BIT(6)
32*4882a593Smuzhiyun #define TPO_R03_SOFTWARE_CTL	BIT(7)
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define TPO_R04_NFLIP_H		BIT(0)
35*4882a593Smuzhiyun #define TPO_R04_NFLIP_V		BIT(1)
36*4882a593Smuzhiyun #define TPO_R04_CP_CLK_FREQ_1H	BIT(2)
37*4882a593Smuzhiyun #define TPO_R04_VGL_FREQ_1H	BIT(4)
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
40*4882a593Smuzhiyun 			TPO_R03_EN_VGL_PUMP |  TPO_R03_EN_PWM | \
41*4882a593Smuzhiyun 			TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
42*4882a593Smuzhiyun 			TPO_R03_SOFTWARE_CTL)
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun #define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
45*4882a593Smuzhiyun 			TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun static const u16 tpo_td043_def_gamma[12] = {
48*4882a593Smuzhiyun 	105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
49*4882a593Smuzhiyun };
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun struct panel_drv_data {
52*4882a593Smuzhiyun 	struct omap_dss_device	dssdev;
53*4882a593Smuzhiyun 	struct omap_dss_device *in;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	struct omap_video_timings videomode;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	int data_lines;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	struct spi_device *spi;
60*4882a593Smuzhiyun 	struct regulator *vcc_reg;
61*4882a593Smuzhiyun 	int nreset_gpio;
62*4882a593Smuzhiyun 	u16 gamma[12];
63*4882a593Smuzhiyun 	u32 mode;
64*4882a593Smuzhiyun 	u32 hmirror:1;
65*4882a593Smuzhiyun 	u32 vmirror:1;
66*4882a593Smuzhiyun 	u32 powered_on:1;
67*4882a593Smuzhiyun 	u32 spi_suspended:1;
68*4882a593Smuzhiyun 	u32 power_on_resume:1;
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun static const struct omap_video_timings tpo_td043_timings = {
72*4882a593Smuzhiyun 	.x_res		= 800,
73*4882a593Smuzhiyun 	.y_res		= 480,
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	.pixelclock	= 36000000,
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	.hsw		= 1,
78*4882a593Smuzhiyun 	.hfp		= 68,
79*4882a593Smuzhiyun 	.hbp		= 214,
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	.vsw		= 1,
82*4882a593Smuzhiyun 	.vfp		= 39,
83*4882a593Smuzhiyun 	.vbp		= 34,
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
86*4882a593Smuzhiyun 	.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
87*4882a593Smuzhiyun 	.data_pclk_edge	= OMAPDSS_DRIVE_SIG_FALLING_EDGE,
88*4882a593Smuzhiyun 	.de_level	= OMAPDSS_SIG_ACTIVE_HIGH,
89*4882a593Smuzhiyun 	.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_RISING_EDGE,
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
93*4882a593Smuzhiyun 
tpo_td043_write(struct spi_device * spi,u8 addr,u8 data)94*4882a593Smuzhiyun static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun 	struct spi_message	m;
97*4882a593Smuzhiyun 	struct spi_transfer	xfer;
98*4882a593Smuzhiyun 	u16			w;
99*4882a593Smuzhiyun 	int			r;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	spi_message_init(&m);
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	memset(&xfer, 0, sizeof(xfer));
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	w = ((u16)addr << 10) | (1 << 8) | data;
106*4882a593Smuzhiyun 	xfer.tx_buf = &w;
107*4882a593Smuzhiyun 	xfer.bits_per_word = 16;
108*4882a593Smuzhiyun 	xfer.len = 2;
109*4882a593Smuzhiyun 	spi_message_add_tail(&xfer, &m);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	r = spi_sync(spi, &m);
112*4882a593Smuzhiyun 	if (r < 0)
113*4882a593Smuzhiyun 		dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
114*4882a593Smuzhiyun 	return r;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
tpo_td043_write_gamma(struct spi_device * spi,u16 gamma[12])117*4882a593Smuzhiyun static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	u8 i, val;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	/* gamma bits [9:8] */
122*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
123*4882a593Smuzhiyun 		val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
124*4882a593Smuzhiyun 	tpo_td043_write(spi, 0x11, val);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
127*4882a593Smuzhiyun 		val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
128*4882a593Smuzhiyun 	tpo_td043_write(spi, 0x12, val);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	for (val = i = 0; i < 4; i++)
131*4882a593Smuzhiyun 		val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
132*4882a593Smuzhiyun 	tpo_td043_write(spi, 0x13, val);
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	/* gamma bits [7:0] */
135*4882a593Smuzhiyun 	for (val = i = 0; i < 12; i++)
136*4882a593Smuzhiyun 		tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
tpo_td043_write_mirror(struct spi_device * spi,bool h,bool v)139*4882a593Smuzhiyun static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
142*4882a593Smuzhiyun 		TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
143*4882a593Smuzhiyun 	if (h)
144*4882a593Smuzhiyun 		reg4 &= ~TPO_R04_NFLIP_H;
145*4882a593Smuzhiyun 	if (v)
146*4882a593Smuzhiyun 		reg4 &= ~TPO_R04_NFLIP_V;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return tpo_td043_write(spi, 4, reg4);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
tpo_td043_set_hmirror(struct omap_dss_device * dssdev,bool enable)151*4882a593Smuzhiyun static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	ddata->hmirror = enable;
156*4882a593Smuzhiyun 	return tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
157*4882a593Smuzhiyun 			ddata->vmirror);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
tpo_td043_get_hmirror(struct omap_dss_device * dssdev)160*4882a593Smuzhiyun static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	return ddata->hmirror;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun 
tpo_td043_vmirror_show(struct device * dev,struct device_attribute * attr,char * buf)167*4882a593Smuzhiyun static ssize_t tpo_td043_vmirror_show(struct device *dev,
168*4882a593Smuzhiyun 	struct device_attribute *attr, char *buf)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	return sysfs_emit(buf, "%d\n", ddata->vmirror);
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
tpo_td043_vmirror_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)175*4882a593Smuzhiyun static ssize_t tpo_td043_vmirror_store(struct device *dev,
176*4882a593Smuzhiyun 	struct device_attribute *attr, const char *buf, size_t count)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
179*4882a593Smuzhiyun 	int val;
180*4882a593Smuzhiyun 	int ret;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	ret = kstrtoint(buf, 0, &val);
183*4882a593Smuzhiyun 	if (ret < 0)
184*4882a593Smuzhiyun 		return ret;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	val = !!val;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val);
189*4882a593Smuzhiyun 	if (ret < 0)
190*4882a593Smuzhiyun 		return ret;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	ddata->vmirror = val;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return count;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
tpo_td043_mode_show(struct device * dev,struct device_attribute * attr,char * buf)197*4882a593Smuzhiyun static ssize_t tpo_td043_mode_show(struct device *dev,
198*4882a593Smuzhiyun 	struct device_attribute *attr, char *buf)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	return sysfs_emit(buf, "%d\n", ddata->mode);
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
tpo_td043_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)205*4882a593Smuzhiyun static ssize_t tpo_td043_mode_store(struct device *dev,
206*4882a593Smuzhiyun 	struct device_attribute *attr, const char *buf, size_t count)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
209*4882a593Smuzhiyun 	long val;
210*4882a593Smuzhiyun 	int ret;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	ret = kstrtol(buf, 0, &val);
213*4882a593Smuzhiyun 	if (ret != 0 || val & ~7)
214*4882a593Smuzhiyun 		return -EINVAL;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	ddata->mode = val;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	val |= TPO_R02_NCLK_RISING;
219*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 2, val);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	return count;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
tpo_td043_gamma_show(struct device * dev,struct device_attribute * attr,char * buf)224*4882a593Smuzhiyun static ssize_t tpo_td043_gamma_show(struct device *dev,
225*4882a593Smuzhiyun 	struct device_attribute *attr, char *buf)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
228*4882a593Smuzhiyun 	ssize_t len = 0;
229*4882a593Smuzhiyun 	int ret;
230*4882a593Smuzhiyun 	int i;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) {
233*4882a593Smuzhiyun 		ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
234*4882a593Smuzhiyun 				ddata->gamma[i]);
235*4882a593Smuzhiyun 		if (ret < 0)
236*4882a593Smuzhiyun 			return ret;
237*4882a593Smuzhiyun 		len += ret;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun 	buf[len - 1] = '\n';
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	return len;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
tpo_td043_gamma_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)244*4882a593Smuzhiyun static ssize_t tpo_td043_gamma_store(struct device *dev,
245*4882a593Smuzhiyun 	struct device_attribute *attr, const char *buf, size_t count)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
248*4882a593Smuzhiyun 	unsigned int g[12];
249*4882a593Smuzhiyun 	int ret;
250*4882a593Smuzhiyun 	int i;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
253*4882a593Smuzhiyun 			&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
254*4882a593Smuzhiyun 			&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	if (ret != 12)
257*4882a593Smuzhiyun 		return -EINVAL;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	for (i = 0; i < 12; i++)
260*4882a593Smuzhiyun 		ddata->gamma[i] = g[i];
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	tpo_td043_write_gamma(ddata->spi, ddata->gamma);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	return count;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
268*4882a593Smuzhiyun 		tpo_td043_vmirror_show, tpo_td043_vmirror_store);
269*4882a593Smuzhiyun static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
270*4882a593Smuzhiyun 		tpo_td043_mode_show, tpo_td043_mode_store);
271*4882a593Smuzhiyun static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
272*4882a593Smuzhiyun 		tpo_td043_gamma_show, tpo_td043_gamma_store);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun static struct attribute *tpo_td043_attrs[] = {
275*4882a593Smuzhiyun 	&dev_attr_vmirror.attr,
276*4882a593Smuzhiyun 	&dev_attr_mode.attr,
277*4882a593Smuzhiyun 	&dev_attr_gamma.attr,
278*4882a593Smuzhiyun 	NULL,
279*4882a593Smuzhiyun };
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun static const struct attribute_group tpo_td043_attr_group = {
282*4882a593Smuzhiyun 	.attrs = tpo_td043_attrs,
283*4882a593Smuzhiyun };
284*4882a593Smuzhiyun 
tpo_td043_power_on(struct panel_drv_data * ddata)285*4882a593Smuzhiyun static int tpo_td043_power_on(struct panel_drv_data *ddata)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun 	int r;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	if (ddata->powered_on)
290*4882a593Smuzhiyun 		return 0;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	r = regulator_enable(ddata->vcc_reg);
293*4882a593Smuzhiyun 	if (r != 0)
294*4882a593Smuzhiyun 		return r;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	/* wait for panel to stabilize */
297*4882a593Smuzhiyun 	msleep(160);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	if (gpio_is_valid(ddata->nreset_gpio))
300*4882a593Smuzhiyun 		gpio_set_value(ddata->nreset_gpio, 1);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 2,
303*4882a593Smuzhiyun 			TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING);
304*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL);
305*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 0x20, 0xf0);
306*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 0x21, 0xf0);
307*4882a593Smuzhiyun 	tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
308*4882a593Smuzhiyun 			ddata->vmirror);
309*4882a593Smuzhiyun 	tpo_td043_write_gamma(ddata->spi, ddata->gamma);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	ddata->powered_on = 1;
312*4882a593Smuzhiyun 	return 0;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
tpo_td043_power_off(struct panel_drv_data * ddata)315*4882a593Smuzhiyun static void tpo_td043_power_off(struct panel_drv_data *ddata)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	if (!ddata->powered_on)
318*4882a593Smuzhiyun 		return;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 3,
321*4882a593Smuzhiyun 			TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (gpio_is_valid(ddata->nreset_gpio))
324*4882a593Smuzhiyun 		gpio_set_value(ddata->nreset_gpio, 0);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	/* wait for at least 2 vsyncs before cutting off power */
327*4882a593Smuzhiyun 	msleep(50);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	regulator_disable(ddata->vcc_reg);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	ddata->powered_on = 0;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
tpo_td043_connect(struct omap_dss_device * dssdev)336*4882a593Smuzhiyun static int tpo_td043_connect(struct omap_dss_device *dssdev)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
339*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
340*4882a593Smuzhiyun 	int r;
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	if (omapdss_device_is_connected(dssdev))
343*4882a593Smuzhiyun 		return 0;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	r = in->ops.dpi->connect(in, dssdev);
346*4882a593Smuzhiyun 	if (r)
347*4882a593Smuzhiyun 		return r;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return 0;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
tpo_td043_disconnect(struct omap_dss_device * dssdev)352*4882a593Smuzhiyun static void tpo_td043_disconnect(struct omap_dss_device *dssdev)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
355*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	if (!omapdss_device_is_connected(dssdev))
358*4882a593Smuzhiyun 		return;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	in->ops.dpi->disconnect(in, dssdev);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
tpo_td043_enable(struct omap_dss_device * dssdev)363*4882a593Smuzhiyun static int tpo_td043_enable(struct omap_dss_device *dssdev)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
366*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
367*4882a593Smuzhiyun 	int r;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	if (!omapdss_device_is_connected(dssdev))
370*4882a593Smuzhiyun 		return -ENODEV;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	if (omapdss_device_is_enabled(dssdev))
373*4882a593Smuzhiyun 		return 0;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	if (ddata->data_lines)
376*4882a593Smuzhiyun 		in->ops.dpi->set_data_lines(in, ddata->data_lines);
377*4882a593Smuzhiyun 	in->ops.dpi->set_timings(in, &ddata->videomode);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	r = in->ops.dpi->enable(in);
380*4882a593Smuzhiyun 	if (r)
381*4882a593Smuzhiyun 		return r;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	/*
384*4882a593Smuzhiyun 	 * If we are resuming from system suspend, SPI clocks might not be
385*4882a593Smuzhiyun 	 * enabled yet, so we'll program the LCD from SPI PM resume callback.
386*4882a593Smuzhiyun 	 */
387*4882a593Smuzhiyun 	if (!ddata->spi_suspended) {
388*4882a593Smuzhiyun 		r = tpo_td043_power_on(ddata);
389*4882a593Smuzhiyun 		if (r) {
390*4882a593Smuzhiyun 			in->ops.dpi->disable(in);
391*4882a593Smuzhiyun 			return r;
392*4882a593Smuzhiyun 		}
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	return 0;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun 
tpo_td043_disable(struct omap_dss_device * dssdev)400*4882a593Smuzhiyun static void tpo_td043_disable(struct omap_dss_device *dssdev)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
403*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (!omapdss_device_is_enabled(dssdev))
406*4882a593Smuzhiyun 		return;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	in->ops.dpi->disable(in);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	if (!ddata->spi_suspended)
411*4882a593Smuzhiyun 		tpo_td043_power_off(ddata);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun 
tpo_td043_set_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)416*4882a593Smuzhiyun static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
417*4882a593Smuzhiyun 		struct omap_video_timings *timings)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
420*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	ddata->videomode = *timings;
423*4882a593Smuzhiyun 	dssdev->panel.timings = *timings;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	in->ops.dpi->set_timings(in, timings);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
tpo_td043_get_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)428*4882a593Smuzhiyun static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
429*4882a593Smuzhiyun 		struct omap_video_timings *timings)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	*timings = ddata->videomode;
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun 
tpo_td043_check_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)436*4882a593Smuzhiyun static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
437*4882a593Smuzhiyun 		struct omap_video_timings *timings)
438*4882a593Smuzhiyun {
439*4882a593Smuzhiyun 	struct panel_drv_data *ddata = to_panel_data(dssdev);
440*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	return in->ops.dpi->check_timings(in, timings);
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun static struct omap_dss_driver tpo_td043_ops = {
446*4882a593Smuzhiyun 	.connect	= tpo_td043_connect,
447*4882a593Smuzhiyun 	.disconnect	= tpo_td043_disconnect,
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	.enable		= tpo_td043_enable,
450*4882a593Smuzhiyun 	.disable	= tpo_td043_disable,
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	.set_timings	= tpo_td043_set_timings,
453*4882a593Smuzhiyun 	.get_timings	= tpo_td043_get_timings,
454*4882a593Smuzhiyun 	.check_timings	= tpo_td043_check_timings,
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	.set_mirror	= tpo_td043_set_hmirror,
457*4882a593Smuzhiyun 	.get_mirror	= tpo_td043_get_hmirror,
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	.get_resolution	= omapdss_default_get_resolution,
460*4882a593Smuzhiyun };
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 
tpo_td043_probe_of(struct spi_device * spi)463*4882a593Smuzhiyun static int tpo_td043_probe_of(struct spi_device *spi)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun 	struct device_node *node = spi->dev.of_node;
466*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
467*4882a593Smuzhiyun 	struct omap_dss_device *in;
468*4882a593Smuzhiyun 	int gpio;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	gpio = of_get_named_gpio(node, "reset-gpios", 0);
471*4882a593Smuzhiyun 	if (!gpio_is_valid(gpio)) {
472*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to parse enable gpio\n");
473*4882a593Smuzhiyun 		return gpio;
474*4882a593Smuzhiyun 	}
475*4882a593Smuzhiyun 	ddata->nreset_gpio = gpio;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	in = omapdss_of_find_source_for_first_ep(node);
478*4882a593Smuzhiyun 	if (IS_ERR(in)) {
479*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to find video source\n");
480*4882a593Smuzhiyun 		return PTR_ERR(in);
481*4882a593Smuzhiyun 	}
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	ddata->in = in;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	return 0;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun 
tpo_td043_probe(struct spi_device * spi)488*4882a593Smuzhiyun static int tpo_td043_probe(struct spi_device *spi)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun 	struct panel_drv_data *ddata;
491*4882a593Smuzhiyun 	struct omap_dss_device *dssdev;
492*4882a593Smuzhiyun 	int r;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	dev_dbg(&spi->dev, "%s\n", __func__);
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	if (!spi->dev.of_node)
497*4882a593Smuzhiyun 		return -ENODEV;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	spi->bits_per_word = 16;
500*4882a593Smuzhiyun 	spi->mode = SPI_MODE_0;
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	r = spi_setup(spi);
503*4882a593Smuzhiyun 	if (r < 0) {
504*4882a593Smuzhiyun 		dev_err(&spi->dev, "spi_setup failed: %d\n", r);
505*4882a593Smuzhiyun 		return r;
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
509*4882a593Smuzhiyun 	if (ddata == NULL)
510*4882a593Smuzhiyun 		return -ENOMEM;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	dev_set_drvdata(&spi->dev, ddata);
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	ddata->spi = spi;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	r = tpo_td043_probe_of(spi);
517*4882a593Smuzhiyun 	if (r)
518*4882a593Smuzhiyun 		return r;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	ddata->mode = TPO_R02_MODE_800x480;
521*4882a593Smuzhiyun 	memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
524*4882a593Smuzhiyun 	if (IS_ERR(ddata->vcc_reg)) {
525*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to get LCD VCC regulator\n");
526*4882a593Smuzhiyun 		r = PTR_ERR(ddata->vcc_reg);
527*4882a593Smuzhiyun 		goto err_regulator;
528*4882a593Smuzhiyun 	}
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	if (gpio_is_valid(ddata->nreset_gpio)) {
531*4882a593Smuzhiyun 		r = devm_gpio_request_one(&spi->dev,
532*4882a593Smuzhiyun 				ddata->nreset_gpio, GPIOF_OUT_INIT_LOW,
533*4882a593Smuzhiyun 				"lcd reset");
534*4882a593Smuzhiyun 		if (r < 0) {
535*4882a593Smuzhiyun 			dev_err(&spi->dev, "couldn't request reset GPIO\n");
536*4882a593Smuzhiyun 			goto err_gpio_req;
537*4882a593Smuzhiyun 		}
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group);
541*4882a593Smuzhiyun 	if (r) {
542*4882a593Smuzhiyun 		dev_err(&spi->dev, "failed to create sysfs files\n");
543*4882a593Smuzhiyun 		goto err_sysfs;
544*4882a593Smuzhiyun 	}
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	ddata->videomode = tpo_td043_timings;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	dssdev = &ddata->dssdev;
549*4882a593Smuzhiyun 	dssdev->dev = &spi->dev;
550*4882a593Smuzhiyun 	dssdev->driver = &tpo_td043_ops;
551*4882a593Smuzhiyun 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
552*4882a593Smuzhiyun 	dssdev->owner = THIS_MODULE;
553*4882a593Smuzhiyun 	dssdev->panel.timings = ddata->videomode;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	r = omapdss_register_display(dssdev);
556*4882a593Smuzhiyun 	if (r) {
557*4882a593Smuzhiyun 		dev_err(&spi->dev, "Failed to register panel\n");
558*4882a593Smuzhiyun 		goto err_reg;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	return 0;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun err_reg:
564*4882a593Smuzhiyun 	sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
565*4882a593Smuzhiyun err_sysfs:
566*4882a593Smuzhiyun err_gpio_req:
567*4882a593Smuzhiyun err_regulator:
568*4882a593Smuzhiyun 	omap_dss_put_device(ddata->in);
569*4882a593Smuzhiyun 	return r;
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun 
tpo_td043_remove(struct spi_device * spi)572*4882a593Smuzhiyun static int tpo_td043_remove(struct spi_device *spi)
573*4882a593Smuzhiyun {
574*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
575*4882a593Smuzhiyun 	struct omap_dss_device *dssdev = &ddata->dssdev;
576*4882a593Smuzhiyun 	struct omap_dss_device *in = ddata->in;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	dev_dbg(&ddata->spi->dev, "%s\n", __func__);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	omapdss_unregister_display(dssdev);
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	tpo_td043_disable(dssdev);
583*4882a593Smuzhiyun 	tpo_td043_disconnect(dssdev);
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	omap_dss_put_device(in);
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	return 0;
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
tpo_td043_spi_suspend(struct device * dev)593*4882a593Smuzhiyun static int tpo_td043_spi_suspend(struct device *dev)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	ddata->power_on_resume = ddata->powered_on;
600*4882a593Smuzhiyun 	tpo_td043_power_off(ddata);
601*4882a593Smuzhiyun 	ddata->spi_suspended = 1;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	return 0;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
tpo_td043_spi_resume(struct device * dev)606*4882a593Smuzhiyun static int tpo_td043_spi_resume(struct device *dev)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
609*4882a593Smuzhiyun 	int ret;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	dev_dbg(dev, "tpo_td043_spi_resume\n");
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	if (ddata->power_on_resume) {
614*4882a593Smuzhiyun 		ret = tpo_td043_power_on(ddata);
615*4882a593Smuzhiyun 		if (ret)
616*4882a593Smuzhiyun 			return ret;
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 	ddata->spi_suspended = 0;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	return 0;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun #endif
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
625*4882a593Smuzhiyun 	tpo_td043_spi_suspend, tpo_td043_spi_resume);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun static const struct of_device_id tpo_td043_of_match[] = {
628*4882a593Smuzhiyun 	{ .compatible = "omapdss,tpo,td043mtea1", },
629*4882a593Smuzhiyun 	{},
630*4882a593Smuzhiyun };
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun static struct spi_driver tpo_td043_spi_driver = {
635*4882a593Smuzhiyun 	.driver = {
636*4882a593Smuzhiyun 		.name	= "panel-tpo-td043mtea1",
637*4882a593Smuzhiyun 		.pm	= &tpo_td043_spi_pm,
638*4882a593Smuzhiyun 		.of_match_table = tpo_td043_of_match,
639*4882a593Smuzhiyun 		.suppress_bind_attrs = true,
640*4882a593Smuzhiyun 	},
641*4882a593Smuzhiyun 	.probe	= tpo_td043_probe,
642*4882a593Smuzhiyun 	.remove	= tpo_td043_remove,
643*4882a593Smuzhiyun };
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun module_spi_driver(tpo_td043_spi_driver);
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun MODULE_ALIAS("spi:tpo,td043mtea1");
648*4882a593Smuzhiyun MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
649*4882a593Smuzhiyun MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
650*4882a593Smuzhiyun MODULE_LICENSE("GPL");
651