xref: /OK3568_Linux_fs/kernel/drivers/video/backlight/otm3225a.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Driver for ORISE Technology OTM3225A SOC for TFT LCD
3*4882a593Smuzhiyun  * Copyright (C) 2017, EETS GmbH, Felix Brack <fb@ltec.ch>
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This driver implements a lcd device for the ORISE OTM3225A display
6*4882a593Smuzhiyun  * controller. The control interface to the display is SPI and the display's
7*4882a593Smuzhiyun  * memory is updated over the 16-bit RGB interface.
8*4882a593Smuzhiyun  * The main source of information for writing this driver was provided by the
9*4882a593Smuzhiyun  * OTM3225A datasheet from ORISE Technology. Some information arise from the
10*4882a593Smuzhiyun  * ILI9328 datasheet from ILITEK as well as from the datasheets and sample code
11*4882a593Smuzhiyun  * provided by Crystalfontz America Inc. who sells the CFAF240320A-032T, a 3.2"
12*4882a593Smuzhiyun  * TFT LC display using the OTM3225A controller.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/delay.h>
16*4882a593Smuzhiyun #include <linux/device.h>
17*4882a593Smuzhiyun #include <linux/kernel.h>
18*4882a593Smuzhiyun #include <linux/lcd.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/spi/spi.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define OTM3225A_INDEX_REG	0x70
23*4882a593Smuzhiyun #define OTM3225A_DATA_REG	0x72
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* instruction register list */
26*4882a593Smuzhiyun #define DRIVER_OUTPUT_CTRL_1	0x01
27*4882a593Smuzhiyun #define DRIVER_WAVEFORM_CTRL	0x02
28*4882a593Smuzhiyun #define ENTRY_MODE		0x03
29*4882a593Smuzhiyun #define SCALING_CTRL		0x04
30*4882a593Smuzhiyun #define DISPLAY_CTRL_1		0x07
31*4882a593Smuzhiyun #define DISPLAY_CTRL_2		0x08
32*4882a593Smuzhiyun #define DISPLAY_CTRL_3		0x09
33*4882a593Smuzhiyun #define FRAME_CYCLE_CTRL	0x0A
34*4882a593Smuzhiyun #define EXT_DISP_IFACE_CTRL_1	0x0C
35*4882a593Smuzhiyun #define FRAME_MAKER_POS		0x0D
36*4882a593Smuzhiyun #define EXT_DISP_IFACE_CTRL_2	0x0F
37*4882a593Smuzhiyun #define POWER_CTRL_1		0x10
38*4882a593Smuzhiyun #define POWER_CTRL_2		0x11
39*4882a593Smuzhiyun #define POWER_CTRL_3		0x12
40*4882a593Smuzhiyun #define POWER_CTRL_4		0x13
41*4882a593Smuzhiyun #define GRAM_ADDR_HORIZ_SET	0x20
42*4882a593Smuzhiyun #define GRAM_ADDR_VERT_SET	0x21
43*4882a593Smuzhiyun #define GRAM_READ_WRITE		0x22
44*4882a593Smuzhiyun #define POWER_CTRL_7		0x29
45*4882a593Smuzhiyun #define FRAME_RATE_CTRL		0x2B
46*4882a593Smuzhiyun #define GAMMA_CTRL_1		0x30
47*4882a593Smuzhiyun #define GAMMA_CTRL_2		0x31
48*4882a593Smuzhiyun #define GAMMA_CTRL_3		0x32
49*4882a593Smuzhiyun #define GAMMA_CTRL_4		0x35
50*4882a593Smuzhiyun #define GAMMA_CTRL_5		0x36
51*4882a593Smuzhiyun #define GAMMA_CTRL_6		0x37
52*4882a593Smuzhiyun #define GAMMA_CTRL_7		0x38
53*4882a593Smuzhiyun #define GAMMA_CTRL_8		0x39
54*4882a593Smuzhiyun #define GAMMA_CTRL_9		0x3C
55*4882a593Smuzhiyun #define GAMMA_CTRL_10		0x3D
56*4882a593Smuzhiyun #define WINDOW_HORIZ_RAM_START	0x50
57*4882a593Smuzhiyun #define WINDOW_HORIZ_RAM_END	0x51
58*4882a593Smuzhiyun #define WINDOW_VERT_RAM_START	0x52
59*4882a593Smuzhiyun #define WINDOW_VERT_RAM_END	0x53
60*4882a593Smuzhiyun #define DRIVER_OUTPUT_CTRL_2	0x60
61*4882a593Smuzhiyun #define BASE_IMG_DISPLAY_CTRL	0x61
62*4882a593Smuzhiyun #define VERT_SCROLL_CTRL	0x6A
63*4882a593Smuzhiyun #define PD1_DISPLAY_POS		0x80
64*4882a593Smuzhiyun #define PD1_RAM_START		0x81
65*4882a593Smuzhiyun #define PD1_RAM_END		0x82
66*4882a593Smuzhiyun #define PD2_DISPLAY_POS		0x83
67*4882a593Smuzhiyun #define PD2_RAM_START		0x84
68*4882a593Smuzhiyun #define PD2_RAM_END		0x85
69*4882a593Smuzhiyun #define PANEL_IFACE_CTRL_1	0x90
70*4882a593Smuzhiyun #define PANEL_IFACE_CTRL_2	0x92
71*4882a593Smuzhiyun #define PANEL_IFACE_CTRL_4	0x95
72*4882a593Smuzhiyun #define PANEL_IFACE_CTRL_5	0x97
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun struct otm3225a_data {
75*4882a593Smuzhiyun 	struct spi_device *spi;
76*4882a593Smuzhiyun 	struct lcd_device *ld;
77*4882a593Smuzhiyun 	int power;
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun struct otm3225a_spi_instruction {
81*4882a593Smuzhiyun 	unsigned char reg;	/* register to write */
82*4882a593Smuzhiyun 	unsigned short value;	/* data to write to 'reg' */
83*4882a593Smuzhiyun 	unsigned short delay;	/* delay in ms after write */
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun static struct otm3225a_spi_instruction display_init[] = {
87*4882a593Smuzhiyun 	{ DRIVER_OUTPUT_CTRL_1,		0x0000, 0 },
88*4882a593Smuzhiyun 	{ DRIVER_WAVEFORM_CTRL,		0x0700, 0 },
89*4882a593Smuzhiyun 	{ ENTRY_MODE,			0x50A0, 0 },
90*4882a593Smuzhiyun 	{ SCALING_CTRL,			0x0000, 0 },
91*4882a593Smuzhiyun 	{ DISPLAY_CTRL_2,		0x0606, 0 },
92*4882a593Smuzhiyun 	{ DISPLAY_CTRL_3,		0x0000, 0 },
93*4882a593Smuzhiyun 	{ FRAME_CYCLE_CTRL,		0x0000, 0 },
94*4882a593Smuzhiyun 	{ EXT_DISP_IFACE_CTRL_1,	0x0000, 0 },
95*4882a593Smuzhiyun 	{ FRAME_MAKER_POS,		0x0000, 0 },
96*4882a593Smuzhiyun 	{ EXT_DISP_IFACE_CTRL_2,	0x0002, 0 },
97*4882a593Smuzhiyun 	{ POWER_CTRL_2,			0x0007, 0 },
98*4882a593Smuzhiyun 	{ POWER_CTRL_3,			0x0000, 0 },
99*4882a593Smuzhiyun 	{ POWER_CTRL_4,			0x0000, 200 },
100*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,		0x0101, 0 },
101*4882a593Smuzhiyun 	{ POWER_CTRL_1,			0x12B0, 0 },
102*4882a593Smuzhiyun 	{ POWER_CTRL_2,			0x0007, 0 },
103*4882a593Smuzhiyun 	{ POWER_CTRL_3,			0x01BB, 50 },
104*4882a593Smuzhiyun 	{ POWER_CTRL_4,			0x0013, 0 },
105*4882a593Smuzhiyun 	{ POWER_CTRL_7,			0x0010, 50 },
106*4882a593Smuzhiyun 	{ GAMMA_CTRL_1,			0x000A, 0 },
107*4882a593Smuzhiyun 	{ GAMMA_CTRL_2,			0x1326, 0 },
108*4882a593Smuzhiyun 	{ GAMMA_CTRL_3,			0x0A29, 0 },
109*4882a593Smuzhiyun 	{ GAMMA_CTRL_4,			0x0A0A, 0 },
110*4882a593Smuzhiyun 	{ GAMMA_CTRL_5,			0x1E03, 0 },
111*4882a593Smuzhiyun 	{ GAMMA_CTRL_6,			0x031E, 0 },
112*4882a593Smuzhiyun 	{ GAMMA_CTRL_7,			0x0706, 0 },
113*4882a593Smuzhiyun 	{ GAMMA_CTRL_8,			0x0303, 0 },
114*4882a593Smuzhiyun 	{ GAMMA_CTRL_9,			0x010E, 0 },
115*4882a593Smuzhiyun 	{ GAMMA_CTRL_10,		0x040E, 0 },
116*4882a593Smuzhiyun 	{ WINDOW_HORIZ_RAM_START,	0x0000, 0 },
117*4882a593Smuzhiyun 	{ WINDOW_HORIZ_RAM_END,		0x00EF, 0 },
118*4882a593Smuzhiyun 	{ WINDOW_VERT_RAM_START,	0x0000, 0 },
119*4882a593Smuzhiyun 	{ WINDOW_VERT_RAM_END,		0x013F, 0 },
120*4882a593Smuzhiyun 	{ DRIVER_OUTPUT_CTRL_2,		0x2700, 0 },
121*4882a593Smuzhiyun 	{ BASE_IMG_DISPLAY_CTRL,	0x0001, 0 },
122*4882a593Smuzhiyun 	{ VERT_SCROLL_CTRL,		0x0000, 0 },
123*4882a593Smuzhiyun 	{ PD1_DISPLAY_POS,		0x0000, 0 },
124*4882a593Smuzhiyun 	{ PD1_RAM_START,		0x0000, 0 },
125*4882a593Smuzhiyun 	{ PD1_RAM_END,			0x0000, 0 },
126*4882a593Smuzhiyun 	{ PD2_DISPLAY_POS,		0x0000, 0 },
127*4882a593Smuzhiyun 	{ PD2_RAM_START,		0x0000, 0 },
128*4882a593Smuzhiyun 	{ PD2_RAM_END,			0x0000, 0 },
129*4882a593Smuzhiyun 	{ PANEL_IFACE_CTRL_1,		0x0010, 0 },
130*4882a593Smuzhiyun 	{ PANEL_IFACE_CTRL_2,		0x0000, 0 },
131*4882a593Smuzhiyun 	{ PANEL_IFACE_CTRL_4,		0x0210, 0 },
132*4882a593Smuzhiyun 	{ PANEL_IFACE_CTRL_5,		0x0000, 0 },
133*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,		0x0133, 0 },
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun static struct otm3225a_spi_instruction display_enable_rgb_interface[] = {
137*4882a593Smuzhiyun 	{ ENTRY_MODE,			0x1080, 0 },
138*4882a593Smuzhiyun 	{ GRAM_ADDR_HORIZ_SET,		0x0000, 0 },
139*4882a593Smuzhiyun 	{ GRAM_ADDR_VERT_SET,		0x0000, 0 },
140*4882a593Smuzhiyun 	{ EXT_DISP_IFACE_CTRL_1,	0x0111, 500 },
141*4882a593Smuzhiyun };
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun static struct otm3225a_spi_instruction display_off[] = {
144*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0131, 100 },
145*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0130, 100 },
146*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0100, 0 },
147*4882a593Smuzhiyun 	{ POWER_CTRL_1,		0x0280, 0 },
148*4882a593Smuzhiyun 	{ POWER_CTRL_3,		0x018B, 0 },
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun static struct otm3225a_spi_instruction display_on[] = {
152*4882a593Smuzhiyun 	{ POWER_CTRL_1,		0x1280, 0 },
153*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0101, 100 },
154*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0121, 0 },
155*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0123, 100 },
156*4882a593Smuzhiyun 	{ DISPLAY_CTRL_1,	0x0133, 10 },
157*4882a593Smuzhiyun };
158*4882a593Smuzhiyun 
otm3225a_write(struct spi_device * spi,struct otm3225a_spi_instruction * instruction,unsigned int count)159*4882a593Smuzhiyun static void otm3225a_write(struct spi_device *spi,
160*4882a593Smuzhiyun 			   struct otm3225a_spi_instruction *instruction,
161*4882a593Smuzhiyun 			   unsigned int count)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	unsigned char buf[3];
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	while (count--) {
166*4882a593Smuzhiyun 		/* address register using index register */
167*4882a593Smuzhiyun 		buf[0] = OTM3225A_INDEX_REG;
168*4882a593Smuzhiyun 		buf[1] = 0x00;
169*4882a593Smuzhiyun 		buf[2] = instruction->reg;
170*4882a593Smuzhiyun 		spi_write(spi, buf, 3);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 		/* write data to addressed register */
173*4882a593Smuzhiyun 		buf[0] = OTM3225A_DATA_REG;
174*4882a593Smuzhiyun 		buf[1] = (instruction->value >> 8) & 0xff;
175*4882a593Smuzhiyun 		buf[2] = instruction->value & 0xff;
176*4882a593Smuzhiyun 		spi_write(spi, buf, 3);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 		/* execute delay if any */
179*4882a593Smuzhiyun 		if (instruction->delay)
180*4882a593Smuzhiyun 			msleep(instruction->delay);
181*4882a593Smuzhiyun 		instruction++;
182*4882a593Smuzhiyun 	}
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
otm3225a_set_power(struct lcd_device * ld,int power)185*4882a593Smuzhiyun static int otm3225a_set_power(struct lcd_device *ld, int power)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct otm3225a_data *dd = lcd_get_data(ld);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	if (power == dd->power)
190*4882a593Smuzhiyun 		return 0;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	if (power > FB_BLANK_UNBLANK)
193*4882a593Smuzhiyun 		otm3225a_write(dd->spi, display_off, ARRAY_SIZE(display_off));
194*4882a593Smuzhiyun 	else
195*4882a593Smuzhiyun 		otm3225a_write(dd->spi, display_on, ARRAY_SIZE(display_on));
196*4882a593Smuzhiyun 	dd->power = power;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	return 0;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
otm3225a_get_power(struct lcd_device * ld)201*4882a593Smuzhiyun static int otm3225a_get_power(struct lcd_device *ld)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	struct otm3225a_data *dd = lcd_get_data(ld);
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	return dd->power;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun static struct lcd_ops otm3225a_ops = {
209*4882a593Smuzhiyun 	.set_power = otm3225a_set_power,
210*4882a593Smuzhiyun 	.get_power = otm3225a_get_power,
211*4882a593Smuzhiyun };
212*4882a593Smuzhiyun 
otm3225a_probe(struct spi_device * spi)213*4882a593Smuzhiyun static int otm3225a_probe(struct spi_device *spi)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	struct otm3225a_data *dd;
216*4882a593Smuzhiyun 	struct lcd_device *ld;
217*4882a593Smuzhiyun 	struct device *dev = &spi->dev;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	dd = devm_kzalloc(dev, sizeof(struct otm3225a_data), GFP_KERNEL);
220*4882a593Smuzhiyun 	if (dd == NULL)
221*4882a593Smuzhiyun 		return -ENOMEM;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	ld = devm_lcd_device_register(dev, dev_name(dev), dev, dd,
224*4882a593Smuzhiyun 				      &otm3225a_ops);
225*4882a593Smuzhiyun 	if (IS_ERR(ld))
226*4882a593Smuzhiyun 		return PTR_ERR(ld);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	dd->spi = spi;
229*4882a593Smuzhiyun 	dd->ld = ld;
230*4882a593Smuzhiyun 	dev_set_drvdata(dev, dd);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	dev_info(dev, "Initializing and switching to RGB interface");
233*4882a593Smuzhiyun 	otm3225a_write(spi, display_init, ARRAY_SIZE(display_init));
234*4882a593Smuzhiyun 	otm3225a_write(spi, display_enable_rgb_interface,
235*4882a593Smuzhiyun 		       ARRAY_SIZE(display_enable_rgb_interface));
236*4882a593Smuzhiyun 	return 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun static struct spi_driver otm3225a_driver = {
240*4882a593Smuzhiyun 	.driver = {
241*4882a593Smuzhiyun 		.name = "otm3225a",
242*4882a593Smuzhiyun 		.owner = THIS_MODULE,
243*4882a593Smuzhiyun 	},
244*4882a593Smuzhiyun 	.probe = otm3225a_probe,
245*4882a593Smuzhiyun };
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun module_spi_driver(otm3225a_driver);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun MODULE_AUTHOR("Felix Brack <fb@ltec.ch>");
250*4882a593Smuzhiyun MODULE_DESCRIPTION("OTM3225A TFT LCD driver");
251*4882a593Smuzhiyun MODULE_VERSION("1.0.0");
252*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
253