/* linux/arch/arm/mach-msm/board-mahimahi-panel.c * * Copyright (c) 2009 Google Inc. * Author: Dima Zavin * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "board-mahimahi.h" #include "devices.h" #define SPI_CONFIG (0x00000000) #define SPI_IO_CONTROL (0x00000004) #define SPI_OPERATIONAL (0x00000030) #define SPI_ERROR_FLAGS_EN (0x00000038) #define SPI_ERROR_FLAGS (0x00000038) #define SPI_OUTPUT_FIFO (0x00000100) static void __iomem *spi_base; static struct clk *spi_clk ; static struct vreg *vreg_lcm_rftx_2v6; static struct vreg *vreg_lcm_aux_2v6; static int qspi_send(uint32_t id, uint8_t data) { uint32_t err; /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { if ((err = readl(spi_base + SPI_ERROR_FLAGS))) { pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, err); return -EIO; } } writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO); udelay(100); return 0; } static int qspi_send_9bit(uint32_t id, uint8_t data) { uint32_t err; while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { err = readl(spi_base + SPI_ERROR_FLAGS); if (err) { pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, err); return -EIO; } } writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO); udelay(100); return 0; } static int lcm_writeb(uint8_t reg, uint8_t val) { qspi_send(0x0, reg); qspi_send(0x1, val); return 0; } static int lcm_writew(uint8_t reg, uint16_t val) { qspi_send(0x0, reg); qspi_send(0x1, val >> 8); qspi_send(0x1, val & 0xff); return 0; } static struct resource resources_msm_fb[] = { { .start = MSM_FB_BASE, .end = MSM_FB_BASE + MSM_FB_SIZE - 1, .flags = IORESOURCE_MEM, }, }; struct lcm_tbl { uint8_t reg; uint8_t val; }; static struct lcm_tbl samsung_oled_rgb565_init_table[] = { { 0x31, 0x08 }, { 0x32, 0x14 }, { 0x30, 0x2 }, { 0x27, 0x1 }, { 0x12, 0x8 }, { 0x13, 0x8 }, { 0x15, 0x0 }, { 0x16, 0x02 }, { 0x39, 0x24 }, { 0x17, 0x22 }, { 0x18, 0x33 }, { 0x19, 0x3 }, { 0x1A, 0x1 }, { 0x22, 0xA4 }, { 0x23, 0x0 }, { 0x26, 0xA0 }, }; static struct lcm_tbl samsung_oled_rgb666_init_table[] = { { 0x31, 0x08 }, { 0x32, 0x14 }, { 0x30, 0x2 }, { 0x27, 0x1 }, { 0x12, 0x8 }, { 0x13, 0x8 }, { 0x15, 0x0 }, { 0x16, 0x01 }, { 0x39, 0x24 }, { 0x17, 0x22 }, { 0x18, 0x33 }, { 0x19, 0x3 }, { 0x1A, 0x1 }, { 0x22, 0xA4 }, { 0x23, 0x0 }, { 0x26, 0xA0 }, }; static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table; static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table); #define OLED_GAMMA_TABLE_SIZE (7 * 3) static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { /* level 10 */ { /* Gamma-R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x3f }, { 0x43, 0x35 }, { 0x44, 0x30 }, { 0x45, 0x2c }, { 0x46, 0x13 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x0 }, { 0x53, 0x0 }, { 0x54, 0x27 }, { 0x55, 0x2b }, { 0x56, 0x12 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x3f }, { 0x63, 0x34 }, { 0x64, 0x2f }, { 0x65, 0x2b }, { 0x66, 0x1b }, }, /* level 40 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x3e }, { 0x43, 0x2e }, { 0x44, 0x2d }, { 0x45, 0x28 }, { 0x46, 0x21 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x0 }, { 0x53, 0x21 }, { 0x54, 0x2a }, { 0x55, 0x28 }, { 0x56, 0x20 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x3e }, { 0x63, 0x2d }, { 0x64, 0x2b }, { 0x65, 0x26 }, { 0x66, 0x2d }, }, /* level 70 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x35 }, { 0x43, 0x2c }, { 0x44, 0x2b }, { 0x45, 0x26 }, { 0x46, 0x29 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x0 }, { 0x53, 0x25 }, { 0x54, 0x29 }, { 0x55, 0x26 }, { 0x56, 0x28 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x34 }, { 0x63, 0x2b }, { 0x64, 0x2a }, { 0x65, 0x23 }, { 0x66, 0x37 }, }, /* level 100 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x30 }, { 0x43, 0x2a }, { 0x44, 0x2b }, { 0x45, 0x24 }, { 0x46, 0x2f }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x0 }, { 0x53, 0x25 }, { 0x54, 0x29 }, { 0x55, 0x24 }, { 0x56, 0x2e }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x2f }, { 0x63, 0x29 }, { 0x64, 0x29 }, { 0x65, 0x21 }, { 0x66, 0x3f }, }, /* level 130 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x2e }, { 0x43, 0x29 }, { 0x44, 0x2a }, { 0x45, 0x23 }, { 0x46, 0x34 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0xa }, { 0x53, 0x25 }, { 0x54, 0x28 }, { 0x55, 0x23 }, { 0x56, 0x33 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x2d }, { 0x63, 0x28 }, { 0x64, 0x27 }, { 0x65, 0x20 }, { 0x66, 0x46 }, }, /* level 160 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x2b }, { 0x43, 0x29 }, { 0x44, 0x28 }, { 0x45, 0x23 }, { 0x46, 0x38 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0xb }, { 0x53, 0x25 }, { 0x54, 0x27 }, { 0x55, 0x23 }, { 0x56, 0x37 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x29 }, { 0x63, 0x28 }, { 0x64, 0x25 }, { 0x65, 0x20 }, { 0x66, 0x4b }, }, /* level 190 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x29 }, { 0x43, 0x29 }, { 0x44, 0x27 }, { 0x45, 0x22 }, { 0x46, 0x3c }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x10 }, { 0x53, 0x26 }, { 0x54, 0x26 }, { 0x55, 0x22 }, { 0x56, 0x3b }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x28 }, { 0x63, 0x28 }, { 0x64, 0x24 }, { 0x65, 0x1f }, { 0x66, 0x50 }, }, /* level 220 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x28 }, { 0x43, 0x28 }, { 0x44, 0x28 }, { 0x45, 0x20 }, { 0x46, 0x40 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x11 }, { 0x53, 0x25 }, { 0x54, 0x27 }, { 0x55, 0x20 }, { 0x56, 0x3f }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x27 }, { 0x63, 0x26 }, { 0x64, 0x26 }, { 0x65, 0x1c }, { 0x66, 0x56 }, }, /* level 250 */ { /* Gamma -R */ { 0x40, 0x0 }, { 0x41, 0x3f }, { 0x42, 0x2a }, { 0x43, 0x27 }, { 0x44, 0x27 }, { 0x45, 0x1f }, { 0x46, 0x44 }, /* Gamma -G */ { 0x50, 0x0 }, { 0x51, 0x0 }, { 0x52, 0x17 }, { 0x53, 0x24 }, { 0x54, 0x26 }, { 0x55, 0x1f }, { 0x56, 0x43 }, /* Gamma -B */ { 0x60, 0x0 }, { 0x61, 0x3f }, { 0x62, 0x2a }, { 0x63, 0x25 }, { 0x64, 0x24 }, { 0x65, 0x1b }, { 0x66, 0x5c }, }, }; #define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table) #define SAMSUNG_OLED_MIN_VAL 10 #define SAMSUNG_OLED_MAX_VAL 250 #define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \ (SAMSUNG_OLED_MAX_VAL - \ SAMSUNG_OLED_MIN_VAL) / 2) #define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \ SAMSUNG_OLED_MIN_VAL) / \ (SAMSUNG_OLED_NUM_LEVELS - 1)) #define SONY_TFT_DEF_USER_VAL 102 #define SONY_TFT_MIN_USER_VAL 30 #define SONY_TFT_MAX_USER_VAL 255 #define SONY_TFT_DEF_PANEL_VAL 155 #define SONY_TFT_MIN_PANEL_VAL 26 #define SONY_TFT_MAX_PANEL_VAL 255 static DEFINE_MUTEX(panel_lock); static struct work_struct brightness_delayed_work; static DEFINE_SPINLOCK(brightness_lock); static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; static uint8_t table_sel_vals[] = { 0x43, 0x34 }; static int table_sel_idx = 0; static uint8_t tft_panel_on; static void gamma_table_bank_select(void) { lcm_writeb(0x39, table_sel_vals[table_sel_idx]); table_sel_idx ^= 1; } static void samsung_oled_set_gamma_val(int val) { int i; int level; int frac; val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL); val = (val / 2) * 2; level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP; frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP; clk_enable(spi_clk); for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) { unsigned int v1; unsigned int v2 = 0; u8 v; if (frac == 0) { v = samsung_oled_gamma_table[level][i].val; } else { v1 = samsung_oled_gamma_table[level][i].val; v2 = samsung_oled_gamma_table[level+1][i].val; v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) + v2 * frac) / SAMSUNG_OLED_LEVEL_STEP; } lcm_writeb(samsung_oled_gamma_table[level][i].reg, v); } gamma_table_bank_select(); clk_disable(spi_clk); last_val = val; } static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops) { pr_info("%s: +()\n", __func__); mutex_lock(&panel_lock); clk_enable(spi_clk); /* Set the gamma write target to 4, leave the current gamma set at 2 */ lcm_writeb(0x39, 0x24); clk_disable(spi_clk); mutex_unlock(&panel_lock); pr_info("%s: -()\n", __func__); return 0; } static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) { int i; pr_info("%s: +()\n", __func__); mutex_lock(&panel_lock); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); udelay(50); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); udelay(20); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); msleep(20); clk_enable(spi_clk); for (i = 0; i < init_table_sz; i++) lcm_writeb(init_tablep[i].reg, init_tablep[i].val); lcm_writew(0xef, 0xd0e8); lcm_writeb(0x1d, 0xa0); table_sel_idx = 0; gamma_table_bank_select(); samsung_oled_set_gamma_val(last_val); msleep(250); lcm_writeb(0x14, 0x03); clk_disable(spi_clk); mutex_unlock(&panel_lock); pr_info("%s: -()\n", __func__); return 0; } static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) { pr_info("%s: +()\n", __func__); mutex_lock(&panel_lock); clk_enable(spi_clk); lcm_writeb(0x14, 0x0); mdelay(1); lcm_writeb(0x1d, 0xa1); clk_disable(spi_clk); msleep(200); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); mutex_unlock(&panel_lock); pr_info("%s: -()\n", __func__); return 0; } struct lcm_cmd { int reg; uint32_t val; unsigned delay; }; #define LCM_GPIO_CFG(gpio, func, str) \ PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) static uint32_t sony_tft_display_on_gpio_table[] = { LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 1, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 1, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 1, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 1, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 1, GPIO_4MA), }; static uint32_t sony_tft_display_off_gpio_table[] = { LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 0, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 0, GPIO_8MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 0, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 0, GPIO_4MA), LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 0, GPIO_4MA), }; #undef LCM_GPIO_CFG #define SONY_TFT_DEF_PANEL_DELTA \ (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL) #define SONY_TFT_DEF_USER_DELTA \ (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL) static void sony_tft_set_pwm_val(int val) { pr_info("%s: %d\n", __func__, val); last_val = val; if (!tft_panel_on) return; if (val <= SONY_TFT_DEF_USER_VAL) { if (val <= SONY_TFT_MIN_USER_VAL) val = SONY_TFT_MIN_PANEL_VAL; else val = SONY_TFT_DEF_PANEL_DELTA * (val - SONY_TFT_MIN_USER_VAL) / SONY_TFT_DEF_USER_DELTA + SONY_TFT_MIN_PANEL_VAL; } else val = (SONY_TFT_MAX_PANEL_VAL - SONY_TFT_DEF_PANEL_VAL) * (val - SONY_TFT_DEF_USER_VAL) / (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) + SONY_TFT_DEF_PANEL_VAL; clk_enable(spi_clk); qspi_send_9bit(0x0, 0x51); qspi_send_9bit(0x1, val); qspi_send_9bit(0x0, 0x53); qspi_send_9bit(0x1, 0x24); clk_disable(spi_clk); } #undef SONY_TFT_DEF_PANEL_DELTA #undef SONY_TFT_DEF_USER_DELTA static void sony_tft_panel_config_gpio_table(uint32_t *table, int len) { int n; unsigned id; for (n = 0; n < len; n++) { id = table[n]; msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); } } static int sony_tft_panel_power(int on) { unsigned id, on_off; if (on) { on_off = 0; vreg_enable(vreg_lcm_aux_2v6); vreg_enable(vreg_lcm_rftx_2v6); id = PM_VREG_PDOWN_AUX_ID; msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); id = PM_VREG_PDOWN_RFTX_ID; msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); mdelay(10); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); mdelay(10); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); udelay(500); gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); mdelay(10); sony_tft_panel_config_gpio_table( sony_tft_display_on_gpio_table, ARRAY_SIZE(sony_tft_display_on_gpio_table)); } else { on_off = 1; gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); mdelay(120); vreg_disable(vreg_lcm_rftx_2v6); vreg_disable(vreg_lcm_aux_2v6); id = PM_VREG_PDOWN_RFTX_ID; msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); id = PM_VREG_PDOWN_AUX_ID; msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); sony_tft_panel_config_gpio_table( sony_tft_display_off_gpio_table, ARRAY_SIZE(sony_tft_display_off_gpio_table)); } return 0; } static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops) { return 0; } static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops) { pr_info("%s: +()\n", __func__); mutex_lock(&panel_lock); if (tft_panel_on) { pr_info("%s: -() already unblanked\n", __func__); goto done; } sony_tft_panel_power(1); msleep(45); clk_enable(spi_clk); qspi_send_9bit(0x0, 0x11); msleep(5); qspi_send_9bit(0x0, 0x3a); qspi_send_9bit(0x1, 0x05); msleep(100); qspi_send_9bit(0x0, 0x29); /* unlock register page for pwm setting */ qspi_send_9bit(0x0, 0xf0); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x0, 0xf1); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x0, 0xd0); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x1, 0x5a); qspi_send_9bit(0x0, 0xc2); qspi_send_9bit(0x1, 0x53); qspi_send_9bit(0x1, 0x12); clk_disable(spi_clk); msleep(100); tft_panel_on = 1; sony_tft_set_pwm_val(last_val); pr_info("%s: -()\n", __func__); done: mutex_unlock(&panel_lock); return 0; } static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops) { pr_info("%s: +()\n", __func__); mutex_lock(&panel_lock); clk_enable(spi_clk); qspi_send_9bit(0x0, 0x28); qspi_send_9bit(0x0, 0x10); clk_disable(spi_clk); msleep(40); sony_tft_panel_power(0); tft_panel_on = 0; mutex_unlock(&panel_lock); pr_info("%s: -()\n", __func__); return 0; } static struct msm_lcdc_panel_ops mahimahi_lcdc_amoled_panel_ops = { .init = samsung_oled_panel_init, .blank = samsung_oled_panel_blank, .unblank = samsung_oled_panel_unblank, }; static struct msm_lcdc_panel_ops mahimahi_lcdc_tft_panel_ops = { .init = sony_tft_panel_init, .blank = sony_tft_panel_blank, .unblank = sony_tft_panel_unblank, }; static struct msm_lcdc_timing mahimahi_lcdc_amoled_timing = { .clk_rate = 24576000, .hsync_pulse_width = 4, .hsync_back_porch = 8, .hsync_front_porch = 8, .hsync_skew = 0, .vsync_pulse_width = 2, .vsync_back_porch = 8, .vsync_front_porch = 8, .vsync_act_low = 1, .hsync_act_low = 1, .den_act_low = 1, }; static struct msm_lcdc_timing mahimahi_lcdc_tft_timing = { .clk_rate = 24576000, .hsync_pulse_width = 2, .hsync_back_porch = 20, .hsync_front_porch = 20, .hsync_skew = 0, .vsync_pulse_width = 2, .vsync_back_porch = 6, .vsync_front_porch = 4, .vsync_act_low = 1, .hsync_act_low = 1, .den_act_low = 0, }; static struct msm_fb_data mahimahi_lcdc_fb_data = { .xres = 480, .yres = 800, .width = 48, .height = 80, .output_format = MSM_MDP_OUT_IF_FMT_RGB565, }; static struct msm_lcdc_platform_data mahimahi_lcdc_amoled_platform_data = { .panel_ops = &mahimahi_lcdc_amoled_panel_ops, .timing = &mahimahi_lcdc_amoled_timing, .fb_id = 0, .fb_data = &mahimahi_lcdc_fb_data, .fb_resource = &resources_msm_fb[0], }; static struct msm_lcdc_platform_data mahimahi_lcdc_tft_platform_data = { .panel_ops = &mahimahi_lcdc_tft_panel_ops, .timing = &mahimahi_lcdc_tft_timing, .fb_id = 0, .fb_data = &mahimahi_lcdc_fb_data, .fb_resource = &resources_msm_fb[0], }; static struct platform_device mahimahi_lcdc_amoled_device = { .name = "msm_mdp_lcdc", .id = -1, .dev = { .platform_data = &mahimahi_lcdc_amoled_platform_data, }, }; static struct platform_device mahimahi_lcdc_tft_device = { .name = "msm_mdp_lcdc", .id = -1, .dev = { .platform_data = &mahimahi_lcdc_tft_platform_data, }, }; static int mahimahi_init_spi_hack(void) { int ret; spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE); if (!spi_base) return -1; spi_clk = clk_get(&msm_device_spi.dev, "spi_clk"); if (IS_ERR(spi_clk)) { pr_err("%s: unable to get spi_clk\n", __func__); ret = PTR_ERR(spi_clk); goto err_clk_get; } clk_enable(spi_clk); printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG)); printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL)); printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL)); printk("spi: SPI_ERROR_FLAGS_EN=%x\n", readl(spi_base + SPI_ERROR_FLAGS_EN)); printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS)); printk("-%s()\n", __FUNCTION__); clk_disable(spi_clk); return 0; err_clk_get: iounmap(spi_base); return ret; } static void mahimahi_brightness_set(struct led_classdev *led_cdev, enum led_brightness val) { unsigned long flags; led_cdev->brightness = val; spin_lock_irqsave(&brightness_lock, flags); new_val = val; spin_unlock_irqrestore(&brightness_lock, flags); schedule_work(&brightness_delayed_work); } static void mahimahi_brightness_amoled_set_work(struct work_struct *work_ptr) { unsigned long flags; uint8_t val; spin_lock_irqsave(&brightness_lock, flags); val = new_val; spin_unlock_irqrestore(&brightness_lock, flags); mutex_lock(&panel_lock); samsung_oled_set_gamma_val(val); mutex_unlock(&panel_lock); } static void mahimahi_brightness_tft_set_work(struct work_struct *work_ptr) { unsigned long flags; uint8_t val; spin_lock_irqsave(&brightness_lock, flags); val = new_val; spin_unlock_irqrestore(&brightness_lock, flags); mutex_lock(&panel_lock); sony_tft_set_pwm_val(val); mutex_unlock(&panel_lock); } static struct led_classdev mahimahi_brightness_led = { .name = "lcd-backlight", .brightness = LED_FULL, .brightness_set = mahimahi_brightness_set, }; int __init mahimahi_init_panel(void) { int ret; if (!machine_is_mahimahi()) return 0; if (system_rev > 0xC0) { /* CDMA version (except for EVT1) supports RGB666 */ init_tablep = samsung_oled_rgb666_init_table; init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); mahimahi_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666; } ret = platform_device_register(&msm_device_mdp); if (ret != 0) return ret; ret = mahimahi_init_spi_hack(); if (ret != 0) return ret; if (gpio_get_value(MAHIMAHI_GPIO_LCD_ID0)) { pr_info("%s: tft panel\n", __func__); vreg_lcm_rftx_2v6 = vreg_get(0, "rftx"); if (IS_ERR(vreg_lcm_rftx_2v6)) return PTR_ERR(vreg_lcm_rftx_2v6); vreg_set_level(vreg_lcm_rftx_2v6, 2600); vreg_lcm_aux_2v6 = vreg_get(0, "gp4"); if (IS_ERR(vreg_lcm_aux_2v6)) return PTR_ERR(vreg_lcm_aux_2v6); if (gpio_get_value(MAHIMAHI_GPIO_LCD_RST_N)) tft_panel_on = 1; ret = platform_device_register(&mahimahi_lcdc_tft_device); INIT_WORK(&brightness_delayed_work, mahimahi_brightness_tft_set_work); } else { pr_info("%s: amoled panel\n", __func__); ret = platform_device_register(&mahimahi_lcdc_amoled_device); INIT_WORK(&brightness_delayed_work, mahimahi_brightness_amoled_set_work); } if (ret != 0) return ret; ret = led_classdev_register(NULL, &mahimahi_brightness_led); if (ret != 0) { pr_err("%s: Cannot register brightness led\n", __func__); return ret; } return 0; } device_initcall(mahimahi_init_panel);