diff options
author | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2013-08-01 04:40:37 -0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-08-02 06:15:08 -0400 |
commit | fb52566873ca8c7006948b9d51c7f3fbcc0b1116 (patch) | |
tree | a0cebd0e95a39fbefcfb23f936ee72c8eb4893bc /drivers/video | |
parent | ccf9901ffec4b41a3a5c22580c4a7d358d88ca9f (diff) |
fb: backlight: HX8357: Add HX8369 support
Add support for the Himax HX8369 controller as it is quite similar to the
hx8357.
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/hx8357.c | 219 |
1 files changed, 204 insertions, 15 deletions
diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c index 47237fa73b0c..c7af8c45ab8a 100644 --- a/drivers/video/backlight/hx8357.c +++ b/drivers/video/backlight/hx8357.c | |||
@@ -71,6 +71,18 @@ | |||
71 | #define HX8357_SET_POWER_NORMAL 0xd2 | 71 | #define HX8357_SET_POWER_NORMAL 0xd2 |
72 | #define HX8357_SET_PANEL_RELATED 0xe9 | 72 | #define HX8357_SET_PANEL_RELATED 0xe9 |
73 | 73 | ||
74 | #define HX8369_SET_DISPLAY_BRIGHTNESS 0x51 | ||
75 | #define HX8369_WRITE_CABC_DISPLAY_VALUE 0x53 | ||
76 | #define HX8369_WRITE_CABC_BRIGHT_CTRL 0x55 | ||
77 | #define HX8369_WRITE_CABC_MIN_BRIGHTNESS 0x5e | ||
78 | #define HX8369_SET_POWER 0xb1 | ||
79 | #define HX8369_SET_DISPLAY_MODE 0xb2 | ||
80 | #define HX8369_SET_DISPLAY_WAVEFORM_CYC 0xb4 | ||
81 | #define HX8369_SET_VCOM 0xb6 | ||
82 | #define HX8369_SET_EXTENSION_COMMAND 0xb9 | ||
83 | #define HX8369_SET_GIP 0xd5 | ||
84 | #define HX8369_SET_GAMMA_CURVE_RELATED 0xe0 | ||
85 | |||
74 | struct hx8357_data { | 86 | struct hx8357_data { |
75 | unsigned im_pins[HX8357_NUM_IM_PINS]; | 87 | unsigned im_pins[HX8357_NUM_IM_PINS]; |
76 | unsigned reset; | 88 | unsigned reset; |
@@ -144,6 +156,61 @@ static u8 hx8357_seq_display_mode[] = { | |||
144 | HX8357_SET_DISPLAY_MODE_RGB_INTERFACE, | 156 | HX8357_SET_DISPLAY_MODE_RGB_INTERFACE, |
145 | }; | 157 | }; |
146 | 158 | ||
159 | static u8 hx8369_seq_write_CABC_min_brightness[] = { | ||
160 | HX8369_WRITE_CABC_MIN_BRIGHTNESS, 0x00, | ||
161 | }; | ||
162 | |||
163 | static u8 hx8369_seq_write_CABC_control[] = { | ||
164 | HX8369_WRITE_CABC_DISPLAY_VALUE, 0x24, | ||
165 | }; | ||
166 | |||
167 | static u8 hx8369_seq_set_display_brightness[] = { | ||
168 | HX8369_SET_DISPLAY_BRIGHTNESS, 0xFF, | ||
169 | }; | ||
170 | |||
171 | static u8 hx8369_seq_write_CABC_control_setting[] = { | ||
172 | HX8369_WRITE_CABC_BRIGHT_CTRL, 0x02, | ||
173 | }; | ||
174 | |||
175 | static u8 hx8369_seq_extension_command[] = { | ||
176 | HX8369_SET_EXTENSION_COMMAND, 0xff, 0x83, 0x69, | ||
177 | }; | ||
178 | |||
179 | static u8 hx8369_seq_display_related[] = { | ||
180 | HX8369_SET_DISPLAY_MODE, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, | ||
181 | 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, | ||
182 | }; | ||
183 | |||
184 | static u8 hx8369_seq_panel_waveform_cycle[] = { | ||
185 | HX8369_SET_DISPLAY_WAVEFORM_CYC, 0x0a, 0x1d, 0x80, 0x06, 0x02, | ||
186 | }; | ||
187 | |||
188 | static u8 hx8369_seq_set_address_mode[] = { | ||
189 | HX8357_SET_ADDRESS_MODE, 0x00, | ||
190 | }; | ||
191 | |||
192 | static u8 hx8369_seq_vcom[] = { | ||
193 | HX8369_SET_VCOM, 0x3e, 0x3e, | ||
194 | }; | ||
195 | |||
196 | static u8 hx8369_seq_gip[] = { | ||
197 | HX8369_SET_GIP, 0x00, 0x01, 0x03, 0x25, 0x01, 0x02, 0x28, 0x70, | ||
198 | 0x11, 0x13, 0x00, 0x00, 0x40, 0x26, 0x51, 0x37, 0x00, 0x00, 0x71, | ||
199 | 0x35, 0x60, 0x24, 0x07, 0x0f, 0x04, 0x04, | ||
200 | }; | ||
201 | |||
202 | static u8 hx8369_seq_power[] = { | ||
203 | HX8369_SET_POWER, 0x01, 0x00, 0x34, 0x03, 0x00, 0x11, 0x11, 0x32, | ||
204 | 0x2f, 0x3f, 0x3f, 0x01, 0x3a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, | ||
205 | }; | ||
206 | |||
207 | static u8 hx8369_seq_gamma_curve_related[] = { | ||
208 | HX8369_SET_GAMMA_CURVE_RELATED, 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, | ||
209 | 0x2e, 0x4a, 0x08, 0x0e, 0x0f, 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, | ||
210 | 0x00, 0x0d, 0x19, 0x2f, 0x3b, 0x3d, 0x2e, 0x4a, 0x08, 0x0e, 0x0f, | ||
211 | 0x14, 0x16, 0x14, 0x14, 0x14, 0x1e, | ||
212 | }; | ||
213 | |||
147 | static int hx8357_spi_write_then_read(struct lcd_device *lcdev, | 214 | static int hx8357_spi_write_then_read(struct lcd_device *lcdev, |
148 | u8 *txbuf, u16 txlen, | 215 | u8 *txbuf, u16 txlen, |
149 | u8 *rxbuf, u16 rxlen) | 216 | u8 *rxbuf, u16 rxlen) |
@@ -220,6 +287,10 @@ static int hx8357_enter_standby(struct lcd_device *lcdev) | |||
220 | if (ret < 0) | 287 | if (ret < 0) |
221 | return ret; | 288 | return ret; |
222 | 289 | ||
290 | /* | ||
291 | * The controller needs 120ms when entering in sleep mode before we can | ||
292 | * send the command to go off sleep mode | ||
293 | */ | ||
223 | msleep(120); | 294 | msleep(120); |
224 | 295 | ||
225 | return 0; | 296 | return 0; |
@@ -233,6 +304,10 @@ static int hx8357_exit_standby(struct lcd_device *lcdev) | |||
233 | if (ret < 0) | 304 | if (ret < 0) |
234 | return ret; | 305 | return ret; |
235 | 306 | ||
307 | /* | ||
308 | * The controller needs 120ms when exiting from sleep mode before we | ||
309 | * can send the command to enter in sleep mode | ||
310 | */ | ||
236 | msleep(120); | 311 | msleep(120); |
237 | 312 | ||
238 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | 313 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); |
@@ -242,6 +317,21 @@ static int hx8357_exit_standby(struct lcd_device *lcdev) | |||
242 | return 0; | 317 | return 0; |
243 | } | 318 | } |
244 | 319 | ||
320 | static void hx8357_lcd_reset(struct lcd_device *lcdev) | ||
321 | { | ||
322 | struct hx8357_data *lcd = lcd_get_data(lcdev); | ||
323 | |||
324 | /* Reset the screen */ | ||
325 | gpio_set_value(lcd->reset, 1); | ||
326 | usleep_range(10000, 12000); | ||
327 | gpio_set_value(lcd->reset, 0); | ||
328 | usleep_range(10000, 12000); | ||
329 | gpio_set_value(lcd->reset, 1); | ||
330 | |||
331 | /* The controller needs 120ms to recover from reset */ | ||
332 | msleep(120); | ||
333 | } | ||
334 | |||
245 | static int hx8357_lcd_init(struct lcd_device *lcdev) | 335 | static int hx8357_lcd_init(struct lcd_device *lcdev) |
246 | { | 336 | { |
247 | struct hx8357_data *lcd = lcd_get_data(lcdev); | 337 | struct hx8357_data *lcd = lcd_get_data(lcdev); |
@@ -257,14 +347,6 @@ static int hx8357_lcd_init(struct lcd_device *lcdev) | |||
257 | gpio_set_value_cansleep(lcd->im_pins[2], 1); | 347 | gpio_set_value_cansleep(lcd->im_pins[2], 1); |
258 | } | 348 | } |
259 | 349 | ||
260 | /* Reset the screen */ | ||
261 | gpio_set_value(lcd->reset, 1); | ||
262 | usleep_range(10000, 12000); | ||
263 | gpio_set_value(lcd->reset, 0); | ||
264 | usleep_range(10000, 12000); | ||
265 | gpio_set_value(lcd->reset, 1); | ||
266 | msleep(120); | ||
267 | |||
268 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_power, | 350 | ret = hx8357_spi_write_array(lcdev, hx8357_seq_power, |
269 | ARRAY_SIZE(hx8357_seq_power)); | 351 | ARRAY_SIZE(hx8357_seq_power)); |
270 | if (ret < 0) | 352 | if (ret < 0) |
@@ -344,6 +426,9 @@ static int hx8357_lcd_init(struct lcd_device *lcdev) | |||
344 | if (ret < 0) | 426 | if (ret < 0) |
345 | return ret; | 427 | return ret; |
346 | 428 | ||
429 | /* | ||
430 | * The controller needs 120ms to fully recover from exiting sleep mode | ||
431 | */ | ||
347 | msleep(120); | 432 | msleep(120); |
348 | 433 | ||
349 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | 434 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); |
@@ -359,6 +444,96 @@ static int hx8357_lcd_init(struct lcd_device *lcdev) | |||
359 | return 0; | 444 | return 0; |
360 | } | 445 | } |
361 | 446 | ||
447 | static int hx8369_lcd_init(struct lcd_device *lcdev) | ||
448 | { | ||
449 | int ret; | ||
450 | |||
451 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_extension_command, | ||
452 | ARRAY_SIZE(hx8369_seq_extension_command)); | ||
453 | if (ret < 0) | ||
454 | return ret; | ||
455 | usleep_range(10000, 12000); | ||
456 | |||
457 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_display_related, | ||
458 | ARRAY_SIZE(hx8369_seq_display_related)); | ||
459 | if (ret < 0) | ||
460 | return ret; | ||
461 | |||
462 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_panel_waveform_cycle, | ||
463 | ARRAY_SIZE(hx8369_seq_panel_waveform_cycle)); | ||
464 | if (ret < 0) | ||
465 | return ret; | ||
466 | |||
467 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_address_mode, | ||
468 | ARRAY_SIZE(hx8369_seq_set_address_mode)); | ||
469 | if (ret < 0) | ||
470 | return ret; | ||
471 | |||
472 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_vcom, | ||
473 | ARRAY_SIZE(hx8369_seq_vcom)); | ||
474 | if (ret < 0) | ||
475 | return ret; | ||
476 | |||
477 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_gip, | ||
478 | ARRAY_SIZE(hx8369_seq_gip)); | ||
479 | if (ret < 0) | ||
480 | return ret; | ||
481 | |||
482 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_power, | ||
483 | ARRAY_SIZE(hx8369_seq_power)); | ||
484 | if (ret < 0) | ||
485 | return ret; | ||
486 | |||
487 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | ||
488 | if (ret < 0) | ||
489 | return ret; | ||
490 | |||
491 | /* | ||
492 | * The controller needs 120ms to fully recover from exiting sleep mode | ||
493 | */ | ||
494 | msleep(120); | ||
495 | |||
496 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_gamma_curve_related, | ||
497 | ARRAY_SIZE(hx8369_seq_gamma_curve_related)); | ||
498 | if (ret < 0) | ||
499 | return ret; | ||
500 | |||
501 | ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); | ||
502 | if (ret < 0) | ||
503 | return ret; | ||
504 | usleep_range(1000, 1200); | ||
505 | |||
506 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_write_CABC_control, | ||
507 | ARRAY_SIZE(hx8369_seq_write_CABC_control)); | ||
508 | if (ret < 0) | ||
509 | return ret; | ||
510 | usleep_range(10000, 12000); | ||
511 | |||
512 | ret = hx8357_spi_write_array(lcdev, | ||
513 | hx8369_seq_write_CABC_control_setting, | ||
514 | ARRAY_SIZE(hx8369_seq_write_CABC_control_setting)); | ||
515 | if (ret < 0) | ||
516 | return ret; | ||
517 | |||
518 | ret = hx8357_spi_write_array(lcdev, | ||
519 | hx8369_seq_write_CABC_min_brightness, | ||
520 | ARRAY_SIZE(hx8369_seq_write_CABC_min_brightness)); | ||
521 | if (ret < 0) | ||
522 | return ret; | ||
523 | usleep_range(10000, 12000); | ||
524 | |||
525 | ret = hx8357_spi_write_array(lcdev, hx8369_seq_set_display_brightness, | ||
526 | ARRAY_SIZE(hx8369_seq_set_display_brightness)); | ||
527 | if (ret < 0) | ||
528 | return ret; | ||
529 | |||
530 | ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); | ||
531 | if (ret < 0) | ||
532 | return ret; | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
362 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) | 537 | #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) |
363 | 538 | ||
364 | static int hx8357_set_power(struct lcd_device *lcdev, int power) | 539 | static int hx8357_set_power(struct lcd_device *lcdev, int power) |
@@ -391,10 +566,24 @@ static struct lcd_ops hx8357_ops = { | |||
391 | .get_power = hx8357_get_power, | 566 | .get_power = hx8357_get_power, |
392 | }; | 567 | }; |
393 | 568 | ||
569 | static const struct of_device_id hx8357_dt_ids[] = { | ||
570 | { | ||
571 | .compatible = "himax,hx8357", | ||
572 | .data = hx8357_lcd_init, | ||
573 | }, | ||
574 | { | ||
575 | .compatible = "himax,hx8369", | ||
576 | .data = hx8369_lcd_init, | ||
577 | }, | ||
578 | {}, | ||
579 | }; | ||
580 | MODULE_DEVICE_TABLE(of, hx8357_dt_ids); | ||
581 | |||
394 | static int hx8357_probe(struct spi_device *spi) | 582 | static int hx8357_probe(struct spi_device *spi) |
395 | { | 583 | { |
396 | struct lcd_device *lcdev; | 584 | struct lcd_device *lcdev; |
397 | struct hx8357_data *lcd; | 585 | struct hx8357_data *lcd; |
586 | const struct of_device_id *match; | ||
398 | int i, ret; | 587 | int i, ret; |
399 | 588 | ||
400 | lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); | 589 | lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); |
@@ -411,6 +600,10 @@ static int hx8357_probe(struct spi_device *spi) | |||
411 | 600 | ||
412 | lcd->spi = spi; | 601 | lcd->spi = spi; |
413 | 602 | ||
603 | match = of_match_device(hx8357_dt_ids, &spi->dev); | ||
604 | if (!match || !match->data) | ||
605 | return -EINVAL; | ||
606 | |||
414 | lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); | 607 | lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); |
415 | if (!gpio_is_valid(lcd->reset)) { | 608 | if (!gpio_is_valid(lcd->reset)) { |
416 | dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); | 609 | dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); |
@@ -462,7 +655,9 @@ static int hx8357_probe(struct spi_device *spi) | |||
462 | } | 655 | } |
463 | spi_set_drvdata(spi, lcdev); | 656 | spi_set_drvdata(spi, lcdev); |
464 | 657 | ||
465 | ret = hx8357_lcd_init(lcdev); | 658 | hx8357_lcd_reset(lcdev); |
659 | |||
660 | ret = ((int (*)(struct lcd_device *))match->data)(lcdev); | ||
466 | if (ret) { | 661 | if (ret) { |
467 | dev_err(&spi->dev, "Couldn't initialize panel\n"); | 662 | dev_err(&spi->dev, "Couldn't initialize panel\n"); |
468 | goto init_error; | 663 | goto init_error; |
@@ -485,12 +680,6 @@ static int hx8357_remove(struct spi_device *spi) | |||
485 | return 0; | 680 | return 0; |
486 | } | 681 | } |
487 | 682 | ||
488 | static const struct of_device_id hx8357_dt_ids[] = { | ||
489 | { .compatible = "himax,hx8357" }, | ||
490 | {}, | ||
491 | }; | ||
492 | MODULE_DEVICE_TABLE(of, hx8357_dt_ids); | ||
493 | |||
494 | static struct spi_driver hx8357_driver = { | 683 | static struct spi_driver hx8357_driver = { |
495 | .probe = hx8357_probe, | 684 | .probe = hx8357_probe, |
496 | .remove = hx8357_remove, | 685 | .remove = hx8357_remove, |