aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/samsung_duallcd/s3cfb_lms501kf03.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/samsung_duallcd/s3cfb_lms501kf03.c')
-rw-r--r--drivers/video/samsung_duallcd/s3cfb_lms501kf03.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/drivers/video/samsung_duallcd/s3cfb_lms501kf03.c b/drivers/video/samsung_duallcd/s3cfb_lms501kf03.c
new file mode 100644
index 0000000..078cb85
--- /dev/null
+++ b/drivers/video/samsung_duallcd/s3cfb_lms501kf03.c
@@ -0,0 +1,415 @@
+/* linux/drivers/video/samsung/s3cfb_lms501kf03.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * LMS501KF03 5.0" WVGA Landscape LCD module driver for the SMDK
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#include <linux/wait.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <plat/gpio-cfg.h>
+
+#include "s3cfb.h"
+
+#define ENDDEF 0xFF00
+#define COMMAND_ONLY 0x00
+#define DATA_ONLY 0x01
+
+#ifdef CONFIG_BACKLIGHT_LMS501KF03_TFT
+
+#define dbg(fmt...)
+
+static int locked;
+struct s5p_lcd {
+ struct spi_device *g_spi;
+ struct lcd_device *lcd_dev;
+ struct backlight_device *bl_dev;
+};
+static struct s5p_lcd lcd;
+#else
+static struct spi_device *g_spi;
+#endif
+
+const unsigned short SEQ_SET_PASSWORD[] = {
+ 0xb9, 0xff, 0x83, 0x69,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_POWER[] = {
+ 0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
+ 0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_DISPLAY[] = {
+ 0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_RGB_IF[] = {
+ 0xb3, 0x09,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_DISPLAY_INV[] = {
+ 0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_VCOM[] = {
+ 0xb6, 0x4c, 0x2e,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_GATE[] = {
+ 0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
+ 0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
+ 0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_PANEL[] = {
+ 0xcc, 0x02,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_COL_MOD[] = {
+ 0x3a, 0x77,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_W_GAMMA[] = {
+ 0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
+ 0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
+ 0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
+ 0x18, 0x16, 0x17, 0x0d, 0x15,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_RGB_GAMMA[] = {
+ 0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
+ 0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
+ 0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
+ 0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
+ 0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
+ 0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
+ 0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
+ 0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
+ 0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
+ 0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
+ 0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
+ 0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_UP_DN[] = {
+ 0x36, 0x10,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_SLEEP_IN[] = {
+ 0x10,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_SLEEP_OUT[] = {
+ 0x11,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_DISPLAY_ON[] = {
+ 0x29,
+ ENDDEF
+};
+
+const unsigned short SEQ_SET_DISPLAY_OFF[] = {
+ 0x10,
+ ENDDEF
+};
+
+static struct s3cfb_lcd lms501kf03 = {
+ .width = 480,
+ .height = 800,
+ .bpp = 24,
+
+ .freq = 60,
+ .timing = {
+ .h_fp = 8,
+ .h_bp = 8,
+ .h_sw = 6,
+ .v_fp = 6,
+ .v_fpe = 1,
+ .v_bp = 6,
+ .v_bpe = 1,
+ .v_sw = 4,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static int lms501kf03_spi_write_driver(int addr, int data)
+{
+ u16 buf[1];
+ struct spi_message msg;
+
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = buf,
+ };
+
+ buf[0] = (addr << 8) | data;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+#ifdef CONFIG_BACKLIGHT_LMS501KF03_TFT
+ int ret;
+ locked = 1;
+ ret = spi_sync(lcd.g_spi, &msg);
+ locked = 0;
+ return ret ;
+#else
+ return spi_sync(g_spi, &msg);
+#endif
+}
+
+static void lms501kf03_spi_write(unsigned char address, unsigned char command)
+{
+ lms501kf03_spi_write_driver(address, command);
+}
+
+static void lms501kf03_panel_send_sequence(const unsigned short *wbuf)
+{
+ int i = 0;
+
+ while (wbuf[i] != ENDDEF) {
+ if (i == 0)
+ lms501kf03_spi_write(COMMAND_ONLY, wbuf[i]);
+ else
+ lms501kf03_spi_write(DATA_ONLY, wbuf[i]);
+
+ udelay(100);
+ i += 1;
+ }
+}
+
+void lms501kf03_ldi_init(void)
+{
+ lms501kf03_panel_send_sequence(SEQ_SET_PASSWORD);
+ lms501kf03_panel_send_sequence(SEQ_SET_POWER);
+ lms501kf03_panel_send_sequence(SEQ_SET_DISPLAY);
+ lms501kf03_panel_send_sequence(SEQ_SET_RGB_IF);
+ lms501kf03_panel_send_sequence(SEQ_SET_DISPLAY_INV);
+ lms501kf03_panel_send_sequence(SEQ_SET_VCOM);
+ lms501kf03_panel_send_sequence(SEQ_SET_GATE);
+ lms501kf03_panel_send_sequence(SEQ_SET_PANEL);
+ lms501kf03_panel_send_sequence(SEQ_SET_COL_MOD);
+ lms501kf03_panel_send_sequence(SEQ_SET_W_GAMMA);
+ lms501kf03_panel_send_sequence(SEQ_SET_RGB_GAMMA);
+ lms501kf03_panel_send_sequence(SEQ_SET_SLEEP_OUT);
+ mdelay(120);
+ lms501kf03_panel_send_sequence(SEQ_SET_DISPLAY_ON);
+}
+
+void lms501kf03_ldi_enable(void)
+{
+ lms501kf03_panel_send_sequence(SEQ_SET_DISPLAY_ON);
+}
+
+void lms501kf03_ldi_disable(void)
+{
+ lms501kf03_panel_send_sequence(SEQ_SET_DISPLAY_OFF);
+ mdelay(120);
+}
+
+void lms501kf03_init_ldi(void)
+{
+ lms501kf03_ldi_init();
+}
+
+void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)
+{
+ lms501kf03.init_ldi = lms501kf03_ldi_init;
+ ctrl->lcd = &lms501kf03;
+}
+
+void lms501kf03_gpio_cfg(void)
+{
+ /* LCD _SPI CS */
+ s3c_gpio_cfgpin(EXYNOS4_GPB(5), S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(EXYNOS4_GPB(5), S3C_GPIO_PULL_NONE);
+
+ /* LCD_SPI SCLK */
+ s3c_gpio_cfgpin(EXYNOS4_GPB(4), S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(EXYNOS4_GPB(4), S3C_GPIO_PULL_NONE);
+
+ /* LCD_SPI MOSI */
+ s3c_gpio_cfgpin(EXYNOS4_GPB(7), S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(EXYNOS4_GPB(7), S3C_GPIO_PULL_NONE);
+}
+
+/* backlight operations and functions */
+#ifdef CONFIG_BACKLIGHT_LMS501KF03_TFT
+static int s5p_bl_update_status(struct backlight_device *bd)
+{
+ int bl = bd->props.brightness;
+ dbg("\nUpdate brightness=%d\n", bd->props.brightness);
+ int level = 0;
+
+ if (!locked) {
+ if ((bl >= 0) && (bl <= 50))
+ level = 1;
+ else if ((bl > 50) && (bl <= 100))
+ level = 2;
+ else if ((bl > 100) && (bl <= 150))
+ level = 3;
+ else if ((bl > 150) && (bl <= 200))
+ level = 4;
+ else if ((bl > 200) && (bl <= 255))
+ level = 5;
+
+ if (level) {
+
+ switch (level) {
+ /* If bl is not halved, variation in brightness is
+ * observed as a curve with the middle region being
+ * brightest and the sides being darker. It is
+ * required that brightness increases gradually
+ * from left to right.*/
+ case 1:
+ lms501kf03_spi_write(0x46, 0x2F);
+ lms501kf03_spi_write(0x56, 0x2E);
+ lms501kf03_spi_write(0x66, 0x3F);
+
+ break;
+ case 2:
+ lms501kf03_spi_write(0x46, 0x37);
+ lms501kf03_spi_write(0x56, 0x36);
+ lms501kf03_spi_write(0x66, 0x4A);
+
+ break;
+ case 3:
+ lms501kf03_spi_write(0x46, 0x3E);
+ lms501kf03_spi_write(0x56, 0x3D);
+ lms501kf03_spi_write(0x66, 0x53);
+
+ break;
+ case 4:
+ lms501kf03_spi_write(0x46, 0x44);
+ lms501kf03_spi_write(0x56, 0x43);
+ lms501kf03_spi_write(0x66, 0x5C);
+
+ break;
+ case 5:
+ lms501kf03_spi_write(0x46, 0x47);
+ lms501kf03_spi_write(0x56, 0x45);
+ lms501kf03_spi_write(0x66, 0x5F);
+ break;
+ default:
+ break;
+ }
+ } /* level check over */
+ } else {
+ dbg("\nLOCKED!!!Brightness cannot be changed now!locked=%d",
+ locked);
+ }
+ return 0;
+}
+
+static int s5p_bl_get_brightness(struct backlilght_device *bd)
+{
+ dbg("\n reading brightness\n");
+ return 0;
+}
+
+static const struct backlight_ops s5p_bl_ops = {
+ .update_status = s5p_bl_update_status,
+ .get_brightness = s5p_bl_get_brightness,
+};
+#endif
+
+static int __devinit lms501kf03_probe(struct spi_device *spi)
+{
+ int ret;
+#ifdef CONFIG_BACKLIGHT_LMS501KF03_TFT
+ struct backlight_properties props;
+#endif
+ spi->bits_per_word = 9;
+ ret = spi_setup(spi);
+
+#ifdef CONFIG_BACKLIGHT_LMS501KF03_TFT
+ lcd.g_spi = spi;
+
+ /* The node is named as pwm-backlight even though PWM
+ * control is not being done since Eclair interface is
+ * looking for "pwm-backlight" for backlight brightness
+ * control*/
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = 255;
+ lcd.bl_dev = backlight_device_register("pwm-backlight",
+ &spi->dev, &lcd, &s5p_bl_ops, &props);
+
+ dev_set_drvdata(&spi->dev, &lcd);
+#else
+ g_spi = spi;
+#endif
+
+ lms501kf03_gpio_cfg();
+ lms501kf03_ldi_init();
+
+ if (ret < 0)
+ return 0;
+
+ return ret;
+}
+#ifdef CONFIG_PM
+int lms501kf03_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ lms501kf03_ldi_disable();
+ return 0;
+}
+
+int lms501kf03_resume(struct platform_device *pdev, pm_message_t state)
+{
+ lms501kf03_ldi_init();
+ return 0;
+}
+#endif
+
+static struct spi_driver lms501kf03_driver = {
+ .driver = {
+ .name = "lms501kf03",
+ .owner = THIS_MODULE,
+ },
+ .probe = lms501kf03_probe,
+ .remove = __exit_p(lms501kf03_remove),
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init lms501kf03_init(void)
+{
+ return spi_register_driver(&lms501kf03_driver);
+}
+
+static void __exit lms501kf03_exit(void)
+{
+ spi_unregister_driver(&lms501kf03_driver);
+}
+
+module_init(lms501kf03_init);
+module_exit(lms501kf03_exit);