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
85struct hx8357_data {
86 struct gpio_descs *im_pins;
87 struct gpio_desc *reset;
88 struct spi_device *spi;
89 int state;
90};
91
92static u8 hx8357_seq_power[] = {
93 HX8357_SET_POWER, 0x44, 0x41, 0x06,
94};
95
96static u8 hx8357_seq_vcom[] = {
97 HX8357_SET_VCOM, 0x40, 0x10,
98};
99
100static u8 hx8357_seq_power_normal[] = {
101 HX8357_SET_POWER_NORMAL, 0x05, 0x12,
102};
103
104static u8 hx8357_seq_panel_driving[] = {
105 HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11,
106};
107
108static u8 hx8357_seq_display_frame[] = {
109 HX8357_SET_DISPLAY_FRAME, 0x0c,
110};
111
112static u8 hx8357_seq_panel_related[] = {
113 HX8357_SET_PANEL_RELATED, 0x01,
114};
115
116static u8 hx8357_seq_undefined1[] = {
117 0xea, 0x03, 0x00, 0x00,
118};
119
120static u8 hx8357_seq_undefined2[] = {
121 0xeb, 0x40, 0x54, 0x26, 0xdb,
122};
123
124static u8 hx8357_seq_gamma[] = {
125 HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00,
126 0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00,
127};
128
129static u8 hx8357_seq_address_mode[] = {
130 HX8357_SET_ADDRESS_MODE, 0xc0,
131};
132
133static 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
139static u8 hx8357_seq_column_address[] = {
140 HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f,
141};
142
143static u8 hx8357_seq_page_address[] = {
144 HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf,
145};
146
147static u8 hx8357_seq_rgb[] = {
148 HX8357_SET_RGB, 0x02,
149};
150
151static 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
157static u8 hx8369_seq_write_CABC_min_brightness[] = {
158 HX8369_WRITE_CABC_MIN_BRIGHTNESS, 0x00,
159};
160
161static u8 hx8369_seq_write_CABC_control[] = {
162 HX8369_WRITE_CABC_DISPLAY_VALUE, 0x24,
163};
164
165static u8 hx8369_seq_set_display_brightness[] = {
166 HX8369_SET_DISPLAY_BRIGHTNESS, 0xFF,
167};
168
169static u8 hx8369_seq_write_CABC_control_setting[] = {
170 HX8369_WRITE_CABC_BRIGHT_CTRL, 0x02,
171};
172
173static u8 hx8369_seq_extension_command[] = {
174 HX8369_SET_EXTENSION_COMMAND, 0xff, 0x83, 0x69,
175};
176
177static 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
182static u8 hx8369_seq_panel_waveform_cycle[] = {
183 HX8369_SET_DISPLAY_WAVEFORM_CYC, 0x0a, 0x1d, 0x80, 0x06, 0x02,
184};
185
186static u8 hx8369_seq_set_address_mode[] = {
187 HX8357_SET_ADDRESS_MODE, 0x00,
188};
189
190static u8 hx8369_seq_vcom[] = {
191 HX8369_SET_VCOM, 0x3e, 0x3e,
192};
193
194static 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
200static 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
205static 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
212static 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
262static 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
268static 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
274static 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
297static 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
318static 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
333static 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
445static 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
537static 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
555static 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
562static struct lcd_ops hx8357_ops = {
563 .set_power = hx8357_set_power,
564 .get_power = hx8357_get_power,
565};
566
567typedef int (*hx8357_init_fn)(struct lcd_device *);
568
569static 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
625static 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};
636MODULE_DEVICE_TABLE(of, hx8357_dt_ids);
637
638static struct spi_driver hx8357_driver = {
639 .probe = hx8357_probe,
640 .driver = {
641 .name = "hx8357",
642 .of_match_table = hx8357_dt_ids,
643 },
644};
645
646module_spi_driver(hx8357_driver);
647
648MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
649MODULE_DESCRIPTION("Himax HX-8357 LCD Driver");
650MODULE_LICENSE("GPL");
651

source code of linux/drivers/video/backlight/hx8357.c