unplugged-kernel/drivers/gpu/drm/panel/ite6113.c

1503 lines
48 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <uapi/linux/sched/types.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include "ite6113.h"
static struct i2c_client *it6112_tx_i2c_client;
static struct i2c_client *it6112_rx_i2c_client;
static struct task_struct *ite_poll_task;
static wait_queue_head_t _ite_poll_task_wq;
static atomic_t _ite_poll_task_wakeup = ATOMIC_INIT(1);
const static struct dcs_setting_entry bypass_mode_table[] = {
{LP_CMD_ENABLE_BYPASS_MODE, LP_CMD_LPDT, 0x23, 2, {0x5A, 0x28}},
{LP_CMD_DISABLE_BYPASS_MODE, LP_CMD_LPDT, 0x23, 2, {0x6C, 0x5F}},
{LP_CMD_ENABLE_BYPASS_SEQ_MODE, LP_CMD_LPDT, 0x5A, 3, {0x28, 0x6C, 0x5F}},
};
const static struct dcs_setting_entry lp_cmd_read[] = {
{SET_MAX_RETURN_SIZE, LP_CMD_LPDT, 0x37, 2,
{LP_CMD_SET_MAX_RETURN_SIZE & 0xFF, LP_CMD_SET_MAX_RETURN_SIZE >> 8}},
{GET_DISPLAY_MODE, LP_CMD_LPDT, 0x06, 2, {PANEL_DATA & 0xFF, PANEL_DATA >> 8}}
};
const static struct dcs_setting_entry suspend_table[] = {
{ENTER_SLEEP_MODE, LP_CMD_LPDT, 0x05, 2, {0x28, 0x00}},
{DELAY, 100, 0, 0, {0}},
{SET_DISPLAY_OFF, LP_CMD_LPDT, 0x05, 2, {0x10, 0x00}},
};
struct mipi_packet_map packet_size_data_id_map[] = {
{0x05, SHORT_PACKET}, /* dcs short write without parameter */
{0x15, SHORT_PACKET}, /* dcs short write with one parameter */
{0x23, SHORT_PACKET}, /* generic short write, 2 parameters */
{0x29, LONG_PACKET}, /* generic long write */
{0x39, LONG_PACKET}, /* dcs long write */
{0x06, SHORT_PACKET},
{0x16, SHORT_PACKET},
{0x37, SHORT_PACKET},
{0x03, SHORT_PACKET},
{0x13, SHORT_PACKET},
{0x23, SHORT_PACKET},
{0x04, SHORT_PACKET},
{0x14, SHORT_PACKET},
{0x24, SHORT_PACKET}
};
static u32 it6112_i2c_tx_write_byte(struct it6112 *it6112_client, u8 addr, u8 data)
{
int ret;
u8 write_data[8];
write_data[0]= addr;
write_data[1] = data;
ret = i2c_master_send(it6112_client->tx_i2c, write_data, 2);
return ret;
}
static u32 it6112_i2c_rx_write_byte(struct it6112 *it6112_client, u8 addr, u8 data)
{
int ret;
u8 write_data[8];
write_data[0]= addr;
write_data[1] = data;
ret = i2c_master_send(it6112_client->rx_i2c, write_data, 2);
return ret;
}
static u32 it6112_i2c_tx_read_byte(struct it6112 *it6112_client, u8 addr, u8 *dataBuffer)
{
u32 ret;
ret = i2c_master_send(it6112_client->tx_i2c, &addr, 1);
if (ret != 1)
pr_info("%s: send fail=0x%x\n", __func__, ret);
ret = i2c_master_recv(it6112_client->tx_i2c, dataBuffer, 1);
if (ret != 1)
pr_info("%s: read fail=0x%x\n", __func__, ret);
return ret;
}
static u32 it6112_i2c_rx_read_byte(struct it6112 *it6112_client, u8 addr, u8 *dataBuffer)
{
u32 ret;
ret = i2c_master_send(it6112_client->rx_i2c, &addr, 1);
if (ret != 1)
pr_info("%s: send fail=0x%x\n", __func__, ret);
ret = i2c_master_recv(it6112_client->rx_i2c, dataBuffer, 1);
if (ret != 1)
pr_info("%s: read fail=0x%x\n", __func__, ret);
return ret;
}
int mipi_tx_write(struct it6112 *it6112_client, int offset, int data)
{
return it6112_i2c_tx_write_byte(it6112_client, offset, data);
}
u8 mipi_tx_read(struct it6112 *it6112_client, int offset)
{
u8 dataBuffer;
it6112_i2c_tx_read_byte(it6112_client, offset, &dataBuffer);
return dataBuffer;
}
int mipi_tx_set_bits(struct it6112 *it6112_client, int offset, int mask, int data)
{
int temp;
temp = mipi_tx_read(it6112_client, offset);
temp = (temp & ((~mask) & 0xFF)) + (mask & data);
return mipi_tx_write(it6112_client, offset, temp);
}
int mipi_rx_write(struct it6112 *it6112_client, int offset, int data)
{
return it6112_i2c_rx_write_byte(it6112_client, offset, data);
}
u8 mipi_rx_read(struct it6112 *it6112_client, int offset)
{
u8 dataBuffer;
it6112_i2c_rx_read_byte(it6112_client, offset, &dataBuffer);
return dataBuffer;
}
int mipi_rx_set_bits(struct it6112 *it6112_client, int offset, int mask, int data)
{
int temp;
temp = mipi_rx_read(it6112_client, offset);
temp = (temp & ((~mask) & 0xFF)) + (mask & data);
return mipi_rx_write(it6112_client, offset, temp);
}
u8 mipi_rx_get_video_state(struct it6112 *it6112_client)
{
return !!(mipi_rx_read(it6112_client, 0x0D) & 0x10);
}
void mipi_tx_calc_hs_para(struct it6112 *it6112_client)
{
u32 mclk_ps, tx_mclk_mhz = it6112_client->mipi_rx_mclk / 1000;
int mipi_tx_calc_hs_end_time;
pr_info( "renable hs auto set\n");
pr_info( "rdefault reg0x22:0x%02x, reg0x1F:0x%02x\n",
it6112_client->mipi_tx_hs_pretime, it6112_client->mipi_tx_hs_end_time);
if (it6112_client->mipi_rx_mclk == 0) {
pr_info( "mipi rx mclk = 0, error! use default reg0x22, reg0x1F\n");
return;
}
mclk_ps = 1000000000 / it6112_client->mipi_rx_mclk;
if (mclk_ps == 0) {
pr_info( " mipi mclk_ps = 0, error! use default reg0x22, reg0x1F\n");
return;
}
if (!it6112_client->enable_mipi_4_lane_mode) {
mclk_ps *= 2;
tx_mclk_mhz /= 2;
}
/* (5.5 * mclk_ps - 20 * 1000) / mclk_ps */
mipi_tx_calc_hs_end_time =
(11 * mclk_ps - 2 * it6112_client->mipi_tx_oclk_ns * 1000) / (2 * mclk_ps);
it6112_client->mipi_tx_hs_pretime = (((145 + ((4 + it6112_client->mipi_tx_lpx_num)
* it6112_client->mipi_tx_oclk_ns)) * 1000 - ((3 * mclk_ps) >> 1)) / mclk_ps)
+ MIPI_TX_HS_AUTO_SET_PREPARE_ZERO_OFFSET;
it6112_client->mipi_tx_hs_end_time = mipi_tx_calc_hs_end_time
+ 1 + MIPI_TX_HS_AUTO_SET_TRAIL_OFFSET;
if (tx_mclk_mhz >= 300)
it6112_client->mipi_tx_hs_pretime += 20;
else if (tx_mclk_mhz >= 150)
it6112_client->mipi_tx_hs_pretime += (12 + (tx_mclk_mhz - 150) / 50 * 3);
else
it6112_client->mipi_tx_hs_pretime += 9;
if (mipi_tx_calc_hs_end_time <= 0)
it6112_client->mipi_tx_hs_end_time = 3;
pr_info( "%s lanes mode, mclk_ns: %d.%d ns\n",
it6112_client->enable_mipi_4_lane_mode ? "4" : "8", mclk_ps / 1000, mclk_ps % 1000);
pr_info( "calc reg0x22:0x%02x, reg0x1F:0x%02x, lpx_num:0x%02x\n",
it6112_client->mipi_tx_hs_pretime, it6112_client->mipi_tx_hs_end_time,
it6112_client->mipi_tx_lpx_num);
}
void mipi_enter_power_saving(struct it6112 *it6112_client)
{
mipi_rx_set_bits(it6112_client, 0x11, 0x08, 0x08);
mipi_tx_set_bits(it6112_client, 0x06, 0x6e, 0x6e);
it6112_client->mipi_power_saving = 1;
pr_info("%s: enter power saving\n", __func__);
}
void mipi_exit_power_saving(struct it6112 *it6112_client)
{
mipi_rx_set_bits(it6112_client, 0x11, 0x08, 0x0);
mipi_tx_set_bits(it6112_client, 0x06, 0x6e, 0x0);
it6112_client->mipi_power_saving = 0;
pr_info("%s: exit power saving\n", __func__);
}
void mipi_rx_reset(struct it6112 *it6112_client)
{
mipi_rx_set_bits(it6112_client, 0x05, 0x03, 0x03);
mipi_rx_set_bits(it6112_client, 0x05, 0x03, 0x00);
}
void mipi_tx_reset(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x05, 0x06, 0x06);
mipi_tx_set_bits(it6112_client, 0x05, 0x06, 0x00);
}
inline void mipi_tx_disable_ppi(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x06, 0x04, 0x04);
it6112_client->enable_ppi = FALSE;
}
inline void mipi_tx_enable_ppi(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x06, 0x04, 0x00);
it6112_client->enable_ppi = TRUE;
}
void mipi_polling_state(struct it6112 *it6112_client)
{
int i, reg0e, reg0d, times = 10;
for (i = 0; i < times; i++) {
reg0e = mipi_tx_read(it6112_client, 0x0E);
reg0d = mipi_rx_read(it6112_client, 0x0D);
pr_info( "polling tx state reg0x0E:0x%02x, rx state reg0x0D:0x%02x\n",
reg0e, reg0d);
if ((reg0e & 0x27) != 0x00)
mipi_tx_reset(it6112_client);
else
break;
usleep_range(2000, 2500);
}
mipi_tx_enable_ppi(it6112_client);
mdelay(10);
pr_info( "after enable ppi tx state reg0x0E:0x%02x, rx state reg0x0D:0x%02x\n",
mipi_tx_read(it6112_client, 0x0E), mipi_rx_read(it6112_client, 0x0D));
}
void mipi_rx_calc_rclk(struct it6112 *it6112_client)
{
int rddata, i, sum, retry = 1;
sum = 0;
for(i = 0; i < retry; i++) {
/* enable rclk 100ms counter */
mipi_rx_set_bits(it6112_client, 0x94, 0x80, 0x80);
mdelay(10);
/* disable rclk 100ms counter */
mipi_rx_set_bits(it6112_client, 0x94, 0x80, 0x00);
rddata = mipi_rx_read(it6112_client, 0x95);
rddata += (mipi_rx_read(it6112_client, 0x96) << 8);
rddata += (mipi_rx_read(it6112_client, 0x97) << 16);
sum += rddata;
}
sum /= retry;
it6112_client->mipi_rx_rclk = sum / 100;
}
void mipi_rx_calc_mclk(struct it6112 *it6112_client)
{
int i, rddata, sum = 0, retry = 1;
for(i = 0; i < retry; i++) {
mipi_rx_set_bits(it6112_client, 0x9B, 0x80, 0x80);
mdelay(2);
mipi_rx_set_bits(it6112_client, 0x9B, 0x80, 0x00);
rddata = mipi_rx_read(it6112_client, 0x9A);
rddata = ((mipi_rx_read(it6112_client, 0x9B) & 0x0F) << 8) + rddata;
sum += rddata;
}
sum /= retry;
it6112_client->mipi_rx_mclk = it6112_client->mipi_rx_rclk * 2048 / sum;
}
int mipi_rx_read_word(struct it6112 *it6112_client, unsigned int reg)
{
int val_0, val_1;
val_0 = mipi_rx_read(it6112_client, reg);
if (val_0 < 0)
return val_0;
val_1 = mipi_rx_read(it6112_client, reg + 1);
if (val_1 < 0)
return val_1;
return (val_1 << 8) | val_0;
}
void mipi_rx_get_mrec_timing(struct it6112 *it6112_client)
{
struct mipi_display_mode *display_mode = &it6112_client->mipi_rx_display_mode;
int MHVR2nd, MVFP2nd;
mdelay(10);
display_mode->m_hsync_width = mipi_rx_read_word(it6112_client, 0x52) & 0x3FFF;
display_mode->m_hfront_porch = mipi_rx_read_word(it6112_client, 0x50) & 0x3FFF;
display_mode->m_hback_porch = mipi_rx_read_word(it6112_client, 0x54) & 0x3FFF;
display_mode->m_hdisplay = mipi_rx_read_word(it6112_client, 0x56) & 0x3FFF;
display_mode->m_htotal = display_mode->m_hfront_porch + display_mode->m_hsync_width
+ display_mode->m_hback_porch + display_mode->m_hdisplay;
MHVR2nd = mipi_rx_read_word(it6112_client, 0x58) & 0x3FFF;
display_mode->m_vsync_width = mipi_rx_read_word(it6112_client, 0x5C) & 0x1FFF;
display_mode->m_vfront_porch = mipi_rx_read_word(it6112_client, 0x5A) & 0x1FFF;
display_mode->m_vback_porch = mipi_rx_read_word(it6112_client, 0x5E) & 0x1FFF;
display_mode->m_vdisplay = mipi_rx_read_word(it6112_client, 0x60) & 0x1FFF;
display_mode->m_vtotal = display_mode->m_vfront_porch + display_mode->m_vsync_width
+ display_mode->m_vback_porch + display_mode->m_vdisplay;
MVFP2nd = mipi_rx_read_word(it6112_client, 0x62) & 0x3FFF;
pr_info( "m_hfront_porch = %d\n", display_mode->m_hfront_porch);
pr_info( "m_hsync_width = %d\n", display_mode->m_hsync_width);
pr_info( "m_hback_porch = %d\n", display_mode->m_hback_porch);
pr_info( "m_hdisplay = %d\n", display_mode->m_hdisplay);
pr_info( "m_htotal = %d\n", display_mode->m_htotal);
pr_info( "MHVR2nd = %d\n", MHVR2nd);
pr_info( "m_vfront_porch = %d\n", display_mode->m_vfront_porch);
pr_info( "m_vsync_width = %d\n", display_mode->m_vsync_width);
pr_info( "m_vback_porch = %d\n", display_mode->m_vback_porch);
pr_info( "m_vdisplay = %d\n", display_mode->m_vdisplay);
pr_info( "m_vtotal = %d\n", display_mode->m_vtotal);
pr_info( "MVFP2nd = %d\n", MVFP2nd);
}
void mipi_rx_init(struct it6112 *it6112_client)
{
int reg30 = (0x08 | MIPI_RX_LANE_0_DATA_SKEW) | ((0x08 | MIPI_RX_LANE_1_DATA_SKEW) << 4);
int reg31 = (0x08 | MIPI_RX_LANE_2_DATA_SKEW) | ((0x08 | MIPI_RX_LANE_3_DATA_SKEW) << 4);
//Enable MPRX interrupt
mipi_rx_write(it6112_client, 0x09, 0xFF);
mipi_rx_write(it6112_client, 0x0A, 0xFF);
mipi_rx_write(it6112_client, 0x0B, 0x3F);
mipi_rx_write(it6112_client, 0x05, 0x03);
mipi_rx_write(it6112_client, 0x05, 0x00);
/* Setup INT Pin: Active Low */
mipi_rx_set_bits(it6112_client, 0x0C, 0x0F, (it6112_client->enable_mipi_rx_lane_swap << 3)
| (it6112_client->enable_mipi_rx_pn_swap << 2) | 0x03);
mipi_rx_set_bits(it6112_client, 0x11, 0x01, it6112_client->enable_mipi_rx_mclk_inverse);
if ((it6112_client->chip_id == IT6113) && (it6112_client->revision >= 0xD0)) {
mipi_rx_set_bits(it6112_client, 0x18, 0xff, (ENABLE_MIPI_RX_SYNC_ERROR_BIT << 7)
| (it6112_client->mipi_rx_hs_skip << 4) | it6112_client->mipi_rx_hs_settle);
} else {
mipi_rx_set_bits(it6112_client, 0x18, 0xf7, (ENABLE_MIPI_RX_SYNC_ERROR_BIT << 7)
| (it6112_client->mipi_rx_hs_skip << 4) | it6112_client->mipi_rx_hs_settle);
}
mipi_rx_set_bits(it6112_client, 0x19, 0xf3, 0x03);
mipi_rx_set_bits(it6112_client, 0x20, 0xf7, 0x03);
/* rx auto detect video format */
mipi_rx_set_bits(it6112_client, 0x21, 0x08, 0x08);
mipi_rx_set_bits(it6112_client, 0x44, 0x22, 0x22);
mipi_rx_write(it6112_client, 0x27, it6112_client->mipi_rx_video_type);
mipi_rx_write(it6112_client, 0x72, 0x07);
/* previous setting is 0x03 */
mipi_rx_set_bits(it6112_client, 0x8A, 0x07, 0x02);
mipi_rx_set_bits(it6112_client, 0xA0, 0x01, 0x01);
if (it6112_client->enable_mipi_4_lane_mode)
mipi_rx_set_bits(it6112_client, 0x80, 0x3F, 0x03);
else
mipi_rx_set_bits(it6112_client, 0x80, 0x3F, 0x07);
if (MIPI_RX_ADJUST_LANE_DATA_SKEW) {
mipi_rx_write(it6112_client, 0x30, reg30);
mipi_rx_write(it6112_client, 0x31, reg31);
pr_info( "MPRX setup lane data skew! reg0x30:0x%02x, reg0x31:0x%02x\n",
mipi_rx_read(it6112_client, 0x30), mipi_rx_read(it6112_client, 0x31));
}
pr_info( "MPRX reg0x18:0x%02x hs_settle(reg0x18[6:4]) hs_skip(reg0x18[%s:0])\n",
mipi_rx_read(it6112_client, 0x18),
((it6112_client->chip_id == IT6113) && (it6112_client->revision >= 0xD0))
? "3" : "2");
pr_info( "MPRX initial done!\n");
}
void mipi_tx_adjusted_oclk(struct it6112 *it6112_client)
{
if (it6112_client->chip_id == IT6113) {
mipi_tx_write(it6112_client, 0xD3, it6112_client->mipi_tx_oclk);
it6112_client->mipi_tx_oclk_ns = MIPI_TX_OCLK_NS; /* oclk 50 MHz, 20 ns */
pr_info( "mipi tx oclk reg0xD3:0x%02x\n", mipi_tx_read(it6112_client, 0xD3));
}
}
void mipi_tx_enable_vrr_detect(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x27, 0x40, 0x40);
}
void mipi_tx_disable_vrr_detect(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x27, 0x40, 0x00);
}
void mipi_tx_setup_lps_time(struct it6112 *it6112_client)
{
mipi_tx_write(it6112_client, 0x23, it6112_client->mipi_tx_v_lps_time & 0xFF);
mipi_tx_set_bits(it6112_client, 0x24, 0x0F,
(it6112_client->mipi_tx_v_lps_time >> 8) & 0x0F);
mipi_tx_write(it6112_client, 0x25, it6112_client->mipi_tx_h_lps_time & 0xFF);
mipi_tx_set_bits(it6112_client, 0x26, 0x0F,
(it6112_client->mipi_tx_h_lps_time >> 8) & 0x0F);
}
void mipi_tx_enable_lp_bypass(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x70, 0x18,
(it6112_client->enable_mipi_tx_lp_bypass_option_fix << 4) | 0x08);
mipi_tx_set_bits(it6112_client, 0x32, 0xBF,
(it6112_client->mipi_tx_fire_lp_start_line << 7)
| it6112_client->mipi_tx_fire_lp_h_line_count);
}
void mipi_tx_disable_lp_bypass(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x70, 0x00, 0x00);
}
void mipi_tx_setup_lps(struct it6112 *it6112_client)
{
/* enable every line enter lps or only enter lps at last line in v blank */
mipi_tx_set_bits(it6112_client, 0x21, 0x02,
it6112_client->enable_mipi_tx_h_enter_lps ? 0x02 : 0x00);
mipi_tx_setup_lps_time(it6112_client);
}
inline void mipi_tx_setup_lp_11_time(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x3C, 0x0F, it6112_client->mipi_tx_lp_11_time);
}
void mipi_tx_init(struct it6112 *it6112_client)
{
u8 mipi_tx_link_lane_config = ((it6112_client->enable_mipi_tx_link_swap << 3)
| (it6112_client->enable_mipi_tx_lane_swap << 1)
| it6112_client->enable_mipi_tx_pn_swap);
// MPTX Software Reset
mipi_tx_write(it6112_client, 0x05, 0xFE);
mipi_tx_write(it6112_client, 0x05, 0x00);
mipi_tx_set_bits(it6112_client, 0x05, 0x01, 0x01); //software reset all, include RX!!
mipi_tx_set_bits(it6112_client, 0x05, 0x20, 0x20); //hold tx first
mipi_tx_set_bits(it6112_client, 0xD0, 0x04,
it6112_client->enable_mipi_tx_external_mclk << 2);
mdelay(2);
mipi_tx_setup_lps(it6112_client);
mipi_tx_setup_lp_11_time(it6112_client);
mipi_tx_set_bits(it6112_client, 0x10, 0x20, OUTPUT_AS_RX_INPUT_MODE ? 0x00 : 0x20);
mipi_tx_write(it6112_client, 0x22, it6112_client->mipi_tx_hs_pretime);
if (!it6112_client->enable_mipi_tx_output_clock_continuous)
mipi_tx_set_bits(it6112_client, 0x46, 0xF0, 0xF0);
if (!OUTPUT_AS_RX_INPUT_MODE)
mipi_tx_set_bits(it6112_client, 0x11, 0x10,
it6112_client->mipi_tx_sync_pulse ? 0x00 : 0x10);
mipi_tx_set_bits(it6112_client, 0x10, 0x02,
(it6112_client->enable_mipi_tx_mclk_inverse << 1));
mipi_tx_set_bits(it6112_client, 0x11, 0x08, (it6112_client->enable_mipi_4_lane_mode << 3));
mipi_tx_set_bits(it6112_client, 0x3C, 0x20, 0x20);
mipi_tx_set_bits(it6112_client, 0x44, 0x04, it6112_client->enable_mipi_tx_pre_1t << 2);
mipi_tx_set_bits(it6112_client, 0x45, 0x0f, it6112_client->mipi_tx_lpx_num);
mipi_tx_set_bits(it6112_client, 0x47, 0xf0, it6112_client->mipi_tx_hs_prepare << 4);
mipi_tx_set_bits(it6112_client, 0xB0, 0xFF, 0x27);
mipi_tx_write(it6112_client, 0x1f, it6112_client->mipi_tx_hs_end_time);
if (it6112_client->chip_id == IT6113) {
/* enable mipi tx non continuous clock hs fire lp not depends on mipi clock */
mipi_tx_set_bits(it6112_client, 0x78, 0x20, 0x20);
switch (it6112_client->revision) {
case 0xD0:
if (it6112_client->enable_mipi_tx_vrr_detect)
mipi_tx_enable_vrr_detect(it6112_client);
mipi_tx_set_bits(it6112_client, 0x8c,0x0b,mipi_tx_link_lane_config);
break;
default:
break;
}
}
mipi_tx_adjusted_oclk(it6112_client);
pr_info( "mipi tx initial done! set reg0x22: 0x%02x, reg0x1F:0x%02x, lpx_num:0x%02x\n",
it6112_client->mipi_tx_hs_pretime,
it6112_client->mipi_tx_hs_end_time,
it6112_client->mipi_tx_lpx_num);
}
void get_support_feature(struct it6112 *it6112_client)
{
switch (it6112_client->chip_id) {
case IT6112:
if (!OUTPUT_AS_RX_INPUT_MODE && !it6112_client->mipi_tx_sync_pulse) {
pr_info( "\not support sync event, output sync pulse\n");
it6112_client->mipi_tx_sync_pulse = true;
}
break;
case IT6113:
if (!it6112_client->enable_mipi_tx_h_enter_lps) {
pr_info( "not support h fire packet\n");
it6112_client->enable_mipi_tx_h_fire_packet = false;
}
if (!it6112_client->enable_mipi_tx_link_swap && it6112_client->revision<0xD0) {
pr_info( "not support link swap\n");
it6112_client->enable_mipi_tx_link_swap = false;
}
break;
default:
break;
}
/* this value depends on mclk */
//it6112_client->mipi_tx_hs_pretime =
// it6112_client->enable_mipi_tx_output_clock_continuous ?
// ((it6112_client->chip_id == IT6112) ? 0x20 : 0x40) : 0x40;
}
void device_init(struct it6112 *it6112_client)
{
get_support_feature(it6112_client);
mipi_rx_init(it6112_client);
//mdelay(50);
mipi_rx_calc_rclk(it6112_client);
mipi_rx_calc_mclk(it6112_client);
//mdelay(50);
if (it6112_client->enable_mipi_tx_hs_auto_set)
mipi_tx_calc_hs_para(it6112_client);
mipi_tx_init(it6112_client);
}
void it6112_mipi_rx_power_down(struct it6112 *it6112_client)
{
/* MPRX Software Reset */
/* Video Clock Domain Reset */
mipi_rx_set_bits(it6112_client, 0x05, 0x02, 0x02);
}
int get_dcs_ecc(int dcshead)
{
int q0, q1, q2, q3, q4, q5;
q0 = ((dcshead >> 0) & (0x01)) ^ ((dcshead >> 1) & (0x01))
^ ((dcshead >> 2) & (0x01)) ^ ((dcshead >> 4) & (0x01))
^ ((dcshead >> 5) & (0x01)) ^ ((dcshead >> 7) & (0x01))
^ ((dcshead >> 10) & (0x01)) ^ ((dcshead >> 11) & (0x01))
^ ((dcshead >> 13) & (0x01)) ^ ((dcshead >> 16) & (0x01))
^ ((dcshead >> 20) & (0x01)) ^ ((dcshead >> 21) & (0x01))
^ ((dcshead >> 22) & (0x01)) ^ ((dcshead >> 23) & (0x01));
q1 = ((dcshead >> 0) & (0x01)) ^ ((dcshead >> 1) & (0x01))
^ ((dcshead >> 3) & (0x01)) ^ ((dcshead >> 4) & (0x01))
^ ((dcshead >> 6) & (0x01)) ^ ((dcshead >> 8) & (0x01))
^ ((dcshead >> 10) & (0x01)) ^ ((dcshead >> 12) & (0x01))
^ ((dcshead >> 14) & (0x01)) ^ ((dcshead >> 17) & (0x01))
^ ((dcshead >> 20) & (0x01)) ^ ((dcshead >> 21) & (0x01))
^ ((dcshead >> 22) & (0x01)) ^ ((dcshead >> 23) & (0x01)) ;
q2 = ((dcshead >> 0) & (0x01)) ^ ((dcshead>> 2) & (0x01))
^ ((dcshead >> 3) & (0x01)) ^ ((dcshead >> 5) & (0x01))
^ ((dcshead >> 6) & (0x01)) ^ ((dcshead >> 9) & (0x01))
^ ((dcshead >> 11) & (0x01)) ^ ((dcshead >> 12) & (0x01))
^ ((dcshead >> 15) & (0x01)) ^ ((dcshead >> 18) & (0x01))
^ ((dcshead >> 20) & (0x01)) ^ ((dcshead >> 21) & (0x01))
^ ((dcshead >> 22) & (0x01));
q3 = ((dcshead >> 1) & (0x01)) ^ ((dcshead>> 2) & (0x01))
^ ((dcshead >> 3) & (0x01)) ^ ((dcshead >> 7) & (0x01))
^ ((dcshead >> 8) & (0x01)) ^ ((dcshead >> 9) & (0x01))
^ ((dcshead >> 13) & (0x01)) ^ ((dcshead >> 14) & (0x01))
^ ((dcshead >> 15) & (0x01)) ^ ((dcshead >> 19) & (0x01))
^ ((dcshead >> 20) & (0x01)) ^ ((dcshead >> 21) & (0x01))
^ ((dcshead >> 23) & (0x01));
q4 = ((dcshead >> 4) & (0x01)) ^ ((dcshead >> 5) & (0x01))
^ ((dcshead >> 6) & (0x01)) ^ ((dcshead >> 7) & (0x01))
^ ((dcshead >> 8) & (0x01)) ^ ((dcshead >> 9) & (0x01))
^ ((dcshead >> 16) & (0x01)) ^ ((dcshead >> 17) & (0x01))
^ ((dcshead >> 18) & (0x01)) ^ ((dcshead >> 19) & (0x01))
^ ((dcshead >> 20) & (0x01)) ^ ((dcshead >> 22) & (0x01))
^ ((dcshead >> 23) & (0x01));
q5 = ((dcshead >> 10) & (0x01)) ^ ((dcshead >> 11) & (0x01))
^ ((dcshead >> 12) & (0x01)) ^ ((dcshead >> 13) & (0x01))
^ ((dcshead >> 14) & (0x01)) ^ ((dcshead >> 15) & (0x01))
^ ((dcshead >> 16) & (0x01)) ^ ((dcshead >> 17) & (0x01))
^ ((dcshead >> 18) & (0x01)) ^ ((dcshead >> 19) & (0x01))
^ ((dcshead >> 21) & (0x01)) ^ ((dcshead >> 22) & (0x01))
^ ((dcshead >> 23) & (0x01));
return (q0 + (q1 << 1) + (q2 << 2) + (q3 << 3) + (q4 << 4) + (q5 << 5));
}
int dcs_crc8t(int crcq16b, const u8 crc8bin)
{
int lfsrout = 0, lfsr[16], i;
lfsr[15] = ((crc8bin >> 7) & 0x01) ^ ((crc8bin >> 3) & 0x01) ^
((crcq16b >> 7) & 0x01) ^ ((crcq16b >> 3) & 0x01);
lfsr[14] = ((crc8bin >> 6) & 0x01) ^ ((crc8bin >> 2) & 0x01) ^
((crcq16b >> 6) & 0x01) ^ ((crcq16b >> 2) & 0x01);
lfsr[13] = ((crc8bin >> 5) & 0x01) ^ ((crc8bin >> 1) & 0x01) ^
((crcq16b >> 5) & 0x01) ^ ((crcq16b >> 1) & 0x01);
lfsr[12] = ((crc8bin >> 4) & 0x01) ^ ((crc8bin >> 0) & 0x01) ^
((crcq16b >> 4) & 0x01) ^ ((crcq16b >> 0) & 0x01);
lfsr[11] = ((crc8bin >> 3) & 0x01) ^
((crcq16b >> 3) & 0x01);
lfsr[10] = ((crc8bin >> 7) & 0x01) ^ ((crc8bin >> 3) & 0x01) ^ ((crc8bin >> 2) & 0x01) ^
((crcq16b >> 7) & 0x01) ^ ((crcq16b >> 3) & 0x01) ^ ((crcq16b >> 2) & 0x01);
lfsr[9] = ((crc8bin >> 6) & 0x01) ^ ((crc8bin >> 2) & 0x01) ^ ((crc8bin >> 1) & 0x01) ^
((crcq16b >> 6) & 0x01) ^ ((crcq16b >> 2) & 0x01) ^ ((crcq16b >> 1) & 0x01);
lfsr[8] = ((crc8bin >> 5) & 0x01) ^ ((crc8bin >> 1) & 0x01) ^ ((crc8bin >> 0) & 0x01) ^
((crcq16b >> 5) & 0x01) ^ ((crcq16b >> 1) & 0x01) ^ ((crcq16b >> 0) & 0x01);
lfsr[7] = ((crc8bin >> 4) & 0x01) ^ ((crc8bin >> 0) & 0x01) ^
((crcq16b >> 15) & 0x01) ^ ((crcq16b >> 4) & 0x01) ^ ((crcq16b >> 0) & 0x01);
lfsr[6] = ((crc8bin >> 3) & 0x01) ^
((crcq16b >> 14) & 0x01) ^ ((crcq16b >> 3) & 0x01);
lfsr[5] = ((crc8bin >> 2) & 0x01) ^
((crcq16b >> 13) & 0x01) ^ ((crcq16b >> 2) & 0x01);
lfsr[4] = ((crc8bin >> 1) & 0x01) ^
((crcq16b >> 12) & 0x01) ^ ((crcq16b >> 1) & 0x01);
lfsr[3] = ((crc8bin >> 7) & 0x01) ^ ((crc8bin >> 3) & 0x01) ^ ((crc8bin >> 0) & 0x01) ^
((crcq16b >> 11) & 0x01) ^ ((crcq16b >> 7) & 0x01)
^ ((crcq16b >> 3) & 0x01) ^ ((crcq16b >> 0) & 0x01);
lfsr[2] = ((crc8bin >> 6) & 0x01) ^ ((crc8bin >> 2) & 0x01) ^
((crcq16b >> 10) & 0x01) ^ ((crcq16b >> 6) & 0x01) ^ ((crcq16b >> 2) & 0x01);
lfsr[1] = ((crc8bin >> 5) & 0x01) ^ ((crc8bin >> 1) & 0x01) ^
((crcq16b >> 9) & 0x01) ^ ((crcq16b >> 5) & 0x01) ^ ((crcq16b >> 1) & 0x01);
lfsr[0] = ((crc8bin >> 4) & 0x01) ^ ((crc8bin >> 0) & 0x01) ^
((crcq16b >> 8) & 0x01) ^ ((crcq16b >> 4) & 0x01) ^ ((crcq16b >> 0) & 0x01);
for (i = 0; i < ARRAY_SIZE(lfsr); i++)
lfsrout = lfsrout + (lfsr[i]<<i);
return lfsrout;
}
int get_dcs_crc(int bytenum, const u8 *crcbyte)
{
int i, crctemp = 0xFFFF;
for (i = 0; i <= bytenum - 1; i++)
crctemp = dcs_crc8t(crctemp, crcbyte[i]);
return crctemp;
}
void mipi_tx_setup_long_packet_header(struct mipi_packet *pheader, u32 word_count)
{
int header;
pheader->word_count_h = word_count >> 8;
pheader->word_count_l = (u8)word_count;
header = pheader->data_id | pheader->word_count_h << 16 | pheader->word_count_l << 8;
pheader->ecc = get_dcs_ecc(header);
}
enum mipi_packet_size mipi_tx_get_packet_size(const struct dcs_setting_entry *dcs_setting_table,
enum dcs_cmd_name cmd_name)
{
u8 i, size = ARRAY_SIZE(packet_size_data_id_map);
for (i = 0; i < size; i++) {
if (dcs_setting_table[cmd_name].data_id == packet_size_data_id_map[i].data_id)
break;
}
if (i == size) {
if (dcs_setting_table[cmd_name].count == 0) {
pr_info( "error! cmd index: %d count = 0\n", cmd_name);
return UNKNOWN_PACKET;
} else if (dcs_setting_table[cmd_name].count < 3) {
return SHORT_PACKET;
} else {
return LONG_PACKET;
}
}
return packet_size_data_id_map[i].packet_size;
}
void mipi_tx_get_packet_fire_state(struct it6112 *it6112_client)
{
u8 lp_cmd_fifo, link0_data_fifo, link1_data_fifo;
lp_cmd_fifo = mipi_tx_read(it6112_client, 0x7F);
link0_data_fifo = mipi_tx_read(it6112_client, 0x80);
link1_data_fifo = mipi_tx_read(it6112_client, 0x81);
if (lp_cmd_fifo != 0)
pr_info( "error! fire low power cmd fail, remain bytes not fire, reg0x7F:0x%02x\n",
lp_cmd_fifo);
if (link0_data_fifo != 0)
pr_info( "error! fire link0 low power data fail, remain %d bytes not fire, reg0x80:0x%02x\n",
link0_data_fifo, link0_data_fifo);
if (link1_data_fifo != 0)
pr_info( "error! fire link1 low power data fail, remain %d bytes not fire, reg0x81:0x%02x\n",
link1_data_fifo, link1_data_fifo);
}
void mipi_tx_setup_packet(struct mipi_packet *pheader,
const struct dcs_setting_entry *dcs_setting_table,
enum dcs_cmd_name cmd_name)
{
int short_cmd;
enum mipi_packet_size packet_size;
pheader->data_id = dcs_setting_table[cmd_name].data_id;
packet_size = mipi_tx_get_packet_size(dcs_setting_table, cmd_name);
if (packet_size == UNKNOWN_PACKET) {
pr_info( "error! unknown packet size and check dcs table parameter\n");
return;
}
if (packet_size == SHORT_PACKET) {
pheader->word_count_l = dcs_setting_table[cmd_name].para_list[0];
pheader->word_count_h = dcs_setting_table[cmd_name].para_list[1];
short_cmd = pheader->data_id |
pheader->word_count_l << 8 | pheader->word_count_h << 16;
pheader->ecc = get_dcs_ecc(short_cmd);
}
if (packet_size == LONG_PACKET)
mipi_tx_setup_long_packet_header(pheader, dcs_setting_table[cmd_name].count);
}
inline void mipi_tx_fire_packet(struct it6112 *it6112_client,
const struct dcs_setting_entry *dcs_setting_table, enum dcs_cmd_name cmd_name)
{
mipi_tx_write(it6112_client, 0x75, dcs_setting_table[cmd_name].cmd);
}
void mipi_tx_setup_packet_process(struct it6112 *it6112_client,
const struct dcs_setting_entry *dcs_setting_table,
enum dcs_cmd_name cmd_name, enum mipi_tx_lp_cmd_header header_select)
{
struct mipi_packet packet;
enum mipi_packet_size packet_size;
u32 long_packet_checksum;
int i, header_crc, data_count;
if (!header_select) {
pr_info( "no header packet\n");
for (i = 0; i < dcs_setting_table[cmd_name].count; i++) {
mipi_tx_write(it6112_client, 0x73,
dcs_setting_table[cmd_name].para_list[i]);
pr_info( "data[%d]: 0x%02x ", i, dcs_setting_table[cmd_name].para_list[i]);
}
header_crc = 0;
goto short_packet;
}
mipi_tx_setup_packet(&packet, dcs_setting_table, cmd_name);
packet_size = mipi_tx_get_packet_size(dcs_setting_table, cmd_name);
for (i = 0; i < sizeof(packet); i++)
mipi_tx_write(it6112_client, 0x73, ((u8 *)(&packet))[i]);
if (packet_size == SHORT_PACKET) {
header_crc = 2;
goto short_packet;
}
header_crc = sizeof(packet) + 2;
long_packet_checksum = get_dcs_crc(dcs_setting_table[cmd_name].count,
dcs_setting_table[cmd_name].para_list);
for (i = 0; i < dcs_setting_table[cmd_name].count; i++)
mipi_tx_write(it6112_client, 0x73, dcs_setting_table[cmd_name].para_list[i]);
mipi_tx_write(it6112_client, 0x73, (u8)long_packet_checksum);
mipi_tx_write(it6112_client, 0x73, (u8)(long_packet_checksum >> 8));
short_packet:
switch (it6112_client->chip_id) {
case IT6112:
mipi_tx_write(it6112_client, 0x74,
0x40 | (dcs_setting_table[cmd_name].count + header_crc));
break;
case IT6113:
data_count = dcs_setting_table[cmd_name].count + header_crc;
if (data_count == IT6113_LP_CMD_FIFO)
data_count = 1;
mipi_tx_write(it6112_client, 0x74,
(it6112_client->enable_mipi_tx_h_fire_packet << 7) | data_count);
break;
default:
break;
}
}
inline u8 mipi_tx_get_video_state(struct it6112 *it6112_client)
{
return mipi_tx_read(it6112_client, 0x0E) & 0x10;
}
void mipi_tx_write_dcs_cmds(struct it6112 *it6112_client,
const struct dcs_setting_entry *dcs_setting_table, int dcs_table_size,
enum dcs_cmd_name start, int count, enum mipi_tx_lp_cmd_header header_select)
{
u8 header_size, i, data_count,
enable_force_lp_mode = !mipi_tx_get_video_state(it6112_client);
u8 lp_cmd_fifo_size[] = { IT6112_LP_CMD_FIFO, IT6113_LP_CMD_FIFO };
if (enable_force_lp_mode) {
mipi_tx_set_bits(it6112_client, 0x11, 0x80, 0x80);
mipi_tx_set_bits(it6112_client, 0x70, 0x04, 0x04);
}
mipi_tx_write(it6112_client, 0x3D, 0x00);
mipi_tx_write(it6112_client, 0x3E, enable_force_lp_mode ? 0x00 : 0x10);
mipi_tx_write(it6112_client, 0x3F, enable_force_lp_mode ? 0x30 : 0x90);
if (it6112_client->chip_id == IT6113 && it6112_client->revision == 0xD0) {
mipi_tx_write(it6112_client, 0x3D, 0xFF);
mipi_tx_write(it6112_client, 0x3E, 0xFF);
mipi_tx_write(it6112_client, 0x3F, 0xD0);
}
for (i = start; i < start + count; i++) {
pr_info( "cmd:%d rx reg0d:0x%02x, tx reg0e:0x%02x\n", i,
mipi_rx_read(it6112_client, 0x0D), mipi_tx_read(it6112_client, 0x0E));
if (i >= dcs_table_size)
goto complete_write_dcs;
if (dcs_setting_table[i].cmd_name == DELAY) {
mdelay(dcs_setting_table[i].cmd);
continue;
}
header_size = header_select ?
((mipi_tx_get_packet_size(dcs_setting_table, i) == SHORT_PACKET) ? 2 : 6)
: 0;
data_count = dcs_setting_table[i].count + header_size;
if (data_count > lp_cmd_fifo_size[it6112_client->chip_id]) {
pr_info( "error! lp cmd: %d, exceed cmd fifo\n", i);
continue;
}
mipi_tx_setup_packet_process(it6112_client, dcs_setting_table, i, header_select);
mipi_tx_fire_packet(it6112_client, dcs_setting_table, i);
if (enable_force_lp_mode)
mipi_tx_get_packet_fire_state(it6112_client);
}
usleep_range(10000, 15000);
complete_write_dcs:
if (i >= dcs_table_size && (start + count > dcs_table_size))
pr_info( "error! exceed maximum dcs setting table index\n");
if (enable_force_lp_mode) {
mipi_tx_set_bits(it6112_client, 0x11, 0x80, 0x00);
mipi_tx_set_bits(it6112_client, 0x70, 0x04, 0x00);
}
mdelay(2);
if (!enable_force_lp_mode)
mipi_tx_get_packet_fire_state(it6112_client);
}
void it6112_enter_bus_turn_around(struct it6112 *it6112_client)
{
u8 enable_force_lp_mode = !mipi_tx_get_video_state(it6112_client);
if (enable_force_lp_mode) {
mipi_tx_set_bits(it6112_client, 0x11, 0x80, 0x80);
mipi_tx_set_bits(it6112_client, 0x70, 0x04, 0x04);
}
mipi_tx_write(it6112_client, 0x3E, 0x10);
mipi_tx_write(it6112_client, 0x3F, 0x90);
if (it6112_client->chip_id == IT6113 && it6112_client->revision == 0xD0) {
mipi_tx_write(it6112_client, 0x3E, 0xFF);
mipi_tx_write(it6112_client, 0x3F, 0xD0);
}
switch (it6112_client->chip_id) {
case IT6112:
mipi_tx_write(it6112_client, 0x74, 0x40);
break;
case IT6113:
mipi_tx_write(it6112_client, 0x74,
it6112_client->enable_mipi_tx_h_fire_packet << 7);
break;
default:
break;
}
mipi_tx_write(it6112_client, 0x75, LP_CMD_BTA);
if (enable_force_lp_mode) {
mipi_tx_set_bits(it6112_client, 0x11, 0x80, 0x00);
mipi_tx_set_bits(it6112_client, 0x70, 0x04, 0x00);
}
}
void mipi_tx_enable_lp_cmd_bypass_mode(struct it6112 *it6112_client)
{
mipi_rx_set_bits(it6112_client, 0x05, 0x02, 0x02);
mdelay(2);
mipi_tx_write_dcs_cmds(it6112_client, bypass_mode_table,
ARRAY_SIZE(bypass_mode_table), 0, 1, CALC_HEADER);
}
void mipi_tx_disable_lp_cmd_bypass_mode(struct it6112 *it6112_client)
{
mipi_tx_write_dcs_cmds(it6112_client, bypass_mode_table,
ARRAY_SIZE(bypass_mode_table), 1, 1, CALC_HEADER);
mdelay(2);
mipi_rx_set_bits(it6112_client, 0x05, 0x02, 0x00);
}
void it6112_mipi_read_panel(struct it6112 *it6112_client,
const struct dcs_setting_entry *dcs_setting_table, int dcs_table_size,
enum dcs_cmd_name cmd_name, u8 *buffer)
{
int /*data,*/ link0_data_count, link0_cmd_type, link1_data_count, link1_cmd_type, i;
mipi_tx_write_dcs_cmds(it6112_client, dcs_setting_table,
dcs_table_size, cmd_name, 1, CALC_HEADER);
it6112_enter_bus_turn_around(it6112_client);
mdelay(5);
link0_data_count = mipi_tx_read(it6112_client, 0x7A);
link0_cmd_type = mipi_tx_read(it6112_client, 0x7B);
for (i = 0; i < link0_data_count; i++) {
buffer[i] = mipi_tx_read(it6112_client, 0x79);
pr_info( "link0_data[%d]:0x%02x\n", i, mipi_tx_read(it6112_client, 0x79));
}
if (it6112_client->chip_id == IT6113) {
link1_data_count = mipi_tx_read(it6112_client, 0x7D);
link1_cmd_type = mipi_tx_read(it6112_client, 0x7E);
for (i = 0; i < link1_data_count; i++)
pr_info( "\n\rlink1_data[%d]:0x%02x", i, mipi_tx_read(it6112_client, 0x7C));
}
}
void mipi_tx_read_back_link0_fifo_data(struct it6112 *it6112_client, u8 *buffer, int data_count)
{
int i;
for (i = 0; i < data_count; i++) {
buffer[i] = mipi_tx_read(it6112_client, 0x79);
pr_info("link0_data[%d]:0x%02x\n", i, buffer[i]);
}
}
int mipi_tx_get_link0_read_back_data_count(struct it6112 *it6112_client)
{
return mipi_tx_read(it6112_client, 0x7A);
}
int mipi_tx_get_link0_read_back_cmd_type(struct it6112 *it6112_client)
{
return mipi_tx_read(it6112_client, 0x7B);
}
int mipi_tx_get_read_stage(struct it6112 *it6112_client)
{
return mipi_tx_read(it6112_client, 0x82);
}
int mipi_tx_read_panel(struct it6112 *it6112_client,
const struct dcs_setting_entry *dcs_setting_table, int dcs_table_size,
enum dcs_cmd_name cmd_name, u8 *buffer)
{
int link0_data_count, link0_cmd_type;
mipi_tx_write_dcs_cmds(it6112_client,
dcs_setting_table, dcs_table_size, cmd_name, 1, CALC_HEADER);
it6112_enter_bus_turn_around(it6112_client);
mdelay(5);
link0_data_count = mipi_tx_get_link0_read_back_data_count(it6112_client);
link0_cmd_type = mipi_tx_get_link0_read_back_cmd_type(it6112_client);
//pr_info(("link0_data_count(reg0x7A):0x%02x, link0_cmd_type(reg0x7B):0x%02x\n",
// link0_data_count, link0_cmd_type));
mipi_tx_read_back_link0_fifo_data(it6112_client, buffer, link0_data_count);
return link0_data_count;
}
int mipi_tx_read_panel_process(struct it6112 *it6112_client, u8 *buffer)
{
int data_count;
mipi_tx_write_dcs_cmds(it6112_client,
lp_cmd_read, ARRAY_SIZE(lp_cmd_read), 0, 1, CALC_HEADER);
data_count = mipi_tx_read_panel(it6112_client,
lp_cmd_read, ARRAY_SIZE(lp_cmd_read), 1, buffer);
return data_count;
}
void mipi_tx_enable_auto_read(struct it6112 *it6112_client, u8 enabled)
{
mipi_tx_write(it6112_client, 0x77, MIPI_TX_AUTO_READ_FRAME_COUNT);
mipi_tx_set_bits(it6112_client, 0x70, 0x40, enabled << 6);
}
void mipi_tx_auto_read_polling(struct it6112 *it6112_client, u8 *buffer)
{
int link0_data_count, link0_cmd_type;
if (mipi_tx_get_read_stage(it6112_client) > 0) {
link0_data_count = mipi_tx_get_link0_read_back_data_count(it6112_client);
link0_cmd_type = mipi_tx_get_link0_read_back_cmd_type(it6112_client);
mipi_tx_read_back_link0_fifo_data(it6112_client, buffer, link0_data_count);
pr_info("%s link0_data_count:%d link0_cmd_type:0x%02x\n",
__func__, link0_data_count, link0_cmd_type);
}
}
void mipi_tx_enable_auto_read_panel(struct it6112 *it6112_client, u8 *buffer)
{
mipi_tx_read_panel_process(it6112_client, buffer);
mipi_tx_enable_auto_read(it6112_client, it6112_client->enable_mipi_tx_hs_auto_read);
pr_info("%s able auto read panel\n",
it6112_client->enable_mipi_tx_hs_auto_read ? "en" : "dis");
}
void mipi_tx_setup_pattern_generator_video_format(struct it6112 *it6112_client)
{
struct mipi_display_mode *display_mode = &it6112_client->mipi_rx_display_mode;
int m_hdisplay = display_mode->m_hdisplay * 3 / 2,
m_hfront_porch = display_mode->m_hfront_porch * 4 / 2;
int m_hback_porch = display_mode->m_hback_porch * 4 / 2,
m_hsync_width = display_mode->m_hsync_width * 4 / 2;
int m_htotal = m_hfront_porch + m_hsync_width + m_hback_porch + m_hdisplay;
mipi_tx_write(it6112_client, 0x91,
(it6112_client->mipi_tx_pattern_generator_color_depth << 4)
| it6112_client->mipi_tx_pattern_generator_format);
mipi_tx_write(it6112_client, 0x92, it6112_client->mipi_tx_pattern_generator_base);
mipi_tx_write(it6112_client, 0x93, it6112_client->mipi_tx_pattern_generator_v_inc);
mipi_tx_write(it6112_client, 0x94, it6112_client->mipi_tx_pattern_generator_h_inc);
mipi_tx_write(it6112_client, 0x95, display_mode->m_vsync_width);
mipi_tx_write(it6112_client, 0x96, m_htotal & 0xFF);
mipi_tx_write(it6112_client, 0x97, (m_htotal & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0x98, m_hdisplay & 0xFF);
mipi_tx_write(it6112_client, 0x99, (m_hdisplay & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0x9A, m_hback_porch & 0xFF);
mipi_tx_write(it6112_client, 0x9B, (m_hback_porch & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0x9C, m_hsync_width & 0xFF);
mipi_tx_write(it6112_client, 0x9D, (m_hsync_width & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0x9E, display_mode->m_vtotal & 0xFF);
mipi_tx_write(it6112_client, 0x9F, (display_mode->m_vtotal & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0xA0, display_mode->m_vdisplay & 0xFF);
mipi_tx_write(it6112_client, 0xA1, (display_mode->m_vdisplay & 0xFF00) >> 8);
mipi_tx_write(it6112_client, 0xA2, display_mode->m_vback_porch & 0xFF);
mipi_tx_write(it6112_client, 0xA3, (display_mode->m_vback_porch & 0xFF00) >> 8);
}
inline void mipi_tx_config_pattern_generator(struct it6112 *it6112_client)
{
mipi_tx_write(it6112_client, 0x90, it6112_client->enable_mipi_tx_pattern_generator);
}
void mipi_tx_setup_vrr_pattern_generator(struct it6112 *it6112_client)
{
mipi_tx_set_bits(it6112_client, 0x34, 0x7f, (MIPI_TX_VRR_PATTERN_GENERATOR_MODE << 5)
| (MIPI_TX_VRR_PATTERN_GENERATOR_CHANGE_INTERVAL << 3)
| (MIPI_TX_VRR_PATTERN_GENERATOR_FREQ << 1)
| it6112_client->enable_mipi_tx_vrr_pattern_generator);
mipi_tx_write(it6112_client, 0x35, MIPI_TX_VRR_PATTERN_GENERATOR_AMP & 0x00FF);
mipi_tx_write(it6112_client, 0x36, (MIPI_TX_VRR_PATTERN_GENERATOR_AMP & 0xFF00) >> 8);
mipi_tx_disable_vrr_detect(it6112_client);
}
void mipi_tx_config_pattern_generator_process(struct it6112 *it6112_client)
{
mipi_tx_setup_pattern_generator_video_format(it6112_client);
if (it6112_client->chip_id == IT6113) {
switch (it6112_client->revision) {
case 0xD0:
if (it6112_client->enable_mipi_tx_vrr_pattern_generator)
mipi_tx_setup_vrr_pattern_generator(it6112_client);
break;
default:
break;
}
}
mipi_tx_config_pattern_generator(it6112_client);
}
void mipi_tx_set_output(struct it6112 *it6112_client)
{
if (it6112_client->enable_mipi_tx_initial_fire_lp_cmd) {
mipi_tx_write_dcs_cmds(it6112_client,
it6112_client->init_table->init_cmd_table,
it6112_client->init_table->count, 0,
it6112_client->init_table->count, CALC_HEADER);
} else {
pr_info( "%s,send cmd by dsi\n",__func__);
it6112_client->push_table(it6112_client->panel,
bypass_mode_table, ARRAY_SIZE(bypass_mode_table), 0, 1);
it6112_client->push_table(it6112_client->panel,
it6112_client->init_table->init_cmd_table,
it6112_client->init_table->count, 0, it6112_client->init_table->count);
it6112_client->push_table(it6112_client->panel,
bypass_mode_table, ARRAY_SIZE(bypass_mode_table), 1, 1);
}
mipi_tx_set_bits(it6112_client, 0x44, 0x01,
it6112_client->enable_mipi_tx_output_clock_continuous);
usleep_range(1000, 1500);
mipi_tx_write(it6112_client, 0x05, 0xfe);
mipi_tx_disable_ppi(it6112_client);
mipi_tx_write(it6112_client, 0x05, 0x00);
mipi_polling_state(it6112_client);
pr_info("output clock:%d tx reg0x44:0x%02x, mipi rx receive video type reg0x28: 0x%02x\n",
it6112_client->enable_mipi_tx_output_clock_continuous,
mipi_tx_read(it6112_client, 0x44),
mipi_rx_read(it6112_client, 0x28));
}
int chip_identify(struct it6112 *it6112_client)
{
static const char * const id_name[] = {"it6112", "it6113"};
u8 id[][4] = {{0x54, 0x49, 0x12, 0x61},
{0x54, 0x49, 0x13, 0x61}};
u8 i, j;
for (i = 0; i < ARRAY_SIZE(id); i++) {
for (j = 0; j < ARRAY_SIZE(id[i]); j++) {
if (mipi_tx_read(it6112_client, j) != id[i][j])
break;
}
if (j != ARRAY_SIZE(id[i]))
break;
}
if (i < ARRAY_SIZE(id)) {
it6112_client->revision = mipi_tx_read(it6112_client, 0x04);
switch (mipi_tx_read(it6112_client, 0x02)) {
case 0x12:
it6112_client->chip_id = IT6112;
break;
case 0x13:
it6112_client->chip_id = IT6113;
break;
default:
break;
}
pr_info( "find %s", id_name[it6112_client->chip_id]);
return 0;
}
pr_info( "can not find chip");
return -1;
}
int init_config(struct it6112 *it6112_client)
{
if (!it6112_tx_i2c_client || !it6112_rx_i2c_client) {
pr_info("it6112 iic NULL, please wait!\n");
return -1;
}
it6112_client->tx_i2c = it6112_tx_i2c_client;
it6112_client->rx_i2c = it6112_rx_i2c_client;
it6112_client->enable_mipi_rx_lane_swap = ENABLE_MIPI_RX_LANE_SWAP;
it6112_client->enable_mipi_rx_pn_swap = ENABLE_MIPI_PN_SWAP;
it6112_client->enable_mipi_4_lane_mode = ENABLE_MIPI_TX_4_LANE_MODE;
it6112_client->mipi_rx_video_type = MIPI_RX_VIDEO_TYPE;
it6112_client->enable_mipi_rx_mclk_inverse = ENABLE_INVERSE_RX_MCLK;
it6112_client->enable_mipi_tx_mclk_inverse = ENABLE_INVERSE_TX_MCLK;
it6112_client->mipi_rx_hs_settle = MIPI_RX_HS_SETTLE;
it6112_client->mipi_rx_hs_skip = MIPI_RX_HS_SKIP;
it6112_client->enable_mipi_tx_vrr_detect = ENABLE_MIPI_TX_VRR_DETECT;
it6112_client->enable_mipi_tx_pattern_generator = ENABLE_MIPI_TX_PATTERN_GENERATOR;
it6112_client->enable_mipi_tx_vrr_pattern_generator = ENABLE_MIPI_TX_VRR_PATTERN_GENERATOR;
it6112_client->enable_mipi_tx_external_mclk = ENABLE_MIPI_TX_EXTERNAL_MCLK;
it6112_client->mipi_tx_pattern_generator_h_inc = MIPI_TX_PATTERN_GENERATOR_H_INC;
it6112_client->mipi_tx_pattern_generator_v_inc = MIPI_TX_PATTERN_GENERATOR_V_INC;
it6112_client->mipi_tx_pattern_generator_color_depth =
MIPI_TX_PATTERN_GENERATOR_COLOR_DEPTH;
it6112_client->mipi_tx_pattern_generator_format = MIPI_TX_PATTERN_GENERATOR_FORMAT;
it6112_client->mipi_tx_pattern_generator_base = MIPI_TX_PATTERN_GENERATOR_BASE;
it6112_client->mipi_tx_hs_end_time = MIPI_TX_HS_TRAIL;/* default: 0x08 */
/* adjust tx low power state's command time interval */
it6112_client->mipi_tx_lpx_num = MIPI_TX_LPX;
it6112_client->mipi_tx_hs_prepare = MIPI_TX_HS_PREPARE;
it6112_client->mipi_tx_hs_pretime = MIPI_TX_HS_PREPARE_ZERO;
it6112_client->enable_mipi_tx_h_enter_lps = TRUE;
it6112_client->enable_mipi_tx_output_clock_continuous =
ENABLE_MIPI_TX_OUTPUT_CLOCK_CONTINUOUS;
it6112_client->mipi_tx_h_lps_time = 0x100;
it6112_client->mipi_tx_v_lps_time = 0x100;
it6112_client->mipi_tx_lp_11_time = 0x03;
it6112_client->mipi_tx_sync_pulse = SYNC_PULSE;
it6112_client->enable_mipi_tx_h_fire_packet = true;
it6112_client->enable_mipi_tx_pre_1t = ENABLE_MIPI_TX_PRE_1T;
it6112_client->enable_mipi_tx_hs_auto_set = ENABLE_MIPI_TX_HS_AUTO_SET;
it6112_client->mipi_tx_fire_lp_h_line_count = MIPI_TX_FIRE_LP_H_LINE_COUNT;
it6112_client->mipi_tx_fire_lp_start_line = MIPI_TX_FIRE_LP_START_LINE;
it6112_client->enable_mipi_tx_lp_bypass = ENABLE_MIPI_TX_LP_BYPASS;
it6112_client->enable_mipi_tx_lp_bypass_option_fix = ENABLE_MIPI_TX_LP_BYPASS_OPTION_FIX;
it6112_client->enable_mipi_tx_hs_auto_read = ENABLE_MIPI_TX_HS_AUTO_READ;
it6112_client->mipi_tx_oclk = MIPI_TX_OCLK;
it6112_client->mipi_tx_oclk_ns = MIPI_TX_OCLK_NS; /* oclk 50 MHz, 20 ns */
it6112_client->enable_mipi_tx_initial_fire_lp_cmd = MIPI_TX_ENABLE_INITIAL_FIRE_LP_CMD;
it6112_client->enable_mipi_tx_link_swap = ENABLE_MIPI_TX_LINK_SWAP;
it6112_client->enable_mipi_tx_pn_swap = ENABLE_MIPI_TX_PN_SWAP;
it6112_client->enable_mipi_tx_lane_swap = ENABLE_MIPI_TX_LANE_SWAP;
return chip_identify(it6112_client);
}
void device_power_off(struct it6112 *it6112_client)
{
ite_poll_enable(0);
it6112_client->enable_mipi_tx_output_clock_continuous = true;
mipi_tx_set_bits(it6112_client, 0x44, 0x01,
it6112_client->enable_mipi_tx_output_clock_continuous);
mdelay(5);
mipi_tx_write_dcs_cmds(it6112_client,
suspend_table, ARRAY_SIZE(suspend_table), 0,
ARRAY_SIZE(suspend_table), CALC_HEADER);
mipi_tx_set_bits(it6112_client, 0x05, 0xFE, 0xFE);
mipi_rx_set_bits(it6112_client, 0x05, 0x03, 0x03);
mipi_tx_set_bits(it6112_client, 0x06, 0x02, 0x02);
}
void device_power_on(struct it6112 *it6112_client)
{
/* Video Clock Domain Reset */
mipi_tx_set_bits(it6112_client, 0x06, 0x02, 0x00);
mdelay(2);
mipi_rx_set_bits(it6112_client, 0x05, 0x0F, 0x00);
/* tx mclk still hold */
mipi_tx_set_bits(it6112_client, 0x05, 0xCC, 0x00);
mdelay(2);
}
void it6112_set_backlight(struct it6112 *it6112_client, int map_level)
{
struct dcs_setting_entry backlight_table = {
REGW0, LP_CMD_LPDT, 0x39, 3, {0x51, 0xff, 0xff}
};
backlight_table.para_list[1] = map_level & 0xFF00 >> 8;
backlight_table.para_list[2] = map_level & 0xff;
mipi_tx_write_dcs_cmds(it6112_client, &backlight_table, 1, REGW0, 1, CALC_HEADER);
pr_info("%s: set backlight map_level:%d, map to %d\n", __func__, map_level);
}
void chip_init(struct it6112 *it6112_client)
{
u8 read_back[10] = {0};
if(!it6112_client->init_table || !it6112_client->push_table) {
pr_info( "Must set init_cmd and push_table before used it!\n");
return;
}
device_power_on(it6112_client);
device_init(it6112_client);
mipi_rx_get_mrec_timing(it6112_client);
if (it6112_client->enable_mipi_tx_pattern_generator)
mipi_tx_config_pattern_generator_process(it6112_client);
mipi_tx_set_output(it6112_client);
mipi_tx_enable_auto_read_panel(it6112_client, read_back);
ite_poll_int(it6112_client);
ite_poll_enable(1);
}
void it6112_read_ddic_reg(struct it6112 *it6112_client, u8 *buff)
{
if(!it6112_client) {
pr_info("%s: error! hane NULL\n", __func__);
return;
}
mipi_tx_read_panel_process(it6112_client, buff);
}
/*
* ret: 1 tx is ok; 0 tx has some error
*
*/
u8 mipi_tx_state(struct it6112 *it6112_client)
{
int reg0e, reg0d;
reg0d = mipi_rx_read(it6112_client, 0x0D);
reg0e = mipi_tx_read(it6112_client, 0x0E);
return ((reg0e & 0x27) == 0x00);
}
void device_esd_reinit(struct it6112 *it6112_client)
{
device_power_on(it6112_client);
device_init(it6112_client);
mipi_rx_get_mrec_timing(it6112_client);
if (it6112_client->enable_mipi_tx_pattern_generator)
mipi_tx_config_pattern_generator_process(it6112_client);
mipi_tx_set_output(it6112_client);
}
void poll(struct it6112 *it6112_client)
{
int reg0d;
reg0d = mipi_rx_read(it6112_client, 0x0D);
pr_info( "it6112 rx reg0D:0x%02x enable_ppi:%d\n", reg0d, it6112_client->enable_ppi);
if ((reg0d & 0x10) && (reg0d & 0x08)) {
pr_info( "it6112 rx clock not stable\n");
mipi_rx_reset(it6112_client);
return;
}
if (reg0d & 0x10) {
if (it6112_client->enable_ppi) {
if (!mipi_tx_state(it6112_client))
device_esd_reinit(it6112_client);
} else {
mipi_polling_state(it6112_client);
}
}
}
static int ite_poll_worker_kthread(void *data)
{
int ret = 0;
struct sched_param param = {
.sched_priority = 99
};
sched_setscheduler(current, SCHED_RR, &param);
while (1) {
msleep(1000);
ret = wait_event_interruptible(_ite_poll_task_wq,
atomic_read(&_ite_poll_task_wakeup));
if (ret < 0) {
pr_info("[ite] ite polling thread waked up accidently!\n");
continue;
}
poll((struct it6112 *)data);
pr_debug("[ite] ite polling!\n");
if (kthread_should_stop())
break;
}
return 0;
}
void ite_poll_enable(int enable)
{
pr_debug("[ite] %s enable=%d\n", __func__, enable);
if (enable) {
atomic_set(&_ite_poll_task_wakeup, 1);
wake_up_interruptible(&_ite_poll_task_wq);
} else
atomic_set(&_ite_poll_task_wakeup, 0);
}
void ite_poll_int(void *data)
{
ite_poll_task = kthread_create(ite_poll_worker_kthread, data, "ite_polling");
init_waitqueue_head(&_ite_poll_task_wq);
wake_up_process(ite_poll_task);
}
/* I2C */
static int it6112_driver_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
pr_info("%s++++:\n", __func__);
it6112_tx_i2c_client = devm_kzalloc(&client->dev,
sizeof(struct i2c_client), GFP_KERNEL);
if (!it6112_tx_i2c_client)
return -ENOMEM;
memcpy(it6112_tx_i2c_client, client, sizeof(struct i2c_client));
it6112_rx_i2c_client = i2c_new_dummy(client->adapter, 0x5e);
pr_info("%s----\n", __func__);
return 0;
}
static int it6112_driver_remove(struct i2c_client *client)
{
pr_info("it6112 iic driver remove!\n");
it6112_rx_i2c_client = NULL;
it6112_tx_i2c_client = NULL;
return 0;
}
static const struct of_device_id it6112_match[] = {
{.compatible = "ite,it6112"},
{},
};
MODULE_DEVICE_TABLE(of, it6112_match);
static struct i2c_driver it6112_driver = {
.driver = {
.name = "ite,it6112",
.of_match_table = it6112_match,
},
.probe = it6112_driver_probe,
.remove = it6112_driver_remove,
};
static int __init it6112_init(void)
{
return i2c_add_driver(&it6112_driver);
}
module_init(it6112_init);
static void __exit it6112_exit(void)
{
i2c_del_driver(&it6112_driver);
}
module_exit(it6112_exit);
MODULE_AUTHOR("henry tu <henry.tu@mediatek.com>");
MODULE_DESCRIPTION("it6112 panel driver");
MODULE_LICENSE("GPL v2");