]>
Commit | Line | Data |
---|---|---|
c51cb3f5 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8a6c1dd5 MR |
2 | /* |
3 | * Driver for the Himax HX-8357 LCD Controller | |
4 | * | |
5 | * Copyright 2012 Free Electrons | |
8a6c1dd5 MR |
6 | */ |
7 | ||
8 | #include <linux/delay.h> | |
9 | #include <linux/lcd.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_device.h> | |
13 | #include <linux/of_gpio.h> | |
14 | #include <linux/spi/spi.h> | |
15 | ||
16 | #define HX8357_NUM_IM_PINS 3 | |
17 | ||
18 | #define HX8357_SWRESET 0x01 | |
19 | #define HX8357_GET_RED_CHANNEL 0x06 | |
20 | #define HX8357_GET_GREEN_CHANNEL 0x07 | |
21 | #define HX8357_GET_BLUE_CHANNEL 0x08 | |
22 | #define HX8357_GET_POWER_MODE 0x0a | |
23 | #define HX8357_GET_MADCTL 0x0b | |
24 | #define HX8357_GET_PIXEL_FORMAT 0x0c | |
25 | #define HX8357_GET_DISPLAY_MODE 0x0d | |
26 | #define HX8357_GET_SIGNAL_MODE 0x0e | |
27 | #define HX8357_GET_DIAGNOSTIC_RESULT 0x0f | |
28 | #define HX8357_ENTER_SLEEP_MODE 0x10 | |
29 | #define HX8357_EXIT_SLEEP_MODE 0x11 | |
30 | #define HX8357_ENTER_PARTIAL_MODE 0x12 | |
31 | #define HX8357_ENTER_NORMAL_MODE 0x13 | |
32 | #define HX8357_EXIT_INVERSION_MODE 0x20 | |
33 | #define HX8357_ENTER_INVERSION_MODE 0x21 | |
34 | #define HX8357_SET_DISPLAY_OFF 0x28 | |
35 | #define HX8357_SET_DISPLAY_ON 0x29 | |
36 | #define HX8357_SET_COLUMN_ADDRESS 0x2a | |
37 | #define HX8357_SET_PAGE_ADDRESS 0x2b | |
38 | #define HX8357_WRITE_MEMORY_START 0x2c | |
39 | #define HX8357_READ_MEMORY_START 0x2e | |
40 | #define HX8357_SET_PARTIAL_AREA 0x30 | |
41 | #define HX8357_SET_SCROLL_AREA 0x33 | |
42 | #define HX8357_SET_TEAR_OFF 0x34 | |
43 | #define HX8357_SET_TEAR_ON 0x35 | |
44 | #define HX8357_SET_ADDRESS_MODE 0x36 | |
45 | #define HX8357_SET_SCROLL_START 0x37 | |
46 | #define HX8357_EXIT_IDLE_MODE 0x38 | |
47 | #define HX8357_ENTER_IDLE_MODE 0x39 | |
48 | #define HX8357_SET_PIXEL_FORMAT 0x3a | |
49 | #define HX8357_SET_PIXEL_FORMAT_DBI_3BIT (0x1) | |
50 | #define HX8357_SET_PIXEL_FORMAT_DBI_16BIT (0x5) | |
51 | #define HX8357_SET_PIXEL_FORMAT_DBI_18BIT (0x6) | |
52 | #define HX8357_SET_PIXEL_FORMAT_DPI_3BIT (0x1 << 4) | |
53 | #define HX8357_SET_PIXEL_FORMAT_DPI_16BIT (0x5 << 4) | |
54 | #define HX8357_SET_PIXEL_FORMAT_DPI_18BIT (0x6 << 4) | |
55 | #define HX8357_WRITE_MEMORY_CONTINUE 0x3c | |
56 | #define HX8357_READ_MEMORY_CONTINUE 0x3e | |
57 | #define HX8357_SET_TEAR_SCAN_LINES 0x44 | |
58 | #define HX8357_GET_SCAN_LINES 0x45 | |
59 | #define HX8357_READ_DDB_START 0xa1 | |
60 | #define HX8357_SET_DISPLAY_MODE 0xb4 | |
61 | #define HX8357_SET_DISPLAY_MODE_RGB_THROUGH (0x3) | |
62 | #define HX8357_SET_DISPLAY_MODE_RGB_INTERFACE (1 << 4) | |
63 | #define HX8357_SET_PANEL_DRIVING 0xc0 | |
64 | #define HX8357_SET_DISPLAY_FRAME 0xc5 | |
65 | #define HX8357_SET_RGB 0xc6 | |
66 | #define HX8357_SET_RGB_ENABLE_HIGH (1 << 1) | |
67 | #define HX8357_SET_GAMMA 0xc8 | |
68 | #define HX8357_SET_POWER 0xd0 | |
69 | #define HX8357_SET_VCOM 0xd1 | |
70 | #define HX8357_SET_POWER_NORMAL 0xd2 | |
71 | #define HX8357_SET_PANEL_RELATED 0xe9 | |
72 | ||
fb525668 AB |
73 | #define HX8369_SET_DISPLAY_BRIGHTNESS 0x51 |
74 | #define HX8369_WRITE_CABC_DISPLAY_VALUE 0x53 | |
75 | #define HX8369_WRITE_CABC_BRIGHT_CTRL 0x55 | |
76 | #define HX8369_WRITE_CABC_MIN_BRIGHTNESS 0x5e | |
77 | #define HX8369_SET_POWER 0xb1 | |
78 | #define HX8369_SET_DISPLAY_MODE 0xb2 | |
79 | #define HX8369_SET_DISPLAY_WAVEFORM_CYC 0xb4 | |
80 | #define HX8369_SET_VCOM 0xb6 | |
81 | #define HX8369_SET_EXTENSION_COMMAND 0xb9 | |
82 | #define HX8369_SET_GIP 0xd5 | |
83 | #define HX8369_SET_GAMMA_CURVE_RELATED 0xe0 | |
84 | ||
8a6c1dd5 MR |
85 | struct hx8357_data { |
86 | unsigned im_pins[HX8357_NUM_IM_PINS]; | |
87 | unsigned reset; | |
88 | struct spi_device *spi; | |
89 | int state; | |
ccf9901f | 90 | bool use_im_pins; |
8a6c1dd5 MR |
91 | }; |
92 | ||
93 | static u8 hx8357_seq_power[] = { | |
94 | HX8357_SET_POWER, 0x44, 0x41, 0x06, | |
95 | }; | |
96 | ||
97 | static u8 hx8357_seq_vcom[] = { | |
98 | HX8357_SET_VCOM, 0x40, 0x10, | |
99 | }; | |
100 | ||
101 | static u8 hx8357_seq_power_normal[] = { | |
102 | HX8357_SET_POWER_NORMAL, 0x05, 0x12, | |
103 | }; | |
104 | ||
105 | static u8 hx8357_seq_panel_driving[] = { | |
106 | HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11, | |
107 | }; | |
108 | ||
109 | static u8 hx8357_seq_display_frame[] = { | |
110 | HX8357_SET_DISPLAY_FRAME, 0x0c, | |
111 | }; | |
112 | ||
113 | static u8 hx8357_seq_panel_related[] = { | |
114 | HX8357_SET_PANEL_RELATED, 0x01, | |
115 | }; | |
116 | ||
117 | static u8 hx8357_seq_undefined1[] = { | |
118 | 0xea, 0x03, 0x00, 0x00, | |
119 | }; | |
120 | ||
121 | static u8 hx8357_seq_undefined2[] = { | |
122 | 0xeb, 0x40, 0x54, 0x26, 0xdb, | |
123 | }; | |
124 | ||
125 | static u8 hx8357_seq_gamma[] = { | |
126 | HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00, | |
127 | 0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00, | |
128 | }; | |
129 | ||
130 | static u8 hx8357_seq_address_mode[] = { | |
131 | HX8357_SET_ADDRESS_MODE, 0xc0, | |
132 | }; | |
133 | ||
134 | static u8 hx8357_seq_pixel_format[] = { | |
135 | HX8357_SET_PIXEL_FORMAT, | |
136 | HX8357_SET_PIXEL_FORMAT_DPI_18BIT | | |
137 | HX8357_SET_PIXEL_FORMAT_DBI_18BIT, | |
138 | }; | |
139 | ||
140 | static u8 hx8357_seq_column_address[] = { | |
141 | HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f, | |
142 | }; | |
143 | ||
144 | static u8 hx8357_seq_page_address[] = { | |
145 | HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf, | |
146 | }; | |
147 | ||
148 | static u8 hx8357_seq_rgb[] = { | |
149 | HX8357_SET_RGB, 0x02, | |
150 | }; | |
151 | ||
152 | static u8 hx8357_seq_display_mode[] = { | |
153 | HX8357_SET_DISPLAY_MODE, | |
154 | HX8357_SET_DISPLAY_MODE_RGB_THROUGH | | |
155 | HX8357_SET_DISPLAY_MODE_RGB_INTERFACE, | |
156 | }; | |
157 | ||
fb525668 AB |
158 | static u8 hx8369_seq_write_CABC_min_brightness[] = { |
159 | HX8369_WRITE_CABC_MIN_BRIGHTNESS, 0x00, | |
160 | }; | |
161 | ||
162 | static u8 hx8369_seq_write_CABC_control[] = { | |
163 | HX8369_WRITE_CABC_DISPLAY_VALUE, 0x24, | |
164 | }; | |
165 | ||
166 | static u8 hx8369_seq_set_display_brightness[] = { | |
167 | HX8369_SET_DISPLAY_BRIGHTNESS, 0xFF, | |
168 | }; | |
169 | ||
170 | static u8 hx8369_seq_write_CABC_control_setting[] = { | |
171 | HX8369_WRITE_CABC_BRIGHT_CTRL, 0x02, | |
172 | }; | |
173 | ||
174 | static u8 hx8369_seq_extension_command[] = { | |
175 | HX8369_SET_EXTENSION_COMMAND, 0xff, 0x83, 0x69, | |
176 | }; | |
177 | ||
178 | static u8 hx8369_seq_display_related[] = { | |
179 | HX8369_SET_DISPLAY_MODE, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, | |
180 | 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, | |
181 | }; | |
182 | ||
183 | static u8 hx8369_seq_panel_waveform_cycle[] = { | |
184 | HX8369_SET_DISPLAY_WAVEFORM_CYC, 0x0a, 0x1d, 0x80, 0x06, 0x02, | |
185 | }; | |
186 | ||
187 | static u8 hx8369_seq_set_address_mode[] = { | |
188 | HX8357_SET_ADDRESS_MODE, 0x00, | |
189 | }; | |
190 | ||
191 | static u8 hx8369_seq_vcom[] = { | |
192 | HX8369_SET_VCOM, 0x3e, 0x3e, | |
193 | }; | |
194 | ||
195 | static u8 hx8369_seq_gip[] = { | |
196 | HX8369_SET_GIP, 0x00, 0x01, 0x03, 0x25, 0x01, 0x02, 0x28, 0x70, | |
197 | 0x11, 0x13, 0x00, 0x00, 0x40, 0x26, 0x51, 0x37, 0x00, 0x00, 0x71, | |
198 | 0x35, 0x60, 0x24, 0x07, 0x0f, 0x04, 0x04, | |
199 | }; | |
200 | ||
201 | static u8 hx8369_seq_power[] = { | |
202 | HX8369_SET_POWER, 0x01, 0x00, 0x34, 0x03, 0x00, 0x11, 0x11, 0x32, | |
203 | 0x2f, 0x3f, 0x3f, 0x01, 0x3a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, | |
204 | }; | |
205 | ||
206 | static u8 hx8369_seq_gamma_curve_related[] = { | |
207 | HX8369_SET_GAMMA_CURVE_RELATED, 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, | |
208 | 0x2e, 0x4a, 0x08, 0x0e, 0x0f, 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, | |
209 | 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, 0x2e, 0x4a, 0x08, 0x0e, 0x0f, | |
210 | 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, | |
211 | }; | |
212 | ||
8a6c1dd5 MR |
213 | static int hx8357_spi_write_then_read(struct lcd_device *lcdev, |
214 | u8 *txbuf, u16 txlen, | |
215 | u8 *rxbuf, u16 rxlen) | |
216 | { | |
217 | struct hx8357_data *lcd = lcd_get_data(lcdev); | |
218 | struct spi_message msg; | |
219 | struct spi_transfer xfer[2]; | |
220 | u16 *local_txbuf = NULL; | |
221 | int ret = 0; | |
222 | ||
223 | memset(xfer, 0, sizeof(xfer)); | |
224 | spi_message_init(&msg); | |
225 | ||
226 | if (txlen) { | |
227 | int i; | |
228 | ||
229 | local_txbuf = kcalloc(txlen, sizeof(*local_txbuf), GFP_KERNEL); | |
230 | ||
231 | if (!local_txbuf) | |
232 | return -ENOMEM; | |
233 | ||
234 | for (i = 0; i < txlen; i++) { | |
235 | local_txbuf[i] = txbuf[i]; | |
236 | if (i > 0) | |
237 | local_txbuf[i] |= 1 << 8; | |
238 | } | |
239 | ||
240 | xfer[0].len = 2 * txlen; | |
241 | xfer[0].bits_per_word = 9; | |
242 | xfer[0].tx_buf = local_txbuf; | |
243 | spi_message_add_tail(&xfer[0], &msg); | |
244 | } | |
245 | ||
246 | if (rxlen) { | |
247 | xfer[1].len = rxlen; | |
248 | xfer[1].bits_per_word = 8; | |
249 | xfer[1].rx_buf = rxbuf; | |
250 | spi_message_add_tail(&xfer[1], &msg); | |
251 | } | |
252 | ||
253 | ret = spi_sync(lcd->spi, &msg); | |
254 | if (ret < 0) | |
255 | dev_err(&lcdev->dev, "Couldn't send SPI data\n"); | |
256 | ||
257 | if (txlen) | |
258 | kfree(local_txbuf); | |
259 | ||
260 | return ret; | |
261 | } | |
262 | ||
263 | static inline int hx8357_spi_write_array(struct lcd_device *lcdev, | |
264 | u8 *value, u8 len) | |
265 | { | |
266 | return hx8357_spi_write_then_read(lcdev, value, len, NULL, 0); | |
267 | } | |
268 | ||
269 | static inline int hx8357_spi_write_byte(struct lcd_device *lcdev, | |
270 | u8 value) | |
271 | { | |
272 | return hx8357_spi_write_then_read(lcdev, &value, 1, NULL, 0); | |
273 | } | |
274 | ||
275 | static int hx8357_enter_standby(struct lcd_device *lcdev) | |
276 | { | |
277 | int ret; | |
278 | ||
279 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF); | |
280 | if (ret < 0) | |
281 | return ret; | |
282 | ||
283 | usleep_range(10000, 12000); | |
284 | ||
285 | ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE); | |
286 | if (ret < 0) | |
287 | return ret; | |
288 | ||
fb525668 AB |
289 | /* |
290 | * The controller needs 120ms when entering in sleep mode before we can | |
291 | * send the command to go off sleep mode | |
292 | */ | |
8a6c1dd5 MR |
293 | msleep(120); |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static int hx8357_exit_standby(struct lcd_device *lcdev) | |
299 | { | |
300 | int ret; | |
301 | ||
302 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | |
303 | if (ret < 0) | |
304 | return ret; | |
305 | ||
fb525668 AB |
306 | /* |
307 | * The controller needs 120ms when exiting from sleep mode before we | |
308 | * can send the command to enter in sleep mode | |
309 | */ | |
8a6c1dd5 MR |
310 | msleep(120); |
311 | ||
312 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | |
313 | if (ret < 0) | |
314 | return ret; | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
fb525668 AB |
319 | static void hx8357_lcd_reset(struct lcd_device *lcdev) |
320 | { | |
321 | struct hx8357_data *lcd = lcd_get_data(lcdev); | |
322 | ||
323 | /* Reset the screen */ | |
324 | gpio_set_value(lcd->reset, 1); | |
325 | usleep_range(10000, 12000); | |
326 | gpio_set_value(lcd->reset, 0); | |
327 | usleep_range(10000, 12000); | |
328 | gpio_set_value(lcd->reset, 1); | |
329 | ||
330 | /* The controller needs 120ms to recover from reset */ | |
331 | msleep(120); | |
332 | } | |
333 | ||
8a6c1dd5 MR |
334 | static int hx8357_lcd_init(struct lcd_device *lcdev) |
335 | { | |
336 | struct hx8357_data *lcd = lcd_get_data(lcdev); | |
337 | int ret; | |
338 | ||
339 | /* | |
340 | * Set the interface selection pins to SPI mode, with three | |
341 | * wires | |
342 | */ | |
ccf9901f MR |
343 | if (lcd->use_im_pins) { |
344 | gpio_set_value_cansleep(lcd->im_pins[0], 1); | |
345 | gpio_set_value_cansleep(lcd->im_pins[1], 0); | |
346 | gpio_set_value_cansleep(lcd->im_pins[2], 1); | |
347 | } | |
8a6c1dd5 | 348 | |
8a6c1dd5 MR |
349 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_power, |
350 | ARRAY_SIZE(hx8357_seq_power)); | |
351 | if (ret < 0) | |
352 | return ret; | |
353 | ||
354 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_vcom, | |
355 | ARRAY_SIZE(hx8357_seq_vcom)); | |
356 | if (ret < 0) | |
357 | return ret; | |
358 | ||
359 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_power_normal, | |
360 | ARRAY_SIZE(hx8357_seq_power_normal)); | |
361 | if (ret < 0) | |
362 | return ret; | |
363 | ||
364 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_driving, | |
365 | ARRAY_SIZE(hx8357_seq_panel_driving)); | |
366 | if (ret < 0) | |
367 | return ret; | |
368 | ||
369 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_frame, | |
370 | ARRAY_SIZE(hx8357_seq_display_frame)); | |
371 | if (ret < 0) | |
372 | return ret; | |
373 | ||
374 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_related, | |
375 | ARRAY_SIZE(hx8357_seq_panel_related)); | |
376 | if (ret < 0) | |
377 | return ret; | |
378 | ||
379 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined1, | |
380 | ARRAY_SIZE(hx8357_seq_undefined1)); | |
381 | if (ret < 0) | |
382 | return ret; | |
383 | ||
384 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined2, | |
385 | ARRAY_SIZE(hx8357_seq_undefined2)); | |
386 | if (ret < 0) | |
387 | return ret; | |
388 | ||
389 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_gamma, | |
390 | ARRAY_SIZE(hx8357_seq_gamma)); | |
391 | if (ret < 0) | |
392 | return ret; | |
393 | ||
394 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_address_mode, | |
395 | ARRAY_SIZE(hx8357_seq_address_mode)); | |
396 | if (ret < 0) | |
397 | return ret; | |
398 | ||
399 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_pixel_format, | |
400 | ARRAY_SIZE(hx8357_seq_pixel_format)); | |
401 | if (ret < 0) | |
402 | return ret; | |
403 | ||
404 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_column_address, | |
405 | ARRAY_SIZE(hx8357_seq_column_address)); | |
406 | if (ret < 0) | |
407 | return ret; | |
408 | ||
409 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_page_address, | |
410 | ARRAY_SIZE(hx8357_seq_page_address)); | |
411 | if (ret < 0) | |
412 | return ret; | |
413 | ||
414 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_rgb, | |
415 | ARRAY_SIZE(hx8357_seq_rgb)); | |
416 | if (ret < 0) | |
417 | return ret; | |
418 | ||
419 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_mode, | |
420 | ARRAY_SIZE(hx8357_seq_display_mode)); | |
421 | if (ret < 0) | |
422 | return ret; | |
423 | ||
424 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | |
425 | if (ret < 0) | |
426 | return ret; | |
427 | ||
fb525668 AB |
428 | /* |
429 | * The controller needs 120ms to fully recover from exiting sleep mode | |
430 | */ | |
8a6c1dd5 MR |
431 | msleep(120); |
432 | ||
433 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | |
434 | if (ret < 0) | |
435 | return ret; | |
436 | ||
437 | usleep_range(5000, 7000); | |
438 | ||
439 | ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START); | |
440 | if (ret < 0) | |
441 | return ret; | |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
fb525668 AB |
446 | static int hx8369_lcd_init(struct lcd_device *lcdev) |
447 | { | |
448 | int ret; | |
449 | ||
450 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_extension_command, | |
451 | ARRAY_SIZE(hx8369_seq_extension_command)); | |
452 | if (ret < 0) | |
453 | return ret; | |
454 | usleep_range(10000, 12000); | |
455 | ||
456 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_display_related, | |
457 | ARRAY_SIZE(hx8369_seq_display_related)); | |
458 | if (ret < 0) | |
459 | return ret; | |
460 | ||
461 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_panel_waveform_cycle, | |
462 | ARRAY_SIZE(hx8369_seq_panel_waveform_cycle)); | |
463 | if (ret < 0) | |
464 | return ret; | |
465 | ||
466 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_address_mode, | |
467 | ARRAY_SIZE(hx8369_seq_set_address_mode)); | |
468 | if (ret < 0) | |
469 | return ret; | |
470 | ||
471 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_vcom, | |
472 | ARRAY_SIZE(hx8369_seq_vcom)); | |
473 | if (ret < 0) | |
474 | return ret; | |
475 | ||
476 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_gip, | |
477 | ARRAY_SIZE(hx8369_seq_gip)); | |
478 | if (ret < 0) | |
479 | return ret; | |
480 | ||
481 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_power, | |
482 | ARRAY_SIZE(hx8369_seq_power)); | |
483 | if (ret < 0) | |
484 | return ret; | |
485 | ||
486 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | |
487 | if (ret < 0) | |
488 | return ret; | |
489 | ||
490 | /* | |
491 | * The controller needs 120ms to fully recover from exiting sleep mode | |
492 | */ | |
493 | msleep(120); | |
494 | ||
495 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_gamma_curve_related, | |
496 | ARRAY_SIZE(hx8369_seq_gamma_curve_related)); | |
497 | if (ret < 0) | |
498 | return ret; | |
499 | ||
500 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | |
501 | if (ret < 0) | |
502 | return ret; | |
503 | usleep_range(1000, 1200); | |
504 | ||
505 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_write_CABC_control, | |
506 | ARRAY_SIZE(hx8369_seq_write_CABC_control)); | |
507 | if (ret < 0) | |
508 | return ret; | |
509 | usleep_range(10000, 12000); | |
510 | ||
511 | ret = hx8357_spi_write_array(lcdev, | |
512 | hx8369_seq_write_CABC_control_setting, | |
513 | ARRAY_SIZE(hx8369_seq_write_CABC_control_setting)); | |
514 | if (ret < 0) | |
515 | return ret; | |
516 | ||
517 | ret = hx8357_spi_write_array(lcdev, | |
518 | hx8369_seq_write_CABC_min_brightness, | |
519 | ARRAY_SIZE(hx8369_seq_write_CABC_min_brightness)); | |
520 | if (ret < 0) | |
521 | return ret; | |
522 | usleep_range(10000, 12000); | |
523 | ||
524 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_display_brightness, | |
525 | ARRAY_SIZE(hx8369_seq_set_display_brightness)); | |
526 | if (ret < 0) | |
527 | return ret; | |
528 | ||
529 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | |
530 | if (ret < 0) | |
531 | return ret; | |
532 | ||
533 | return 0; | |
534 | } | |
535 | ||
8a6c1dd5 MR |
536 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) |
537 | ||
538 | static int hx8357_set_power(struct lcd_device *lcdev, int power) | |
539 | { | |
540 | struct hx8357_data *lcd = lcd_get_data(lcdev); | |
541 | int ret = 0; | |
542 | ||
543 | if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state)) | |
544 | ret = hx8357_exit_standby(lcdev); | |
545 | else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state)) | |
546 | ret = hx8357_enter_standby(lcdev); | |
547 | ||
548 | if (ret == 0) | |
549 | lcd->state = power; | |
550 | else | |
551 | dev_warn(&lcdev->dev, "failed to set power mode %d\n", power); | |
552 | ||
553 | return ret; | |
554 | } | |
555 | ||
556 | static int hx8357_get_power(struct lcd_device *lcdev) | |
557 | { | |
558 | struct hx8357_data *lcd = lcd_get_data(lcdev); | |
559 | ||
560 | return lcd->state; | |
561 | } | |
562 | ||
563 | static struct lcd_ops hx8357_ops = { | |
564 | .set_power = hx8357_set_power, | |
565 | .get_power = hx8357_get_power, | |
566 | }; | |
567 | ||
fb525668 AB |
568 | static const struct of_device_id hx8357_dt_ids[] = { |
569 | { | |
570 | .compatible = "himax,hx8357", | |
571 | .data = hx8357_lcd_init, | |
572 | }, | |
573 | { | |
574 | .compatible = "himax,hx8369", | |
575 | .data = hx8369_lcd_init, | |
576 | }, | |
577 | {}, | |
578 | }; | |
579 | MODULE_DEVICE_TABLE(of, hx8357_dt_ids); | |
580 | ||
8a6c1dd5 MR |
581 | static int hx8357_probe(struct spi_device *spi) |
582 | { | |
583 | struct lcd_device *lcdev; | |
584 | struct hx8357_data *lcd; | |
fb525668 | 585 | const struct of_device_id *match; |
8a6c1dd5 MR |
586 | int i, ret; |
587 | ||
588 | lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); | |
aff70f9a | 589 | if (!lcd) |
8a6c1dd5 | 590 | return -ENOMEM; |
8a6c1dd5 MR |
591 | |
592 | ret = spi_setup(spi); | |
593 | if (ret < 0) { | |
594 | dev_err(&spi->dev, "SPI setup failed.\n"); | |
595 | return ret; | |
596 | } | |
597 | ||
598 | lcd->spi = spi; | |
599 | ||
fb525668 AB |
600 | match = of_match_device(hx8357_dt_ids, &spi->dev); |
601 | if (!match || !match->data) | |
602 | return -EINVAL; | |
603 | ||
8a6c1dd5 MR |
604 | lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); |
605 | if (!gpio_is_valid(lcd->reset)) { | |
606 | dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); | |
607 | return -EINVAL; | |
608 | } | |
609 | ||
610 | ret = devm_gpio_request_one(&spi->dev, lcd->reset, | |
611 | GPIOF_OUT_INIT_HIGH, | |
612 | "hx8357-reset"); | |
613 | if (ret) { | |
614 | dev_err(&spi->dev, | |
615 | "failed to request gpio %d: %d\n", | |
616 | lcd->reset, ret); | |
617 | return -EINVAL; | |
618 | } | |
619 | ||
ccf9901f MR |
620 | if (of_find_property(spi->dev.of_node, "im-gpios", NULL)) { |
621 | lcd->use_im_pins = 1; | |
622 | ||
623 | for (i = 0; i < HX8357_NUM_IM_PINS; i++) { | |
624 | lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node, | |
625 | "im-gpios", i); | |
626 | if (lcd->im_pins[i] == -EPROBE_DEFER) { | |
627 | dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n"); | |
628 | return -EPROBE_DEFER; | |
629 | } | |
630 | if (!gpio_is_valid(lcd->im_pins[i])) { | |
631 | dev_err(&spi->dev, "Missing dt property: im-gpios\n"); | |
632 | return -EINVAL; | |
633 | } | |
634 | ||
635 | ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i], | |
636 | GPIOF_OUT_INIT_LOW, | |
637 | "im_pins"); | |
638 | if (ret) { | |
639 | dev_err(&spi->dev, "failed to request gpio %d: %d\n", | |
640 | lcd->im_pins[i], ret); | |
641 | return -EINVAL; | |
642 | } | |
8a6c1dd5 | 643 | } |
ccf9901f MR |
644 | } else { |
645 | lcd->use_im_pins = 0; | |
8a6c1dd5 MR |
646 | } |
647 | ||
ea5092c8 JH |
648 | lcdev = devm_lcd_device_register(&spi->dev, "mxsfb", &spi->dev, lcd, |
649 | &hx8357_ops); | |
8a6c1dd5 MR |
650 | if (IS_ERR(lcdev)) { |
651 | ret = PTR_ERR(lcdev); | |
652 | return ret; | |
653 | } | |
654 | spi_set_drvdata(spi, lcdev); | |
655 | ||
fb525668 AB |
656 | hx8357_lcd_reset(lcdev); |
657 | ||
658 | ret = ((int (*)(struct lcd_device *))match->data)(lcdev); | |
8a6c1dd5 MR |
659 | if (ret) { |
660 | dev_err(&spi->dev, "Couldn't initialize panel\n"); | |
ea5092c8 | 661 | return ret; |
8a6c1dd5 MR |
662 | } |
663 | ||
664 | dev_info(&spi->dev, "Panel probed\n"); | |
665 | ||
666 | return 0; | |
8a6c1dd5 MR |
667 | } |
668 | ||
8a6c1dd5 MR |
669 | static struct spi_driver hx8357_driver = { |
670 | .probe = hx8357_probe, | |
8a6c1dd5 MR |
671 | .driver = { |
672 | .name = "hx8357", | |
c962f7b9 | 673 | .of_match_table = hx8357_dt_ids, |
8a6c1dd5 MR |
674 | }, |
675 | }; | |
676 | ||
677 | module_spi_driver(hx8357_driver); | |
678 | ||
679 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); | |
680 | MODULE_DESCRIPTION("Himax HX-8357 LCD Driver"); | |
681 | MODULE_LICENSE("GPL"); |