| #include "asrfb.h" |
| #include "dsi_drv.h" |
| |
| //#define DSI_VERSION_14 |
| #define DSI_VERSION_24 |
| //#define DSI_VERSION_4 |
| |
| #if defined(DSI_VERSION_14) || defined(DSI_VERSION_24) || defined(DSI_VERSION_4) |
| #define DSI_GEN4 |
| #endif |
| |
| #define dsi_ex_pixel_cnt 0 |
| #define dsi_hex_en 0 |
| #define to_dsi_bcnt(timing, bpp) (((timing) * (bpp)) >> 3) |
| |
| static unsigned int dsi_lane[5] = { 0, 0x1, 0x3, 0x7, 0xf }; |
| static struct dsi_phy_timing phy = { |
| .hs_prep_constant = 40, /* Unit: ns. */ |
| .hs_prep_ui = 4, |
| .hs_zero_constant = 145, |
| .hs_zero_ui = 10, |
| .hs_trail_constant = 60, |
| .hs_trail_ui = 4, |
| .hs_exit_constant = 100, |
| .hs_exit_ui = 0, |
| .ck_zero_constant = 300, |
| .ck_zero_ui = 0, |
| .ck_trail_constant = 60, |
| .ck_trail_ui = 0, |
| .req_ready = 0x3c, |
| .wakeup_constant = 1000000, |
| .wakeup_ui = 0, |
| /* |
| * According to the D-PHY spec, Tlpx >= 50 ns |
| * For the panel otm1281a, it works unstable when Tlpx = 50, |
| * so increase to 60ns. |
| */ |
| .lpx_constant = 60, |
| .lpx_ui = 0, |
| }; |
| |
| static void dphy_write(unsigned int reg, unsigned int value) |
| { |
| writel(value, reg); |
| } |
| |
| static unsigned int dphy_read(unsigned int reg) |
| { |
| return readl(reg); |
| } |
| |
| static void dsi_write(unsigned int reg, unsigned int value) |
| { |
| writel(value, reg); |
| } |
| |
| static unsigned int dsi_read(unsigned int reg) |
| { |
| return readl(reg); |
| } |
| |
| #if 0 |
| static void dsi_set_bits(unsigned int bits, unsigned int reg) |
| { |
| dsi_write(reg, dsi_read(reg) | bits); |
| } |
| #endif |
| |
| static void dsi_clear_bits(unsigned int bits, unsigned int reg) |
| { |
| dsi_write(reg, dsi_read(reg) & ~bits); |
| } |
| |
| static unsigned char dsi_bit(unsigned int index, unsigned char *pdata) |
| { |
| unsigned char ret; |
| unsigned int cindex, bindex; |
| |
| cindex = index / 8; |
| bindex = index % 8; |
| |
| if (pdata[cindex] & (0x1 << bindex)) { |
| ret = (unsigned char) 0x1; |
| } |
| else { |
| ret = (unsigned char) 0x0; |
| } |
| |
| return ret; |
| } |
| |
| static unsigned char calculate_ecc(unsigned char *pdata) |
| { |
| unsigned char ret; |
| unsigned char p[8]; |
| |
| p[7] = (unsigned char) 0x0; |
| p[6] = (unsigned char) 0x0; |
| |
| p[5] = (unsigned char) ( |
| ( |
| dsi_bit(10, pdata) ^ |
| dsi_bit(11, pdata) ^ |
| dsi_bit(12, pdata) ^ |
| dsi_bit(13, pdata) ^ |
| dsi_bit(14, pdata) ^ |
| dsi_bit(15, pdata) ^ |
| dsi_bit(16, pdata) ^ |
| dsi_bit(17, pdata) ^ |
| dsi_bit(18, pdata) ^ |
| dsi_bit(19, pdata) ^ |
| dsi_bit(21, pdata) ^ |
| dsi_bit(22, pdata) ^ |
| dsi_bit(23, pdata) |
| ) |
| ); |
| p[4] = (unsigned char) ( |
| dsi_bit(4, pdata) ^ |
| dsi_bit(5, pdata) ^ |
| dsi_bit(6, pdata) ^ |
| dsi_bit(7, pdata) ^ |
| dsi_bit(8, pdata) ^ |
| dsi_bit(9, pdata) ^ |
| dsi_bit(16, pdata) ^ |
| dsi_bit(17, pdata) ^ |
| dsi_bit(18, pdata) ^ |
| dsi_bit(19, pdata) ^ |
| dsi_bit(20, pdata) ^ |
| dsi_bit(22, pdata) ^ |
| dsi_bit(23, pdata) |
| ); |
| p[3] = (unsigned char) ( |
| ( |
| dsi_bit(1, pdata) ^ |
| dsi_bit(2, pdata) ^ |
| dsi_bit(3, pdata) ^ |
| dsi_bit(7, pdata) ^ |
| dsi_bit(8, pdata) ^ |
| dsi_bit(9, pdata) ^ |
| dsi_bit(13, pdata) ^ |
| dsi_bit(14, pdata) ^ |
| dsi_bit(15, pdata) ^ |
| dsi_bit(19, pdata) ^ |
| dsi_bit(20, pdata) ^ |
| dsi_bit(21, pdata) ^ |
| dsi_bit(23, pdata) |
| ) |
| ); |
| p[2] = (unsigned char) ( |
| ( |
| dsi_bit(0, pdata) ^ |
| dsi_bit(2, pdata) ^ |
| dsi_bit(3, pdata) ^ |
| dsi_bit(5, pdata) ^ |
| dsi_bit(6, pdata) ^ |
| dsi_bit(9, pdata) ^ |
| dsi_bit(11, pdata) ^ |
| dsi_bit(12, pdata) ^ |
| dsi_bit(15, pdata) ^ |
| dsi_bit(18, pdata) ^ |
| dsi_bit(20, pdata) ^ |
| dsi_bit(21, pdata) ^ |
| dsi_bit(22, pdata) |
| ) |
| ); |
| p[1] = (unsigned char) ( |
| ( |
| dsi_bit(0, pdata) ^ |
| dsi_bit(1, pdata) ^ |
| dsi_bit(3, pdata) ^ |
| dsi_bit(4, pdata) ^ |
| dsi_bit(6, pdata) ^ |
| dsi_bit(8, pdata) ^ |
| dsi_bit(10, pdata) ^ |
| dsi_bit(12, pdata) ^ |
| dsi_bit(14, pdata) ^ |
| dsi_bit(17, pdata) ^ |
| dsi_bit(20, pdata) ^ |
| dsi_bit(21, pdata) ^ |
| dsi_bit(22, pdata) ^ |
| dsi_bit(23, pdata) |
| ) |
| ); |
| p[0] = (unsigned char) ( |
| ( |
| dsi_bit(0, pdata) ^ |
| dsi_bit(1, pdata) ^ |
| dsi_bit(2, pdata) ^ |
| dsi_bit(4, pdata) ^ |
| dsi_bit(5, pdata) ^ |
| dsi_bit(7, pdata) ^ |
| dsi_bit(10, pdata) ^ |
| dsi_bit(11, pdata) ^ |
| dsi_bit(13, pdata) ^ |
| dsi_bit(16, pdata) ^ |
| dsi_bit(20, pdata) ^ |
| dsi_bit(21, pdata) ^ |
| dsi_bit(22, pdata) ^ |
| dsi_bit(23, pdata) |
| ) |
| ); |
| ret = (unsigned char)( |
| p[0] | |
| (p[1] << 0x1) | |
| (p[2] << 0x2) | |
| (p[3] << 0x3) | |
| (p[4] << 0x4) | |
| (p[5] << 0x5) |
| ); |
| return ret; |
| } |
| |
| static unsigned short gs_crc16_generation_code = 0x8408; |
| static unsigned short calculate_crc16(unsigned char *pdata, unsigned |
| short count) |
| { |
| unsigned short byte_counter; |
| unsigned char bit_counter; |
| unsigned char data; |
| unsigned short crc16_result = 0xFFFF; |
| |
| if (count > 0) { |
| for (byte_counter = 0; byte_counter < count; |
| byte_counter++) { |
| data = *(pdata + byte_counter); |
| for (bit_counter = 0; bit_counter < 8; bit_counter++) { |
| if (((crc16_result & 0x0001) ^ ((0x0001 * |
| data) & 0x0001)) > 0) { |
| crc16_result = ((crc16_result >> 1) |
| & 0x7FFF) ^ gs_crc16_generation_code; |
| } |
| else { |
| crc16_result = (crc16_result >> 1) |
| & 0x7FFF; |
| } |
| data = (data >> 1) & 0x7F; |
| } |
| } |
| } |
| return crc16_result; |
| } |
| |
| static void dsi_set_ctrl0(struct dsi_regs *dsi, |
| unsigned int mask, unsigned int set) |
| { |
| unsigned int tmp; |
| |
| tmp = dsi_read((unsigned int)&dsi->ctrl0); |
| tmp &= ~mask; |
| tmp |= set; |
| dsi_write((unsigned int)&dsi->ctrl0, tmp); |
| } |
| |
| static void dsi_set_dphy_ctrl1(struct dsi_regs *dsi, unsigned int mask, unsigned int set) |
| { |
| #ifdef DSI_GEN4 |
| void *phy_ctrl1_reg = (void *)&dsi->phy.phy_ctrl1; |
| #else |
| void *phy_ctrl1_reg = &dsi->phy_ctrl1; |
| #endif |
| unsigned int tmp; |
| |
| tmp = dphy_read((unsigned int)phy_ctrl1_reg); |
| tmp &= ~mask; |
| tmp |= set; |
| dphy_write((unsigned int)phy_ctrl1_reg, tmp); |
| } |
| |
| static void dsi_lanes_enable(struct dsi_regs *dsi, int en, int lane_num) |
| { |
| #ifdef DSI_GEN4 |
| volatile unsigned int phy_ctrl2_reg = (unsigned int)&dsi->phy.phy_ctrl2; |
| #else |
| volatile unsigned int phy_ctrl2_reg = (unsigned int)&dsi->phy_ctrl2; |
| #endif |
| unsigned int reg1 = dphy_read(phy_ctrl2_reg) & |
| (~DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_MASK); |
| unsigned int reg2 = dsi_read((unsigned int)&dsi->cmd1) & |
| ~(0xf << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT); |
| |
| if (en) { |
| reg1 |= (dsi_lane[lane_num] << |
| DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT); |
| reg2 |= (dsi_lane[lane_num] << |
| DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT); |
| } |
| |
| reg1 |= BIT_14; |
| dphy_write((unsigned int)phy_ctrl2_reg, reg1); |
| dsi_write((unsigned int)&dsi->cmd1, reg2); |
| } |
| |
| static void dsi_set_dphy(struct dsi_regs *dsi, struct mipi_info *mipi_info) |
| { |
| unsigned int bitclk; |
| int ui, lpx_clk, lpx_time, ta_get, ta_go, wakeup, reg; |
| int hs_prep, hs_zero, hs_trail, hs_exit, ck_zero, ck_trail, ck_exit; |
| |
| void *phy_timing0_reg, *phy_timing1_reg, *phy_timing2_reg, *phy_timing3_reg; |
| |
| bitclk = mipi_info->phy_feq / 1000000; /*MHz*/ |
| ui = 1000 / bitclk + 1; |
| |
| lpx_clk = (phy.lpx_constant + phy.lpx_ui * ui) / DSI_ESC_CLK_T + 1; |
| lpx_time = lpx_clk * DSI_ESC_CLK_T; |
| |
| /* Below is for NT35451 */ |
| ta_get = lpx_time * 5 / DSI_ESC_CLK_T - 1; |
| ta_go = lpx_time * 4 / DSI_ESC_CLK_T - 1; |
| |
| wakeup = phy.wakeup_constant; |
| wakeup = wakeup / DSI_ESC_CLK_T + 1; |
| |
| hs_prep = phy.hs_prep_constant + phy.hs_prep_ui * ui; |
| #if 0 |
| hs_prep = hs_prep / DSI_ESC_CLK_T + 1; |
| #else |
| hs_prep = hs_prep / DSI_ESC_CLK_T; |
| // if(hs_prep >= 1) |
| // hs_prep = hs_prep -1; |
| #endif |
| /* Our hardware added 3-byte clk automatically. |
| * 3-byte 3 * 8 * ui. |
| */ |
| hs_zero = phy.hs_zero_constant + phy.hs_zero_ui * ui - |
| (hs_prep + 1) * DSI_ESC_CLK_T; |
| hs_zero = (hs_zero - (3 * ui << 3)) / DSI_ESC_CLK_T + 4; |
| if (hs_zero < 0) { |
| hs_zero = 0; |
| } |
| |
| hs_trail = phy.hs_trail_constant + phy.hs_trail_ui * ui; |
| hs_trail = ((8 * ui) >= hs_trail) ? (8 * ui) : hs_trail; |
| hs_trail = hs_trail / DSI_ESC_CLK_T + 1; |
| if (hs_trail > 3) { |
| hs_trail -= 3; |
| } |
| else { |
| hs_trail = 0; |
| } |
| |
| hs_exit = phy.hs_exit_constant + phy.hs_exit_ui * ui; |
| hs_exit = hs_exit / DSI_ESC_CLK_T + 1; |
| |
| ck_zero = phy.ck_zero_constant + phy.ck_zero_ui * ui - |
| (hs_prep + 1) * DSI_ESC_CLK_T; |
| ck_zero = ck_zero / DSI_ESC_CLK_T + 1; |
| |
| ck_trail = phy.ck_trail_constant + phy.ck_trail_ui * ui; |
| ck_trail = ck_trail / DSI_ESC_CLK_T + 1; |
| |
| ck_exit = hs_exit; |
| |
| /* bandgap ref enabled by default */ |
| #ifndef DSI_GEN4 |
| // reg = BU_REG_READ(&dsi->phy_rcomp0); |
| // reg |= DPHY_BG_VREF_EN; |
| |
| phy_timing0_reg = &dsi->phy_timing0; |
| phy_timing1_reg = &dsi->phy_timing1; |
| phy_timing2_reg = &dsi->phy_timing2; |
| phy_timing3_reg = &dsi->phy_timing3; |
| // BU_REG_WRITE(&dsi->phy_rcomp0, reg); |
| #else |
| // reg = BU_REG_READ(&dsi->phy.phy_rcomp0); |
| // reg |= DPHY_BG_VREF_EN_DC4; |
| |
| phy_timing0_reg = (void *)&dsi->phy.phy_timing0; |
| phy_timing1_reg = (void *)&dsi->phy.phy_timing1; |
| phy_timing2_reg = (void *)&dsi->phy.phy_timing2; |
| phy_timing3_reg = (void *)&dsi->phy.phy_timing3; |
| // BU_REG_WRITE(&dsi->phy.phy_rcomp0, reg); |
| #endif |
| |
| /* timing_0 */ |
| reg = (hs_exit << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_SHIFT) |
| | (hs_trail << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_SHIFT) |
| | (hs_zero << DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_SHIFT) |
| | (hs_prep); |
| dphy_write((unsigned)phy_timing0_reg, reg); |
| |
| reg = (ta_get << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_SHIFT) |
| | (ta_go << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_SHIFT) |
| | wakeup; |
| dphy_write((unsigned)phy_timing1_reg, reg); |
| |
| reg = (ck_exit << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_SHIFT) |
| | (ck_trail << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_SHIFT) |
| | (ck_zero << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_SHIFT) |
| | lpx_clk; |
| dphy_write((unsigned)phy_timing2_reg, reg); |
| |
| reg = (lpx_clk << DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_SHIFT) |
| | phy.req_ready; |
| dphy_write((unsigned)phy_timing3_reg, reg); |
| /* calculated timing on brownstone: |
| * DSI_PHY_TIME_0 0x06080204 |
| * DSI_PHY_TIME_1 0x6d2bfff0 |
| * DSI_PHY_TIME_2 0x603130a |
| * DSI_PHY_TIME_3 0xa3c |
| */ |
| } |
| |
| static void dphy_config(struct dsi_regs *dsi, struct panel_spec *p_panel) |
| { |
| int reg_value; |
| |
| /*clk src select*/ |
| /*use pll5 clk src*/ |
| reg_value = dphy_read((unsigned int)&dsi->phy.phy_ana_ctrl1); |
| reg_value &= ~(BIT_23); |
| reg_value |= BIT_11; |
| |
| //zhimi add 0227 for cranegt |
| dphy_write((unsigned int)&dsi->phy.phy_ana_pwr_ctrl, 0x100); |
| dphy_write((unsigned int)&dsi->phy.phy_ana_pwr_ctrl, 0x101); |
| mdelay(10); |
| dphy_write((unsigned int)&dsi->phy.phy_ana_ctrl1, 0x3ea); |
| |
| /* digital and analog power on */ |
| dsi_set_dphy_ctrl1(dsi, 0x0, DPHY_CFG_VDD_VALID); |
| |
| /* turn on DSI continuous clock for HS */ |
| dsi_set_dphy_ctrl1(dsi, 0x0, 0x1); |
| |
| /* set dphy */ |
| dsi_set_dphy(dsi, p_panel->info.mipi); |
| |
| /* enable data lanes */ |
| dsi_lanes_enable(dsi, 0, p_panel->info.mipi->lan_number); |
| dsi_lanes_enable(dsi, 1, p_panel->info.mipi->lan_number); |
| } |
| |
| static void dsi_reset(struct dsi_regs *dsi, int hold) |
| { |
| /* software reset DSI module */ |
| dsi_set_ctrl0(dsi, 0, DSI_CTRL_0_CFG_SOFT_RST); |
| /* Note: there need some delay after set DSI_CTRL_0_CFG_SOFT_RST */ |
| udelay(1000); |
| dsi_set_ctrl0(dsi, (unsigned int)DSI_CTRL_0_CFG_LCD1_EN, 0); |
| |
| if (!hold) { |
| dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_SOFT_RST, 0); |
| udelay(1000); |
| dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_SOFT_RST_REG, 0); |
| } |
| } |
| |
| static void dsi_set_video_panel(struct dsi_regs *dsi, struct panel_spec *p_panel) |
| { |
| struct dsi_lcd_regs *dsi_lcd = &dsi->lcd1; |
| unsigned int hsync_b, hbp_b, hact_b, hex_b, hfp_b, httl_b; |
| unsigned int hsync, hbp, hact, httl, v_total; |
| unsigned int hsa_wc, hbp_wc, hact_wc, hex_wc, hfp_wc, hlp_wc; |
| unsigned int bpp, hss_bcnt = 4, hse_bct = 4, lgp_over_head = 6, reg; |
| unsigned int slot_cnt0, slot_cnt1; |
| union dsi_lcd_ctrl1 lcd_ctrl1; |
| struct mipi_info *mipi = p_panel->info.mipi; |
| struct timing_rgb *timing = mipi->timing; |
| |
| #ifdef DSI_GEN4 |
| void *phy_ctrl2_reg = (void *)&dsi->phy.phy_ctrl2; |
| #else |
| void *phy_ctrl2_reg = &dsi->phy_ctrl2; |
| #endif |
| unsigned int width = 0; |
| |
| if (p_panel->info.mipi->dsi_number == 2) { |
| width = p_panel->width / 2; |
| } |
| else { |
| width = p_panel->width; |
| } |
| |
| bpp = mipi->video_bus_width; |
| |
| v_total = p_panel->height + timing->vfp + timing->vbp + timing->vsync; |
| |
| hact_b = to_dsi_bcnt(width, bpp); |
| hfp_b = to_dsi_bcnt(timing->hfp, bpp); |
| hbp_b = to_dsi_bcnt(timing->hbp, bpp); |
| hsync_b = to_dsi_bcnt(timing->hsync, bpp); |
| hex_b = to_dsi_bcnt(dsi_ex_pixel_cnt, bpp); |
| httl_b = hact_b + hsync_b + hfp_b + hbp_b + hex_b; |
| slot_cnt0 = (httl_b - hact_b) / mipi->lan_number + 3; |
| slot_cnt1 = slot_cnt0; |
| |
| hact = hact_b / mipi->lan_number; |
| hbp = hbp_b / mipi->lan_number; |
| hsync = hsync_b / mipi->lan_number; |
| httl = (hact_b + hfp_b + hbp_b + hsync_b) / mipi->lan_number; |
| |
| /* word count in the unit of byte */ |
| hsa_wc = (mipi->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? |
| (hsync_b - hss_bcnt - lgp_over_head) : 0; |
| |
| /* Hse is with backporch */ |
| hbp_wc = (mipi->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? |
| (hbp_b - hse_bct - lgp_over_head) |
| : (hsync_b + hbp_b - hss_bcnt - lgp_over_head); |
| |
| hfp_wc = ((mipi->burst_mode == DSI_BURST_MODE_BURST) && (dsi_hex_en == 0)) ? |
| (hfp_b + hex_b - lgp_over_head - lgp_over_head) : |
| (hfp_b - lgp_over_head - lgp_over_head); |
| |
| hact_wc = ((width) * bpp) >> 3; |
| |
| /* disable Hex currently */ |
| hex_wc = 0; |
| |
| /* There is no hlp with active data segment. */ |
| hlp_wc = (mipi->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? |
| (httl_b - hsync_b - hse_bct - lgp_over_head) : |
| (httl_b - hss_bcnt - lgp_over_head); |
| |
| /* FIXME - need to double check the (*3) is bytes_per_pixel |
| * from input data or output to panel |
| */ |
| /* dsi_lane_enable - Set according to specified DSI lane count */ |
| dphy_write((unsigned int)phy_ctrl2_reg, dsi_lane[mipi->lan_number] << DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT); |
| dsi_write((unsigned int)&dsi->cmd1, dsi_lane[mipi->lan_number] << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT); |
| dsi_write((unsigned int)&dsi_lcd->ctrl0, (0x50 << 16) | 0xc08); |
| |
| /* SET UP LCD1 TIMING REGISTERS FOR DSI BUS */ |
| /* NOTE: Some register values were obtained by trial and error */ |
| dsi_write((unsigned int)&dsi_lcd->timing0, (hact << 16) | httl); |
| dsi_write((unsigned int)&dsi_lcd->timing1, (hsync << 16) | hbp); |
| /* |
| * For now the active size is set really low (we'll use 10) to allow |
| * the hardware to attain V Sync. Once the DSI bus is up and running, |
| * the final value will be put in place for the active size (this is |
| * done below). In a later stepping of the processor this requirement |
| * will not be required. |
| */ |
| dsi_write((unsigned int)&dsi_lcd->timing2, ((p_panel->height) << 16) | (v_total)); |
| dsi_write((unsigned int)&dsi_lcd->timing3, ((timing->vsync) << 16) | (timing->vbp)); |
| |
| /* SET UP LCD1 WORD COUNT REGISTERS FOR DSI BUS */ |
| /* Set up for word(byte) count register 0 */ |
| dsi_write((unsigned int)&dsi_lcd->wc0, (hbp_wc << 16) | hsa_wc); |
| dsi_write((unsigned int)&dsi_lcd->wc1, (hfp_wc << 16) | hact_wc); |
| dsi_write((unsigned int)&dsi_lcd->wc2, (hex_wc << 16) | hlp_wc); |
| |
| dsi_write((unsigned int)&dsi_lcd->slot_cnt0, (slot_cnt0 << 16) | slot_cnt0); |
| dsi_write((unsigned int)&dsi_lcd->slot_cnt1, (slot_cnt1 << 16) | slot_cnt1); |
| |
| /* Configure LCD control register 1 FOR DSI BUS */ |
| lcd_ctrl1.val = dsi_read((unsigned int)&dsi_lcd->ctrl1); |
| lcd_ctrl1.bit.input_fmt = mipi->rgb_mode; |
| lcd_ctrl1.bit.burst_mode = mipi->burst_mode; |
| lcd_ctrl1.bit.lpm_frame_en = mipi->lpm_line_en; |
| lcd_ctrl1.bit.last_line_turn = mipi->last_line_turn; |
| lcd_ctrl1.bit.hex_slot_en = 0; |
| lcd_ctrl1.bit.hsa_pkt_en = mipi->hsa_en; |
| lcd_ctrl1.bit.hse_pkt_en = mipi->hse_en; |
| if (mipi->burst_mode == DSI_BURST_MODE_SYNC_PULSE) { |
| lcd_ctrl1.bit.hse_pkt_en = 1; |
| } |
| else { |
| lcd_ctrl1.bit.hsa_pkt_en = 0; |
| } |
| lcd_ctrl1.bit.hbp_pkt_en = mipi->hbp_en; |
| lcd_ctrl1.bit.hfp_pkt_en = mipi->hfp_en; |
| lcd_ctrl1.bit.hex_pkt_en = 0; |
| lcd_ctrl1.bit.hlp_pkt_en = mipi->hlp_en; |
| lcd_ctrl1.bit.vsync_rst_en = 1; //Jessica: 1 |
| #ifndef DSI_GEN4 |
| /* These bits were removed for DC4 */ |
| lcd_ctrl1.bit.lpm_line_en = mipi->lpm_line_en; |
| lcd_ctrl1.bit.hact_pkt_en = mipi->hact_en; |
| lcd_ctrl1.bit.m2k_en = 0; |
| lcd_ctrl1.bit.all_slot_en = 0; |
| #else |
| /* |
| * FIXME: some extend features in DC4, default enable |
| * 1. word count auto calculation. |
| * 2. check timing before request DPHY for TX. |
| * 3. auto vsync delay count calculation. |
| */ |
| lcd_ctrl1.bit.auto_wc_dis = 0; |
| lcd_ctrl1.bit.hact_wc_en = 0; |
| lcd_ctrl1.bit.timing_check_dis = 1; //Jessica: 0 |
| lcd_ctrl1.bit.auto_dly_dis = 1; |
| #endif |
| dsi_write((unsigned int)&dsi_lcd->ctrl1, lcd_ctrl1.val); |
| |
| /*Start the transfer of LCD data over the DSI bus*/ |
| /* DSI_CTRL_1 */ |
| reg = dsi_read((unsigned int)&dsi->ctrl1); |
| #if 0 /*Jessica*/ |
| reg &= ~(DSI_CTRL_1_CFG_LCD2_VCH_NO_MASK |
| | DSI_CTRL_1_CFG_LCD1_VCH_NO_MASK); |
| reg |= 0x1 << DSI_CTRL_1_CFG_LCD1_VCH_NO_SHIFT; |
| #endif |
| |
| reg &= ~(DSI_CTRL_1_CFG_EOTP); |
| if (mipi->eotp_en) { |
| reg |= DSI_CTRL_1_CFG_EOTP; /* EOTP */ |
| |
| } |
| dsi_write((unsigned int)&dsi->ctrl1, reg); |
| /* DSI_CTRL_0 */ |
| reg = (mipi->master_mode ? 0 : DSI_CTRL_0_CFG_LCD1_SLV) | |
| DSI_CTRL_0_CFG_LCD1_TX_EN | DSI_CTRL_0_CFG_LCD1_EN; |
| dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_LCD1_SLV, reg); |
| udelay(100000); |
| |
| lcd_clear_bits(LCD_SMPN_CTRL, BIT_0); |
| // lcd_set_bits(LCD_DUMB_CTRL, BIT_0); |
| lcd_set_bits(LCD_MISC_CNTL, BIT_1); //should be set, otherwise color will be error |
| } |
| |
| static void dsi_set_cmd_panel(struct dsi_regs *dsi, struct panel_spec *p_panel) |
| { |
| int cfg_cpn_te_en = 0x0; |
| int cfg_cpn_dma_dis = 0; |
| int cfg_smt_wr_cycle = 0x0; //pixel write cycle = cfg_smt_wr_cycle +1 |
| int cfg_cpn_addr0_en = 0; //fix to 0 |
| int cfg_cpn_burst_mode; //0x1: new smart panel if |
| //0x0: old smart panel if |
| //configure it to 0x1 when cfg_smt_wr_cycle = 0x0, or else configure it to 1. |
| //int cfg_cpn_2cyc_en = 0; |
| int cfg_cpn_rgb_type; |
| //0x0: 888 |
| //0x1: 666 unpack |
| //0x2: 565: G3B5R5G3?? |
| //0x3:444 |
| //0x4:332 |
| //0x5:111 |
| //0x6: 565: R5G6B5 |
| int dsi_cpn_cmd; |
| |
| int cfg_cpn_fifo_full_level = 0x302; |
| int cfg_cpn_pkt_len = p_panel->width * p_panel->info.mipi->video_bus_width / 8 + 1; |
| int dsi_cpn_ctrl_1 = (cfg_cpn_pkt_len << 16) | |
| (cfg_cpn_fifo_full_level); |
| |
| lcd_write_bits(LCD_SMPN_CTRL, 0, MASK4, BIT_20); //cfg_smt_wr_cycle = 0; |
| cfg_cpn_burst_mode = (cfg_smt_wr_cycle == 0) ? 1 : 0; |
| switch (p_panel->info.mipi->rgb_mode) { |
| case DSI_LCD_INPUT_DATA_RGB_MODE_565: |
| cfg_cpn_rgb_type = 0x6; |
| break; |
| case DSI_LCD_INPUT_DATA_RGB_MODE_666UNPACKET: |
| cfg_cpn_rgb_type = 0x1; |
| break; |
| case DSI_LCD_INPUT_DATA_RGB_MODE_888: |
| cfg_cpn_rgb_type = 0x0; |
| break; |
| default: |
| printf("%s: Invalid mipi format, use DSI_LCD_INPUT_DATA_RGB_MODE_888 as default!\n", __func__); |
| cfg_cpn_rgb_type = 0x0; |
| } |
| |
| dsi_cpn_cmd = (cfg_cpn_te_en << 28) | |
| (cfg_cpn_rgb_type << 24) | |
| (cfg_cpn_burst_mode << 3) | |
| (cfg_cpn_dma_dis << 1) | |
| (cfg_cpn_addr0_en); |
| |
| dsi_clear_bits(BIT_6 | BIT_7, (unsigned int)&dsi->ctrl1); |
| dsi_write((unsigned int)&dsi->smt_cmd, dsi_cpn_cmd); |
| dsi_write((unsigned int)&dsi->smt_ctrl1, dsi_cpn_ctrl_1); |
| dsi_write((unsigned int)&dsi->lcd_bdg_ctrl0, 0x0); |
| dsi_write((unsigned int)&dsi->lcd_bdg_ctrl1, 0x800000); |
| dsi_set_ctrl0(dsi, 0, DSI_CTRL_0_CFG_SMT_EN | DSI_CTRL_0_CFG_LCD1_TX_EN | DSI_CTRL_0_CFG_LCD1_SLV); |
| |
| lcd_set_bits(LCD_SMPN_CTRL, BIT_0); |
| lcd_clear_bits(LCD_DUMB_CTRL, BIT_0); |
| lcd_set_bits(LCD_MISC_CNTL, BIT_1); //should be set, otherwise color will be error |
| } |
| |
| static void dsi_send_cmds(unsigned char *parameter, unsigned char count, unsigned char lp, u32 dsi_base) |
| { |
| struct dsi_regs *dsi = (struct dsi_regs *)(dsi_base); |
| unsigned int send_data = 0, waddr, timeout, tmp, i, turnaround; |
| unsigned int len; |
| |
| /* write all packet bytes to packet data buffer */ |
| for (i = 0; i < count; i++) { |
| send_data |= parameter[i] << ((i % 4) * 8); |
| if (!((i + 1) % 4)) { |
| dsi_write((unsigned int)&dsi->dat0, send_data); |
| waddr = DSI_CFG_CPU_DAT_REQ_MASK | |
| DSI_CFG_CPU_DAT_RW_MASK | |
| ((i - 3) << DSI_CFG_CPU_DAT_ADDR_SHIFT); |
| dsi_write((unsigned int)&dsi->cmd3, waddr); |
| /* wait write operation done */ |
| timeout = 1000; |
| do { |
| timeout--; |
| tmp = dsi_read((unsigned int)&dsi->cmd3); |
| } while ((tmp & DSI_CFG_CPU_DAT_REQ_MASK) && timeout); |
| if (!timeout) |
| printf("%s %d: DSI write data to the packet data buffer not done.\n", __func__, __LINE__); |
| send_data = 0; |
| } |
| } |
| |
| /* handle last none 4Byte align data */ |
| if (i % 4) { |
| dsi_write((unsigned int)&dsi->dat0, send_data); |
| waddr = DSI_CFG_CPU_DAT_REQ_MASK | |
| DSI_CFG_CPU_DAT_RW_MASK | |
| ((4 * (i / 4)) << DSI_CFG_CPU_DAT_ADDR_SHIFT); |
| dsi_write((unsigned int)&dsi->cmd3, waddr); |
| /* wait write operation done */ |
| timeout = 1000; |
| do { |
| timeout--; |
| tmp = dsi_read((unsigned int)&dsi->cmd3); |
| } while ((tmp & DSI_CFG_CPU_DAT_REQ_MASK) && timeout); |
| if (!timeout) |
| printf("%s %d: DSI write data to the packet data buffer not done.\n", __func__, __LINE__); |
| send_data = 0; |
| } |
| |
| if (parameter[0] == DSI_DI_DCS_READ || |
| parameter[0] == DSI_DI_GENERIC_READ1) { |
| turnaround = 0x1; |
| } |
| else { |
| turnaround = 0x0; |
| } |
| |
| len = count; |
| |
| #ifndef DSI_GEN4 |
| if ((parameter[0] == DSI_DI_DCS_LWRITE || |
| parameter[0] == DSI_DI_GENERIC_LWRITE) && !lp) { |
| len = count - 6; |
| } |
| #endif |
| |
| waddr = DSI_CFG_CPU_CMD_REQ_MASK | |
| ((count == 4) ? DSI_CFG_CPU_SP_MASK : 0) | |
| (turnaround << DSI_CFG_CPU_TURN_SHIFT) | |
| (lp ? DSI_CFG_CPU_TXLP_MASK : 0) | |
| (len << DSI_CFG_CPU_WC_SHIFT); |
| |
| /* send out the packet */ |
| dsi_write((unsigned int)&dsi->cmd0, waddr); |
| /* wait packet be sent out */ |
| timeout = 1000; |
| do { |
| timeout--; |
| tmp = dsi_read((unsigned int)&dsi->cmd0); |
| if (0 == timeout % 50) |
| printf("%s: read cmd0 is 0x%x\n", __func__, tmp); |
| udelay(50); |
| } while ((tmp & DSI_CFG_CPU_CMD_REQ_MASK) && timeout); |
| if (!timeout) |
| printf("%s: DSI send out packet maybe failed. 0x%x \n", __func__, tmp); |
| |
| tmp = dsi_read((unsigned int)&dsi->irq_status); |
| #if 0 |
| printf("%s: DSI irq status: 0x%x \n", __func__, tmp); |
| #endif |
| } |
| |
| void asr_dsi_cmd_array_tx(struct dsi_cmd_desc cmds[], unsigned int count, struct mipi_info *mipi) |
| { |
| struct dsi_cmd_desc cmd_line; |
| unsigned char command, parameter[DSI_MAX_DATA_BYTES], len; |
| unsigned int crc, loop; |
| |
| for (loop = 0; loop < count; loop++) { |
| cmd_line = cmds[loop]; |
| command = cmd_line.data_type; |
| len = cmd_line.length; |
| memset(parameter, 0x00, len + 6); |
| parameter[0] = command & 0xff; |
| switch (command) { |
| case DSI_DI_DCS_SWRITE: |
| case DSI_DI_DCS_SWRITE1: |
| case DSI_DI_DCS_READ: |
| case DSI_DI_GENERIC_READ1: |
| case DSI_DI_SET_MAX_PKT_SIZE: |
| memcpy(¶meter[1], cmd_line.data, len); |
| len = 4; |
| break; |
| case DSI_DI_GENERIC_LWRITE: |
| case DSI_DI_DCS_LWRITE: |
| parameter[1] = len & 0xff; |
| parameter[2] = 0; |
| memcpy(¶meter[4], cmd_line.data, len); |
| crc = calculate_crc16(¶meter[4], len); |
| parameter[len + 4] = crc & 0xff; |
| parameter[len + 5] = (crc >> 8) & 0xff; |
| len += 6; |
| break; |
| default: |
| printf("%s: data type not supported 0x%8x\n", |
| __func__, command); |
| break; |
| } |
| |
| parameter[3] = calculate_ecc(parameter); |
| |
| /* support to send lower power mode command on multi lanes */ |
| #if (!defined(DSI_VERSION_24)) && (!defined(DSI_VERSION_14)) |
| unsigned char *temp; |
| if (cmd_line.lp) { |
| temp = malloc(sizeof(unsigned char) * DSI_MAX_DATA_BYTES |
| * mipi->lan_number); |
| if (temp == NULL) { |
| return; |
| } |
| for (int i = 0; i < len * mipi->lan_number; i += mipi->lan_number) { |
| for (int j = 0; j < mipi->lan_number; j++) { |
| temp[i + j] = parameter[i / mipi->lan_number]; |
| } |
| } |
| len = mipi->lan_number * len; |
| memcpy(parameter, temp, len); |
| free(temp); |
| } |
| #endif |
| (void)mipi; |
| /* send dsi commands */ |
| dsi_send_cmds(parameter, len, cmd_line.lp, mipi->dsi_base); |
| |
| if (cmd_line.delay) |
| udelay(cmd_line.delay * 1000); |
| } |
| } |
| |
| void asr_dsi_cmd_array_rx(struct dsi_buf *dbuf, struct dsi_cmd_desc cmds[], |
| unsigned int count, struct mipi_info *mipi) |
| { |
| struct dsi_regs *dsi = (struct dsi_regs *)(mipi->dsi_base); |
| |
| unsigned char parameter[DSI_DI_SET_MAX_PKT_SIZE]; |
| unsigned int i, rx_reg, timeout, tmp, packet, |
| data_pointer, byte_count; |
| |
| memset(dbuf, 0x0, sizeof(struct dsi_buf)); |
| asr_dsi_cmd_array_tx(cmds, count, mipi); |
| |
| timeout = 1000; |
| do { |
| timeout--; |
| tmp = dsi_read((unsigned int)&dsi->irq_status); |
| } while (((tmp & 0x4) == 0) && timeout); |
| if (!timeout) { |
| printf("%s: error: dsi didn't receive packet, irq status 0x%x\n", |
| __func__, dsi_read((unsigned int)&dsi->irq_status)); |
| return; |
| } |
| |
| if (tmp & IRQ_RX_TRG2) |
| printf("%s: ACK received\n", __func__); |
| if (tmp & IRQ_RX_TRG1) |
| printf("%s: TE trigger received\n", __func__); |
| if (tmp & IRQ_RX_ERR) { |
| printf("%s: error: ACK with error report\n", __func__); |
| tmp = dsi_read((unsigned int)&dsi->rx0_header); |
| } |
| |
| packet = dsi_read((unsigned int)&dsi->rx0_status); |
| data_pointer = (packet & RX_PKT0_PKT_PTR_MASK) >> RX_PKT0_PKT_PTR_SHIFT; |
| tmp = dsi_read((unsigned int)&dsi->rx_ctrl1); |
| byte_count = tmp & RX_PKT_BCNT_MASK; |
| |
| memset(parameter, 0x00, byte_count); |
| for (i = data_pointer; i < data_pointer + byte_count; i++) { |
| rx_reg = dsi_read((unsigned int)&dsi->rx_ctrl); |
| rx_reg &= ~RX_PKT_RD_PTR_MASK; |
| rx_reg |= RX_PKT_RD_REQ | (i << RX_PKT_RD_PTR_SHIFT); |
| dsi_write((unsigned int)&dsi->rx_ctrl, rx_reg); |
| count = 10000; |
| do { |
| count--; |
| rx_reg = dsi_read((unsigned int)&dsi->rx_ctrl); |
| } while (rx_reg & RX_PKT_RD_REQ && count); |
| if (!count) { |
| printf("%s: error: Rx packet FIFO error\n", __func__); |
| } |
| parameter[i - data_pointer] = rx_reg & 0xff; |
| } |
| switch (parameter[0]) { |
| case DSI_DI_ACK_ERR_RESP: |
| printf("%s: error: Acknowledge with error report\n", __func__); |
| break; |
| case DSI_DI_EOTP: |
| printf("%s: error: End of Transmission packet\n", __func__); |
| break; |
| case DSI_DI_GEN_READ1_RESP: |
| case DSI_DI_DCS_READ1_RESP: |
| dbuf->data_type = parameter[0]; |
| dbuf->length = 1; |
| memcpy(dbuf->data, ¶meter[1], dbuf->length); |
| break; |
| case DSI_DI_GEN_READ2_RESP: |
| case DSI_DI_DCS_READ2_RESP: |
| dbuf->data_type = parameter[0]; |
| dbuf->length = 2; |
| memcpy(dbuf->data, ¶meter[1], dbuf->length); |
| break; |
| case DSI_DI_GEN_LREAD_RESP: |
| case DSI_DI_DCS_LREAD_RESP: |
| dbuf->data_type = parameter[0]; |
| dbuf->length = (parameter[2] << 8) | parameter[1]; |
| memcpy(dbuf->data, ¶meter[4], dbuf->length); |
| printf("%s: read %d data: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", __func__, dbuf->length, |
| parameter[4], parameter[5], parameter[6], parameter[7], parameter[8]); |
| break; |
| } |
| } |
| |
| void asr_dsi_init(struct panel_spec *p_panel) |
| { |
| struct dsi_regs *dsi = (struct dsi_regs *)(p_panel->info.mipi->dsi_base); |
| |
| /* reset and de-assert DSI controller */ |
| dsi_reset(dsi, 0); |
| dphy_config(dsi, p_panel); |
| } |
| |
| int asr_dsi_ready(struct panel_spec *p_panel) |
| { |
| int reg_value; |
| struct dsi_regs *dsi = (struct dsi_regs *)(p_panel->info.mipi->dsi_base); |
| |
| if (p_panel->info.mipi->work_mode == MIPI_MODE_CMD) |
| dsi_set_cmd_panel(dsi, p_panel); |
| else |
| dsi_set_video_panel(dsi, p_panel); |
| |
| reg_value = dsi_read((unsigned int)&dsi->lcd_bdg_ctrl0); |
| reg_value = reg_value | BIT_6; |
| reg_value = reg_value | BIT_1; |
| dsi_write((unsigned int)&dsi->lcd_bdg_ctrl0, reg_value); |
| return 0; |
| } |
| |