]>
Commit | Line | Data |
---|---|---|
f1a74918 NK |
1 | /* |
2 | * scf0403.c -- support for DataImage SCF0403 LCD | |
3 | * | |
4 | * Copyright (c) 2013 Adapted from Linux driver: | |
5 | * Copyright (c) 2012 Anders Electronics plc. All Rights Reserved. | |
6 | * Copyright (c) 2012 CompuLab, Ltd | |
7 | * Dmitry Lifshitz <[email protected]> | |
8 | * Ilya Ledvich <[email protected]> | |
9 | * Inspired by Alberto Panizzo <[email protected]> & | |
10 | * Marek Vasut work in l4f00242t03.c | |
11 | * | |
12 | * U-Boot port: Nikita Kiryanov <[email protected]> | |
13 | * | |
14 | * SPDX-License-Identifier: GPL-2.0+ | |
15 | */ | |
16 | ||
17 | #include <common.h> | |
18 | #include <asm/gpio.h> | |
19 | #include <spi.h> | |
20 | ||
21 | struct scf0403_cmd { | |
22 | u16 cmd; | |
23 | u16 *params; | |
24 | int count; | |
25 | }; | |
26 | ||
27 | struct scf0403_initseq_entry { | |
28 | struct scf0403_cmd cmd; | |
29 | int delay_ms; | |
30 | }; | |
31 | ||
32 | struct scf0403_priv { | |
33 | struct spi_slave *spi; | |
34 | unsigned int reset_gpio; | |
35 | u32 rddid; | |
36 | struct scf0403_initseq_entry *init_seq; | |
37 | int seq_size; | |
38 | }; | |
39 | ||
40 | struct scf0403_priv priv; | |
41 | ||
42 | #define SCF0403852GGU04_ID 0x000080 | |
43 | ||
44 | /* SCF0403526GGU20 model commands parameters */ | |
45 | static u16 extcmd_params_sn20[] = {0xff, 0x98, 0x06}; | |
46 | static u16 spiinttype_params_sn20[] = {0x60}; | |
47 | static u16 bc_params_sn20[] = { | |
48 | 0x01, 0x10, 0x61, 0x74, 0x01, 0x01, 0x1B, | |
49 | 0x12, 0x71, 0x00, 0x00, 0x00, 0x01, 0x01, | |
50 | 0x05, 0x00, 0xFF, 0xF2, 0x01, 0x00, 0x40, | |
51 | }; | |
52 | static u16 bd_params_sn20[] = {0x01, 0x23, 0x45, 0x67, 0x01, 0x23, 0x45, 0x67}; | |
53 | static u16 be_params_sn20[] = { | |
54 | 0x01, 0x22, 0x22, 0xBA, 0xDC, 0x26, 0x28, 0x22, 0x22, | |
55 | }; | |
56 | static u16 vcom_params_sn20[] = {0x74}; | |
57 | static u16 vmesur_params_sn20[] = {0x7F, 0x0F, 0x00}; | |
58 | static u16 powerctl_params_sn20[] = {0x03, 0x0b, 0x00}; | |
59 | static u16 lvglvolt_params_sn20[] = {0x08}; | |
60 | static u16 engsetting_params_sn20[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x20}; | |
61 | static u16 dispfunc_params_sn20[] = {0xa0}; | |
62 | static u16 dvddvolt_params_sn20[] = {0x74}; | |
63 | static u16 dispinv_params_sn20[] = {0x00, 0x00, 0x00}; | |
64 | static u16 panelres_params_sn20[] = {0x82}; | |
65 | static u16 framerate_params_sn20[] = {0x00, 0x13, 0x13}; | |
66 | static u16 timing_params_sn20[] = {0x80, 0x05, 0x40, 0x28}; | |
67 | static u16 powerctl2_params_sn20[] = {0x17, 0x75, 0x79, 0x20}; | |
68 | static u16 memaccess_params_sn20[] = {0x00}; | |
69 | static u16 pixfmt_params_sn20[] = {0x66}; | |
70 | static u16 pgamma_params_sn20[] = { | |
71 | 0x00, 0x03, 0x0b, 0x0c, 0x0e, 0x08, 0xc5, 0x04, | |
72 | 0x08, 0x0c, 0x13, 0x11, 0x11, 0x14, 0x0c, 0x10, | |
73 | }; | |
74 | static u16 ngamma_params_sn20[] = { | |
75 | 0x00, 0x0d, 0x11, 0x0c, 0x0c, 0x04, 0x76, 0x03, | |
76 | 0x08, 0x0b, 0x16, 0x10, 0x0d, 0x16, 0x0a, 0x00, | |
77 | }; | |
78 | static u16 tearing_params_sn20[] = {0x00}; | |
79 | ||
80 | /* SCF0403852GGU04 model commands parameters */ | |
81 | static u16 memaccess_params_sn04[] = {0x08}; | |
82 | static u16 pixfmt_params_sn04[] = {0x66}; | |
83 | static u16 modectl_params_sn04[] = {0x01}; | |
84 | static u16 dispfunc_params_sn04[] = {0x22, 0xe2, 0xFF, 0x04}; | |
85 | static u16 vcom_params_sn04[] = {0x00, 0x6A}; | |
86 | static u16 pgamma_params_sn04[] = { | |
87 | 0x00, 0x07, 0x0d, 0x10, 0x13, 0x19, 0x0f, 0x0c, | |
88 | 0x05, 0x08, 0x06, 0x13, 0x0f, 0x30, 0x20, 0x1f, | |
89 | }; | |
90 | static u16 ngamma_params_sn04[] = { | |
91 | 0x1F, 0x20, 0x30, 0x0F, 0x13, 0x06, 0x08, 0x05, | |
92 | 0x0C, 0x0F, 0x19, 0x13, 0x10, 0x0D, 0x07, 0x00, | |
93 | }; | |
94 | static u16 dispinv_params_sn04[] = {0x02}; | |
95 | ||
96 | /* Common commands */ | |
97 | static struct scf0403_cmd scf0403_cmd_slpout = {0x11, NULL, 0}; | |
98 | static struct scf0403_cmd scf0403_cmd_dison = {0x29, NULL, 0}; | |
99 | ||
100 | /* SCF0403852GGU04 init sequence */ | |
101 | static struct scf0403_initseq_entry scf0403_initseq_sn04[] = { | |
102 | {{0x36, memaccess_params_sn04, ARRAY_SIZE(memaccess_params_sn04)}, 0}, | |
103 | {{0x3A, pixfmt_params_sn04, ARRAY_SIZE(pixfmt_params_sn04)}, 0}, | |
104 | {{0xB6, dispfunc_params_sn04, ARRAY_SIZE(dispfunc_params_sn04)}, 0}, | |
105 | {{0xC5, vcom_params_sn04, ARRAY_SIZE(vcom_params_sn04)}, 0}, | |
106 | {{0xE0, pgamma_params_sn04, ARRAY_SIZE(pgamma_params_sn04)}, 0}, | |
107 | {{0xE1, ngamma_params_sn04, ARRAY_SIZE(ngamma_params_sn04)}, 20}, | |
108 | {{0xB0, modectl_params_sn04, ARRAY_SIZE(modectl_params_sn04)}, 0}, | |
109 | {{0xB4, dispinv_params_sn04, ARRAY_SIZE(dispinv_params_sn04)}, 100}, | |
110 | }; | |
111 | ||
112 | /* SCF0403526GGU20 init sequence */ | |
113 | static struct scf0403_initseq_entry scf0403_initseq_sn20[] = { | |
114 | {{0xff, extcmd_params_sn20, ARRAY_SIZE(extcmd_params_sn20)}, 0}, | |
115 | {{0xba, spiinttype_params_sn20, ARRAY_SIZE(spiinttype_params_sn20)}, 0}, | |
116 | {{0xbc, bc_params_sn20, ARRAY_SIZE(bc_params_sn20)}, 0}, | |
117 | {{0xbd, bd_params_sn20, ARRAY_SIZE(bd_params_sn20)}, 0}, | |
118 | {{0xbe, be_params_sn20, ARRAY_SIZE(be_params_sn20)}, 0}, | |
119 | {{0xc7, vcom_params_sn20, ARRAY_SIZE(vcom_params_sn20)}, 0}, | |
120 | {{0xed, vmesur_params_sn20, ARRAY_SIZE(vmesur_params_sn20)}, 0}, | |
121 | {{0xc0, powerctl_params_sn20, ARRAY_SIZE(powerctl_params_sn20)}, 0}, | |
122 | {{0xfc, lvglvolt_params_sn20, ARRAY_SIZE(lvglvolt_params_sn20)}, 0}, | |
123 | {{0xb6, dispfunc_params_sn20, ARRAY_SIZE(dispfunc_params_sn20)}, 0}, | |
124 | {{0xdf, engsetting_params_sn20, ARRAY_SIZE(engsetting_params_sn20)}, 0}, | |
125 | {{0xf3, dvddvolt_params_sn20, ARRAY_SIZE(dvddvolt_params_sn20)}, 0}, | |
126 | {{0xb4, dispinv_params_sn20, ARRAY_SIZE(dispinv_params_sn20)}, 0}, | |
127 | {{0xf7, panelres_params_sn20, ARRAY_SIZE(panelres_params_sn20)}, 0}, | |
128 | {{0xb1, framerate_params_sn20, ARRAY_SIZE(framerate_params_sn20)}, 0}, | |
129 | {{0xf2, timing_params_sn20, ARRAY_SIZE(timing_params_sn20)}, 0}, | |
130 | {{0xc1, powerctl2_params_sn20, ARRAY_SIZE(powerctl2_params_sn20)}, 0}, | |
131 | {{0x36, memaccess_params_sn20, ARRAY_SIZE(memaccess_params_sn20)}, 0}, | |
132 | {{0x3a, pixfmt_params_sn20, ARRAY_SIZE(pixfmt_params_sn20)}, 0}, | |
133 | {{0xe0, pgamma_params_sn20, ARRAY_SIZE(pgamma_params_sn20)}, 0}, | |
134 | {{0xe1, ngamma_params_sn20, ARRAY_SIZE(ngamma_params_sn20)}, 0}, | |
135 | {{0x35, tearing_params_sn20, ARRAY_SIZE(tearing_params_sn20)}, 0}, | |
136 | }; | |
137 | ||
138 | static void scf0403_gpio_reset(unsigned int gpio) | |
139 | { | |
140 | if (!gpio_is_valid(gpio)) | |
141 | return; | |
142 | ||
143 | gpio_set_value(gpio, 1); | |
144 | mdelay(100); | |
145 | gpio_set_value(gpio, 0); | |
146 | mdelay(40); | |
147 | gpio_set_value(gpio, 1); | |
148 | mdelay(100); | |
149 | } | |
150 | ||
151 | static int scf0403_spi_read_rddid(struct spi_slave *spi, u32 *rddid) | |
152 | { | |
153 | int error = 0; | |
154 | u8 ids_buf = 0x00; | |
155 | u16 dummy_buf = 0x00; | |
156 | u16 cmd = 0x04; | |
157 | ||
158 | error = spi_set_wordlen(spi, 9); | |
159 | if (error) | |
160 | return error; | |
161 | ||
162 | /* Here 9 bits required to transmit a command */ | |
163 | error = spi_xfer(spi, 9, &cmd, NULL, SPI_XFER_ONCE); | |
164 | if (error) | |
165 | return error; | |
166 | ||
167 | /* | |
168 | * Here 8 + 1 bits required to arrange extra clock cycle | |
169 | * before the first data bit. | |
170 | * According to the datasheet - first parameter is the dummy data. | |
171 | */ | |
172 | error = spi_xfer(spi, 9, NULL, &dummy_buf, SPI_XFER_ONCE); | |
173 | if (error) | |
174 | return error; | |
175 | ||
176 | error = spi_set_wordlen(spi, 8); | |
177 | if (error) | |
178 | return error; | |
179 | ||
180 | /* Read rest of the data */ | |
181 | error = spi_xfer(spi, 8, NULL, &ids_buf, SPI_XFER_ONCE); | |
182 | if (error) | |
183 | return error; | |
184 | ||
185 | *rddid = ids_buf; | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int scf0403_spi_transfer(struct spi_slave *spi, struct scf0403_cmd *cmd) | |
191 | { | |
192 | int i, error; | |
193 | u32 command = cmd->cmd; | |
194 | u32 msg; | |
195 | ||
196 | error = spi_set_wordlen(spi, 9); | |
197 | if (error) | |
198 | return error; | |
199 | ||
200 | error = spi_xfer(spi, 9, &command, NULL, SPI_XFER_ONCE); | |
201 | if (error) | |
202 | return error; | |
203 | ||
204 | for (i = 0; i < cmd->count; i++) { | |
205 | msg = (cmd->params[i] | 0x100); | |
206 | error = spi_xfer(spi, 9, &msg, NULL, SPI_XFER_ONCE); | |
207 | if (error) | |
208 | return error; | |
209 | } | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | static void scf0403_lcd_init(struct scf0403_priv *priv) | |
215 | { | |
216 | int i; | |
217 | ||
218 | /* reset LCD */ | |
219 | scf0403_gpio_reset(priv->reset_gpio); | |
220 | ||
221 | for (i = 0; i < priv->seq_size; i++) { | |
222 | if (scf0403_spi_transfer(priv->spi, &priv->init_seq[i].cmd) < 0) | |
223 | puts("SPI transfer failed\n"); | |
224 | ||
225 | mdelay(priv->init_seq[i].delay_ms); | |
226 | } | |
227 | } | |
228 | ||
229 | static int scf0403_request_reset_gpio(unsigned gpio) | |
230 | { | |
231 | int err = gpio_request(gpio, "lcd reset"); | |
232 | ||
233 | if (err) | |
234 | return err; | |
235 | ||
236 | err = gpio_direction_output(gpio, 0); | |
237 | if (err) | |
238 | gpio_free(gpio); | |
239 | ||
240 | return err; | |
241 | } | |
242 | ||
243 | int scf0403_init(int reset_gpio) | |
244 | { | |
245 | int error; | |
246 | ||
247 | if (gpio_is_valid(reset_gpio)) { | |
248 | error = scf0403_request_reset_gpio(reset_gpio); | |
249 | if (error) { | |
250 | printf("Failed requesting reset GPIO%d: %d\n", | |
251 | reset_gpio, error); | |
252 | return error; | |
253 | } | |
254 | } | |
255 | ||
256 | priv.reset_gpio = reset_gpio; | |
257 | priv.spi = spi_setup_slave(3, 0, 1000000, SPI_MODE_0); | |
258 | error = spi_claim_bus(priv.spi); | |
259 | if (error) | |
260 | goto bus_claim_fail; | |
261 | ||
262 | /* reset LCD */ | |
263 | scf0403_gpio_reset(reset_gpio); | |
264 | ||
265 | error = scf0403_spi_read_rddid(priv.spi, &priv.rddid); | |
266 | if (error) { | |
267 | puts("IDs read failed\n"); | |
268 | goto readid_fail; | |
269 | } | |
270 | ||
271 | if (priv.rddid == SCF0403852GGU04_ID) { | |
272 | priv.init_seq = scf0403_initseq_sn04; | |
273 | priv.seq_size = ARRAY_SIZE(scf0403_initseq_sn04); | |
274 | } else { | |
275 | priv.init_seq = scf0403_initseq_sn20; | |
276 | priv.seq_size = ARRAY_SIZE(scf0403_initseq_sn20); | |
277 | } | |
278 | ||
279 | scf0403_lcd_init(&priv); | |
280 | ||
281 | /* Start operation */ | |
282 | scf0403_spi_transfer(priv.spi, &scf0403_cmd_dison); | |
283 | mdelay(100); | |
284 | scf0403_spi_transfer(priv.spi, &scf0403_cmd_slpout); | |
285 | spi_release_bus(priv.spi); | |
286 | ||
287 | return 0; | |
288 | ||
289 | readid_fail: | |
290 | spi_release_bus(priv.spi); | |
291 | bus_claim_fail: | |
292 | if (gpio_is_valid(priv.reset_gpio)) | |
293 | gpio_free(priv.reset_gpio); | |
294 | ||
295 | return error; | |
296 | } |