M7350/kernel/drivers/video/msm/mipi_novatek.c
2024-09-09 08:52:07 +00:00

690 lines
16 KiB
C

/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
#ifdef CONFIG_SPI_QUP
#include <linux/spi/spi.h>
#endif
#include <linux/leds.h>
#include "msm_fb.h"
#include "mipi_dsi.h"
#include "mipi_novatek.h"
#include "mdp4.h"
static struct mipi_dsi_panel_platform_data *mipi_novatek_pdata;
static struct dsi_buf novatek_tx_buf;
static struct dsi_buf novatek_rx_buf;
static int mipi_novatek_lcd_init(void);
static int wled_trigger_initialized;
#define MIPI_DSI_NOVATEK_SPI_DEVICE_NAME "dsi_novatek_3d_panel_spi"
#define HPCI_FPGA_READ_CMD 0x84
#define HPCI_FPGA_WRITE_CMD 0x04
#ifdef CONFIG_SPI_QUP
static struct spi_device *panel_3d_spi_client;
static void novatek_fpga_write(uint8 addr, uint16 value)
{
char tx_buf[32];
int rc;
struct spi_message m;
struct spi_transfer t;
u8 data[4] = {0x0, 0x0, 0x0, 0x0};
if (!panel_3d_spi_client) {
pr_err("%s panel_3d_spi_client is NULL\n", __func__);
return;
}
data[0] = HPCI_FPGA_WRITE_CMD;
data[1] = addr;
data[2] = ((value >> 8) & 0xFF);
data[3] = (value & 0xFF);
memset(&t, 0, sizeof t);
memset(tx_buf, 0, sizeof tx_buf);
t.tx_buf = data;
t.len = 4;
spi_setup(panel_3d_spi_client);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
rc = spi_sync(panel_3d_spi_client, &m);
if (rc)
pr_err("%s: SPI transfer failed\n", __func__);
return;
}
static void novatek_fpga_read(uint8 addr)
{
char tx_buf[32];
int rc;
struct spi_message m;
struct spi_transfer t;
struct spi_transfer rx;
char rx_value[2];
u8 data[4] = {0x0, 0x0};
if (!panel_3d_spi_client) {
pr_err("%s panel_3d_spi_client is NULL\n", __func__);
return;
}
data[0] = HPCI_FPGA_READ_CMD;
data[1] = addr;
memset(&t, 0, sizeof t);
memset(tx_buf, 0, sizeof tx_buf);
memset(&rx, 0, sizeof rx);
memset(rx_value, 0, sizeof rx_value);
t.tx_buf = data;
t.len = 2;
rx.rx_buf = rx_value;
rx.len = 2;
spi_setup(panel_3d_spi_client);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
spi_message_add_tail(&rx, &m);
rc = spi_sync(panel_3d_spi_client, &m);
if (rc)
pr_err("%s: SPI transfer failed\n", __func__);
else
pr_info("%s: rx_value = 0x%x, 0x%x\n", __func__,
rx_value[0], rx_value[1]);
return;
}
static int __devinit panel_3d_spi_probe(struct spi_device *spi)
{
panel_3d_spi_client = spi;
return 0;
}
static int __devexit panel_3d_spi_remove(struct spi_device *spi)
{
panel_3d_spi_client = NULL;
return 0;
}
static struct spi_driver panel_3d_spi_driver = {
.probe = panel_3d_spi_probe,
.remove = __devexit_p(panel_3d_spi_remove),
.driver = {
.name = "dsi_novatek_3d_panel_spi",
.owner = THIS_MODULE,
}
};
#else
static void novatek_fpga_write(uint8 addr, uint16 value)
{
return;
}
static void novatek_fpga_read(uint8 addr)
{
return;
}
#endif
/* novatek blue panel */
#ifdef NOVETAK_COMMANDS_UNUSED
static char display_config_cmd_mode1[] = {
/* TYPE_DCS_LWRITE */
0x2A, 0x00, 0x00, 0x01,
0x3F, 0xFF, 0xFF, 0xFF
};
static char display_config_cmd_mode2[] = {
/* DTYPE_DCS_LWRITE */
0x2B, 0x00, 0x00, 0x01,
0xDF, 0xFF, 0xFF, 0xFF
};
static char display_config_cmd_mode3_666[] = {
/* DTYPE_DCS_WRITE1 */
0x3A, 0x66, 0x15, 0x80 /* 666 Packed (18-bits) */
};
static char display_config_cmd_mode3_565[] = {
/* DTYPE_DCS_WRITE1 */
0x3A, 0x55, 0x15, 0x80 /* 565 mode */
};
static char display_config_321[] = {
/* DTYPE_DCS_WRITE1 */
0x66, 0x2e, 0x15, 0x00 /* Reg 0x66 : 2E */
};
static char display_config_323[] = {
/* DTYPE_DCS_WRITE */
0x13, 0x00, 0x05, 0x00 /* Reg 0x13 < Set for Normal Mode> */
};
static char display_config_2lan[] = {
/* DTYPE_DCS_WRITE */
0x61, 0x01, 0x02, 0xff /* Reg 0x61 : 01,02 < Set for 2 Data Lane > */
};
static char display_config_exit_sleep[] = {
/* DTYPE_DCS_WRITE */
0x11, 0x00, 0x05, 0x80 /* Reg 0x11 < exit sleep mode> */
};
static char display_config_TE_ON[] = {
/* DTYPE_DCS_WRITE1 */
0x35, 0x00, 0x15, 0x80
};
static char display_config_39H[] = {
/* DTYPE_DCS_WRITE */
0x39, 0x00, 0x05, 0x80
};
static char display_config_set_tear_scanline[] = {
/* DTYPE_DCS_LWRITE */
0x44, 0x00, 0x00, 0xff
};
static char display_config_set_twolane[] = {
/* DTYPE_DCS_WRITE1 */
0xae, 0x03, 0x15, 0x80
};
static char display_config_set_threelane[] = {
/* DTYPE_DCS_WRITE1 */
0xae, 0x05, 0x15, 0x80
};
#else
static char sw_reset[2] = {0x01, 0x00}; /* DTYPE_DCS_WRITE */
static char enter_sleep[2] = {0x10, 0x00}; /* DTYPE_DCS_WRITE */
static char exit_sleep[2] = {0x11, 0x00}; /* DTYPE_DCS_WRITE */
static char display_off[2] = {0x28, 0x00}; /* DTYPE_DCS_WRITE */
static char display_on[2] = {0x29, 0x00}; /* DTYPE_DCS_WRITE */
static char rgb_888[2] = {0x3A, 0x77}; /* DTYPE_DCS_WRITE1 */
#if defined(NOVATEK_TWO_LANE)
static char set_num_of_lanes[2] = {0xae, 0x03}; /* DTYPE_DCS_WRITE1 */
#else /* 1 lane */
static char set_num_of_lanes[2] = {0xae, 0x01}; /* DTYPE_DCS_WRITE1 */
#endif
/* commands by Novatke */
static char novatek_f4[2] = {0xf4, 0x55}; /* DTYPE_DCS_WRITE1 */
static char novatek_8c[16] = { /* DTYPE_DCS_LWRITE */
0x8C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x08, 0x00, 0x30, 0xC0, 0xB7, 0x37};
static char novatek_ff[2] = {0xff, 0x55 }; /* DTYPE_DCS_WRITE1 */
static char set_width[5] = { /* DTYPE_DCS_LWRITE */
0x2A, 0x00, 0x00, 0x02, 0x1B}; /* 540 - 1 */
static char set_height[5] = { /* DTYPE_DCS_LWRITE */
0x2B, 0x00, 0x00, 0x03, 0xBF}; /* 960 - 1 */
#endif
static char led_pwm2[2] = {0x53, 0x24}; /* DTYPE_DCS_WRITE1 */
static char led_pwm3[2] = {0x55, 0x00}; /* DTYPE_DCS_WRITE1 */
static struct dsi_cmd_desc novatek_video_on_cmds[] = {
{DTYPE_DCS_WRITE, 1, 0, 0, 50,
sizeof(sw_reset), sw_reset},
{DTYPE_DCS_WRITE, 1, 0, 0, 10,
sizeof(exit_sleep), exit_sleep},
{DTYPE_DCS_WRITE, 1, 0, 0, 10,
sizeof(display_on), display_on},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(set_num_of_lanes), set_num_of_lanes},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(rgb_888), rgb_888},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(led_pwm2), led_pwm2},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(led_pwm3), led_pwm3},
};
static struct dsi_cmd_desc novatek_cmd_on_cmds[] = {
{DTYPE_DCS_WRITE, 1, 0, 0, 50,
sizeof(sw_reset), sw_reset},
{DTYPE_DCS_WRITE, 1, 0, 0, 10,
sizeof(exit_sleep), exit_sleep},
{DTYPE_DCS_WRITE, 1, 0, 0, 10,
sizeof(display_on), display_on},
{DTYPE_DCS_WRITE1, 1, 0, 0, 50,
sizeof(novatek_f4), novatek_f4},
{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
sizeof(novatek_8c), novatek_8c},
{DTYPE_DCS_WRITE1, 1, 0, 0, 50,
sizeof(novatek_ff), novatek_ff},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(set_num_of_lanes), set_num_of_lanes},
{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
sizeof(set_width), set_width},
{DTYPE_DCS_LWRITE, 1, 0, 0, 50,
sizeof(set_height), set_height},
{DTYPE_DCS_WRITE1, 1, 0, 0, 10,
sizeof(rgb_888), rgb_888},
{DTYPE_DCS_WRITE1, 1, 0, 0, 1,
sizeof(led_pwm2), led_pwm2},
{DTYPE_DCS_WRITE1, 1, 0, 0, 1,
sizeof(led_pwm3), led_pwm3},
};
static struct dsi_cmd_desc novatek_display_off_cmds[] = {
{DTYPE_DCS_WRITE, 1, 0, 0, 10,
sizeof(display_off), display_off},
{DTYPE_DCS_WRITE, 1, 0, 0, 120,
sizeof(enter_sleep), enter_sleep}
};
static char manufacture_id[2] = {0x04, 0x00}; /* DTYPE_DCS_READ */
static struct dsi_cmd_desc novatek_manufacture_id_cmd = {
DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(manufacture_id), manufacture_id};
static u32 manu_id;
static void mipi_novatek_manufacture_cb(u32 data)
{
manu_id = data;
pr_info("%s: manufacture_id=%x\n", __func__, manu_id);
}
static uint32 mipi_novatek_manufacture_id(struct msm_fb_data_type *mfd)
{
struct dcs_cmd_req cmdreq;
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = &novatek_manufacture_id_cmd;
cmdreq.cmds_cnt = 1;
cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT;
cmdreq.rlen = 3;
cmdreq.rbuf = NULL;
cmdreq.cb = mipi_novatek_manufacture_cb; /* call back */
mipi_dsi_cmdlist_put(&cmdreq);
/*
* blocked here, untill call back called
*/
return manu_id;
}
static int fpga_addr;
static int fpga_access_mode;
static bool support_3d;
static void mipi_novatek_3d_init(int addr, int mode)
{
fpga_addr = addr;
fpga_access_mode = mode;
}
static void mipi_dsi_enable_3d_barrier(int mode)
{
void __iomem *fpga_ptr;
uint32_t ptr_value = 0;
if (!fpga_addr && support_3d) {
pr_err("%s: fpga_addr not set. Failed to enable 3D barrier\n",
__func__);
return;
}
if (fpga_access_mode == FPGA_SPI_INTF) {
if (mode == LANDSCAPE)
novatek_fpga_write(fpga_addr, 1);
else if (mode == PORTRAIT)
novatek_fpga_write(fpga_addr, 3);
else
novatek_fpga_write(fpga_addr, 0);
mb();
novatek_fpga_read(fpga_addr);
} else if (fpga_access_mode == FPGA_EBI2_INTF) {
fpga_ptr = ioremap_nocache(fpga_addr, sizeof(uint32_t));
if (!fpga_ptr) {
pr_err("%s: FPGA ioremap failed."
"Failed to enable 3D barrier\n",
__func__);
return;
}
ptr_value = readl_relaxed(fpga_ptr);
if (mode == LANDSCAPE)
writel_relaxed(((0xFFFF0000 & ptr_value) | 1),
fpga_ptr);
else if (mode == PORTRAIT)
writel_relaxed(((0xFFFF0000 & ptr_value) | 3),
fpga_ptr);
else
writel_relaxed((0xFFFF0000 & ptr_value),
fpga_ptr);
mb();
iounmap(fpga_ptr);
} else
pr_err("%s: 3D barrier not configured correctly\n",
__func__);
}
static int mipi_novatek_lcd_on(struct platform_device *pdev)
{
struct msm_fb_data_type *mfd;
struct mipi_panel_info *mipi;
struct msm_panel_info *pinfo;
struct dcs_cmd_req cmdreq;
mfd = platform_get_drvdata(pdev);
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
pinfo = &mfd->panel_info;
if (pinfo->is_3d_panel)
support_3d = TRUE;
mipi = &mfd->panel_info.mipi;
if (mipi->mode == DSI_VIDEO_MODE) {
cmdreq.cmds = novatek_video_on_cmds;
cmdreq.cmds_cnt = ARRAY_SIZE(novatek_video_on_cmds);
cmdreq.flags = CMD_REQ_COMMIT;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mipi_dsi_cmdlist_put(&cmdreq);
} else {
cmdreq.cmds = novatek_cmd_on_cmds;
cmdreq.cmds_cnt = ARRAY_SIZE(novatek_cmd_on_cmds);
cmdreq.flags = CMD_REQ_COMMIT;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mipi_dsi_cmdlist_put(&cmdreq);
/* clean up ack_err_status */
mipi_dsi_cmd_bta_sw_trigger();
mipi_novatek_manufacture_id(mfd);
}
return 0;
}
static int mipi_novatek_lcd_off(struct platform_device *pdev)
{
struct msm_fb_data_type *mfd;
struct dcs_cmd_req cmdreq;
mfd = platform_get_drvdata(pdev);
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
cmdreq.cmds = novatek_display_off_cmds;
cmdreq.cmds_cnt = ARRAY_SIZE(novatek_display_off_cmds);
cmdreq.flags = CMD_REQ_COMMIT;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mipi_dsi_cmdlist_put(&cmdreq);
return 0;
}
static int mipi_novatek_lcd_late_init(struct platform_device *pdev)
{
return 0;
}
DEFINE_LED_TRIGGER(bkl_led_trigger);
static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */
static struct dsi_cmd_desc backlight_cmd = {
DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm1), led_pwm1};
static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd)
{
struct dcs_cmd_req cmdreq;
if (mipi_novatek_pdata &&
mipi_novatek_pdata->gpio_set_backlight) {
mipi_novatek_pdata->gpio_set_backlight(mfd->bl_level);
return;
}
if ((mipi_novatek_pdata->enable_wled_bl_ctrl)
&& (wled_trigger_initialized)) {
led_trigger_event(bkl_led_trigger, mfd->bl_level);
return;
}
led_pwm1[1] = (unsigned char)mfd->bl_level;
cmdreq.cmds = &backlight_cmd;
cmdreq.cmds_cnt = 1;
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL;
cmdreq.rlen = 0;
cmdreq.cb = NULL;
mipi_dsi_cmdlist_put(&cmdreq);
}
static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev);
static int barrier_mode;
static int __devinit mipi_novatek_lcd_probe(struct platform_device *pdev)
{
struct msm_fb_data_type *mfd;
struct mipi_panel_info *mipi;
struct platform_device *current_pdev;
static struct mipi_dsi_phy_ctrl *phy_settings;
static char dlane_swap;
if (pdev->id == 0) {
mipi_novatek_pdata = pdev->dev.platform_data;
if (mipi_novatek_pdata
&& mipi_novatek_pdata->phy_ctrl_settings) {
phy_settings = (mipi_novatek_pdata->phy_ctrl_settings);
}
if (mipi_novatek_pdata
&& mipi_novatek_pdata->dlane_swap) {
dlane_swap = (mipi_novatek_pdata->dlane_swap);
}
if (mipi_novatek_pdata
&& mipi_novatek_pdata->fpga_3d_config_addr)
mipi_novatek_3d_init(mipi_novatek_pdata
->fpga_3d_config_addr, mipi_novatek_pdata->fpga_ctrl_mode);
/* create sysfs to control 3D barrier for the Sharp panel */
if (mipi_dsi_3d_barrier_sysfs_register(&pdev->dev)) {
pr_err("%s: Failed to register 3d Barrier sysfs\n",
__func__);
return -ENODEV;
}
barrier_mode = 0;
return 0;
}
current_pdev = msm_fb_add_device(pdev);
if (current_pdev) {
mfd = platform_get_drvdata(current_pdev);
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
mipi = &mfd->panel_info.mipi;
if (phy_settings != NULL)
mipi->dsi_phy_db = phy_settings;
if (dlane_swap)
mipi->dlane_swap = dlane_swap;
}
return 0;
}
static struct platform_driver this_driver = {
.probe = mipi_novatek_lcd_probe,
.driver = {
.name = "mipi_novatek",
},
};
static struct msm_fb_panel_data novatek_panel_data = {
.on = mipi_novatek_lcd_on,
.off = mipi_novatek_lcd_off,
.late_init = mipi_novatek_lcd_late_init,
.set_backlight = mipi_novatek_set_backlight,
};
static ssize_t mipi_dsi_3d_barrier_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf((char *)buf, sizeof(buf), "%u\n", barrier_mode);
}
static ssize_t mipi_dsi_3d_barrier_write(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int ret = -1;
u32 data = 0;
if (sscanf((char *)buf, "%u", &data) != 1) {
dev_err(dev, "%s\n", __func__);
ret = -EINVAL;
} else {
barrier_mode = data;
if (data == 1)
mipi_dsi_enable_3d_barrier(LANDSCAPE);
else if (data == 2)
mipi_dsi_enable_3d_barrier(PORTRAIT);
else
mipi_dsi_enable_3d_barrier(0);
}
return count;
}
static struct device_attribute mipi_dsi_3d_barrier_attributes[] = {
__ATTR(enable_3d_barrier, 0664, mipi_dsi_3d_barrier_read,
mipi_dsi_3d_barrier_write),
};
static int mipi_dsi_3d_barrier_sysfs_register(struct device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(mipi_dsi_3d_barrier_attributes); i++)
if (device_create_file(dev, mipi_dsi_3d_barrier_attributes + i))
goto error;
return 0;
error:
for (; i >= 0 ; i--)
device_remove_file(dev, mipi_dsi_3d_barrier_attributes + i);
pr_err("%s: Unable to create interface\n", __func__);
return -ENODEV;
}
static int ch_used[3];
int mipi_novatek_device_register(struct msm_panel_info *pinfo,
u32 channel, u32 panel)
{
struct platform_device *pdev = NULL;
int ret;
if ((channel >= 3) || ch_used[channel])
return -ENODEV;
ch_used[channel] = TRUE;
ret = mipi_novatek_lcd_init();
if (ret) {
pr_err("mipi_novatek_lcd_init() failed with ret %u\n", ret);
return ret;
}
pdev = platform_device_alloc("mipi_novatek", (panel << 8)|channel);
if (!pdev)
return -ENOMEM;
novatek_panel_data.panel_info = *pinfo;
ret = platform_device_add_data(pdev, &novatek_panel_data,
sizeof(novatek_panel_data));
if (ret) {
printk(KERN_ERR
"%s: platform_device_add_data failed!\n", __func__);
goto err_device_put;
}
ret = platform_device_add(pdev);
if (ret) {
printk(KERN_ERR
"%s: platform_device_register failed!\n", __func__);
goto err_device_put;
}
return 0;
err_device_put:
platform_device_put(pdev);
return ret;
}
static int mipi_novatek_lcd_init(void)
{
#ifdef CONFIG_SPI_QUP
int ret;
ret = spi_register_driver(&panel_3d_spi_driver);
if (ret) {
pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
platform_driver_unregister(&this_driver);
} else
pr_info("%s: SUCCESS (SPI)\n", __func__);
#endif
led_trigger_register_simple("bkl_trigger", &bkl_led_trigger);
pr_info("%s: SUCCESS (WLED TRIGGER)\n", __func__);
wled_trigger_initialized = 1;
mipi_dsi_buf_alloc(&novatek_tx_buf, DSI_BUF_SIZE);
mipi_dsi_buf_alloc(&novatek_rx_buf, DSI_BUF_SIZE);
return platform_driver_register(&this_driver);
}