Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0ee2e37a LW |
2 | /* |
3 | * Ilitek ILI9322 TFT LCD drm_panel driver. | |
4 | * | |
5 | * This panel can be configured to support: | |
6 | * - 8-bit serial RGB interface | |
7 | * - 24-bit parallel RGB interface | |
8 | * - 8-bit ITU-R BT.601 interface | |
9 | * - 8-bit ITU-R BT.656 interface | |
10 | * - Up to 320RGBx240 dots resolution TFT LCD displays | |
11 | * - Scaling, brightness and contrast | |
12 | * | |
13 | * The scaling means that the display accepts a 640x480 or 720x480 | |
14 | * input and rescales it to fit to the 320x240 display. So what we | |
15 | * present to the system is something else than what comes out on the | |
16 | * actual display. | |
17 | * | |
18 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> | |
19 | * Derived from drivers/drm/gpu/panel/panel-samsung-ld9040.c | |
0ee2e37a LW |
20 | */ |
21 | ||
0ee2e37a LW |
22 | #include <linux/bitops.h> |
23 | #include <linux/gpio/consumer.h> | |
24 | #include <linux/module.h> | |
cb23eae3 | 25 | #include <linux/of_device.h> |
0ee2e37a LW |
26 | #include <linux/regmap.h> |
27 | #include <linux/regulator/consumer.h> | |
28 | #include <linux/spi/spi.h> | |
29 | ||
30 | #include <video/mipi_display.h> | |
31 | #include <video/of_videomode.h> | |
32 | #include <video/videomode.h> | |
33 | ||
cb23eae3 SR |
34 | #include <drm/drm_modes.h> |
35 | #include <drm/drm_panel.h> | |
cb23eae3 | 36 | |
0ee2e37a LW |
37 | #define ILI9322_CHIP_ID 0x00 |
38 | #define ILI9322_CHIP_ID_MAGIC 0x96 | |
39 | ||
40 | /* | |
41 | * Voltage on the communication interface, from 0.7 (0x00) | |
42 | * to 1.32 (0x1f) times the VREG1OUT voltage in 2% increments. | |
43 | * 1.00 (0x0f) is the default. | |
44 | */ | |
45 | #define ILI9322_VCOM_AMP 0x01 | |
46 | ||
47 | /* | |
48 | * High voltage on the communication signals, from 0.37 (0x00) to | |
49 | * 1.0 (0x3f) times the VREGOUT1 voltage in 1% increments. | |
50 | * 0.83 (0x2e) is the default. | |
51 | */ | |
52 | #define ILI9322_VCOM_HIGH 0x02 | |
53 | ||
54 | /* | |
55 | * VREG1 voltage regulator from 3.6V (0x00) to 6.0V (0x18) in 0.1V | |
56 | * increments. 5.4V (0x12) is the default. This is the reference | |
57 | * voltage for the VCOM levels and the greyscale level. | |
58 | */ | |
59 | #define ILI9322_VREG1_VOLTAGE 0x03 | |
60 | ||
61 | /* Describes the incoming signal */ | |
62 | #define ILI9322_ENTRY 0x06 | |
63 | /* 0 = right-to-left, 1 = left-to-right (default), horizontal flip */ | |
64 | #define ILI9322_ENTRY_HDIR BIT(0) | |
65 | /* 0 = down-to-up, 1 = up-to-down (default), vertical flip */ | |
66 | #define ILI9322_ENTRY_VDIR BIT(1) | |
67 | /* NTSC, PAL or autodetect */ | |
68 | #define ILI9322_ENTRY_NTSC (0 << 2) | |
69 | #define ILI9322_ENTRY_PAL (1 << 2) | |
70 | #define ILI9322_ENTRY_AUTODETECT (3 << 2) | |
71 | /* Input format */ | |
72 | #define ILI9322_ENTRY_SERIAL_RGB_THROUGH (0 << 4) | |
73 | #define ILI9322_ENTRY_SERIAL_RGB_ALIGNED (1 << 4) | |
74 | #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_320X240 (2 << 4) | |
75 | #define ILI9322_ENTRY_SERIAL_RGB_DUMMY_360X240 (3 << 4) | |
76 | #define ILI9322_ENTRY_DISABLE_1 (4 << 4) | |
77 | #define ILI9322_ENTRY_PARALLEL_RGB_THROUGH (5 << 4) | |
78 | #define ILI9322_ENTRY_PARALLEL_RGB_ALIGNED (6 << 4) | |
79 | #define ILI9322_ENTRY_YUV_640Y_320CBCR_25_54_MHZ (7 << 4) | |
80 | #define ILI9322_ENTRY_YUV_720Y_360CBCR_27_MHZ (8 << 4) | |
81 | #define ILI9322_ENTRY_DISABLE_2 (9 << 4) | |
82 | #define ILI9322_ENTRY_ITU_R_BT_656_720X360 (10 << 4) | |
83 | #define ILI9322_ENTRY_ITU_R_BT_656_640X320 (11 << 4) | |
84 | ||
85 | /* Power control */ | |
86 | #define ILI9322_POW_CTRL 0x07 | |
87 | #define ILI9322_POW_CTRL_STB BIT(0) /* 0 = standby, 1 = normal */ | |
88 | #define ILI9322_POW_CTRL_VGL BIT(1) /* 0 = off, 1 = on */ | |
89 | #define ILI9322_POW_CTRL_VGH BIT(2) /* 0 = off, 1 = on */ | |
90 | #define ILI9322_POW_CTRL_DDVDH BIT(3) /* 0 = off, 1 = on */ | |
91 | #define ILI9322_POW_CTRL_VCOM BIT(4) /* 0 = off, 1 = on */ | |
92 | #define ILI9322_POW_CTRL_VCL BIT(5) /* 0 = off, 1 = on */ | |
93 | #define ILI9322_POW_CTRL_AUTO BIT(6) /* 0 = interactive, 1 = auto */ | |
94 | #define ILI9322_POW_CTRL_STANDBY (ILI9322_POW_CTRL_VGL | \ | |
95 | ILI9322_POW_CTRL_VGH | \ | |
96 | ILI9322_POW_CTRL_DDVDH | \ | |
97 | ILI9322_POW_CTRL_VCL | \ | |
98 | ILI9322_POW_CTRL_AUTO | \ | |
99 | BIT(7)) | |
100 | #define ILI9322_POW_CTRL_DEFAULT (ILI9322_POW_CTRL_STANDBY | \ | |
101 | ILI9322_POW_CTRL_STB) | |
102 | ||
103 | /* Vertical back porch bits 0..5 */ | |
104 | #define ILI9322_VBP 0x08 | |
105 | ||
106 | /* Horizontal back porch, 8 bits */ | |
107 | #define ILI9322_HBP 0x09 | |
108 | ||
109 | /* | |
110 | * Polarity settings: | |
111 | * 1 = positive polarity | |
112 | * 0 = negative polarity | |
113 | */ | |
114 | #define ILI9322_POL 0x0a | |
115 | #define ILI9322_POL_DCLK BIT(0) /* 1 default */ | |
116 | #define ILI9322_POL_HSYNC BIT(1) /* 0 default */ | |
117 | #define ILI9322_POL_VSYNC BIT(2) /* 0 default */ | |
118 | #define ILI9322_POL_DE BIT(3) /* 1 default */ | |
119 | /* | |
120 | * 0 means YCBCR are ordered Cb0,Y0,Cr0,Y1,Cb2,Y2,Cr2,Y3 (default) | |
121 | * in RGB mode this means RGB comes in RGBRGB | |
122 | * 1 means YCBCR are ordered Cr0,Y0,Cb0,Y1,Cr2,Y2,Cb2,Y3 | |
123 | * in RGB mode this means RGB comes in BGRBGR | |
124 | */ | |
125 | #define ILI9322_POL_YCBCR_MODE BIT(4) | |
126 | /* Formula A for YCbCR->RGB = 0, Formula B = 1 */ | |
127 | #define ILI9322_POL_FORMULA BIT(5) | |
128 | /* Reverse polarity: 0 = 0..255, 1 = 255..0 */ | |
129 | #define ILI9322_POL_REV BIT(6) | |
130 | ||
131 | #define ILI9322_IF_CTRL 0x0b | |
132 | #define ILI9322_IF_CTRL_HSYNC_VSYNC 0x00 | |
133 | #define ILI9322_IF_CTRL_HSYNC_VSYNC_DE BIT(2) | |
134 | #define ILI9322_IF_CTRL_DE_ONLY BIT(3) | |
135 | #define ILI9322_IF_CTRL_SYNC_DISABLED (BIT(2) | BIT(3)) | |
136 | #define ILI9322_IF_CTRL_LINE_INVERSION BIT(0) /* Not set means frame inv */ | |
137 | ||
138 | #define ILI9322_GLOBAL_RESET 0x04 | |
139 | #define ILI9322_GLOBAL_RESET_ASSERT 0x00 /* bit 0 = 0 -> reset */ | |
140 | ||
141 | /* | |
142 | * 4+4 bits of negative and positive gamma correction | |
143 | * Upper nybble, bits 4-7 are negative gamma | |
144 | * Lower nybble, bits 0-3 are positive gamma | |
145 | */ | |
146 | #define ILI9322_GAMMA_1 0x10 | |
147 | #define ILI9322_GAMMA_2 0x11 | |
148 | #define ILI9322_GAMMA_3 0x12 | |
149 | #define ILI9322_GAMMA_4 0x13 | |
150 | #define ILI9322_GAMMA_5 0x14 | |
151 | #define ILI9322_GAMMA_6 0x15 | |
152 | #define ILI9322_GAMMA_7 0x16 | |
153 | #define ILI9322_GAMMA_8 0x17 | |
154 | ||
5d89045b | 155 | /* |
0ee2e37a LW |
156 | * enum ili9322_input - the format of the incoming signal to the panel |
157 | * | |
158 | * The panel can be connected to various input streams and four of them can | |
159 | * be selected by electronic straps on the display. However it is possible | |
160 | * to select another mode or override the electronic default with this | |
161 | * setting. | |
162 | */ | |
163 | enum ili9322_input { | |
164 | ILI9322_INPUT_SRGB_THROUGH = 0x0, | |
165 | ILI9322_INPUT_SRGB_ALIGNED = 0x1, | |
166 | ILI9322_INPUT_SRGB_DUMMY_320X240 = 0x2, | |
167 | ILI9322_INPUT_SRGB_DUMMY_360X240 = 0x3, | |
168 | ILI9322_INPUT_DISABLED_1 = 0x4, | |
169 | ILI9322_INPUT_PRGB_THROUGH = 0x5, | |
170 | ILI9322_INPUT_PRGB_ALIGNED = 0x6, | |
171 | ILI9322_INPUT_YUV_640X320_YCBCR = 0x7, | |
172 | ILI9322_INPUT_YUV_720X360_YCBCR = 0x8, | |
173 | ILI9322_INPUT_DISABLED_2 = 0x9, | |
174 | ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR = 0xa, | |
175 | ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR = 0xb, | |
176 | ILI9322_INPUT_UNKNOWN = 0xc, | |
177 | }; | |
178 | ||
7ff9288c | 179 | static const char * const ili9322_inputs[] = { |
0ee2e37a LW |
180 | "8 bit serial RGB through", |
181 | "8 bit serial RGB aligned", | |
182 | "8 bit serial RGB dummy 320x240", | |
183 | "8 bit serial RGB dummy 360x240", | |
184 | "disabled 1", | |
185 | "24 bit parallel RGB through", | |
186 | "24 bit parallel RGB aligned", | |
187 | "24 bit YUV 640Y 320CbCr", | |
188 | "24 bit YUV 720Y 360CbCr", | |
189 | "disabled 2", | |
190 | "8 bit ITU-R BT.656 720Y 360CbCr", | |
191 | "8 bit ITU-R BT.656 640Y 320CbCr", | |
192 | }; | |
193 | ||
194 | /** | |
195 | * struct ili9322_config - the system specific ILI9322 configuration | |
196 | * @width_mm: physical panel width [mm] | |
197 | * @height_mm: physical panel height [mm] | |
198 | * @flip_horizontal: flip the image horizontally (right-to-left scan) | |
199 | * (only in RGB and YUV modes) | |
200 | * @flip_vertical: flip the image vertically (down-to-up scan) | |
201 | * (only in RGB and YUV modes) | |
202 | * @input: the input/entry type used in this system, if this is set to | |
203 | * ILI9322_INPUT_UNKNOWN the driver will try to figure it out by probing | |
204 | * the hardware | |
205 | * @vreg1out_mv: the output in microvolts for the VREGOUT1 regulator used | |
206 | * to drive the physical display. Valid ranges are 3600 thru 6000 in 100 | |
207 | * microvolt increments. If not specified, hardware defaults will be | |
208 | * used (4.5V). | |
209 | * @vcom_high_percent: the percentage of VREGOUT1 used for the peak | |
210 | * voltage on the communications link. Valid ranges are 37 thru 100 | |
211 | * percent. If not specified, hardware defaults will be used (91%). | |
212 | * @vcom_amplitude_percent: the percentage of VREGOUT1 used for the | |
213 | * peak-to-peak amplitude of the communcation signals to the physical | |
214 | * display. Valid ranges are 70 thru 132 percent in increments if two | |
215 | * percent. Odd percentages will be truncated. If not specified, hardware | |
216 | * defaults will be used (114%). | |
217 | * @dclk_active_high: data/pixel clock active high, data will be clocked | |
218 | * in on the rising edge of the DCLK (this is usually the case). | |
219 | * @syncmode: The synchronization mode, what sync signals are emitted. | |
220 | * See the enum for details. | |
221 | * @de_active_high: DE (data entry) is active high | |
222 | * @hsync_active_high: HSYNC is active high | |
223 | * @vsync_active_high: VSYNC is active high | |
224 | * @gamma_corr_pos: a set of 8 nybbles describing positive | |
225 | * gamma correction for voltages V1 thru V8. Valid range 0..15 | |
226 | * @gamma_corr_neg: a set of 8 nybbles describing negative | |
227 | * gamma correction for voltages V1 thru V8. Valid range 0..15 | |
228 | * | |
229 | * These adjust what grayscale voltage will be output for input data V1 = 0, | |
230 | * V2 = 16, V3 = 48, V4 = 96, V5 = 160, V6 = 208, V7 = 240 and V8 = 255. | |
231 | * The curve is shaped like this: | |
232 | * | |
233 | * ^ | |
234 | * | V8 | |
235 | * | V7 | |
236 | * | V6 | |
237 | * | V5 | |
238 | * | V4 | |
239 | * | V3 | |
240 | * | V2 | |
241 | * | V1 | |
242 | * +-----------------------------------------------------------> | |
243 | * 0 16 48 96 160 208 240 255 | |
244 | * | |
245 | * The negative and postive gamma values adjust the V1 thru V8 up/down | |
246 | * according to the datasheet specifications. This is a property of the | |
247 | * physical display connected to the display controller and may vary. | |
248 | * If defined, both arrays must be supplied in full. If the properties | |
249 | * are not supplied, hardware defaults will be used. | |
250 | */ | |
251 | struct ili9322_config { | |
252 | u32 width_mm; | |
253 | u32 height_mm; | |
254 | bool flip_horizontal; | |
255 | bool flip_vertical; | |
256 | enum ili9322_input input; | |
257 | u32 vreg1out_mv; | |
258 | u32 vcom_high_percent; | |
259 | u32 vcom_amplitude_percent; | |
260 | bool dclk_active_high; | |
261 | bool de_active_high; | |
262 | bool hsync_active_high; | |
263 | bool vsync_active_high; | |
264 | u8 syncmode; | |
265 | u8 gamma_corr_pos[8]; | |
266 | u8 gamma_corr_neg[8]; | |
267 | }; | |
268 | ||
269 | struct ili9322 { | |
270 | struct device *dev; | |
271 | const struct ili9322_config *conf; | |
272 | struct drm_panel panel; | |
273 | struct regmap *regmap; | |
274 | struct regulator_bulk_data supplies[3]; | |
275 | struct gpio_desc *reset_gpio; | |
276 | enum ili9322_input input; | |
277 | struct videomode vm; | |
278 | u8 gamma[8]; | |
279 | u8 vreg1out; | |
280 | u8 vcom_high; | |
281 | u8 vcom_amplitude; | |
282 | }; | |
283 | ||
284 | static inline struct ili9322 *panel_to_ili9322(struct drm_panel *panel) | |
285 | { | |
286 | return container_of(panel, struct ili9322, panel); | |
287 | } | |
288 | ||
289 | static int ili9322_regmap_spi_write(void *context, const void *data, | |
290 | size_t count) | |
291 | { | |
292 | struct device *dev = context; | |
293 | struct spi_device *spi = to_spi_device(dev); | |
294 | u8 buf[2]; | |
295 | ||
296 | /* Clear bit 7 to write */ | |
297 | memcpy(buf, data, 2); | |
298 | buf[0] &= ~0x80; | |
299 | ||
300 | dev_dbg(dev, "WRITE: %02x %02x\n", buf[0], buf[1]); | |
301 | return spi_write_then_read(spi, buf, 2, NULL, 0); | |
302 | } | |
303 | ||
304 | static int ili9322_regmap_spi_read(void *context, const void *reg, | |
305 | size_t reg_size, void *val, size_t val_size) | |
306 | { | |
307 | struct device *dev = context; | |
308 | struct spi_device *spi = to_spi_device(dev); | |
309 | u8 buf[1]; | |
310 | ||
311 | /* Set bit 7 to 1 to read */ | |
312 | memcpy(buf, reg, 1); | |
313 | dev_dbg(dev, "READ: %02x reg size = %zu, val size = %zu\n", | |
314 | buf[0], reg_size, val_size); | |
315 | buf[0] |= 0x80; | |
316 | ||
317 | return spi_write_then_read(spi, buf, 1, val, 1); | |
318 | } | |
319 | ||
320 | static struct regmap_bus ili9322_regmap_bus = { | |
321 | .write = ili9322_regmap_spi_write, | |
322 | .read = ili9322_regmap_spi_read, | |
323 | .reg_format_endian_default = REGMAP_ENDIAN_BIG, | |
324 | .val_format_endian_default = REGMAP_ENDIAN_BIG, | |
325 | }; | |
326 | ||
327 | static bool ili9322_volatile_reg(struct device *dev, unsigned int reg) | |
328 | { | |
329 | return false; | |
330 | } | |
331 | ||
332 | static bool ili9322_writeable_reg(struct device *dev, unsigned int reg) | |
333 | { | |
334 | /* Just register 0 is read-only */ | |
335 | if (reg == 0x00) | |
336 | return false; | |
337 | return true; | |
338 | } | |
339 | ||
7ff9288c | 340 | static const struct regmap_config ili9322_regmap_config = { |
0ee2e37a LW |
341 | .reg_bits = 8, |
342 | .val_bits = 8, | |
343 | .max_register = 0x44, | |
344 | .cache_type = REGCACHE_RBTREE, | |
345 | .volatile_reg = ili9322_volatile_reg, | |
346 | .writeable_reg = ili9322_writeable_reg, | |
347 | }; | |
348 | ||
349 | static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili) | |
350 | { | |
0ee2e37a LW |
351 | u8 reg; |
352 | int ret; | |
353 | int i; | |
354 | ||
355 | /* Reset display */ | |
356 | ret = regmap_write(ili->regmap, ILI9322_GLOBAL_RESET, | |
357 | ILI9322_GLOBAL_RESET_ASSERT); | |
358 | if (ret) { | |
359 | dev_err(ili->dev, "can't issue GRESET (%d)\n", ret); | |
360 | return ret; | |
361 | } | |
362 | ||
363 | /* Set up the main voltage regulator */ | |
364 | if (ili->vreg1out != U8_MAX) { | |
365 | ret = regmap_write(ili->regmap, ILI9322_VREG1_VOLTAGE, | |
366 | ili->vreg1out); | |
367 | if (ret) { | |
368 | dev_err(ili->dev, "can't set up VREG1OUT (%d)\n", ret); | |
369 | return ret; | |
370 | } | |
371 | } | |
372 | ||
373 | if (ili->vcom_amplitude != U8_MAX) { | |
374 | ret = regmap_write(ili->regmap, ILI9322_VCOM_AMP, | |
375 | ili->vcom_amplitude); | |
376 | if (ret) { | |
377 | dev_err(ili->dev, | |
378 | "can't set up VCOM amplitude (%d)\n", ret); | |
379 | return ret; | |
380 | } | |
a29b57e9 | 381 | } |
0ee2e37a LW |
382 | |
383 | if (ili->vcom_high != U8_MAX) { | |
384 | ret = regmap_write(ili->regmap, ILI9322_VCOM_HIGH, | |
385 | ili->vcom_high); | |
386 | if (ret) { | |
387 | dev_err(ili->dev, "can't set up VCOM high (%d)\n", ret); | |
388 | return ret; | |
389 | } | |
a29b57e9 | 390 | } |
0ee2e37a LW |
391 | |
392 | /* Set up gamma correction */ | |
393 | for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) { | |
394 | ret = regmap_write(ili->regmap, ILI9322_GAMMA_1 + i, | |
395 | ili->gamma[i]); | |
396 | if (ret) { | |
397 | dev_err(ili->dev, | |
398 | "can't write gamma V%d to 0x%02x (%d)\n", | |
399 | i + 1, ILI9322_GAMMA_1 + i, ret); | |
400 | return ret; | |
401 | } | |
402 | } | |
403 | ||
404 | /* | |
405 | * Polarity and inverted color order for RGB input. | |
406 | * None of this applies in the BT.656 mode. | |
407 | */ | |
9d027b8e SR |
408 | reg = 0; |
409 | if (ili->conf->dclk_active_high) | |
0ee2e37a | 410 | reg = ILI9322_POL_DCLK; |
9d027b8e | 411 | if (ili->conf->de_active_high) |
0ee2e37a | 412 | reg |= ILI9322_POL_DE; |
0ee2e37a LW |
413 | if (ili->conf->hsync_active_high) |
414 | reg |= ILI9322_POL_HSYNC; | |
415 | if (ili->conf->vsync_active_high) | |
416 | reg |= ILI9322_POL_VSYNC; | |
417 | ret = regmap_write(ili->regmap, ILI9322_POL, reg); | |
418 | if (ret) { | |
419 | dev_err(ili->dev, "can't write POL register (%d)\n", ret); | |
420 | return ret; | |
421 | } | |
422 | ||
423 | /* | |
424 | * Set up interface control. | |
425 | * This is not used in the BT.656 mode (no H/Vsync or DE signals). | |
426 | */ | |
427 | reg = ili->conf->syncmode; | |
428 | reg |= ILI9322_IF_CTRL_LINE_INVERSION; | |
429 | ret = regmap_write(ili->regmap, ILI9322_IF_CTRL, reg); | |
430 | if (ret) { | |
431 | dev_err(ili->dev, "can't write IF CTRL register (%d)\n", ret); | |
432 | return ret; | |
433 | } | |
434 | ||
435 | /* Set up the input mode */ | |
436 | reg = (ili->input << 4); | |
437 | /* These are inverted, setting to 1 is the default, clearing flips */ | |
438 | if (!ili->conf->flip_horizontal) | |
439 | reg |= ILI9322_ENTRY_HDIR; | |
440 | if (!ili->conf->flip_vertical) | |
441 | reg |= ILI9322_ENTRY_VDIR; | |
442 | reg |= ILI9322_ENTRY_AUTODETECT; | |
443 | ret = regmap_write(ili->regmap, ILI9322_ENTRY, reg); | |
444 | if (ret) { | |
445 | dev_err(ili->dev, "can't write ENTRY reg (%d)\n", ret); | |
446 | return ret; | |
447 | } | |
448 | dev_info(ili->dev, "display is in %s mode, syncmode %02x\n", | |
449 | ili9322_inputs[ili->input], | |
450 | ili->conf->syncmode); | |
451 | ||
452 | dev_info(ili->dev, "initialized display\n"); | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
457 | /* | |
458 | * This power-on sequence if from the datasheet, page 57. | |
459 | */ | |
460 | static int ili9322_power_on(struct ili9322 *ili) | |
461 | { | |
462 | int ret; | |
463 | ||
464 | /* Assert RESET */ | |
465 | gpiod_set_value(ili->reset_gpio, 1); | |
466 | ||
467 | ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies); | |
468 | if (ret < 0) { | |
469 | dev_err(ili->dev, "unable to enable regulators\n"); | |
470 | return ret; | |
471 | } | |
472 | msleep(20); | |
473 | ||
474 | /* De-assert RESET */ | |
475 | gpiod_set_value(ili->reset_gpio, 0); | |
476 | ||
477 | msleep(10); | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
482 | static int ili9322_power_off(struct ili9322 *ili) | |
483 | { | |
484 | return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies); | |
485 | } | |
486 | ||
487 | static int ili9322_disable(struct drm_panel *panel) | |
488 | { | |
489 | struct ili9322 *ili = panel_to_ili9322(panel); | |
490 | int ret; | |
491 | ||
492 | ret = regmap_write(ili->regmap, ILI9322_POW_CTRL, | |
493 | ILI9322_POW_CTRL_STANDBY); | |
494 | if (ret) { | |
495 | dev_err(ili->dev, "unable to go to standby mode\n"); | |
496 | return ret; | |
497 | } | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static int ili9322_unprepare(struct drm_panel *panel) | |
503 | { | |
504 | struct ili9322 *ili = panel_to_ili9322(panel); | |
505 | ||
506 | return ili9322_power_off(ili); | |
507 | } | |
508 | ||
509 | static int ili9322_prepare(struct drm_panel *panel) | |
510 | { | |
511 | struct ili9322 *ili = panel_to_ili9322(panel); | |
512 | int ret; | |
513 | ||
514 | ret = ili9322_power_on(ili); | |
515 | if (ret < 0) | |
516 | return ret; | |
517 | ||
518 | ret = ili9322_init(panel, ili); | |
519 | if (ret < 0) | |
520 | ili9322_unprepare(panel); | |
521 | ||
522 | return ret; | |
523 | } | |
524 | ||
525 | static int ili9322_enable(struct drm_panel *panel) | |
526 | { | |
527 | struct ili9322 *ili = panel_to_ili9322(panel); | |
528 | int ret; | |
529 | ||
530 | ret = regmap_write(ili->regmap, ILI9322_POW_CTRL, | |
531 | ILI9322_POW_CTRL_DEFAULT); | |
532 | if (ret) { | |
533 | dev_err(ili->dev, "unable to enable panel\n"); | |
534 | return ret; | |
535 | } | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | /* Serial RGB modes */ | |
541 | static const struct drm_display_mode srgb_320x240_mode = { | |
e4d2a287 | 542 | .clock = 24535, |
0ee2e37a LW |
543 | .hdisplay = 320, |
544 | .hsync_start = 320 + 359, | |
545 | .hsync_end = 320 + 359 + 1, | |
546 | .htotal = 320 + 359 + 1 + 241, | |
547 | .vdisplay = 240, | |
548 | .vsync_start = 240 + 4, | |
549 | .vsync_end = 240 + 4 + 1, | |
550 | .vtotal = 262, | |
0ee2e37a LW |
551 | .flags = 0, |
552 | }; | |
553 | ||
554 | static const struct drm_display_mode srgb_360x240_mode = { | |
e4d2a287 | 555 | .clock = 27000, |
0ee2e37a LW |
556 | .hdisplay = 360, |
557 | .hsync_start = 360 + 35, | |
558 | .hsync_end = 360 + 35 + 1, | |
559 | .htotal = 360 + 35 + 1 + 241, | |
560 | .vdisplay = 240, | |
561 | .vsync_start = 240 + 21, | |
562 | .vsync_end = 240 + 21 + 1, | |
563 | .vtotal = 262, | |
0ee2e37a LW |
564 | .flags = 0, |
565 | }; | |
566 | ||
567 | /* This is the only mode listed for parallel RGB in the datasheet */ | |
568 | static const struct drm_display_mode prgb_320x240_mode = { | |
e4d2a287 | 569 | .clock = 64000, |
0ee2e37a LW |
570 | .hdisplay = 320, |
571 | .hsync_start = 320 + 38, | |
572 | .hsync_end = 320 + 38 + 1, | |
573 | .htotal = 320 + 38 + 1 + 50, | |
574 | .vdisplay = 240, | |
575 | .vsync_start = 240 + 4, | |
576 | .vsync_end = 240 + 4 + 1, | |
577 | .vtotal = 262, | |
0ee2e37a LW |
578 | .flags = 0, |
579 | }; | |
580 | ||
581 | /* YUV modes */ | |
582 | static const struct drm_display_mode yuv_640x320_mode = { | |
e4d2a287 | 583 | .clock = 24540, |
0ee2e37a LW |
584 | .hdisplay = 640, |
585 | .hsync_start = 640 + 252, | |
586 | .hsync_end = 640 + 252 + 1, | |
587 | .htotal = 640 + 252 + 1 + 28, | |
588 | .vdisplay = 320, | |
589 | .vsync_start = 320 + 4, | |
590 | .vsync_end = 320 + 4 + 1, | |
591 | .vtotal = 320 + 4 + 1 + 18, | |
0ee2e37a LW |
592 | .flags = 0, |
593 | }; | |
594 | ||
595 | static const struct drm_display_mode yuv_720x360_mode = { | |
e4d2a287 | 596 | .clock = 27000, |
0ee2e37a LW |
597 | .hdisplay = 720, |
598 | .hsync_start = 720 + 252, | |
599 | .hsync_end = 720 + 252 + 1, | |
600 | .htotal = 720 + 252 + 1 + 24, | |
601 | .vdisplay = 360, | |
602 | .vsync_start = 360 + 4, | |
603 | .vsync_end = 360 + 4 + 1, | |
604 | .vtotal = 360 + 4 + 1 + 18, | |
0ee2e37a LW |
605 | .flags = 0, |
606 | }; | |
607 | ||
608 | /* BT.656 VGA mode, 640x480 */ | |
609 | static const struct drm_display_mode itu_r_bt_656_640_mode = { | |
e4d2a287 | 610 | .clock = 24540, |
0ee2e37a LW |
611 | .hdisplay = 640, |
612 | .hsync_start = 640 + 3, | |
613 | .hsync_end = 640 + 3 + 1, | |
614 | .htotal = 640 + 3 + 1 + 272, | |
615 | .vdisplay = 480, | |
616 | .vsync_start = 480 + 4, | |
617 | .vsync_end = 480 + 4 + 1, | |
618 | .vtotal = 500, | |
0ee2e37a LW |
619 | .flags = 0, |
620 | }; | |
621 | ||
622 | /* BT.656 D1 mode 720x480 */ | |
623 | static const struct drm_display_mode itu_r_bt_656_720_mode = { | |
e4d2a287 | 624 | .clock = 27000, |
0ee2e37a LW |
625 | .hdisplay = 720, |
626 | .hsync_start = 720 + 3, | |
627 | .hsync_end = 720 + 3 + 1, | |
628 | .htotal = 720 + 3 + 1 + 272, | |
629 | .vdisplay = 480, | |
630 | .vsync_start = 480 + 4, | |
631 | .vsync_end = 480 + 4 + 1, | |
632 | .vtotal = 500, | |
0ee2e37a LW |
633 | .flags = 0, |
634 | }; | |
635 | ||
0ce8ddd8 SR |
636 | static int ili9322_get_modes(struct drm_panel *panel, |
637 | struct drm_connector *connector) | |
0ee2e37a | 638 | { |
0ee2e37a | 639 | struct ili9322 *ili = panel_to_ili9322(panel); |
aa6c4364 | 640 | struct drm_device *drm = connector->dev; |
0ee2e37a | 641 | struct drm_display_mode *mode; |
9d027b8e SR |
642 | struct drm_display_info *info; |
643 | ||
644 | info = &connector->display_info; | |
645 | info->width_mm = ili->conf->width_mm; | |
646 | info->height_mm = ili->conf->height_mm; | |
647 | if (ili->conf->dclk_active_high) | |
648 | info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; | |
649 | else | |
650 | info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; | |
651 | ||
652 | if (ili->conf->de_active_high) | |
653 | info->bus_flags |= DRM_BUS_FLAG_DE_HIGH; | |
654 | else | |
655 | info->bus_flags |= DRM_BUS_FLAG_DE_LOW; | |
0ee2e37a LW |
656 | |
657 | switch (ili->input) { | |
658 | case ILI9322_INPUT_SRGB_DUMMY_320X240: | |
aa6c4364 | 659 | mode = drm_mode_duplicate(drm, &srgb_320x240_mode); |
0ee2e37a LW |
660 | break; |
661 | case ILI9322_INPUT_SRGB_DUMMY_360X240: | |
aa6c4364 | 662 | mode = drm_mode_duplicate(drm, &srgb_360x240_mode); |
0ee2e37a LW |
663 | break; |
664 | case ILI9322_INPUT_PRGB_THROUGH: | |
665 | case ILI9322_INPUT_PRGB_ALIGNED: | |
aa6c4364 | 666 | mode = drm_mode_duplicate(drm, &prgb_320x240_mode); |
0ee2e37a LW |
667 | break; |
668 | case ILI9322_INPUT_YUV_640X320_YCBCR: | |
aa6c4364 | 669 | mode = drm_mode_duplicate(drm, &yuv_640x320_mode); |
0ee2e37a LW |
670 | break; |
671 | case ILI9322_INPUT_YUV_720X360_YCBCR: | |
aa6c4364 | 672 | mode = drm_mode_duplicate(drm, &yuv_720x360_mode); |
0ee2e37a LW |
673 | break; |
674 | case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR: | |
aa6c4364 | 675 | mode = drm_mode_duplicate(drm, &itu_r_bt_656_720_mode); |
0ee2e37a LW |
676 | break; |
677 | case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR: | |
aa6c4364 | 678 | mode = drm_mode_duplicate(drm, &itu_r_bt_656_640_mode); |
0ee2e37a LW |
679 | break; |
680 | default: | |
681 | mode = NULL; | |
682 | break; | |
683 | } | |
684 | if (!mode) { | |
a25b6b27 | 685 | dev_err(panel->dev, "bad mode or failed to add mode\n"); |
0ee2e37a LW |
686 | return -EINVAL; |
687 | } | |
688 | drm_mode_set_name(mode); | |
689 | /* | |
690 | * This is the preferred mode because most people are going | |
691 | * to want to use the display with VGA type graphics. | |
692 | */ | |
693 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | |
694 | ||
695 | /* Set up the polarity */ | |
696 | if (ili->conf->hsync_active_high) | |
697 | mode->flags |= DRM_MODE_FLAG_PHSYNC; | |
698 | else | |
699 | mode->flags |= DRM_MODE_FLAG_NHSYNC; | |
700 | if (ili->conf->vsync_active_high) | |
701 | mode->flags |= DRM_MODE_FLAG_PVSYNC; | |
702 | else | |
703 | mode->flags |= DRM_MODE_FLAG_NVSYNC; | |
704 | ||
705 | mode->width_mm = ili->conf->width_mm; | |
706 | mode->height_mm = ili->conf->height_mm; | |
707 | drm_mode_probed_add(connector, mode); | |
708 | ||
709 | return 1; /* Number of modes */ | |
710 | } | |
711 | ||
712 | static const struct drm_panel_funcs ili9322_drm_funcs = { | |
713 | .disable = ili9322_disable, | |
714 | .unprepare = ili9322_unprepare, | |
715 | .prepare = ili9322_prepare, | |
716 | .enable = ili9322_enable, | |
717 | .get_modes = ili9322_get_modes, | |
718 | }; | |
719 | ||
720 | static int ili9322_probe(struct spi_device *spi) | |
721 | { | |
722 | struct device *dev = &spi->dev; | |
723 | struct ili9322 *ili; | |
724 | const struct regmap_config *regmap_config; | |
725 | u8 gamma; | |
726 | u32 val; | |
727 | int ret; | |
728 | int i; | |
729 | ||
730 | ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL); | |
731 | if (!ili) | |
732 | return -ENOMEM; | |
733 | ||
734 | spi_set_drvdata(spi, ili); | |
735 | ||
736 | ili->dev = dev; | |
737 | ||
738 | /* | |
739 | * Every new incarnation of this display must have a unique | |
740 | * data entry for the system in this driver. | |
741 | */ | |
742 | ili->conf = of_device_get_match_data(dev); | |
743 | if (!ili->conf) { | |
744 | dev_err(dev, "missing device configuration\n"); | |
745 | return -ENODEV; | |
746 | } | |
747 | ||
748 | val = ili->conf->vreg1out_mv; | |
749 | if (!val) { | |
750 | /* Default HW value, do not touch (should be 4.5V) */ | |
751 | ili->vreg1out = U8_MAX; | |
752 | } else { | |
753 | if (val < 3600) { | |
754 | dev_err(dev, "too low VREG1OUT\n"); | |
755 | return -EINVAL; | |
756 | } | |
757 | if (val > 6000) { | |
758 | dev_err(dev, "too high VREG1OUT\n"); | |
759 | return -EINVAL; | |
760 | } | |
761 | if ((val % 100) != 0) { | |
762 | dev_err(dev, "VREG1OUT is no even 100 microvolt\n"); | |
763 | return -EINVAL; | |
764 | } | |
765 | val -= 3600; | |
766 | val /= 100; | |
767 | dev_dbg(dev, "VREG1OUT = 0x%02x\n", val); | |
768 | ili->vreg1out = val; | |
769 | } | |
770 | ||
771 | val = ili->conf->vcom_high_percent; | |
772 | if (!val) { | |
773 | /* Default HW value, do not touch (should be 91%) */ | |
774 | ili->vcom_high = U8_MAX; | |
775 | } else { | |
776 | if (val < 37) { | |
777 | dev_err(dev, "too low VCOM high\n"); | |
778 | return -EINVAL; | |
779 | } | |
780 | if (val > 100) { | |
781 | dev_err(dev, "too high VCOM high\n"); | |
782 | return -EINVAL; | |
783 | } | |
784 | val -= 37; | |
785 | dev_dbg(dev, "VCOM high = 0x%02x\n", val); | |
786 | ili->vcom_high = val; | |
787 | } | |
788 | ||
789 | val = ili->conf->vcom_amplitude_percent; | |
790 | if (!val) { | |
791 | /* Default HW value, do not touch (should be 114%) */ | |
792 | ili->vcom_high = U8_MAX; | |
793 | } else { | |
794 | if (val < 70) { | |
795 | dev_err(dev, "too low VCOM amplitude\n"); | |
796 | return -EINVAL; | |
797 | } | |
798 | if (val > 132) { | |
799 | dev_err(dev, "too high VCOM amplitude\n"); | |
800 | return -EINVAL; | |
801 | } | |
802 | val -= 70; | |
803 | val >>= 1; /* Increments of 2% */ | |
804 | dev_dbg(dev, "VCOM amplitude = 0x%02x\n", val); | |
805 | ili->vcom_amplitude = val; | |
806 | } | |
807 | ||
808 | for (i = 0; i < ARRAY_SIZE(ili->gamma); i++) { | |
809 | val = ili->conf->gamma_corr_neg[i]; | |
810 | if (val > 15) { | |
811 | dev_err(dev, "negative gamma %u > 15, capping\n", val); | |
812 | val = 15; | |
813 | } | |
814 | gamma = val << 4; | |
815 | val = ili->conf->gamma_corr_pos[i]; | |
816 | if (val > 15) { | |
817 | dev_err(dev, "positive gamma %u > 15, capping\n", val); | |
818 | val = 15; | |
819 | } | |
820 | gamma |= val; | |
821 | ili->gamma[i] = gamma; | |
822 | dev_dbg(dev, "gamma V%d: 0x%02x\n", i + 1, gamma); | |
823 | } | |
824 | ||
825 | ili->supplies[0].supply = "vcc"; /* 2.7-3.6 V */ | |
826 | ili->supplies[1].supply = "iovcc"; /* 1.65-3.6V */ | |
827 | ili->supplies[2].supply = "vci"; /* 2.7-3.6V */ | |
828 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies), | |
829 | ili->supplies); | |
830 | if (ret < 0) | |
831 | return ret; | |
832 | ret = regulator_set_voltage(ili->supplies[0].consumer, | |
833 | 2700000, 3600000); | |
834 | if (ret) | |
835 | return ret; | |
836 | ret = regulator_set_voltage(ili->supplies[1].consumer, | |
837 | 1650000, 3600000); | |
838 | if (ret) | |
839 | return ret; | |
840 | ret = regulator_set_voltage(ili->supplies[2].consumer, | |
841 | 2700000, 3600000); | |
842 | if (ret) | |
843 | return ret; | |
844 | ||
845 | ili->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | |
846 | if (IS_ERR(ili->reset_gpio)) { | |
847 | dev_err(dev, "failed to get RESET GPIO\n"); | |
848 | return PTR_ERR(ili->reset_gpio); | |
849 | } | |
850 | ||
851 | spi->bits_per_word = 8; | |
852 | ret = spi_setup(spi); | |
853 | if (ret < 0) { | |
854 | dev_err(dev, "spi setup failed.\n"); | |
855 | return ret; | |
856 | } | |
857 | regmap_config = &ili9322_regmap_config; | |
858 | ili->regmap = devm_regmap_init(dev, &ili9322_regmap_bus, dev, | |
859 | regmap_config); | |
860 | if (IS_ERR(ili->regmap)) { | |
861 | dev_err(dev, "failed to allocate register map\n"); | |
862 | return PTR_ERR(ili->regmap); | |
863 | } | |
864 | ||
865 | ret = regmap_read(ili->regmap, ILI9322_CHIP_ID, &val); | |
866 | if (ret) { | |
867 | dev_err(dev, "can't get chip ID (%d)\n", ret); | |
868 | return ret; | |
869 | } | |
870 | if (val != ILI9322_CHIP_ID_MAGIC) { | |
871 | dev_err(dev, "chip ID 0x%0x2, expected 0x%02x\n", val, | |
872 | ILI9322_CHIP_ID_MAGIC); | |
873 | return -ENODEV; | |
874 | } | |
875 | ||
876 | /* Probe the system to find the display setting */ | |
877 | if (ili->conf->input == ILI9322_INPUT_UNKNOWN) { | |
878 | ret = regmap_read(ili->regmap, ILI9322_ENTRY, &val); | |
879 | if (ret) { | |
880 | dev_err(dev, "can't get entry setting (%d)\n", ret); | |
881 | return ret; | |
882 | } | |
883 | /* Input enum corresponds to HW setting */ | |
884 | ili->input = (val >> 4) & 0x0f; | |
885 | if (ili->input >= ILI9322_INPUT_UNKNOWN) | |
886 | ili->input = ILI9322_INPUT_UNKNOWN; | |
887 | } else { | |
888 | ili->input = ili->conf->input; | |
889 | } | |
890 | ||
9a2654c0 LP |
891 | drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs, |
892 | DRM_MODE_CONNECTOR_DPI); | |
0ee2e37a | 893 | |
c3ee8c65 BZ |
894 | drm_panel_add(&ili->panel); |
895 | ||
896 | return 0; | |
0ee2e37a LW |
897 | } |
898 | ||
899 | static int ili9322_remove(struct spi_device *spi) | |
900 | { | |
901 | struct ili9322 *ili = spi_get_drvdata(spi); | |
902 | ||
903 | ili9322_power_off(ili); | |
904 | drm_panel_remove(&ili->panel); | |
905 | ||
906 | return 0; | |
907 | } | |
908 | ||
909 | /* | |
910 | * The D-Link DIR-685 panel is marked LM918A01-1A SY-B4-091116-E0199 | |
911 | */ | |
912 | static const struct ili9322_config ili9322_dir_685 = { | |
913 | .width_mm = 65, | |
914 | .height_mm = 50, | |
915 | .input = ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR, | |
916 | .vreg1out_mv = 4600, | |
917 | .vcom_high_percent = 91, | |
918 | .vcom_amplitude_percent = 114, | |
919 | .syncmode = ILI9322_IF_CTRL_SYNC_DISABLED, | |
920 | .dclk_active_high = true, | |
921 | .gamma_corr_neg = { 0xa, 0x5, 0x7, 0x7, 0x7, 0x5, 0x1, 0x6 }, | |
922 | .gamma_corr_pos = { 0x7, 0x7, 0x3, 0x2, 0x3, 0x5, 0x7, 0x2 }, | |
923 | }; | |
924 | ||
925 | static const struct of_device_id ili9322_of_match[] = { | |
926 | { | |
927 | .compatible = "dlink,dir-685-panel", | |
928 | .data = &ili9322_dir_685, | |
929 | }, | |
930 | { | |
931 | .compatible = "ilitek,ili9322", | |
932 | .data = NULL, | |
933 | }, | |
934 | { } | |
935 | }; | |
936 | MODULE_DEVICE_TABLE(of, ili9322_of_match); | |
937 | ||
938 | static struct spi_driver ili9322_driver = { | |
939 | .probe = ili9322_probe, | |
940 | .remove = ili9322_remove, | |
941 | .driver = { | |
942 | .name = "panel-ilitek-ili9322", | |
943 | .of_match_table = ili9322_of_match, | |
944 | }, | |
945 | }; | |
946 | module_spi_driver(ili9322_driver); | |
947 | ||
948 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | |
949 | MODULE_DESCRIPTION("ILI9322 LCD panel driver"); | |
950 | MODULE_LICENSE("GPL v2"); |