/* * Copyright 2018 NXP * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #define MIPI_FIFO_TIMEOUT 250000 /* 250ms */ #define DRIVER_NAME "imx_sec_mipi_dsim" /* dsim registers */ #define DSIM_VERSION 0x00 #define DSIM_STATUS 0x04 #define DSIM_RGB_STATUS 0x08 #define DSIM_SWRST 0x0c #define DSIM_CLKCTRL 0x10 #define DSIM_TIMEOUT 0x14 #define DSIM_CONFIG 0x18 #define DSIM_ESCMODE 0x1c #define DSIM_MDRESOL 0x20 #define DSIM_MVPORCH 0x24 #define DSIM_MHPORCH 0x28 #define DSIM_MSYNC 0x2c #define DSIM_SDRESOL 0x30 #define DSIM_INTSRC 0x34 #define DSIM_INTMSK 0x38 /* packet */ #define DSIM_PKTHDR 0x3c #define DSIM_PAYLOAD 0x40 #define DSIM_RXFIFO 0x44 #define DSIM_FIFOTHLD 0x48 #define DSIM_FIFOCTRL 0x4c #define DSIM_MEMACCHR 0x50 #define DSIM_MULTI_PKT 0x78 /* pll control */ #define DSIM_PLLCTRL_1G 0x90 #define DSIM_PLLCTRL 0x94 #define DSIM_PLLCTRL1 0x98 #define DSIM_PLLCTRL2 0x9c #define DSIM_PLLTMR 0xa0 /* dphy */ #define DSIM_PHYTIMING 0xb4 #define DSIM_PHYTIMING1 0xb8 #define DSIM_PHYTIMING2 0xbc /* reg bit manipulation */ #define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s)) #define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s)) #define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s)) /* register bit fields */ #define STATUS_PLLSTABLE BIT(31) #define STATUS_SWRSTRLS BIT(20) #define STATUS_TXREADYHSCLK BIT(10) #define STATUS_ULPSCLK BIT(9) #define STATUS_STOPSTATECLK BIT(8) #define STATUS_GET_ULPSDAT(x) REG_GET(x, 7, 4) #define STATUS_GET_STOPSTATEDAT(x) REG_GET(x, 3, 0) #define RGB_STATUS_CMDMODE_INSEL BIT(31) #define RGB_STATUS_GET_RGBSTATE(x) REG_GET(x, 12, 0) #define CLKCTRL_TXREQUESTHSCLK BIT(31) #define CLKCTRL_DPHY_SEL_1G BIT(29) #define CLKCTRL_DPHY_SEL_1P5G (0x0 << 29) #define CLKCTRL_ESCCLKEN BIT(28) #define CLKCTRL_PLLBYPASS BIT(29) #define CLKCTRL_BYTECLKSRC_DPHY_PLL REG_PUT(0, 26, 25) #define CLKCTRL_BYTECLKEN BIT(24) #define CLKCTRL_SET_LANEESCCLKEN(x) REG_PUT(x, 23, 19) #define CLKCTRL_SET_ESCPRESCALER(x) REG_PUT(x, 15, 0) #define TIMEOUT_SET_BTAOUT(x) REG_PUT(x, 23, 16) #define TIMEOUT_SET_LPDRTOUT(x) REG_PUT(x, 15, 0) #define CONFIG_NON_CONTINOUS_CLOCK_LANE BIT(31) #define CONFIG_CLKLANE_STOP_START BIT(30) #define CONFIG_MFLUSH_VS BIT(29) #define CONFIG_EOT_R03 BIT(28) #define CONFIG_SYNCINFORM BIT(27) #define CONFIG_BURSTMODE BIT(26) #define CONFIG_VIDEOMODE BIT(25) #define CONFIG_AUTOMODE BIT(24) #define CONFIG_HSEDISABLEMODE BIT(23) #define CONFIG_HFPDISABLEMODE BIT(22) #define CONFIG_HBPDISABLEMODE BIT(21) #define CONFIG_HSADISABLEMODE BIT(20) #define CONFIG_SET_MAINVC(x) REG_PUT(x, 19, 18) #define CONFIG_SET_SUBVC(x) REG_PUT(x, 17, 16) #define CONFIG_SET_MAINPIXFORMAT(x) REG_PUT(x, 14, 12) #define CONFIG_SET_SUBPIXFORMAT(x) REG_PUT(x, 10, 8) #define CONFIG_SET_NUMOFDATLANE(x) REG_PUT(x, 6, 5) #define CONFIG_SET_LANEEN(x) REG_PUT(x, 4, 0) #define MDRESOL_MAINSTANDBY BIT(31) #define MDRESOL_SET_MAINVRESOL(x) REG_PUT(x, 27, 16) #define MDRESOL_SET_MAINHRESOL(x) REG_PUT(x, 11, 0) #define MVPORCH_SET_CMDALLOW(x) REG_PUT(x, 31, 28) #define MVPORCH_SET_STABLEVFP(x) REG_PUT(x, 26, 16) #define MVPORCH_SET_MAINVBP(x) REG_PUT(x, 10, 0) #define MHPORCH_SET_MAINHFP(x) REG_PUT(x, 31, 16) #define MHPORCH_SET_MAINHBP(x) REG_PUT(x, 15, 0) #define MSYNC_SET_MAINVSA(x) REG_PUT(x, 31, 22) #define MSYNC_SET_MAINHSA(x) REG_PUT(x, 15, 0) #define INTSRC_PLLSTABLE BIT(31) #define INTSRC_SWRSTRELEASE BIT(30) #define INTSRC_SFRPLFIFOEMPTY BIT(29) #define INTSRC_SFRPHFIFOEMPTY BIT(28) #define INTSRC_FRAMEDONE BIT(24) #define INTSRC_LPDRTOUT BIT(21) #define INTSRC_TATOUT BIT(20) #define INTSRC_RXDATDONE BIT(18) #define INTSRC_MASK (INTSRC_PLLSTABLE | \ INTSRC_SWRSTRELEASE | \ INTSRC_SFRPLFIFOEMPTY | \ INTSRC_SFRPHFIFOEMPTY | \ INTSRC_FRAMEDONE | \ INTSRC_LPDRTOUT | \ INTSRC_TATOUT | \ INTSRC_RXDATDONE) #define INTMSK_MSKPLLSTABLE BIT(31) #define INTMSK_MSKSWRELEASE BIT(30) #define INTMSK_MSKSFRPLFIFOEMPTY BIT(29) #define INTMSK_MSKSFRPHFIFOEMPTY BIT(28) #define INTMSK_MSKFRAMEDONE BIT(24) #define INTMSK_MSKLPDRTOUT BIT(21) #define INTMSK_MSKTATOUT BIT(20) #define INTMSK_MSKRXDATDONE BIT(18) #define PLLCTRL_DPDNSWAP_CLK BIT(25) #define PLLCTRL_DPDNSWAP_DAT BIT(24) #define PLLCTRL_PLLEN BIT(23) #define PLLCTRL_SET_PMS(x) REG_PUT(x, 19, 1) #define PHYTIMING_SET_M_TLPXCTL(x) REG_PUT(x, 15, 8) #define PHYTIMING_SET_M_THSEXITCTL(x) REG_PUT(x, 7, 0) #define PHYTIMING1_SET_M_TCLKPRPRCTL(x) REG_PUT(x, 31, 24) #define PHYTIMING1_SET_M_TCLKZEROCTL(x) REG_PUT(x, 23, 16) #define PHYTIMING1_SET_M_TCLKPOSTCTL(x) REG_PUT(x, 15, 8) #define PHYTIMING1_SET_M_TCLKTRAILCTL(x) REG_PUT(x, 7, 0) #define PHYTIMING2_SET_M_THSPRPRCTL(x) REG_PUT(x, 23, 16) #define PHYTIMING2_SET_M_THSZEROCTL(x) REG_PUT(x, 15, 8) #define PHYTIMING2_SET_M_THSTRAILCTL(x) REG_PUT(x, 7, 0) #define dsim_read(dsim, reg) readl(dsim->base + reg) #define dsim_write(dsim, val, reg) writel(val, dsim->base + reg) /* fixed phy ref clk rate */ #define PHY_REF_CLK 27000000 #define MAX_MAIN_HRESOL 2047 #define MAX_MAIN_VRESOL 2047 #define MAX_SUB_HRESOL 1024 #define MAX_SUB_VRESOL 1024 /* in KHZ */ #define MAX_ESC_CLK_FREQ 20000 /* dsim all irqs index */ #define PLLSTABLE 1 #define SWRSTRELEASE 2 #define SFRPLFIFOEMPTY 3 #define SFRPHFIFOEMPTY 4 #define SYNCOVERRIDE 5 #define BUSTURNOVER 6 #define FRAMEDONE 7 #define LPDRTOUT 8 #define TATOUT 9 #define RXDATDONE 10 #define RXTE 11 #define RXACK 12 #define ERRRXECC 13 #define ERRRXCRC 14 #define ERRESC3 15 #define ERRESC2 16 #define ERRESC1 17 #define ERRESC0 18 #define ERRSYNC3 19 #define ERRSYNC2 20 #define ERRSYNC1 21 #define ERRSYNC0 22 #define ERRCONTROL3 23 #define ERRCONTROL2 24 #define ERRCONTROL1 25 #define ERRCONTROL0 26 /* Dispmix Control & GPR Registers */ #define DISPLAY_MIX_SFT_RSTN_CSR 0x00 #ifdef CONFIG_IMX8MN #define MIPI_DSI_I_PRESETn_SFT_EN BIT(0) | BIT(1) #else #define MIPI_DSI_I_PRESETn_SFT_EN BIT(5) #endif #define DISPLAY_MIX_CLK_EN_CSR 0x04 #ifdef CONFIG_IMX8MN #define MIPI_DSI_PCLK_SFT_EN BIT(0) #define MIPI_DSI_CLKREF_SFT_EN BIT(1) #else #define MIPI_DSI_PCLK_SFT_EN BIT(8) #define MIPI_DSI_CLKREF_SFT_EN BIT(9) #endif #define GPR_MIPI_RESET_DIV 0x08 /* Clock & Data lanes reset: Active Low */ #define GPR_MIPI_S_RESETN BIT(16) #define GPR_MIPI_M_RESETN BIT(17) #define PS2KHZ(ps) (1000000000UL / (ps)) /* DSIM PLL configuration from spec: * * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S) * Fin_pll = Fin / P (6 ~ 12 MHz) * S: [2:0], M: [12:3], P: [18:13], so * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33] * */ struct sec_mipi_dsim { void __iomem *base; void __iomem *disp_mix_gpr_base; /* kHz clocks */ uint64_t pix_clk; uint64_t bit_clk; unsigned int lanes; unsigned int channel; /* virtual channel */ enum mipi_dsi_pixel_format format; unsigned long mode_flags; unsigned int pms; unsigned int p; unsigned int m; unsigned int s; struct fb_videomode vmode; const struct sec_mipi_dsim_plat_data *pdata; struct mipi_dsi_client_dev *dsi_panel_dev; struct mipi_dsi_client_driver *dsi_panel_drv; }; static void disp_mix_dsim_soft_reset_release(struct sec_mipi_dsim *dsim, bool release) { if (release) /* release dsi blk reset */ setbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_SFT_RSTN_CSR, MIPI_DSI_I_PRESETn_SFT_EN); else clrbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_SFT_RSTN_CSR, MIPI_DSI_I_PRESETn_SFT_EN); } static void disp_mix_dsim_clks_enable(struct sec_mipi_dsim *dsim, bool enable) { if (enable) setbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_CLK_EN_CSR, MIPI_DSI_PCLK_SFT_EN | MIPI_DSI_CLKREF_SFT_EN); else clrbits_le32(dsim->disp_mix_gpr_base + DISPLAY_MIX_CLK_EN_CSR, MIPI_DSI_PCLK_SFT_EN | MIPI_DSI_CLKREF_SFT_EN); } static void disp_mix_dsim_lanes_reset(struct sec_mipi_dsim *dsim, bool reset) { if (!reset) /* release lanes reset */ setbits_le32(dsim->disp_mix_gpr_base + GPR_MIPI_RESET_DIV, GPR_MIPI_M_RESETN); else /* reset lanes */ clrbits_le32(dsim->disp_mix_gpr_base + GPR_MIPI_RESET_DIV, GPR_MIPI_M_RESETN); } static void sec_mipi_dsim_wr_tx_header(struct sec_mipi_dsim *dsim, u8 di, u8 data0, u8 data1) { unsigned int reg; reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0); dsim_write(dsim, reg, DSIM_PKTHDR); } static void sec_mipi_dsim_wr_tx_data(struct sec_mipi_dsim *dsim, unsigned int tx_data) { dsim_write(dsim, tx_data, DSIM_PAYLOAD); } static void sec_mipi_dsim_long_data_wr(struct sec_mipi_dsim *dsim, const unsigned char *data0, unsigned int data_size) { unsigned int data_cnt = 0, payload = 0; /* in case that data count is more then 4 */ for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { /* * after sending 4bytes per one time, * send remainder data less then 4. */ if ((data_size - data_cnt) < 4) { if ((data_size - data_cnt) == 3) { payload = data0[data_cnt] | data0[data_cnt + 1] << 8 | data0[data_cnt + 2] << 16; debug("count = 3 payload = %x, %x %x %x\n", payload, data0[data_cnt], data0[data_cnt + 1], data0[data_cnt + 2]); } else if ((data_size - data_cnt) == 2) { payload = data0[data_cnt] | data0[data_cnt + 1] << 8; debug("count = 2 payload = %x, %x %x\n", payload, data0[data_cnt], data0[data_cnt + 1]); } else if ((data_size - data_cnt) == 1) { payload = data0[data_cnt]; } sec_mipi_dsim_wr_tx_data(dsim, payload); /* send 4bytes per one time. */ } else { payload = data0[data_cnt] | data0[data_cnt + 1] << 8 | data0[data_cnt + 2] << 16 | data0[data_cnt + 3] << 24; debug("count = 4 payload = %x, %x %x %x %x\n", payload, *(u8 *)(data0 + data_cnt), data0[data_cnt + 1], data0[data_cnt + 2], data0[data_cnt + 3]); sec_mipi_dsim_wr_tx_data(dsim, payload); } } } static int sec_mipi_dsim_wait_for_pkt_done(struct sec_mipi_dsim *dsim, unsigned long timeout) { uint32_t intsrc; do { intsrc = dsim_read(dsim, DSIM_INTSRC); if (intsrc & INTSRC_SFRPLFIFOEMPTY) { dsim_write(dsim, INTSRC_SFRPLFIFOEMPTY, DSIM_INTSRC); return 0; } udelay(1); } while (--timeout); return -ETIMEDOUT; } static int sec_mipi_dsim_pkt_write(struct sec_mipi_dsim *dsim, u8 data_type, const u8 *buf, int len) { int ret = 0; const unsigned char *data = (const unsigned char*)buf; if (len == 0) /* handle generic short write command */ sec_mipi_dsim_wr_tx_header(dsim, data_type, data[0], data[1]); else { /* handle generic long write command */ sec_mipi_dsim_long_data_wr(dsim, data, len); sec_mipi_dsim_wr_tx_header(dsim, data_type, len & 0xff, (len & 0xff00) >> 8); ret = sec_mipi_dsim_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT); if (ret) { printf("wait tx done timeout!\n"); return -ETIMEDOUT; } } mdelay(10); return 0; } static int sec_mipi_dsim_wait_pll_stable(struct sec_mipi_dsim *dsim) { uint32_t status; ulong start; start = get_timer(0); /* Get current timestamp */ do { status = dsim_read(dsim, DSIM_STATUS); if (status & STATUS_PLLSTABLE) return 0; } while (get_timer(0) < (start + 100)); /* Wait 100ms */ return -ETIMEDOUT; } static int sec_mipi_dsim_config_pll(struct sec_mipi_dsim *dsim) { int ret; uint32_t pllctrl = 0, status, data_lanes_en, stop; dsim_write(dsim, 0x8000, DSIM_PLLTMR); /* TODO: config dp/dn swap if requires */ pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN; dsim_write(dsim, pllctrl, DSIM_PLLCTRL); ret = sec_mipi_dsim_wait_pll_stable(dsim); if (ret) { printf("wait for pll stable time out\n"); return ret; } /* wait for clk & data lanes to go to stop state */ mdelay(1); data_lanes_en = (0x1 << dsim->lanes) - 1; status = dsim_read(dsim, DSIM_STATUS); if (!(status & STATUS_STOPSTATECLK)) { printf("clock is not in stop state\n"); return -EBUSY; } stop = STATUS_GET_STOPSTATEDAT(status); if ((stop & data_lanes_en) != data_lanes_en) { printf("one or more data lanes is not in stop state\n"); return -EBUSY; } return 0; } static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim) { uint32_t bpp, hfp_wc, hbp_wc, hsa_wc; uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0; struct fb_videomode *vmode = &dsim->vmode; mdresol |= MDRESOL_SET_MAINVRESOL(vmode->yres) | MDRESOL_SET_MAINHRESOL(vmode->xres); dsim_write(dsim, mdresol, DSIM_MDRESOL); mvporch |= MVPORCH_SET_MAINVBP(vmode->upper_margin) | MVPORCH_SET_STABLEVFP(vmode->lower_margin) | MVPORCH_SET_CMDALLOW(0x0); dsim_write(dsim, mvporch, DSIM_MVPORCH); bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); /* calculate hfp & hbp word counts */ if (dsim->dsi_panel_drv) { /* Panel driver is registered, will work with panel */ hfp_wc = vmode->right_margin * (bpp >> 3); hbp_wc = vmode->left_margin * (bpp >> 3); } else { hfp_wc = vmode->right_margin * (bpp >> 3) / dsim->lanes - 6; hbp_wc = vmode->left_margin * (bpp >> 3) / dsim->lanes - 6; } mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) | MHPORCH_SET_MAINHBP(hbp_wc); dsim_write(dsim, mhporch, DSIM_MHPORCH); /* calculate hsa word counts */ if (dsim->dsi_panel_drv) { hsa_wc = vmode->hsync_len * (bpp >> 3); } else { hsa_wc = vmode->hsync_len * (bpp >> 3) / dsim->lanes - 6; } msync |= MSYNC_SET_MAINVSA(vmode->vsync_len) | MSYNC_SET_MAINHSA(hsa_wc); debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc); dsim_write(dsim, msync, DSIM_MSYNC); } static void sec_mipi_dsim_config_dpi(struct sec_mipi_dsim *dsim) { uint32_t config = 0, rgb_status = 0, data_lanes_en; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) rgb_status &= ~RGB_STATUS_CMDMODE_INSEL; else rgb_status |= RGB_STATUS_CMDMODE_INSEL; dsim_write(dsim, rgb_status, DSIM_RGB_STATUS); if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) config |= CONFIG_CLKLANE_STOP_START; if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH) config |= CONFIG_MFLUSH_VS; /* disable EoT packets in HS mode */ if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET) config |= CONFIG_EOT_R03; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { config |= CONFIG_VIDEOMODE; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) config |= CONFIG_BURSTMODE; else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) config |= CONFIG_SYNCINFORM; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) config |= CONFIG_AUTOMODE; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) config |= CONFIG_HSEDISABLEMODE; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP) config |= CONFIG_HFPDISABLEMODE; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP) config |= CONFIG_HBPDISABLEMODE; if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA) config |= CONFIG_HSADISABLEMODE; } config |= CONFIG_SET_MAINVC(dsim->channel); if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { switch (dsim->format) { case MIPI_DSI_FMT_RGB565: config |= CONFIG_SET_MAINPIXFORMAT(0x4); break; case MIPI_DSI_FMT_RGB666_PACKED: config |= CONFIG_SET_MAINPIXFORMAT(0x5); break; case MIPI_DSI_FMT_RGB666: config |= CONFIG_SET_MAINPIXFORMAT(0x6); break; case MIPI_DSI_FMT_RGB888: config |= CONFIG_SET_MAINPIXFORMAT(0x7); break; default: config |= CONFIG_SET_MAINPIXFORMAT(0x7); break; } } /* config data lanes number and enable lanes */ data_lanes_en = (0x1 << dsim->lanes) - 1; config |= CONFIG_SET_NUMOFDATLANE(dsim->lanes - 1); config |= CONFIG_SET_LANEEN(0x1 | data_lanes_en << 1); dsim_write(dsim, config, DSIM_CONFIG); } static void sec_mipi_dsim_config_dphy(struct sec_mipi_dsim *dsim) { uint32_t phytiming = 0, phytiming1 = 0, phytiming2 = 0, timeout = 0; /* TODO: add a PHY timing table arranged by the pll Fout */ phytiming |= PHYTIMING_SET_M_TLPXCTL(6) | PHYTIMING_SET_M_THSEXITCTL(11); dsim_write(dsim, phytiming, DSIM_PHYTIMING); phytiming1 |= PHYTIMING1_SET_M_TCLKPRPRCTL(7) | PHYTIMING1_SET_M_TCLKZEROCTL(38) | PHYTIMING1_SET_M_TCLKPOSTCTL(13) | PHYTIMING1_SET_M_TCLKTRAILCTL(8); dsim_write(dsim, phytiming1, DSIM_PHYTIMING1); phytiming2 |= PHYTIMING2_SET_M_THSPRPRCTL(8) | PHYTIMING2_SET_M_THSZEROCTL(13) | PHYTIMING2_SET_M_THSTRAILCTL(11); dsim_write(dsim, phytiming2, DSIM_PHYTIMING2); timeout |= TIMEOUT_SET_BTAOUT(0xf) | TIMEOUT_SET_LPDRTOUT(0xf); dsim_write(dsim, 0xf000f, DSIM_TIMEOUT); } static void sec_mipi_dsim_config_clkctrl(struct sec_mipi_dsim *dsim) { uint32_t clkctrl = 0, data_lanes_en; uint64_t byte_clk, esc_prescaler; clkctrl |= CLKCTRL_TXREQUESTHSCLK; /* using 1.5Gbps PHY */ clkctrl |= CLKCTRL_DPHY_SEL_1P5G; clkctrl |= CLKCTRL_ESCCLKEN; clkctrl &= ~CLKCTRL_PLLBYPASS; clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL; clkctrl |= CLKCTRL_BYTECLKEN; data_lanes_en = (0x1 << dsim->lanes) - 1; clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1); /* calculate esc prescaler from byte clock: * EscClk = ByteClk / EscPrescaler; */ byte_clk = dsim->bit_clk >> 3; esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ); clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler); dsim_write(dsim, clkctrl, DSIM_CLKCTRL); } static void sec_mipi_dsim_set_standby(struct sec_mipi_dsim *dsim, bool standby) { uint32_t mdresol = 0; mdresol = dsim_read(dsim, DSIM_MDRESOL); if (standby) mdresol |= MDRESOL_MAINSTANDBY; else mdresol &= ~MDRESOL_MAINSTANDBY; dsim_write(dsim, mdresol, DSIM_MDRESOL); } static void sec_mipi_dsim_disable_clkctrl(struct sec_mipi_dsim *dsim) { uint32_t clkctrl; clkctrl = dsim_read(dsim, DSIM_CLKCTRL); clkctrl &= ~CLKCTRL_TXREQUESTHSCLK; clkctrl &= ~CLKCTRL_ESCCLKEN; clkctrl &= ~CLKCTRL_BYTECLKEN; dsim_write(dsim, clkctrl, DSIM_CLKCTRL); } static void sec_mipi_dsim_disable_pll(struct sec_mipi_dsim *dsim) { uint32_t pllctrl; pllctrl = dsim_read(dsim, DSIM_PLLCTRL); pllctrl &= ~PLLCTRL_PLLEN; dsim_write(dsim, pllctrl, DSIM_PLLCTRL); } /* For now, dsim only support one device attached */ static int sec_mipi_dsim_bridge_attach(struct mipi_dsi_bridge_driver *bridge_driver, struct mipi_dsi_client_dev *dsi_dev) { struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; if (!dsi_dev->lanes || dsi_dev->lanes > dsim_host->pdata->max_data_lanes) { printf("invalid data lanes number\n"); return -EINVAL; } if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO) || !((dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) || (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) { printf("unsupported dsi mode\n"); return -EINVAL; } if (dsi_dev->format != MIPI_DSI_FMT_RGB888 && dsi_dev->format != MIPI_DSI_FMT_RGB565 && dsi_dev->format != MIPI_DSI_FMT_RGB666 && dsi_dev->format != MIPI_DSI_FMT_RGB666_PACKED) { printf("unsupported pixel format: %#x\n", dsi_dev->format); return -EINVAL; } if (!dsi_dev->name) { printf("panel_device name is NULL.\n"); return -EFAULT; } if (dsim_host->dsi_panel_drv) { if (strcmp(dsi_dev->name, dsim_host->dsi_panel_drv->name)) { printf("The panel device name %s is not for LCD driver %s\n", dsi_dev->name, dsim_host->dsi_panel_drv->name); return -EFAULT; } } dsim_host->dsi_panel_dev = dsi_dev; dsim_host->lanes = dsi_dev->lanes; dsim_host->channel = dsi_dev->channel; dsim_host->format = dsi_dev->format; dsim_host->mode_flags = dsi_dev->mode_flags; return 0; } static int sec_mipi_dsim_bridge_enable(struct mipi_dsi_bridge_driver *bridge_driver) { int ret; struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; /* At this moment, the dsim bridge's preceding encoder has * already been enabled. So the dsim can be configed here */ /* config main display mode */ sec_mipi_dsim_set_main_mode(dsim_host); /* config dsim dpi */ sec_mipi_dsim_config_dpi(dsim_host); /* config dsim pll */ ret = sec_mipi_dsim_config_pll(dsim_host); if (ret) { printf("dsim pll config failed: %d\n", ret); return ret; } /* config dphy timings */ sec_mipi_dsim_config_dphy(dsim_host); /* config esc clock, byte clock and etc */ sec_mipi_dsim_config_clkctrl(dsim_host); /* enable data transfer of dsim */ sec_mipi_dsim_set_standby(dsim_host, true); /* Call panel driver's setup */ if (dsim_host->dsi_panel_drv && dsim_host->dsi_panel_drv->dsi_client_setup) { ret = dsim_host->dsi_panel_drv->dsi_client_setup(dsim_host->dsi_panel_dev); if (ret < 0) { printf("failed to init mipi lcd.\n"); return ret; } } return 0; } static int sec_mipi_dsim_bridge_disable(struct mipi_dsi_bridge_driver *bridge_driver) { uint32_t intsrc; struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; /* disable data transfer of dsim */ sec_mipi_dsim_set_standby(dsim_host, false); /* disable esc clock & byte clock */ sec_mipi_dsim_disable_clkctrl(dsim_host); /* disable dsim pll */ sec_mipi_dsim_disable_pll(dsim_host); /* Clear all intsrc */ intsrc = dsim_read(dsim_host, DSIM_INTSRC); dsim_write(dsim_host, intsrc, DSIM_INTSRC); return 0; } static int sec_mipi_dsim_bridge_mode_set(struct mipi_dsi_bridge_driver *bridge_driver, struct fb_videomode *fbmode) { int bpp; uint64_t pix_clk, bit_clk; struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; dsim_host->vmode = *fbmode; bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format); if (bpp < 0) return -EINVAL; pix_clk = PS2KHZ(fbmode->pixclock) * 1000; bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes); if (bit_clk > dsim_host->pdata->max_data_rate) { printf("request bit clk freq exceeds lane's maximum value\n"); return -EINVAL; } dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000); dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000); if (dsim_host->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { /* TODO: add PMS calculate and check * Only support '1080p@60Hz' for now, * add other modes support later */ dsim_host->pms = 0x4210; } debug("%s: bitclk %llu pixclk %llu\n", __func__, dsim_host->bit_clk, dsim_host->pix_clk); return 0; } /* Add a LCD panel driver, will search the panel device to bind with them */ int sec_mipi_dsim_bridge_add_client_driver(struct mipi_dsi_bridge_driver *bridge_driver, struct mipi_dsi_client_driver *panel_drv) { struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; if (!panel_drv) { printf("mipi_dsi_northwest_panel_driver is NULL.\n"); return -EFAULT; } if (!panel_drv->name) { printf("mipi_dsi_northwest_panel_driver name is NULL.\n"); return -EFAULT; } if (dsim_host->dsi_panel_dev) { if (strcmp(panel_drv->name, dsim_host->dsi_panel_dev->name)) { printf("The panel driver name %s is not for LCD device %s\n", panel_drv->name, dsim_host->dsi_panel_dev->name); return -EFAULT; } } dsim_host->dsi_panel_drv = panel_drv; return 0; } static int sec_mipi_dsim_bridge_pkt_write(struct mipi_dsi_bridge_driver *bridge_driver, u8 data_type, const u8 *buf, int len) { struct sec_mipi_dsim *dsim_host = (struct sec_mipi_dsim *)bridge_driver->driver_private; #ifdef DEBUG int i = 0; printf("sec_mipi_dsim_bridge_pkt_write, data_type %u, len %d buf: \n", data_type, len); if (len == 0) len = 2; for (i; i < len; i++) { printf("0x%.2x ", buf[i]); } printf("\n"); #endif return sec_mipi_dsim_pkt_write(dsim_host, data_type, buf, len); } struct mipi_dsi_bridge_driver imx_sec_dsim_driver = { .attach = sec_mipi_dsim_bridge_attach, .enable = sec_mipi_dsim_bridge_enable, .disable = sec_mipi_dsim_bridge_disable, .mode_set = sec_mipi_dsim_bridge_mode_set, .pkt_write = sec_mipi_dsim_bridge_pkt_write, .add_client_driver = sec_mipi_dsim_bridge_add_client_driver, .name = DRIVER_NAME, }; int sec_mipi_dsim_setup(const struct sec_mipi_dsim_plat_data *plat_data) { struct sec_mipi_dsim *dsim_host; if (!plat_data) { printf("Invalid platform data \n"); return -EINVAL; } dsim_host = (struct sec_mipi_dsim *)malloc(sizeof(struct sec_mipi_dsim)); if (!dsim_host) { printf("failed to allocate sec_mipi_dsim object.\n"); return -ENOMEM; } dsim_host->base = (void __iomem *)plat_data->reg_base; dsim_host->disp_mix_gpr_base = (void __iomem *)plat_data->gpr_base; dsim_host->pdata = plat_data; dsim_host->dsi_panel_drv = NULL; dsim_host->dsi_panel_dev = NULL; /* Pull dsim out of reset */ disp_mix_dsim_soft_reset_release(dsim_host, true); disp_mix_dsim_clks_enable(dsim_host, true); disp_mix_dsim_lanes_reset(dsim_host, false); imx_sec_dsim_driver.driver_private = dsim_host; return imx_mipi_dsi_bridge_register_driver(&imx_sec_dsim_driver); }