999 lines
22 KiB
C
999 lines
22 KiB
C
/* linux/arch/arm/mach-msm/board-mahimahi-panel.c
|
|
*
|
|
* Copyright (c) 2009 Google Inc.
|
|
* Author: Dima Zavin <dima@android.com>
|
|
*
|
|
* 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 <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/mach-types.h>
|
|
|
|
#include <mach/msm_fb.h>
|
|
#include <mach/msm_iomap.h>
|
|
#include <mach/vreg.h>
|
|
#include <mach/proc_comm.h>
|
|
|
|
#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);
|