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(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(&msg);
224
225	if (txlen) {
226		int i;
227
228		local_txbuf = kcalloc(txlen, 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(&xfer[0], &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(&xfer[1], &msg);
250	}
251
252	ret = spi_sync(lcd->spi, &msg);
253	if (ret < 0)
254		dev_err(&lcdev->dev, "Couldn't send SPI data\n");
255
256	if (txlen)
257		kfree(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, value, len, NULL, 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, &value, 1, NULL, 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(10000, 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(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(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(lcdev);
321
322	/* Reset the screen */
323	gpiod_set_value(lcd->reset, 0);
324	usleep_range(10000, 12000);
325	gpiod_set_value(lcd->reset, 1);
326	usleep_range(10000, 12000);
327	gpiod_set_value(lcd->reset, 0);
328
329	/* The controller needs 120ms to recover from reset */
330	msleep(120);
331}
332
333static int hx8357_lcd_init(struct lcd_device *lcdev)
334{
335	struct hx8357_data *lcd = lcd_get_data(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(lcd->im_pins->desc[0], 1);
344		gpiod_set_value_cansleep(lcd->im_pins->desc[1], 0);
345		gpiod_set_value_cansleep(lcd->im_pins->desc[2], 1);
346	}
347
348	ret = hx8357_spi_write_array(lcdev, hx8357_seq_power,
349				ARRAY_SIZE(hx8357_seq_power));
350	if (ret < 0)
351		return ret;
352
353	ret = hx8357_spi_write_array(lcdev, hx8357_seq_vcom,
354				ARRAY_SIZE(hx8357_seq_vcom));
355	if (ret < 0)
356		return ret;
357
358	ret = hx8357_spi_write_array(lcdev, 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, 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, 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, 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, hx8357_seq_undefined1,
379				ARRAY_SIZE(hx8357_seq_undefined1));
380	if (ret < 0)
381		return ret;
382
383	ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined2,
384				ARRAY_SIZE(hx8357_seq_undefined2));
385	if (ret < 0)
386		return ret;
387
388	ret = hx8357_spi_write_array(lcdev, hx8357_seq_gamma,
389				ARRAY_SIZE(hx8357_seq_gamma));
390	if (ret < 0)
391		return ret;
392
393	ret = hx8357_spi_write_array(lcdev, 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, 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, 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, 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, hx8357_seq_rgb,
414				ARRAY_SIZE(hx8357_seq_rgb));
415	if (ret < 0)
416		return ret;
417
418	ret = hx8357_spi_write_array(lcdev, 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(120);
431
432	ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
433	if (ret < 0)
434		return ret;
435
436	usleep_range(5000, 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, hx8369_seq_extension_command,
450				ARRAY_SIZE(hx8369_seq_extension_command));
451	if (ret < 0)
452		return ret;
453	usleep_range(10000, 12000);
454
455	ret = hx8357_spi_write_array(lcdev, 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, 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, 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, hx8369_seq_vcom,
471				ARRAY_SIZE(hx8369_seq_vcom));
472	if (ret < 0)
473		return ret;
474
475	ret = hx8357_spi_write_array(lcdev, hx8369_seq_gip,
476				ARRAY_SIZE(hx8369_seq_gip));
477	if (ret < 0)
478		return ret;
479
480	ret = hx8357_spi_write_array(lcdev, 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(120);
493
494	ret = hx8357_spi_write_array(lcdev, 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(1000, 1200);
503
504	ret = hx8357_spi_write_array(lcdev, hx8369_seq_write_CABC_control,
505				ARRAY_SIZE(hx8369_seq_write_CABC_control));
506	if (ret < 0)
507		return ret;
508	usleep_range(10000, 12000);
509
510	ret = hx8357_spi_write_array(lcdev,
511			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			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(10000, 12000);
522
523	ret = hx8357_spi_write_array(lcdev, 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(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(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, 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, ret, "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, "reset", GPIOD_OUT_LOW);
592	if (IS_ERR(lcd->reset))
593		return dev_err_probe(dev, PTR_ERR(lcd->reset), "failed to request reset GPIO\n");
594	gpiod_set_consumer_name(lcd->reset, "hx8357-reset");
595
596	lcd->im_pins = devm_gpiod_get_array_optional(dev, "im", GPIOD_OUT_LOW);
597	if (IS_ERR(lcd->im_pins))
598		return dev_err_probe(dev, PTR_ERR(lcd->im_pins), "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, -EINVAL, "not enough im GPIOs\n");
602
603		for (i = 0; i < HX8357_NUM_IM_PINS; i++)
604			gpiod_set_consumer_name(lcd->im_pins->desc[i], "im_pins");
605	}
606
607	lcdev = devm_lcd_device_register(dev, "mxsfb", dev, lcd, &hx8357_ops);
608	if (IS_ERR(lcdev)) {
609		ret = PTR_ERR(lcdev);
610		return ret;
611	}
612	spi_set_drvdata(spi, lcdev);
613
614	hx8357_lcd_reset(lcdev);
615
616	ret = init_fn(lcdev);
617	if (ret)
618		return dev_err_probe(dev, ret, "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