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