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