1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Panel driver for the TPO TPG110 400CH LTPS TFT LCD Single Chip
4*4882a593Smuzhiyun * Digital Driver.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * This chip drives a TFT LCD, so it does not know what kind of
7*4882a593Smuzhiyun * display is actually connected to it, so the width and height of that
8*4882a593Smuzhiyun * display needs to be supplied from the machine configuration.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Author:
11*4882a593Smuzhiyun * Linus Walleij <linus.walleij@linaro.org>
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun #include <drm/drm_modes.h>
14*4882a593Smuzhiyun #include <drm/drm_panel.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/bitops.h>
17*4882a593Smuzhiyun #include <linux/delay.h>
18*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
19*4882a593Smuzhiyun #include <linux/init.h>
20*4882a593Smuzhiyun #include <linux/kernel.h>
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/of.h>
23*4882a593Smuzhiyun #include <linux/platform_device.h>
24*4882a593Smuzhiyun #include <linux/spi/spi.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define TPG110_TEST 0x00
27*4882a593Smuzhiyun #define TPG110_CHIPID 0x01
28*4882a593Smuzhiyun #define TPG110_CTRL1 0x02
29*4882a593Smuzhiyun #define TPG110_RES_MASK GENMASK(2, 0)
30*4882a593Smuzhiyun #define TPG110_RES_800X480 0x07
31*4882a593Smuzhiyun #define TPG110_RES_640X480 0x06
32*4882a593Smuzhiyun #define TPG110_RES_480X272 0x05
33*4882a593Smuzhiyun #define TPG110_RES_480X640 0x04
34*4882a593Smuzhiyun #define TPG110_RES_480X272_D 0x01 /* Dual scan: outputs 800x480 */
35*4882a593Smuzhiyun #define TPG110_RES_400X240_D 0x00 /* Dual scan: outputs 800x480 */
36*4882a593Smuzhiyun #define TPG110_CTRL2 0x03
37*4882a593Smuzhiyun #define TPG110_CTRL2_PM BIT(0)
38*4882a593Smuzhiyun #define TPG110_CTRL2_RES_PM_CTRL BIT(7)
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /**
41*4882a593Smuzhiyun * struct tpg110_panel_mode - lookup struct for the supported modes
42*4882a593Smuzhiyun */
43*4882a593Smuzhiyun struct tpg110_panel_mode {
44*4882a593Smuzhiyun /**
45*4882a593Smuzhiyun * @name: the name of this panel
46*4882a593Smuzhiyun */
47*4882a593Smuzhiyun const char *name;
48*4882a593Smuzhiyun /**
49*4882a593Smuzhiyun * @magic: the magic value from the detection register
50*4882a593Smuzhiyun */
51*4882a593Smuzhiyun u32 magic;
52*4882a593Smuzhiyun /**
53*4882a593Smuzhiyun * @mode: the DRM display mode for this panel
54*4882a593Smuzhiyun */
55*4882a593Smuzhiyun struct drm_display_mode mode;
56*4882a593Smuzhiyun /**
57*4882a593Smuzhiyun * @bus_flags: the DRM bus flags for this panel e.g. inverted clock
58*4882a593Smuzhiyun */
59*4882a593Smuzhiyun u32 bus_flags;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /**
63*4882a593Smuzhiyun * struct tpg110 - state container for the TPG110 panel
64*4882a593Smuzhiyun */
65*4882a593Smuzhiyun struct tpg110 {
66*4882a593Smuzhiyun /**
67*4882a593Smuzhiyun * @dev: the container device
68*4882a593Smuzhiyun */
69*4882a593Smuzhiyun struct device *dev;
70*4882a593Smuzhiyun /**
71*4882a593Smuzhiyun * @spi: the corresponding SPI device
72*4882a593Smuzhiyun */
73*4882a593Smuzhiyun struct spi_device *spi;
74*4882a593Smuzhiyun /**
75*4882a593Smuzhiyun * @panel: the DRM panel instance for this device
76*4882a593Smuzhiyun */
77*4882a593Smuzhiyun struct drm_panel panel;
78*4882a593Smuzhiyun /**
79*4882a593Smuzhiyun * @panel_type: the panel mode as detected
80*4882a593Smuzhiyun */
81*4882a593Smuzhiyun const struct tpg110_panel_mode *panel_mode;
82*4882a593Smuzhiyun /**
83*4882a593Smuzhiyun * @width: the width of this panel in mm
84*4882a593Smuzhiyun */
85*4882a593Smuzhiyun u32 width;
86*4882a593Smuzhiyun /**
87*4882a593Smuzhiyun * @height: the height of this panel in mm
88*4882a593Smuzhiyun */
89*4882a593Smuzhiyun u32 height;
90*4882a593Smuzhiyun /**
91*4882a593Smuzhiyun * @grestb: reset GPIO line
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun struct gpio_desc *grestb;
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun /*
97*4882a593Smuzhiyun * TPG110 modes, these are the simple modes, the dualscan modes that
98*4882a593Smuzhiyun * take 400x240 or 480x272 in and display as 800x480 are not listed.
99*4882a593Smuzhiyun */
100*4882a593Smuzhiyun static const struct tpg110_panel_mode tpg110_modes[] = {
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun .name = "800x480 RGB",
103*4882a593Smuzhiyun .magic = TPG110_RES_800X480,
104*4882a593Smuzhiyun .mode = {
105*4882a593Smuzhiyun .clock = 33200,
106*4882a593Smuzhiyun .hdisplay = 800,
107*4882a593Smuzhiyun .hsync_start = 800 + 40,
108*4882a593Smuzhiyun .hsync_end = 800 + 40 + 1,
109*4882a593Smuzhiyun .htotal = 800 + 40 + 1 + 216,
110*4882a593Smuzhiyun .vdisplay = 480,
111*4882a593Smuzhiyun .vsync_start = 480 + 10,
112*4882a593Smuzhiyun .vsync_end = 480 + 10 + 1,
113*4882a593Smuzhiyun .vtotal = 480 + 10 + 1 + 35,
114*4882a593Smuzhiyun },
115*4882a593Smuzhiyun .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
116*4882a593Smuzhiyun },
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun .name = "640x480 RGB",
119*4882a593Smuzhiyun .magic = TPG110_RES_640X480,
120*4882a593Smuzhiyun .mode = {
121*4882a593Smuzhiyun .clock = 25200,
122*4882a593Smuzhiyun .hdisplay = 640,
123*4882a593Smuzhiyun .hsync_start = 640 + 24,
124*4882a593Smuzhiyun .hsync_end = 640 + 24 + 1,
125*4882a593Smuzhiyun .htotal = 640 + 24 + 1 + 136,
126*4882a593Smuzhiyun .vdisplay = 480,
127*4882a593Smuzhiyun .vsync_start = 480 + 18,
128*4882a593Smuzhiyun .vsync_end = 480 + 18 + 1,
129*4882a593Smuzhiyun .vtotal = 480 + 18 + 1 + 27,
130*4882a593Smuzhiyun },
131*4882a593Smuzhiyun .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
132*4882a593Smuzhiyun },
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun .name = "480x272 RGB",
135*4882a593Smuzhiyun .magic = TPG110_RES_480X272,
136*4882a593Smuzhiyun .mode = {
137*4882a593Smuzhiyun .clock = 9000,
138*4882a593Smuzhiyun .hdisplay = 480,
139*4882a593Smuzhiyun .hsync_start = 480 + 2,
140*4882a593Smuzhiyun .hsync_end = 480 + 2 + 1,
141*4882a593Smuzhiyun .htotal = 480 + 2 + 1 + 43,
142*4882a593Smuzhiyun .vdisplay = 272,
143*4882a593Smuzhiyun .vsync_start = 272 + 2,
144*4882a593Smuzhiyun .vsync_end = 272 + 2 + 1,
145*4882a593Smuzhiyun .vtotal = 272 + 2 + 1 + 12,
146*4882a593Smuzhiyun },
147*4882a593Smuzhiyun .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
148*4882a593Smuzhiyun },
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun .name = "480x640 RGB",
151*4882a593Smuzhiyun .magic = TPG110_RES_480X640,
152*4882a593Smuzhiyun .mode = {
153*4882a593Smuzhiyun .clock = 20500,
154*4882a593Smuzhiyun .hdisplay = 480,
155*4882a593Smuzhiyun .hsync_start = 480 + 2,
156*4882a593Smuzhiyun .hsync_end = 480 + 2 + 1,
157*4882a593Smuzhiyun .htotal = 480 + 2 + 1 + 43,
158*4882a593Smuzhiyun .vdisplay = 640,
159*4882a593Smuzhiyun .vsync_start = 640 + 4,
160*4882a593Smuzhiyun .vsync_end = 640 + 4 + 1,
161*4882a593Smuzhiyun .vtotal = 640 + 4 + 1 + 8,
162*4882a593Smuzhiyun },
163*4882a593Smuzhiyun .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
164*4882a593Smuzhiyun },
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun .name = "400x240 RGB",
167*4882a593Smuzhiyun .magic = TPG110_RES_400X240_D,
168*4882a593Smuzhiyun .mode = {
169*4882a593Smuzhiyun .clock = 8300,
170*4882a593Smuzhiyun .hdisplay = 400,
171*4882a593Smuzhiyun .hsync_start = 400 + 20,
172*4882a593Smuzhiyun .hsync_end = 400 + 20 + 1,
173*4882a593Smuzhiyun .htotal = 400 + 20 + 1 + 108,
174*4882a593Smuzhiyun .vdisplay = 240,
175*4882a593Smuzhiyun .vsync_start = 240 + 2,
176*4882a593Smuzhiyun .vsync_end = 240 + 2 + 1,
177*4882a593Smuzhiyun .vtotal = 240 + 2 + 1 + 20,
178*4882a593Smuzhiyun },
179*4882a593Smuzhiyun .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
180*4882a593Smuzhiyun },
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun static inline struct tpg110 *
to_tpg110(struct drm_panel * panel)184*4882a593Smuzhiyun to_tpg110(struct drm_panel *panel)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun return container_of(panel, struct tpg110, panel);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
tpg110_readwrite_reg(struct tpg110 * tpg,bool write,u8 address,u8 outval)189*4882a593Smuzhiyun static u8 tpg110_readwrite_reg(struct tpg110 *tpg, bool write,
190*4882a593Smuzhiyun u8 address, u8 outval)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct spi_message m;
193*4882a593Smuzhiyun struct spi_transfer t[2];
194*4882a593Smuzhiyun u8 buf[2];
195*4882a593Smuzhiyun int ret;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun spi_message_init(&m);
198*4882a593Smuzhiyun memset(t, 0, sizeof(t));
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun if (write) {
201*4882a593Smuzhiyun /*
202*4882a593Smuzhiyun * Clear address bit 0, 1 when writing, just to be sure
203*4882a593Smuzhiyun * The actual bit indicating a write here is bit 1, bit
204*4882a593Smuzhiyun * 0 is just surplus to pad it up to 8 bits.
205*4882a593Smuzhiyun */
206*4882a593Smuzhiyun buf[0] = address << 2;
207*4882a593Smuzhiyun buf[0] &= ~0x03;
208*4882a593Smuzhiyun buf[1] = outval;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun t[0].bits_per_word = 8;
211*4882a593Smuzhiyun t[0].tx_buf = &buf[0];
212*4882a593Smuzhiyun t[0].len = 1;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun t[1].tx_buf = &buf[1];
215*4882a593Smuzhiyun t[1].len = 1;
216*4882a593Smuzhiyun t[1].bits_per_word = 8;
217*4882a593Smuzhiyun } else {
218*4882a593Smuzhiyun /* Set address bit 0 to 1 to read */
219*4882a593Smuzhiyun buf[0] = address << 1;
220*4882a593Smuzhiyun buf[0] |= 0x01;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun /*
223*4882a593Smuzhiyun * The last bit/clock is Hi-Z turnaround cycle, so we need
224*4882a593Smuzhiyun * to send only 7 bits here. The 8th bit is the high impedance
225*4882a593Smuzhiyun * turn-around cycle.
226*4882a593Smuzhiyun */
227*4882a593Smuzhiyun t[0].bits_per_word = 7;
228*4882a593Smuzhiyun t[0].tx_buf = &buf[0];
229*4882a593Smuzhiyun t[0].len = 1;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun t[1].rx_buf = &buf[1];
232*4882a593Smuzhiyun t[1].len = 1;
233*4882a593Smuzhiyun t[1].bits_per_word = 8;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun spi_message_add_tail(&t[0], &m);
237*4882a593Smuzhiyun spi_message_add_tail(&t[1], &m);
238*4882a593Smuzhiyun ret = spi_sync(tpg->spi, &m);
239*4882a593Smuzhiyun if (ret) {
240*4882a593Smuzhiyun dev_err(tpg->dev, "SPI message error %d\n", ret);
241*4882a593Smuzhiyun return ret;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun if (write)
244*4882a593Smuzhiyun return 0;
245*4882a593Smuzhiyun /* Read */
246*4882a593Smuzhiyun return buf[1];
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
tpg110_read_reg(struct tpg110 * tpg,u8 address)249*4882a593Smuzhiyun static u8 tpg110_read_reg(struct tpg110 *tpg, u8 address)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun return tpg110_readwrite_reg(tpg, false, address, 0);
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
tpg110_write_reg(struct tpg110 * tpg,u8 address,u8 outval)254*4882a593Smuzhiyun static void tpg110_write_reg(struct tpg110 *tpg, u8 address, u8 outval)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun tpg110_readwrite_reg(tpg, true, address, outval);
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
tpg110_startup(struct tpg110 * tpg)259*4882a593Smuzhiyun static int tpg110_startup(struct tpg110 *tpg)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun u8 val;
262*4882a593Smuzhiyun int i;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun /* De-assert the reset signal */
265*4882a593Smuzhiyun gpiod_set_value_cansleep(tpg->grestb, 0);
266*4882a593Smuzhiyun usleep_range(1000, 2000);
267*4882a593Smuzhiyun dev_dbg(tpg->dev, "de-asserted GRESTB\n");
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun /* Test display communication */
270*4882a593Smuzhiyun tpg110_write_reg(tpg, TPG110_TEST, 0x55);
271*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_TEST);
272*4882a593Smuzhiyun if (val != 0x55) {
273*4882a593Smuzhiyun dev_err(tpg->dev, "failed communication test\n");
274*4882a593Smuzhiyun return -ENODEV;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_CHIPID);
278*4882a593Smuzhiyun dev_info(tpg->dev, "TPG110 chip ID: %d version: %d\n",
279*4882a593Smuzhiyun val >> 4, val & 0x0f);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun /* Show display resolution */
282*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_CTRL1);
283*4882a593Smuzhiyun val &= TPG110_RES_MASK;
284*4882a593Smuzhiyun switch (val) {
285*4882a593Smuzhiyun case TPG110_RES_400X240_D:
286*4882a593Smuzhiyun dev_info(tpg->dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)\n");
287*4882a593Smuzhiyun break;
288*4882a593Smuzhiyun case TPG110_RES_480X272_D:
289*4882a593Smuzhiyun dev_info(tpg->dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)\n");
290*4882a593Smuzhiyun break;
291*4882a593Smuzhiyun case TPG110_RES_480X640:
292*4882a593Smuzhiyun dev_info(tpg->dev, "480x640 RGB\n");
293*4882a593Smuzhiyun break;
294*4882a593Smuzhiyun case TPG110_RES_480X272:
295*4882a593Smuzhiyun dev_info(tpg->dev, "480x272 RGB\n");
296*4882a593Smuzhiyun break;
297*4882a593Smuzhiyun case TPG110_RES_640X480:
298*4882a593Smuzhiyun dev_info(tpg->dev, "640x480 RGB\n");
299*4882a593Smuzhiyun break;
300*4882a593Smuzhiyun case TPG110_RES_800X480:
301*4882a593Smuzhiyun dev_info(tpg->dev, "800x480 RGB\n");
302*4882a593Smuzhiyun break;
303*4882a593Smuzhiyun default:
304*4882a593Smuzhiyun dev_err(tpg->dev, "ILLEGAL RESOLUTION 0x%02x\n", val);
305*4882a593Smuzhiyun break;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun /* From the producer side, this is the same resolution */
309*4882a593Smuzhiyun if (val == TPG110_RES_480X272_D)
310*4882a593Smuzhiyun val = TPG110_RES_480X272;
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(tpg110_modes); i++) {
313*4882a593Smuzhiyun const struct tpg110_panel_mode *pm;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun pm = &tpg110_modes[i];
316*4882a593Smuzhiyun if (pm->magic == val) {
317*4882a593Smuzhiyun tpg->panel_mode = pm;
318*4882a593Smuzhiyun break;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun if (i == ARRAY_SIZE(tpg110_modes)) {
322*4882a593Smuzhiyun dev_err(tpg->dev, "unsupported mode (%02x) detected\n", val);
323*4882a593Smuzhiyun return -ENODEV;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_CTRL2);
327*4882a593Smuzhiyun dev_info(tpg->dev, "resolution and standby is controlled by %s\n",
328*4882a593Smuzhiyun (val & TPG110_CTRL2_RES_PM_CTRL) ? "software" : "hardware");
329*4882a593Smuzhiyun /* Take control over resolution and standby */
330*4882a593Smuzhiyun val |= TPG110_CTRL2_RES_PM_CTRL;
331*4882a593Smuzhiyun tpg110_write_reg(tpg, TPG110_CTRL2, val);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun return 0;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
tpg110_disable(struct drm_panel * panel)336*4882a593Smuzhiyun static int tpg110_disable(struct drm_panel *panel)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun struct tpg110 *tpg = to_tpg110(panel);
339*4882a593Smuzhiyun u8 val;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /* Put chip into standby */
342*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_CTRL2_PM);
343*4882a593Smuzhiyun val &= ~TPG110_CTRL2_PM;
344*4882a593Smuzhiyun tpg110_write_reg(tpg, TPG110_CTRL2_PM, val);
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun return 0;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
tpg110_enable(struct drm_panel * panel)349*4882a593Smuzhiyun static int tpg110_enable(struct drm_panel *panel)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun struct tpg110 *tpg = to_tpg110(panel);
352*4882a593Smuzhiyun u8 val;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun /* Take chip out of standby */
355*4882a593Smuzhiyun val = tpg110_read_reg(tpg, TPG110_CTRL2_PM);
356*4882a593Smuzhiyun val |= TPG110_CTRL2_PM;
357*4882a593Smuzhiyun tpg110_write_reg(tpg, TPG110_CTRL2_PM, val);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun return 0;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /**
363*4882a593Smuzhiyun * tpg110_get_modes() - return the appropriate mode
364*4882a593Smuzhiyun * @panel: the panel to get the mode for
365*4882a593Smuzhiyun *
366*4882a593Smuzhiyun * This currently does not present a forest of modes, instead it
367*4882a593Smuzhiyun * presents the mode that is configured for the system under use,
368*4882a593Smuzhiyun * and which is detected by reading the registers of the display.
369*4882a593Smuzhiyun */
tpg110_get_modes(struct drm_panel * panel,struct drm_connector * connector)370*4882a593Smuzhiyun static int tpg110_get_modes(struct drm_panel *panel,
371*4882a593Smuzhiyun struct drm_connector *connector)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun struct tpg110 *tpg = to_tpg110(panel);
374*4882a593Smuzhiyun struct drm_display_mode *mode;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun connector->display_info.width_mm = tpg->width;
377*4882a593Smuzhiyun connector->display_info.height_mm = tpg->height;
378*4882a593Smuzhiyun connector->display_info.bus_flags = tpg->panel_mode->bus_flags;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun mode = drm_mode_duplicate(connector->dev, &tpg->panel_mode->mode);
381*4882a593Smuzhiyun drm_mode_set_name(mode);
382*4882a593Smuzhiyun mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun mode->width_mm = tpg->width;
385*4882a593Smuzhiyun mode->height_mm = tpg->height;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun drm_mode_probed_add(connector, mode);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun return 1;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun static const struct drm_panel_funcs tpg110_drm_funcs = {
393*4882a593Smuzhiyun .disable = tpg110_disable,
394*4882a593Smuzhiyun .enable = tpg110_enable,
395*4882a593Smuzhiyun .get_modes = tpg110_get_modes,
396*4882a593Smuzhiyun };
397*4882a593Smuzhiyun
tpg110_probe(struct spi_device * spi)398*4882a593Smuzhiyun static int tpg110_probe(struct spi_device *spi)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun struct device *dev = &spi->dev;
401*4882a593Smuzhiyun struct device_node *np = dev->of_node;
402*4882a593Smuzhiyun struct tpg110 *tpg;
403*4882a593Smuzhiyun int ret;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun tpg = devm_kzalloc(dev, sizeof(*tpg), GFP_KERNEL);
406*4882a593Smuzhiyun if (!tpg)
407*4882a593Smuzhiyun return -ENOMEM;
408*4882a593Smuzhiyun tpg->dev = dev;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun /* We get the physical display dimensions from the DT */
411*4882a593Smuzhiyun ret = of_property_read_u32(np, "width-mm", &tpg->width);
412*4882a593Smuzhiyun if (ret)
413*4882a593Smuzhiyun dev_err(dev, "no panel width specified\n");
414*4882a593Smuzhiyun ret = of_property_read_u32(np, "height-mm", &tpg->height);
415*4882a593Smuzhiyun if (ret)
416*4882a593Smuzhiyun dev_err(dev, "no panel height specified\n");
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun /* This asserts the GRESTB signal, putting the display into reset */
419*4882a593Smuzhiyun tpg->grestb = devm_gpiod_get(dev, "grestb", GPIOD_OUT_HIGH);
420*4882a593Smuzhiyun if (IS_ERR(tpg->grestb)) {
421*4882a593Smuzhiyun dev_err(dev, "no GRESTB GPIO\n");
422*4882a593Smuzhiyun return -ENODEV;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun spi->bits_per_word = 8;
426*4882a593Smuzhiyun spi->mode |= SPI_3WIRE_HIZ;
427*4882a593Smuzhiyun ret = spi_setup(spi);
428*4882a593Smuzhiyun if (ret < 0) {
429*4882a593Smuzhiyun dev_err(dev, "spi setup failed.\n");
430*4882a593Smuzhiyun return ret;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun tpg->spi = spi;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun ret = tpg110_startup(tpg);
435*4882a593Smuzhiyun if (ret)
436*4882a593Smuzhiyun return ret;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun drm_panel_init(&tpg->panel, dev, &tpg110_drm_funcs,
439*4882a593Smuzhiyun DRM_MODE_CONNECTOR_DPI);
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun ret = drm_panel_of_backlight(&tpg->panel);
442*4882a593Smuzhiyun if (ret)
443*4882a593Smuzhiyun return ret;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun spi_set_drvdata(spi, tpg);
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun drm_panel_add(&tpg->panel);
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun return 0;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
tpg110_remove(struct spi_device * spi)452*4882a593Smuzhiyun static int tpg110_remove(struct spi_device *spi)
453*4882a593Smuzhiyun {
454*4882a593Smuzhiyun struct tpg110 *tpg = spi_get_drvdata(spi);
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun drm_panel_remove(&tpg->panel);
457*4882a593Smuzhiyun return 0;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun static const struct of_device_id tpg110_match[] = {
461*4882a593Smuzhiyun { .compatible = "tpo,tpg110", },
462*4882a593Smuzhiyun {},
463*4882a593Smuzhiyun };
464*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, tpg110_match);
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun static struct spi_driver tpg110_driver = {
467*4882a593Smuzhiyun .probe = tpg110_probe,
468*4882a593Smuzhiyun .remove = tpg110_remove,
469*4882a593Smuzhiyun .driver = {
470*4882a593Smuzhiyun .name = "tpo-tpg110-panel",
471*4882a593Smuzhiyun .of_match_table = tpg110_match,
472*4882a593Smuzhiyun },
473*4882a593Smuzhiyun };
474*4882a593Smuzhiyun module_spi_driver(tpg110_driver);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
477*4882a593Smuzhiyun MODULE_DESCRIPTION("TPO TPG110 panel driver");
478*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
479