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