1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for the Himax HX-8357 LCD Controller |
4 | * |
5 | * Copyright 2012 Free Electrons |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/lcd.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/property.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 | |
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 | |
85 | struct hx8357_data { |
86 | struct gpio_descs *im_pins; |
87 | struct gpio_desc *reset; |
88 | struct spi_device *spi; |
89 | int state; |
90 | }; |
91 | |
92 | static u8 hx8357_seq_power[] = { |
93 | HX8357_SET_POWER, 0x44, 0x41, 0x06, |
94 | }; |
95 | |
96 | static u8 hx8357_seq_vcom[] = { |
97 | HX8357_SET_VCOM, 0x40, 0x10, |
98 | }; |
99 | |
100 | static u8 hx8357_seq_power_normal[] = { |
101 | HX8357_SET_POWER_NORMAL, 0x05, 0x12, |
102 | }; |
103 | |
104 | static u8 hx8357_seq_panel_driving[] = { |
105 | HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11, |
106 | }; |
107 | |
108 | static u8 hx8357_seq_display_frame[] = { |
109 | HX8357_SET_DISPLAY_FRAME, 0x0c, |
110 | }; |
111 | |
112 | static u8 hx8357_seq_panel_related[] = { |
113 | HX8357_SET_PANEL_RELATED, 0x01, |
114 | }; |
115 | |
116 | static u8 hx8357_seq_undefined1[] = { |
117 | 0xea, 0x03, 0x00, 0x00, |
118 | }; |
119 | |
120 | static u8 hx8357_seq_undefined2[] = { |
121 | 0xeb, 0x40, 0x54, 0x26, 0xdb, |
122 | }; |
123 | |
124 | static u8 hx8357_seq_gamma[] = { |
125 | HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00, |
126 | 0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00, |
127 | }; |
128 | |
129 | static u8 hx8357_seq_address_mode[] = { |
130 | HX8357_SET_ADDRESS_MODE, 0xc0, |
131 | }; |
132 | |
133 | static u8 hx8357_seq_pixel_format[] = { |
134 | HX8357_SET_PIXEL_FORMAT, |
135 | HX8357_SET_PIXEL_FORMAT_DPI_18BIT | |
136 | HX8357_SET_PIXEL_FORMAT_DBI_18BIT, |
137 | }; |
138 | |
139 | static u8 hx8357_seq_column_address[] = { |
140 | HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f, |
141 | }; |
142 | |
143 | static u8 hx8357_seq_page_address[] = { |
144 | HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf, |
145 | }; |
146 | |
147 | static u8 hx8357_seq_rgb[] = { |
148 | HX8357_SET_RGB, 0x02, |
149 | }; |
150 | |
151 | static u8 hx8357_seq_display_mode[] = { |
152 | HX8357_SET_DISPLAY_MODE, |
153 | HX8357_SET_DISPLAY_MODE_RGB_THROUGH | |
154 | HX8357_SET_DISPLAY_MODE_RGB_INTERFACE, |
155 | }; |
156 | |
157 | static u8 hx8369_seq_write_CABC_min_brightness[] = { |
158 | HX8369_WRITE_CABC_MIN_BRIGHTNESS, 0x00, |
159 | }; |
160 | |
161 | static u8 hx8369_seq_write_CABC_control[] = { |
162 | HX8369_WRITE_CABC_DISPLAY_VALUE, 0x24, |
163 | }; |
164 | |
165 | static u8 hx8369_seq_set_display_brightness[] = { |
166 | HX8369_SET_DISPLAY_BRIGHTNESS, 0xFF, |
167 | }; |
168 | |
169 | static u8 hx8369_seq_write_CABC_control_setting[] = { |
170 | HX8369_WRITE_CABC_BRIGHT_CTRL, 0x02, |
171 | }; |
172 | |
173 | static u8 hx8369_seq_extension_command[] = { |
174 | HX8369_SET_EXTENSION_COMMAND, 0xff, 0x83, 0x69, |
175 | }; |
176 | |
177 | static u8 hx8369_seq_display_related[] = { |
178 | HX8369_SET_DISPLAY_MODE, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, |
179 | 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, |
180 | }; |
181 | |
182 | static u8 hx8369_seq_panel_waveform_cycle[] = { |
183 | HX8369_SET_DISPLAY_WAVEFORM_CYC, 0x0a, 0x1d, 0x80, 0x06, 0x02, |
184 | }; |
185 | |
186 | static u8 hx8369_seq_set_address_mode[] = { |
187 | HX8357_SET_ADDRESS_MODE, 0x00, |
188 | }; |
189 | |
190 | static u8 hx8369_seq_vcom[] = { |
191 | HX8369_SET_VCOM, 0x3e, 0x3e, |
192 | }; |
193 | |
194 | static u8 hx8369_seq_gip[] = { |
195 | HX8369_SET_GIP, 0x00, 0x01, 0x03, 0x25, 0x01, 0x02, 0x28, 0x70, |
196 | 0x11, 0x13, 0x00, 0x00, 0x40, 0x26, 0x51, 0x37, 0x00, 0x00, 0x71, |
197 | 0x35, 0x60, 0x24, 0x07, 0x0f, 0x04, 0x04, |
198 | }; |
199 | |
200 | static u8 hx8369_seq_power[] = { |
201 | HX8369_SET_POWER, 0x01, 0x00, 0x34, 0x03, 0x00, 0x11, 0x11, 0x32, |
202 | 0x2f, 0x3f, 0x3f, 0x01, 0x3a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, |
203 | }; |
204 | |
205 | static u8 hx8369_seq_gamma_curve_related[] = { |
206 | HX8369_SET_GAMMA_CURVE_RELATED, 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, |
207 | 0x2e, 0x4a, 0x08, 0x0e, 0x0f, 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, |
208 | 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, 0x2e, 0x4a, 0x08, 0x0e, 0x0f, |
209 | 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, |
210 | }; |
211 | |
212 | static int hx8357_spi_write_then_read(struct lcd_device *lcdev, |
213 | u8 *txbuf, u16 txlen, |
214 | u8 *rxbuf, u16 rxlen) |
215 | { |
216 | struct hx8357_data *lcd = lcd_get_data(ld_dev: lcdev); |
217 | struct spi_message msg; |
218 | struct spi_transfer xfer[2]; |
219 | u16 *local_txbuf = NULL; |
220 | int ret = 0; |
221 | |
222 | memset(xfer, 0, sizeof(xfer)); |
223 | spi_message_init(m: &msg); |
224 | |
225 | if (txlen) { |
226 | int i; |
227 | |
228 | local_txbuf = kcalloc(n: txlen, size: sizeof(*local_txbuf), GFP_KERNEL); |
229 | |
230 | if (!local_txbuf) |
231 | return -ENOMEM; |
232 | |
233 | for (i = 0; i < txlen; i++) { |
234 | local_txbuf[i] = txbuf[i]; |
235 | if (i > 0) |
236 | local_txbuf[i] |= 1 << 8; |
237 | } |
238 | |
239 | xfer[0].len = 2 * txlen; |
240 | xfer[0].bits_per_word = 9; |
241 | xfer[0].tx_buf = local_txbuf; |
242 | spi_message_add_tail(t: &xfer[0], m: &msg); |
243 | } |
244 | |
245 | if (rxlen) { |
246 | xfer[1].len = rxlen; |
247 | xfer[1].bits_per_word = 8; |
248 | xfer[1].rx_buf = rxbuf; |
249 | spi_message_add_tail(t: &xfer[1], m: &msg); |
250 | } |
251 | |
252 | ret = spi_sync(spi: lcd->spi, message: &msg); |
253 | if (ret < 0) |
254 | dev_err(&lcdev->dev, "Couldn't send SPI data\n" ); |
255 | |
256 | if (txlen) |
257 | kfree(objp: local_txbuf); |
258 | |
259 | return ret; |
260 | } |
261 | |
262 | static inline int hx8357_spi_write_array(struct lcd_device *lcdev, |
263 | u8 *value, u8 len) |
264 | { |
265 | return hx8357_spi_write_then_read(lcdev, txbuf: value, txlen: len, NULL, rxlen: 0); |
266 | } |
267 | |
268 | static inline int hx8357_spi_write_byte(struct lcd_device *lcdev, |
269 | u8 value) |
270 | { |
271 | return hx8357_spi_write_then_read(lcdev, txbuf: &value, txlen: 1, NULL, rxlen: 0); |
272 | } |
273 | |
274 | static int hx8357_enter_standby(struct lcd_device *lcdev) |
275 | { |
276 | int ret; |
277 | |
278 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF); |
279 | if (ret < 0) |
280 | return ret; |
281 | |
282 | usleep_range(min: 10000, max: 12000); |
283 | |
284 | ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE); |
285 | if (ret < 0) |
286 | return ret; |
287 | |
288 | /* |
289 | * The controller needs 120ms when entering in sleep mode before we can |
290 | * send the command to go off sleep mode |
291 | */ |
292 | msleep(msecs: 120); |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | static int hx8357_exit_standby(struct lcd_device *lcdev) |
298 | { |
299 | int ret; |
300 | |
301 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); |
302 | if (ret < 0) |
303 | return ret; |
304 | |
305 | /* |
306 | * The controller needs 120ms when exiting from sleep mode before we |
307 | * can send the command to enter in sleep mode |
308 | */ |
309 | msleep(msecs: 120); |
310 | |
311 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); |
312 | if (ret < 0) |
313 | return ret; |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static void hx8357_lcd_reset(struct lcd_device *lcdev) |
319 | { |
320 | struct hx8357_data *lcd = lcd_get_data(ld_dev: lcdev); |
321 | |
322 | /* Reset the screen */ |
323 | gpiod_set_value(desc: lcd->reset, value: 0); |
324 | usleep_range(min: 10000, max: 12000); |
325 | gpiod_set_value(desc: lcd->reset, value: 1); |
326 | usleep_range(min: 10000, max: 12000); |
327 | gpiod_set_value(desc: lcd->reset, value: 0); |
328 | |
329 | /* The controller needs 120ms to recover from reset */ |
330 | msleep(msecs: 120); |
331 | } |
332 | |
333 | static int hx8357_lcd_init(struct lcd_device *lcdev) |
334 | { |
335 | struct hx8357_data *lcd = lcd_get_data(ld_dev: lcdev); |
336 | int ret; |
337 | |
338 | /* |
339 | * Set the interface selection pins to SPI mode, with three |
340 | * wires |
341 | */ |
342 | if (lcd->im_pins) { |
343 | gpiod_set_value_cansleep(desc: lcd->im_pins->desc[0], value: 1); |
344 | gpiod_set_value_cansleep(desc: lcd->im_pins->desc[1], value: 0); |
345 | gpiod_set_value_cansleep(desc: lcd->im_pins->desc[2], value: 1); |
346 | } |
347 | |
348 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_power, |
349 | ARRAY_SIZE(hx8357_seq_power)); |
350 | if (ret < 0) |
351 | return ret; |
352 | |
353 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_vcom, |
354 | ARRAY_SIZE(hx8357_seq_vcom)); |
355 | if (ret < 0) |
356 | return ret; |
357 | |
358 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_power_normal, |
359 | ARRAY_SIZE(hx8357_seq_power_normal)); |
360 | if (ret < 0) |
361 | return ret; |
362 | |
363 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_panel_driving, |
364 | ARRAY_SIZE(hx8357_seq_panel_driving)); |
365 | if (ret < 0) |
366 | return ret; |
367 | |
368 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_display_frame, |
369 | ARRAY_SIZE(hx8357_seq_display_frame)); |
370 | if (ret < 0) |
371 | return ret; |
372 | |
373 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_panel_related, |
374 | ARRAY_SIZE(hx8357_seq_panel_related)); |
375 | if (ret < 0) |
376 | return ret; |
377 | |
378 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_undefined1, |
379 | ARRAY_SIZE(hx8357_seq_undefined1)); |
380 | if (ret < 0) |
381 | return ret; |
382 | |
383 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_undefined2, |
384 | ARRAY_SIZE(hx8357_seq_undefined2)); |
385 | if (ret < 0) |
386 | return ret; |
387 | |
388 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_gamma, |
389 | ARRAY_SIZE(hx8357_seq_gamma)); |
390 | if (ret < 0) |
391 | return ret; |
392 | |
393 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_address_mode, |
394 | ARRAY_SIZE(hx8357_seq_address_mode)); |
395 | if (ret < 0) |
396 | return ret; |
397 | |
398 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_pixel_format, |
399 | ARRAY_SIZE(hx8357_seq_pixel_format)); |
400 | if (ret < 0) |
401 | return ret; |
402 | |
403 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_column_address, |
404 | ARRAY_SIZE(hx8357_seq_column_address)); |
405 | if (ret < 0) |
406 | return ret; |
407 | |
408 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_page_address, |
409 | ARRAY_SIZE(hx8357_seq_page_address)); |
410 | if (ret < 0) |
411 | return ret; |
412 | |
413 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_rgb, |
414 | ARRAY_SIZE(hx8357_seq_rgb)); |
415 | if (ret < 0) |
416 | return ret; |
417 | |
418 | ret = hx8357_spi_write_array(lcdev, value: hx8357_seq_display_mode, |
419 | ARRAY_SIZE(hx8357_seq_display_mode)); |
420 | if (ret < 0) |
421 | return ret; |
422 | |
423 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); |
424 | if (ret < 0) |
425 | return ret; |
426 | |
427 | /* |
428 | * The controller needs 120ms to fully recover from exiting sleep mode |
429 | */ |
430 | msleep(msecs: 120); |
431 | |
432 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); |
433 | if (ret < 0) |
434 | return ret; |
435 | |
436 | usleep_range(min: 5000, max: 7000); |
437 | |
438 | ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START); |
439 | if (ret < 0) |
440 | return ret; |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | static int hx8369_lcd_init(struct lcd_device *lcdev) |
446 | { |
447 | int ret; |
448 | |
449 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_extension_command, |
450 | ARRAY_SIZE(hx8369_seq_extension_command)); |
451 | if (ret < 0) |
452 | return ret; |
453 | usleep_range(min: 10000, max: 12000); |
454 | |
455 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_display_related, |
456 | ARRAY_SIZE(hx8369_seq_display_related)); |
457 | if (ret < 0) |
458 | return ret; |
459 | |
460 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_panel_waveform_cycle, |
461 | ARRAY_SIZE(hx8369_seq_panel_waveform_cycle)); |
462 | if (ret < 0) |
463 | return ret; |
464 | |
465 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_set_address_mode, |
466 | ARRAY_SIZE(hx8369_seq_set_address_mode)); |
467 | if (ret < 0) |
468 | return ret; |
469 | |
470 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_vcom, |
471 | ARRAY_SIZE(hx8369_seq_vcom)); |
472 | if (ret < 0) |
473 | return ret; |
474 | |
475 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_gip, |
476 | ARRAY_SIZE(hx8369_seq_gip)); |
477 | if (ret < 0) |
478 | return ret; |
479 | |
480 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_power, |
481 | ARRAY_SIZE(hx8369_seq_power)); |
482 | if (ret < 0) |
483 | return ret; |
484 | |
485 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); |
486 | if (ret < 0) |
487 | return ret; |
488 | |
489 | /* |
490 | * The controller needs 120ms to fully recover from exiting sleep mode |
491 | */ |
492 | msleep(msecs: 120); |
493 | |
494 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_gamma_curve_related, |
495 | ARRAY_SIZE(hx8369_seq_gamma_curve_related)); |
496 | if (ret < 0) |
497 | return ret; |
498 | |
499 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); |
500 | if (ret < 0) |
501 | return ret; |
502 | usleep_range(min: 1000, max: 1200); |
503 | |
504 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_write_CABC_control, |
505 | ARRAY_SIZE(hx8369_seq_write_CABC_control)); |
506 | if (ret < 0) |
507 | return ret; |
508 | usleep_range(min: 10000, max: 12000); |
509 | |
510 | ret = hx8357_spi_write_array(lcdev, |
511 | value: hx8369_seq_write_CABC_control_setting, |
512 | ARRAY_SIZE(hx8369_seq_write_CABC_control_setting)); |
513 | if (ret < 0) |
514 | return ret; |
515 | |
516 | ret = hx8357_spi_write_array(lcdev, |
517 | value: hx8369_seq_write_CABC_min_brightness, |
518 | ARRAY_SIZE(hx8369_seq_write_CABC_min_brightness)); |
519 | if (ret < 0) |
520 | return ret; |
521 | usleep_range(min: 10000, max: 12000); |
522 | |
523 | ret = hx8357_spi_write_array(lcdev, value: hx8369_seq_set_display_brightness, |
524 | ARRAY_SIZE(hx8369_seq_set_display_brightness)); |
525 | if (ret < 0) |
526 | return ret; |
527 | |
528 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); |
529 | if (ret < 0) |
530 | return ret; |
531 | |
532 | return 0; |
533 | } |
534 | |
535 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) |
536 | |
537 | static int hx8357_set_power(struct lcd_device *lcdev, int power) |
538 | { |
539 | struct hx8357_data *lcd = lcd_get_data(ld_dev: lcdev); |
540 | int ret = 0; |
541 | |
542 | if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state)) |
543 | ret = hx8357_exit_standby(lcdev); |
544 | else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state)) |
545 | ret = hx8357_enter_standby(lcdev); |
546 | |
547 | if (ret == 0) |
548 | lcd->state = power; |
549 | else |
550 | dev_warn(&lcdev->dev, "failed to set power mode %d\n" , power); |
551 | |
552 | return ret; |
553 | } |
554 | |
555 | static int hx8357_get_power(struct lcd_device *lcdev) |
556 | { |
557 | struct hx8357_data *lcd = lcd_get_data(ld_dev: lcdev); |
558 | |
559 | return lcd->state; |
560 | } |
561 | |
562 | static struct lcd_ops hx8357_ops = { |
563 | .set_power = hx8357_set_power, |
564 | .get_power = hx8357_get_power, |
565 | }; |
566 | |
567 | typedef int (*hx8357_init_fn)(struct lcd_device *); |
568 | |
569 | static int hx8357_probe(struct spi_device *spi) |
570 | { |
571 | struct device *dev = &spi->dev; |
572 | struct lcd_device *lcdev; |
573 | struct hx8357_data *lcd; |
574 | hx8357_init_fn init_fn; |
575 | int i, ret; |
576 | |
577 | lcd = devm_kzalloc(dev, size: sizeof(*lcd), GFP_KERNEL); |
578 | if (!lcd) |
579 | return -ENOMEM; |
580 | |
581 | ret = spi_setup(spi); |
582 | if (ret < 0) |
583 | return dev_err_probe(dev, err: ret, fmt: "SPI setup failed.\n" ); |
584 | |
585 | lcd->spi = spi; |
586 | |
587 | init_fn = device_get_match_data(dev); |
588 | if (!init_fn) |
589 | return -EINVAL; |
590 | |
591 | lcd->reset = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
592 | if (IS_ERR(ptr: lcd->reset)) |
593 | return dev_err_probe(dev, err: PTR_ERR(ptr: lcd->reset), fmt: "failed to request reset GPIO\n" ); |
594 | gpiod_set_consumer_name(desc: lcd->reset, name: "hx8357-reset" ); |
595 | |
596 | lcd->im_pins = devm_gpiod_get_array_optional(dev, con_id: "im" , flags: GPIOD_OUT_LOW); |
597 | if (IS_ERR(ptr: lcd->im_pins)) |
598 | return dev_err_probe(dev, err: PTR_ERR(ptr: lcd->im_pins), fmt: "failed to request im GPIOs\n" ); |
599 | if (lcd->im_pins) { |
600 | if (lcd->im_pins->ndescs < HX8357_NUM_IM_PINS) |
601 | return dev_err_probe(dev, err: -EINVAL, fmt: "not enough im GPIOs\n" ); |
602 | |
603 | for (i = 0; i < HX8357_NUM_IM_PINS; i++) |
604 | gpiod_set_consumer_name(desc: lcd->im_pins->desc[i], name: "im_pins" ); |
605 | } |
606 | |
607 | lcdev = devm_lcd_device_register(dev, name: "mxsfb" , parent: dev, devdata: lcd, ops: &hx8357_ops); |
608 | if (IS_ERR(ptr: lcdev)) { |
609 | ret = PTR_ERR(ptr: lcdev); |
610 | return ret; |
611 | } |
612 | spi_set_drvdata(spi, data: lcdev); |
613 | |
614 | hx8357_lcd_reset(lcdev); |
615 | |
616 | ret = init_fn(lcdev); |
617 | if (ret) |
618 | return dev_err_probe(dev, err: ret, fmt: "Couldn't initialize panel\n" ); |
619 | |
620 | dev_info(dev, "Panel probed\n" ); |
621 | |
622 | return 0; |
623 | } |
624 | |
625 | static const struct of_device_id hx8357_dt_ids[] = { |
626 | { |
627 | .compatible = "himax,hx8357" , |
628 | .data = hx8357_lcd_init, |
629 | }, |
630 | { |
631 | .compatible = "himax,hx8369" , |
632 | .data = hx8369_lcd_init, |
633 | }, |
634 | {} |
635 | }; |
636 | MODULE_DEVICE_TABLE(of, hx8357_dt_ids); |
637 | |
638 | static struct spi_driver hx8357_driver = { |
639 | .probe = hx8357_probe, |
640 | .driver = { |
641 | .name = "hx8357" , |
642 | .of_match_table = hx8357_dt_ids, |
643 | }, |
644 | }; |
645 | |
646 | module_spi_driver(hx8357_driver); |
647 | |
648 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>" ); |
649 | MODULE_DESCRIPTION("Himax HX-8357 LCD Driver" ); |
650 | MODULE_LICENSE("GPL" ); |
651 | |