xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/panel/panel-tpo-tpg110.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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