1*cd6d4128SChristophe Branchereau // SPDX-License-Identifier: GPL-2.0
2*cd6d4128SChristophe Branchereau /*
3*cd6d4128SChristophe Branchereau  * Orisetech OTA5601A TFT LCD panel driver
4*cd6d4128SChristophe Branchereau  *
5*cd6d4128SChristophe Branchereau  * Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com>
6*cd6d4128SChristophe Branchereau  */
7*cd6d4128SChristophe Branchereau 
8*cd6d4128SChristophe Branchereau #include <linux/bits.h>
9*cd6d4128SChristophe Branchereau #include <linux/delay.h>
10*cd6d4128SChristophe Branchereau #include <linux/device.h>
11*cd6d4128SChristophe Branchereau #include <linux/gpio/consumer.h>
12*cd6d4128SChristophe Branchereau #include <linux/media-bus-format.h>
13*cd6d4128SChristophe Branchereau #include <linux/module.h>
14*cd6d4128SChristophe Branchereau #include <linux/of.h>
15*cd6d4128SChristophe Branchereau #include <linux/regmap.h>
16*cd6d4128SChristophe Branchereau #include <linux/regulator/consumer.h>
17*cd6d4128SChristophe Branchereau #include <linux/spi/spi.h>
18*cd6d4128SChristophe Branchereau 
19*cd6d4128SChristophe Branchereau #include <drm/drm_modes.h>
20*cd6d4128SChristophe Branchereau #include <drm/drm_panel.h>
21*cd6d4128SChristophe Branchereau 
22*cd6d4128SChristophe Branchereau #define OTA5601A_CTL 0x01
23*cd6d4128SChristophe Branchereau #define OTA5601A_CTL_OFF 0x00
24*cd6d4128SChristophe Branchereau #define OTA5601A_CTL_ON BIT(0)
25*cd6d4128SChristophe Branchereau 
26*cd6d4128SChristophe Branchereau struct ota5601a_panel_info {
27*cd6d4128SChristophe Branchereau 	const struct drm_display_mode *display_modes;
28*cd6d4128SChristophe Branchereau 	unsigned int num_modes;
29*cd6d4128SChristophe Branchereau 	u16 width_mm, height_mm;
30*cd6d4128SChristophe Branchereau 	u32 bus_format, bus_flags;
31*cd6d4128SChristophe Branchereau };
32*cd6d4128SChristophe Branchereau 
33*cd6d4128SChristophe Branchereau struct ota5601a {
34*cd6d4128SChristophe Branchereau 	struct drm_panel drm_panel;
35*cd6d4128SChristophe Branchereau 	struct regmap *map;
36*cd6d4128SChristophe Branchereau 	struct regulator *supply;
37*cd6d4128SChristophe Branchereau 	const struct ota5601a_panel_info *panel_info;
38*cd6d4128SChristophe Branchereau 
39*cd6d4128SChristophe Branchereau 	struct gpio_desc *reset_gpio;
40*cd6d4128SChristophe Branchereau };
41*cd6d4128SChristophe Branchereau 
to_ota5601a(struct drm_panel * panel)42*cd6d4128SChristophe Branchereau static inline struct ota5601a *to_ota5601a(struct drm_panel *panel)
43*cd6d4128SChristophe Branchereau {
44*cd6d4128SChristophe Branchereau 	return container_of(panel, struct ota5601a, drm_panel);
45*cd6d4128SChristophe Branchereau }
46*cd6d4128SChristophe Branchereau 
47*cd6d4128SChristophe Branchereau static const struct reg_sequence ota5601a_panel_regs[] = {
48*cd6d4128SChristophe Branchereau 	{ 0xfd, 0x00 }, /* Page Shift */
49*cd6d4128SChristophe Branchereau 	{ 0x02, 0x00 }, /* Reset */
50*cd6d4128SChristophe Branchereau 
51*cd6d4128SChristophe Branchereau 	{ 0x18, 0x00 }, /* Interface Sel: RGB 24 Bits */
52*cd6d4128SChristophe Branchereau 	{ 0x34, 0x20 }, /* Undocumented */
53*cd6d4128SChristophe Branchereau 
54*cd6d4128SChristophe Branchereau 	{ 0x0c, 0x01 }, /* Contrast set by CMD1 == within page 0x00 */
55*cd6d4128SChristophe Branchereau 	{ 0x0d, 0x48 }, /* R Brightness */
56*cd6d4128SChristophe Branchereau 	{ 0x0e, 0x48 }, /* G Brightness */
57*cd6d4128SChristophe Branchereau 	{ 0x0f, 0x48 }, /* B Brightness */
58*cd6d4128SChristophe Branchereau 	{ 0x07, 0x40 }, /* R Contrast */
59*cd6d4128SChristophe Branchereau 	{ 0x08, 0x33 }, /* G Contrast */
60*cd6d4128SChristophe Branchereau 	{ 0x09, 0x3a }, /* B Contrast */
61*cd6d4128SChristophe Branchereau 
62*cd6d4128SChristophe Branchereau 	{ 0x16, 0x01 }, /* NTSC Sel */
63*cd6d4128SChristophe Branchereau 	{ 0x19, 0x8d }, /* VBLK */
64*cd6d4128SChristophe Branchereau 	{ 0x1a, 0x28 }, /* HBLK */
65*cd6d4128SChristophe Branchereau 	{ 0x1c, 0x00 }, /* Scan Shift Dir. */
66*cd6d4128SChristophe Branchereau 
67*cd6d4128SChristophe Branchereau 	{ 0xfd, 0xc5 }, /* Page Shift */
68*cd6d4128SChristophe Branchereau 	{ 0x82, 0x0c }, /* PWR_CTRL Pump */
69*cd6d4128SChristophe Branchereau 	{ 0xa2, 0xb4 }, /* PWR_CTRL VGH/VGL */
70*cd6d4128SChristophe Branchereau 
71*cd6d4128SChristophe Branchereau 	{ 0xfd, 0xc4 }, /* Page Shift - What follows is listed as "RGB 24bit Timing Set" */
72*cd6d4128SChristophe Branchereau 	{ 0x82, 0x45 },
73*cd6d4128SChristophe Branchereau 
74*cd6d4128SChristophe Branchereau 	{ 0xfd, 0xc1 },
75*cd6d4128SChristophe Branchereau 	{ 0x91, 0x02 },
76*cd6d4128SChristophe Branchereau 
77*cd6d4128SChristophe Branchereau 	{ 0xfd, 0xc0 },
78*cd6d4128SChristophe Branchereau 	{ 0xa1, 0x01 },
79*cd6d4128SChristophe Branchereau 	{ 0xa2, 0x1f },
80*cd6d4128SChristophe Branchereau 	{ 0xa3, 0x0b },
81*cd6d4128SChristophe Branchereau 	{ 0xa4, 0x38 },
82*cd6d4128SChristophe Branchereau 	{ 0xa5, 0x00 },
83*cd6d4128SChristophe Branchereau 	{ 0xa6, 0x0a },
84*cd6d4128SChristophe Branchereau 	{ 0xa7, 0x38 },
85*cd6d4128SChristophe Branchereau 	{ 0xa8, 0x00 },
86*cd6d4128SChristophe Branchereau 	{ 0xa9, 0x0a },
87*cd6d4128SChristophe Branchereau 	{ 0xaa, 0x37 },
88*cd6d4128SChristophe Branchereau 
89*cd6d4128SChristophe Branchereau 	{ 0xfd, 0xce },
90*cd6d4128SChristophe Branchereau 	{ 0x81, 0x18 },
91*cd6d4128SChristophe Branchereau 	{ 0x82, 0x43 },
92*cd6d4128SChristophe Branchereau 	{ 0x83, 0x43 },
93*cd6d4128SChristophe Branchereau 	{ 0x91, 0x06 },
94*cd6d4128SChristophe Branchereau 	{ 0x93, 0x38 },
95*cd6d4128SChristophe Branchereau 	{ 0x94, 0x02 },
96*cd6d4128SChristophe Branchereau 	{ 0x95, 0x06 },
97*cd6d4128SChristophe Branchereau 	{ 0x97, 0x38 },
98*cd6d4128SChristophe Branchereau 	{ 0x98, 0x02 },
99*cd6d4128SChristophe Branchereau 	{ 0x99, 0x06 },
100*cd6d4128SChristophe Branchereau 	{ 0x9b, 0x38 },
101*cd6d4128SChristophe Branchereau 	{ 0x9c, 0x02 },
102*cd6d4128SChristophe Branchereau 
103*cd6d4128SChristophe Branchereau 	{ 0xfd, 0x00 }, /* Page Shift */
104*cd6d4128SChristophe Branchereau };
105*cd6d4128SChristophe Branchereau 
106*cd6d4128SChristophe Branchereau static const struct regmap_config ota5601a_regmap_config = {
107*cd6d4128SChristophe Branchereau 	.reg_bits = 8,
108*cd6d4128SChristophe Branchereau 	.val_bits = 8,
109*cd6d4128SChristophe Branchereau };
110*cd6d4128SChristophe Branchereau 
ota5601a_prepare(struct drm_panel * drm_panel)111*cd6d4128SChristophe Branchereau static int ota5601a_prepare(struct drm_panel *drm_panel)
112*cd6d4128SChristophe Branchereau {
113*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = to_ota5601a(drm_panel);
114*cd6d4128SChristophe Branchereau 	int err;
115*cd6d4128SChristophe Branchereau 
116*cd6d4128SChristophe Branchereau 	err = regulator_enable(panel->supply);
117*cd6d4128SChristophe Branchereau 	if (err) {
118*cd6d4128SChristophe Branchereau 		dev_err(drm_panel->dev, "Failed to enable power supply: %d\n", err);
119*cd6d4128SChristophe Branchereau 		return err;
120*cd6d4128SChristophe Branchereau 	}
121*cd6d4128SChristophe Branchereau 
122*cd6d4128SChristophe Branchereau 	/* Reset to be held low for 10us min according to the doc, 10ms before sending commands */
123*cd6d4128SChristophe Branchereau 	gpiod_set_value_cansleep(panel->reset_gpio, 1);
124*cd6d4128SChristophe Branchereau 	usleep_range(10, 30);
125*cd6d4128SChristophe Branchereau 	gpiod_set_value_cansleep(panel->reset_gpio, 0);
126*cd6d4128SChristophe Branchereau 	usleep_range(10000, 20000);
127*cd6d4128SChristophe Branchereau 
128*cd6d4128SChristophe Branchereau 	/* Init all registers. */
129*cd6d4128SChristophe Branchereau 	err = regmap_multi_reg_write(panel->map, ota5601a_panel_regs,
130*cd6d4128SChristophe Branchereau 				     ARRAY_SIZE(ota5601a_panel_regs));
131*cd6d4128SChristophe Branchereau 	if (err) {
132*cd6d4128SChristophe Branchereau 		dev_err(drm_panel->dev, "Failed to init registers: %d\n", err);
133*cd6d4128SChristophe Branchereau 		goto err_disable_regulator;
134*cd6d4128SChristophe Branchereau 	}
135*cd6d4128SChristophe Branchereau 
136*cd6d4128SChristophe Branchereau 	msleep(120);
137*cd6d4128SChristophe Branchereau 
138*cd6d4128SChristophe Branchereau 	return 0;
139*cd6d4128SChristophe Branchereau 
140*cd6d4128SChristophe Branchereau err_disable_regulator:
141*cd6d4128SChristophe Branchereau 	regulator_disable(panel->supply);
142*cd6d4128SChristophe Branchereau 	return err;
143*cd6d4128SChristophe Branchereau }
144*cd6d4128SChristophe Branchereau 
ota5601a_unprepare(struct drm_panel * drm_panel)145*cd6d4128SChristophe Branchereau static int ota5601a_unprepare(struct drm_panel *drm_panel)
146*cd6d4128SChristophe Branchereau {
147*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = to_ota5601a(drm_panel);
148*cd6d4128SChristophe Branchereau 
149*cd6d4128SChristophe Branchereau 	gpiod_set_value_cansleep(panel->reset_gpio, 1);
150*cd6d4128SChristophe Branchereau 
151*cd6d4128SChristophe Branchereau 	regulator_disable(panel->supply);
152*cd6d4128SChristophe Branchereau 
153*cd6d4128SChristophe Branchereau 	return 0;
154*cd6d4128SChristophe Branchereau }
155*cd6d4128SChristophe Branchereau 
ota5601a_enable(struct drm_panel * drm_panel)156*cd6d4128SChristophe Branchereau static int ota5601a_enable(struct drm_panel *drm_panel)
157*cd6d4128SChristophe Branchereau {
158*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = to_ota5601a(drm_panel);
159*cd6d4128SChristophe Branchereau 	int err;
160*cd6d4128SChristophe Branchereau 
161*cd6d4128SChristophe Branchereau 	err = regmap_write(panel->map, OTA5601A_CTL, OTA5601A_CTL_ON);
162*cd6d4128SChristophe Branchereau 
163*cd6d4128SChristophe Branchereau 	if (err) {
164*cd6d4128SChristophe Branchereau 		dev_err(drm_panel->dev, "Unable to enable panel: %d\n", err);
165*cd6d4128SChristophe Branchereau 		return err;
166*cd6d4128SChristophe Branchereau 	}
167*cd6d4128SChristophe Branchereau 
168*cd6d4128SChristophe Branchereau 	if (drm_panel->backlight) {
169*cd6d4128SChristophe Branchereau 		/* Wait for the picture to be ready before enabling backlight */
170*cd6d4128SChristophe Branchereau 		msleep(120);
171*cd6d4128SChristophe Branchereau 	}
172*cd6d4128SChristophe Branchereau 
173*cd6d4128SChristophe Branchereau 	return 0;
174*cd6d4128SChristophe Branchereau }
175*cd6d4128SChristophe Branchereau 
ota5601a_disable(struct drm_panel * drm_panel)176*cd6d4128SChristophe Branchereau static int ota5601a_disable(struct drm_panel *drm_panel)
177*cd6d4128SChristophe Branchereau {
178*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = to_ota5601a(drm_panel);
179*cd6d4128SChristophe Branchereau 	int err;
180*cd6d4128SChristophe Branchereau 
181*cd6d4128SChristophe Branchereau 	err = regmap_write(panel->map, OTA5601A_CTL, OTA5601A_CTL_OFF);
182*cd6d4128SChristophe Branchereau 
183*cd6d4128SChristophe Branchereau 	if (err) {
184*cd6d4128SChristophe Branchereau 		dev_err(drm_panel->dev, "Unable to disable panel: %d\n", err);
185*cd6d4128SChristophe Branchereau 		return err;
186*cd6d4128SChristophe Branchereau 	}
187*cd6d4128SChristophe Branchereau 
188*cd6d4128SChristophe Branchereau 	return 0;
189*cd6d4128SChristophe Branchereau }
190*cd6d4128SChristophe Branchereau 
ota5601a_get_modes(struct drm_panel * drm_panel,struct drm_connector * connector)191*cd6d4128SChristophe Branchereau static int ota5601a_get_modes(struct drm_panel *drm_panel,
192*cd6d4128SChristophe Branchereau 			      struct drm_connector *connector)
193*cd6d4128SChristophe Branchereau {
194*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = to_ota5601a(drm_panel);
195*cd6d4128SChristophe Branchereau 	const struct ota5601a_panel_info *panel_info = panel->panel_info;
196*cd6d4128SChristophe Branchereau 	struct drm_display_mode *mode;
197*cd6d4128SChristophe Branchereau 	unsigned int i;
198*cd6d4128SChristophe Branchereau 
199*cd6d4128SChristophe Branchereau 	for (i = 0; i < panel_info->num_modes; i++) {
200*cd6d4128SChristophe Branchereau 		mode = drm_mode_duplicate(connector->dev,
201*cd6d4128SChristophe Branchereau 					  &panel_info->display_modes[i]);
202*cd6d4128SChristophe Branchereau 		if (!mode)
203*cd6d4128SChristophe Branchereau 			return -ENOMEM;
204*cd6d4128SChristophe Branchereau 
205*cd6d4128SChristophe Branchereau 		drm_mode_set_name(mode);
206*cd6d4128SChristophe Branchereau 
207*cd6d4128SChristophe Branchereau 		mode->type = DRM_MODE_TYPE_DRIVER;
208*cd6d4128SChristophe Branchereau 		if (panel_info->num_modes == 1)
209*cd6d4128SChristophe Branchereau 			mode->type |= DRM_MODE_TYPE_PREFERRED;
210*cd6d4128SChristophe Branchereau 
211*cd6d4128SChristophe Branchereau 		drm_mode_probed_add(connector, mode);
212*cd6d4128SChristophe Branchereau 	}
213*cd6d4128SChristophe Branchereau 
214*cd6d4128SChristophe Branchereau 	connector->display_info.bpc = 8;
215*cd6d4128SChristophe Branchereau 	connector->display_info.width_mm = panel_info->width_mm;
216*cd6d4128SChristophe Branchereau 	connector->display_info.height_mm = panel_info->height_mm;
217*cd6d4128SChristophe Branchereau 
218*cd6d4128SChristophe Branchereau 	drm_display_info_set_bus_formats(&connector->display_info,
219*cd6d4128SChristophe Branchereau 					 &panel_info->bus_format, 1);
220*cd6d4128SChristophe Branchereau 	connector->display_info.bus_flags = panel_info->bus_flags;
221*cd6d4128SChristophe Branchereau 
222*cd6d4128SChristophe Branchereau 	return panel_info->num_modes;
223*cd6d4128SChristophe Branchereau }
224*cd6d4128SChristophe Branchereau 
225*cd6d4128SChristophe Branchereau static const struct drm_panel_funcs ota5601a_funcs = {
226*cd6d4128SChristophe Branchereau 	.prepare	= ota5601a_prepare,
227*cd6d4128SChristophe Branchereau 	.unprepare	= ota5601a_unprepare,
228*cd6d4128SChristophe Branchereau 	.enable		= ota5601a_enable,
229*cd6d4128SChristophe Branchereau 	.disable	= ota5601a_disable,
230*cd6d4128SChristophe Branchereau 	.get_modes	= ota5601a_get_modes,
231*cd6d4128SChristophe Branchereau };
232*cd6d4128SChristophe Branchereau 
ota5601a_probe(struct spi_device * spi)233*cd6d4128SChristophe Branchereau static int ota5601a_probe(struct spi_device *spi)
234*cd6d4128SChristophe Branchereau {
235*cd6d4128SChristophe Branchereau 	const struct spi_device_id *id = spi_get_device_id(spi);
236*cd6d4128SChristophe Branchereau 	struct device *dev = &spi->dev;
237*cd6d4128SChristophe Branchereau 	struct ota5601a *panel;
238*cd6d4128SChristophe Branchereau 	int err;
239*cd6d4128SChristophe Branchereau 
240*cd6d4128SChristophe Branchereau 	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
241*cd6d4128SChristophe Branchereau 	if (!panel)
242*cd6d4128SChristophe Branchereau 		return -ENOMEM;
243*cd6d4128SChristophe Branchereau 
244*cd6d4128SChristophe Branchereau 	spi_set_drvdata(spi, panel);
245*cd6d4128SChristophe Branchereau 
246*cd6d4128SChristophe Branchereau 	panel->panel_info = (const struct ota5601a_panel_info *)id->driver_data;
247*cd6d4128SChristophe Branchereau 	if (!panel->panel_info)
248*cd6d4128SChristophe Branchereau 		return -EINVAL;
249*cd6d4128SChristophe Branchereau 
250*cd6d4128SChristophe Branchereau 	panel->supply = devm_regulator_get(dev, "power");
251*cd6d4128SChristophe Branchereau 	if (IS_ERR(panel->supply)) {
252*cd6d4128SChristophe Branchereau 		dev_err(dev, "Failed to get power supply\n");
253*cd6d4128SChristophe Branchereau 		return PTR_ERR(panel->supply);
254*cd6d4128SChristophe Branchereau 	}
255*cd6d4128SChristophe Branchereau 
256*cd6d4128SChristophe Branchereau 	panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
257*cd6d4128SChristophe Branchereau 	if (IS_ERR(panel->reset_gpio)) {
258*cd6d4128SChristophe Branchereau 		dev_err(dev, "Failed to get reset GPIO\n");
259*cd6d4128SChristophe Branchereau 		return PTR_ERR(panel->reset_gpio);
260*cd6d4128SChristophe Branchereau 	}
261*cd6d4128SChristophe Branchereau 
262*cd6d4128SChristophe Branchereau 	spi->bits_per_word = 8;
263*cd6d4128SChristophe Branchereau 	spi->mode = SPI_MODE_3 | SPI_3WIRE;
264*cd6d4128SChristophe Branchereau 	err = spi_setup(spi);
265*cd6d4128SChristophe Branchereau 	if (err) {
266*cd6d4128SChristophe Branchereau 		dev_err(dev, "Failed to setup SPI\n");
267*cd6d4128SChristophe Branchereau 		return err;
268*cd6d4128SChristophe Branchereau 	}
269*cd6d4128SChristophe Branchereau 
270*cd6d4128SChristophe Branchereau 	panel->map = devm_regmap_init_spi(spi, &ota5601a_regmap_config);
271*cd6d4128SChristophe Branchereau 	if (IS_ERR(panel->map)) {
272*cd6d4128SChristophe Branchereau 		dev_err(dev, "Failed to init regmap\n");
273*cd6d4128SChristophe Branchereau 		return PTR_ERR(panel->map);
274*cd6d4128SChristophe Branchereau 	}
275*cd6d4128SChristophe Branchereau 
276*cd6d4128SChristophe Branchereau 	drm_panel_init(&panel->drm_panel, dev, &ota5601a_funcs,
277*cd6d4128SChristophe Branchereau 		       DRM_MODE_CONNECTOR_DPI);
278*cd6d4128SChristophe Branchereau 
279*cd6d4128SChristophe Branchereau 	err = drm_panel_of_backlight(&panel->drm_panel);
280*cd6d4128SChristophe Branchereau 	if (err) {
281*cd6d4128SChristophe Branchereau 		if (err != -EPROBE_DEFER)
282*cd6d4128SChristophe Branchereau 			dev_err(dev, "Failed to get backlight handle\n");
283*cd6d4128SChristophe Branchereau 		return err;
284*cd6d4128SChristophe Branchereau 	}
285*cd6d4128SChristophe Branchereau 
286*cd6d4128SChristophe Branchereau 	drm_panel_add(&panel->drm_panel);
287*cd6d4128SChristophe Branchereau 
288*cd6d4128SChristophe Branchereau 	return 0;
289*cd6d4128SChristophe Branchereau }
290*cd6d4128SChristophe Branchereau 
ota5601a_remove(struct spi_device * spi)291*cd6d4128SChristophe Branchereau static void ota5601a_remove(struct spi_device *spi)
292*cd6d4128SChristophe Branchereau {
293*cd6d4128SChristophe Branchereau 	struct ota5601a *panel = spi_get_drvdata(spi);
294*cd6d4128SChristophe Branchereau 
295*cd6d4128SChristophe Branchereau 	drm_panel_remove(&panel->drm_panel);
296*cd6d4128SChristophe Branchereau 
297*cd6d4128SChristophe Branchereau 	ota5601a_disable(&panel->drm_panel);
298*cd6d4128SChristophe Branchereau 	ota5601a_unprepare(&panel->drm_panel);
299*cd6d4128SChristophe Branchereau }
300*cd6d4128SChristophe Branchereau 
301*cd6d4128SChristophe Branchereau static const struct drm_display_mode gpt3_display_modes[] = {
302*cd6d4128SChristophe Branchereau 	{ /* 60 Hz */
303*cd6d4128SChristophe Branchereau 		.clock = 27000,
304*cd6d4128SChristophe Branchereau 		.hdisplay = 640,
305*cd6d4128SChristophe Branchereau 		.hsync_start = 640 + 220,
306*cd6d4128SChristophe Branchereau 		.hsync_end = 640 + 220 + 20,
307*cd6d4128SChristophe Branchereau 		.htotal = 640 + 220 + 20 + 20,
308*cd6d4128SChristophe Branchereau 		.vdisplay = 480,
309*cd6d4128SChristophe Branchereau 		.vsync_start = 480 + 7,
310*cd6d4128SChristophe Branchereau 		.vsync_end = 480 + 7 + 6,
311*cd6d4128SChristophe Branchereau 		.vtotal = 480 + 7 + 6 + 7,
312*cd6d4128SChristophe Branchereau 		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
313*cd6d4128SChristophe Branchereau 	},
314*cd6d4128SChristophe Branchereau 
315*cd6d4128SChristophe Branchereau 	{ /* 50 Hz */
316*cd6d4128SChristophe Branchereau 		.clock = 24000,
317*cd6d4128SChristophe Branchereau 		.hdisplay = 640,
318*cd6d4128SChristophe Branchereau 		.hsync_start = 640 + 280,
319*cd6d4128SChristophe Branchereau 		.hsync_end = 640 + 280 + 20,
320*cd6d4128SChristophe Branchereau 		.htotal = 640 + 280 + 20 + 20,
321*cd6d4128SChristophe Branchereau 		.vdisplay = 480,
322*cd6d4128SChristophe Branchereau 		.vsync_start = 480 + 7,
323*cd6d4128SChristophe Branchereau 		.vsync_end = 480 + 7 + 6,
324*cd6d4128SChristophe Branchereau 		.vtotal = 480 + 7 + 6 + 7,
325*cd6d4128SChristophe Branchereau 		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
326*cd6d4128SChristophe Branchereau 	},
327*cd6d4128SChristophe Branchereau };
328*cd6d4128SChristophe Branchereau 
329*cd6d4128SChristophe Branchereau static const struct ota5601a_panel_info gpt3_info = {
330*cd6d4128SChristophe Branchereau 	.display_modes = gpt3_display_modes,
331*cd6d4128SChristophe Branchereau 	.num_modes = ARRAY_SIZE(gpt3_display_modes),
332*cd6d4128SChristophe Branchereau 	.width_mm = 71,
333*cd6d4128SChristophe Branchereau 	.height_mm = 51,
334*cd6d4128SChristophe Branchereau 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
335*cd6d4128SChristophe Branchereau 	.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
336*cd6d4128SChristophe Branchereau };
337*cd6d4128SChristophe Branchereau 
338*cd6d4128SChristophe Branchereau static const struct spi_device_id gpt3_id[] = {
339*cd6d4128SChristophe Branchereau 	{ "gpt3", (kernel_ulong_t)&gpt3_info },
340*cd6d4128SChristophe Branchereau 	{ /* sentinel */ }
341*cd6d4128SChristophe Branchereau };
342*cd6d4128SChristophe Branchereau MODULE_DEVICE_TABLE(spi, gpt3_id);
343*cd6d4128SChristophe Branchereau 
344*cd6d4128SChristophe Branchereau static const struct of_device_id ota5601a_of_match[] = {
345*cd6d4128SChristophe Branchereau 	{ .compatible = "focaltech,gpt3" },
346*cd6d4128SChristophe Branchereau 	{ /* sentinel */ }
347*cd6d4128SChristophe Branchereau };
348*cd6d4128SChristophe Branchereau MODULE_DEVICE_TABLE(of, ota5601a_of_match);
349*cd6d4128SChristophe Branchereau 
350*cd6d4128SChristophe Branchereau static struct spi_driver ota5601a_driver = {
351*cd6d4128SChristophe Branchereau 	.driver = {
352*cd6d4128SChristophe Branchereau 		.name = "ota5601a",
353*cd6d4128SChristophe Branchereau 		.of_match_table = ota5601a_of_match,
354*cd6d4128SChristophe Branchereau 	},
355*cd6d4128SChristophe Branchereau 	.id_table = gpt3_id,
356*cd6d4128SChristophe Branchereau 	.probe = ota5601a_probe,
357*cd6d4128SChristophe Branchereau 	.remove = ota5601a_remove,
358*cd6d4128SChristophe Branchereau };
359*cd6d4128SChristophe Branchereau 
360*cd6d4128SChristophe Branchereau module_spi_driver(ota5601a_driver);
361*cd6d4128SChristophe Branchereau 
362*cd6d4128SChristophe Branchereau MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
363*cd6d4128SChristophe Branchereau MODULE_LICENSE("GPL");
364