// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #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]<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, ¶m); 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 "); MODULE_DESCRIPTION("it6112 panel driver"); MODULE_LICENSE("GPL v2");