diff options
author | Torbjorn Svensson <torbjorn.x.svensson@stericsson.com> | 2010-12-15 12:05:18 +0100 |
---|---|---|
committer | Michael BRANDT <michael.brandt@stericsson.com> | 2011-01-18 09:07:11 +0100 |
commit | 80c29457c21dbe1f9994bf2d173c329cf3c7227a (patch) | |
tree | 9c3ca1b1f6a9944fdae53e29d14c6950a39307b1 /board/st/u8500/mcde_hw.c | |
parent | c6458cdfa19cb2fdc37febf8341efcc5fb023bbf (diff) |
U8500: Generic display driver
This patch introduces combined DSI and DPI display support for u-boot.
The code is also similar to the kernel code for easy maintenance.
ST-Ericsson ID: ER319241
ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10069
Change-Id: Ic232b6f738348cbedb67e27418678ddd223d7800
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/11038
Reviewed-by: Jimmy RUBIN <jimmy.rubin@stericsson.com>
Reviewed-by: Michael BRANDT <michael.brandt@stericsson.com>
Tested-by: Torbjorn SVENSSON <torbjorn.x.svensson@stericsson.com>
Diffstat (limited to 'board/st/u8500/mcde_hw.c')
-rw-r--r-- | board/st/u8500/mcde_hw.c | 1543 |
1 files changed, 1241 insertions, 302 deletions
diff --git a/board/st/u8500/mcde_hw.c b/board/st/u8500/mcde_hw.c index d89a8eb52..72e4596ae 100644 --- a/board/st/u8500/mcde_hw.c +++ b/board/st/u8500/mcde_hw.c @@ -1,6 +1,8 @@ /* * Copyright (C) ST-Ericsson SA 2010 * + * ST-Ericsson MCDE driver for u-boot + * * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com> * for ST-Ericsson. * @@ -20,14 +22,29 @@ #include "mcde_regs.h" #include "mcde.h" #include <asm/arch/hardware.h> +#define SCREEN_PPL_HIGH 1920 -#define DEBUG 0 -#define dbg_printk(format, arg...) \ - if (DEBUG) \ - printf("mcde: " format, ##arg) +#define false (0) +#define true (1) -u8 *mcdeio; -u8 **dsiio; +/* For compatability with kernel code */ +#define dev_info(dev, format, arg...) \ + debug("mcde: " format, ##arg) +#define dev_warn(dev, format, arg...) \ + debug("mcde: " format, ##arg) +#define dev_dbg(dev, format, arg...) \ + debug("mcde: " format, ##arg) +#define dev_vdbg(dev, format, arg...) \ + debug("mcde: " format, ##arg) + +#define msleep(m) mdelay(m) + +extern void print_vmode(struct mcde_video_mode *vmode); /* for debugging */ + +static u8 *mcdeio; +static u8 **dsiio; + +static u8 hardware_version; static inline u32 dsi_rreg(int i, u32 reg) { @@ -63,19 +80,19 @@ static inline void mcde_wreg(u32 reg, u32 val) struct ovly_regs { u8 ch_id; - t_bool enabled; + u8 enabled; u32 baseaddress0; u32 baseaddress1; - t_bool buf_id; + u8 reset_buf_id; u8 bits_per_pixel; u8 bpp; - t_bool bgr; - t_bool bebo; - t_bool opq; + u8 bgr; + u8 bebo; + u8 opq; + u8 col_conv; u8 pixoff; u16 ppl; u16 lpf; - u32 ljinc; u16 cropx; u16 cropy; u16 xpos; @@ -84,7 +101,7 @@ struct ovly_regs { }; struct mcde_ovly_state { - t_bool inuse; + u8 inuse; u8 idx; /* MCDE overlay index */ struct mcde_chnl_state *chnl; /* Owner channel */ u32 transactionid; /* Apply time stamp */ @@ -116,29 +133,78 @@ static struct mcde_ovly_state overlays[] = { }; struct chnl_regs { - t_bool floen; + u8 floen; u16 x; u16 y; u16 ppl; u16 lpf; u8 bpp; + u8 internal_clk; + u16 pcd; + u8 clksel; + u8 cdwin; + u8 bcd; + u8 synchronized_update; + u8 roten; + /* DSI */ - u8 dsipacking; + u8 dsipacking; +}; + +struct col_regs { + u16 y_red; + u16 y_green; + u16 y_blue; + u16 cb_red; + u16 cb_green; + u16 cb_blue; + u16 cr_red; + u16 cr_green; + u16 cr_blue; + u16 off_red; + u16 off_green; + u16 off_blue; +}; + +struct tv_regs { + u16 dho; /* TV mode: left border width; destination horizontal offset */ + /* LCD MODE: horizontal back porch */ + u16 alw; /* TV mode: right border width */ + /* LCD mode: horizontal front porch */ + u16 hsw; /* horizontal synch width */ + u16 dvo; /* TV mode: top border width; destination horizontal offset */ + /* LCD MODE: vertical back porch */ + u16 bsl; /* TV mode: bottom border width; blanking start line */ + /* LCD MODE: vertical front porch */ + /* field 1 */ + u16 bel1; /* TV mode: field total vertical blanking lines */ + /* LCD mode: vertical sync width */ + u16 fsl1; /* field vbp */ + /* field 2 */ + u16 bel2; + u16 fsl2; + u8 tv_mode; + u8 sel_mode_tv; + u8 interlaced_en; + u32 lcdtim1; }; struct mcde_chnl_state { - t_bool inuse; + u8 inuse; enum mcde_chnl id; enum mcde_fifo fifo; struct mcde_port port; struct mcde_ovly_state *ovly0; struct mcde_ovly_state *ovly1; + const struct chnl_config *cfg; u32 transactionid; u32 transactionid_regs; + enum mcde_display_power_mode power_mode; /* Staged settings */ - t_bool synchronized_update; + u8 synchronized_update; enum mcde_port_pix_fmt pix_fmt; + struct mcde_video_mode vmode; u16 update_x; u16 update_y; u16 update_w; @@ -146,36 +212,289 @@ struct mcde_chnl_state { /* Applied settings */ struct chnl_regs regs; + struct col_regs col_regs; + struct tv_regs tv_regs; + + u8 continous_running; }; static struct mcde_chnl_state channels[] = { { + .id = MCDE_CHNL_A, + .ovly0 = &overlays[0], + .ovly1 = &overlays[1], + }, + { + .id = MCDE_CHNL_B, + .ovly0 = &overlays[2], + .ovly1 = &overlays[3], + }, + { .id = MCDE_CHNL_C0, .ovly0 = &overlays[4], .ovly1 = NULL, }, + { + .id = MCDE_CHNL_C1, + .ovly0 = &overlays[5], + .ovly1 = NULL, + }, +}; + +struct chnl_config { + /* Key */ + enum mcde_chnl_path path; + + /* Value */ + u8 swap_a_c0; + u8 swap_a_c0_set; + u8 swap_b_c1; + u8 swap_b_c1_set; + u8 fabmux; + u8 fabmux_set; + u8 f01mux; + u8 f01mux_set; }; +int mcde_chnl_set_video_mode(struct mcde_chnl_state *chnl, + struct mcde_video_mode *vmode) +{ + if (chnl == NULL || vmode == NULL) + return -EINVAL; + + chnl->vmode = *vmode; + + return 0; +} + +static void dpi_video_mode_apply(struct mcde_chnl_state *chnl) +{ + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + chnl->tv_regs.interlaced_en = chnl->vmode.interlaced; + + chnl->tv_regs.sel_mode_tv = chnl->port.phy.dpi.tv_mode; + if (chnl->tv_regs.sel_mode_tv) { + /* TV mode */ + u32 bel = chnl->vmode.vbp1 + chnl->vmode.vfp1 + + chnl->vmode.vbp2 + chnl->vmode.vfp2; + /* -4 since hsw is excluding SAV/EAV, 2 bytes each */ + chnl->tv_regs.hsw = chnl->vmode.hbp + chnl->vmode.hfp - 4; + chnl->tv_regs.dho = MCDE_CONFIG_TVOUT_HBORDER; + chnl->tv_regs.alw = MCDE_CONFIG_TVOUT_HBORDER; + /* in TV mode: bel1 = bel2 + 1 */ + chnl->tv_regs.bel2 = bel / 2; + chnl->tv_regs.bel1 = bel - chnl->tv_regs.bel2; + chnl->tv_regs.dvo = MCDE_CONFIG_TVOUT_VBORDER; + chnl->tv_regs.bsl = MCDE_CONFIG_TVOUT_VBORDER; + chnl->tv_regs.fsl1 = chnl->vmode.vbp1; + chnl->tv_regs.fsl2 = chnl->vmode.vbp2; + if (chnl->port.phy.dpi.bus_width == 4) + chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P_BE; + else + chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P; + } else { + /* LCD mode */ + u32 polarity; + chnl->tv_regs.hsw = chnl->vmode.hsw; + chnl->tv_regs.dho = chnl->vmode.hbp; + chnl->tv_regs.alw = chnl->vmode.hfp; + chnl->tv_regs.bel1 = chnl->vmode.vsw; + chnl->tv_regs.bel2 = chnl->tv_regs.bel1; + chnl->tv_regs.dvo = chnl->vmode.vbp1 + chnl->vmode.vbp2; + chnl->tv_regs.bsl = chnl->vmode.vfp1 + chnl->vmode.vfp2; + chnl->tv_regs.fsl1 = 0; + chnl->tv_regs.fsl2 = 0; + polarity = chnl->port.phy.dpi.polarity; + chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IPC( + (polarity & DPI_ACT_ON_FALLING_EDGE) != 0); + chnl->tv_regs.lcdtim1 = MCDE_LCDTIM1A_IHS( + (polarity & DPI_ACT_LOW_HSYNC) != 0); + chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IVS( + (polarity & DPI_ACT_LOW_VSYNC) != 0); + chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IOE( + (polarity & DPI_ACT_LOW_DATA_ENABLE) != 0); + } + debug("%s: Leaving\n", __func__); +} + +static void update_dpi_registers(enum mcde_chnl chnl_id, struct tv_regs *regs) +{ + u8 idx = chnl_id; + + dev_dbg(&mcde_dev->dev, "%s\n", __func__); + mcde_wreg(MCDE_TVCRA + idx * MCDE_TVCRA_GROUPOFFSET, + MCDE_TVCRA_SEL_MOD(regs->sel_mode_tv) | + MCDE_TVCRA_INTEREN(regs->interlaced_en) | + MCDE_TVCRA_IFIELD(1) | + MCDE_TVCRA_TVMODE(regs->tv_mode) | + MCDE_TVCRA_SDTVMODE(MCDE_TVCRA_SDTVMODE_Y0CBY1CR) | + MCDE_TVCRA_AVRGEN(0)); + mcde_wreg(MCDE_TVBLUA + idx * MCDE_TVBLUA_GROUPOFFSET, + MCDE_TVBLUA_TVBLU(MCDE_CONFIG_TVOUT_BACKGROUND_LUMINANCE) | + MCDE_TVBLUA_TVBCB(MCDE_CONFIG_TVOUT_BACKGROUND_CHROMINANCE_CB)| + MCDE_TVBLUA_TVBCR(MCDE_CONFIG_TVOUT_BACKGROUND_CHROMINANCE_CR)); + + /* Vertical timing registers */ + mcde_wreg(MCDE_TVDVOA + idx * MCDE_TVDVOA_GROUPOFFSET, + MCDE_TVDVOA_DVO1(regs->dvo) | + MCDE_TVDVOA_DVO2(regs->dvo)); + mcde_wreg(MCDE_TVBL1A + idx * MCDE_TVBL1A_GROUPOFFSET, + MCDE_TVBL1A_BEL1(regs->bel1) | + MCDE_TVBL1A_BSL1(regs->bsl)); + mcde_wreg(MCDE_TVBL2A + idx * MCDE_TVBL1A_GROUPOFFSET, + MCDE_TVBL2A_BEL2(regs->bel2) | + MCDE_TVBL2A_BSL2(regs->bsl)); + mcde_wreg(MCDE_TVISLA + idx * MCDE_TVISLA_GROUPOFFSET, + MCDE_TVISLA_FSL1(regs->fsl1) | + MCDE_TVISLA_FSL2(regs->fsl2)); + + /* Horizontal timing registers */ + if (!regs->sel_mode_tv || + hardware_version == MCDE_CHIP_VERSION_3_0_8) { + mcde_wreg(MCDE_TVLBALWA + idx * MCDE_TVLBALWA_GROUPOFFSET, + MCDE_TVLBALWA_LBW(regs->hsw) | + MCDE_TVLBALWA_ALW(regs->alw)); + mcde_wreg(MCDE_TVTIM1A + idx * MCDE_TVTIM1A_GROUPOFFSET, + MCDE_TVTIM1A_DHO(regs->dho)); + } else { + /* + * in earlier versions the LBW and DHO fields are swapped + * TV mode only + */ + mcde_wreg(MCDE_TVLBALWA + idx * MCDE_TVLBALWA_GROUPOFFSET, + MCDE_TVLBALWA_LBW(regs->dho) | + MCDE_TVLBALWA_ALW(regs->alw)); + mcde_wreg(MCDE_TVTIM1A + idx * MCDE_TVTIM1A_GROUPOFFSET, + MCDE_TVTIM1A_DHO(regs->hsw)); + } + if (!regs->sel_mode_tv) + mcde_wreg(MCDE_LCDTIM1A + idx * MCDE_LCDTIM1A_GROUPOFFSET, + regs->lcdtim1); + debug("%s: Leaving\n", __func__); +} + +static void update_col_registers(enum mcde_chnl chnl_id, struct col_regs *regs) +{ + u8 idx = chnl_id; + + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + mcde_wreg(MCDE_RGBCONV1A + idx * MCDE_RGBCONV1A_GROUPOFFSET, + MCDE_RGBCONV1A_YR_RED(regs->y_red) | + MCDE_RGBCONV1A_YR_GREEN(regs->y_green)); + mcde_wreg(MCDE_RGBCONV2A + idx * MCDE_RGBCONV2A_GROUPOFFSET, + MCDE_RGBCONV2A_YR_BLUE(regs->y_blue) | + MCDE_RGBCONV2A_CR_RED(regs->cr_red)); + mcde_wreg(MCDE_RGBCONV3A + idx * MCDE_RGBCONV3A_GROUPOFFSET, + MCDE_RGBCONV3A_CR_GREEN(regs->cr_green) | + MCDE_RGBCONV3A_CR_BLUE(regs->cr_blue)); + mcde_wreg(MCDE_RGBCONV4A + idx * MCDE_RGBCONV4A_GROUPOFFSET, + MCDE_RGBCONV4A_CB_RED(regs->cb_red) | + MCDE_RGBCONV4A_CB_GREEN(regs->cb_green)); + mcde_wreg(MCDE_RGBCONV5A + idx * MCDE_RGBCONV5A_GROUPOFFSET, + MCDE_RGBCONV5A_CB_BLUE(regs->cb_blue) | + MCDE_RGBCONV5A_OFF_RED(regs->off_red)); + mcde_wreg(MCDE_RGBCONV6A + idx * MCDE_RGBCONV6A_GROUPOFFSET, + MCDE_RGBCONV6A_OFF_GREEN(regs->off_green) | + MCDE_RGBCONV6A_OFF_BLUE(regs->off_blue)); +} + /* MCDE internal helpers */ static u8 portfmt2dsipacking(enum mcde_port_pix_fmt pix_fmt) { switch (pix_fmt) { + case MCDE_PORTPIXFMT_DSI_16BPP: + return MCDE_DSIVID0CONF0_PACKING_RGB565; + case MCDE_PORTPIXFMT_DSI_18BPP_PACKED: + return MCDE_DSIVID0CONF0_PACKING_RGB666; + case MCDE_PORTPIXFMT_DSI_18BPP: case MCDE_PORTPIXFMT_DSI_24BPP: default: return MCDE_DSIVID0CONF0_PACKING_RGB888; + case MCDE_PORTPIXFMT_DSI_YCBCR422: + return MCDE_DSIVID0CONF0_PACKING_HDTV; } } static u8 portfmt2bpp(enum mcde_port_pix_fmt pix_fmt) { switch (pix_fmt) { + case MCDE_PORTPIXFMT_DPI_16BPP_C1: + case MCDE_PORTPIXFMT_DPI_16BPP_C2: + case MCDE_PORTPIXFMT_DPI_16BPP_C3: + case MCDE_PORTPIXFMT_DSI_16BPP: + case MCDE_PORTPIXFMT_DSI_YCBCR422: + return 16; + case MCDE_PORTPIXFMT_DPI_18BPP_C1: + case MCDE_PORTPIXFMT_DPI_18BPP_C2: + case MCDE_PORTPIXFMT_DSI_18BPP_PACKED: + return 18; + case MCDE_PORTPIXFMT_DSI_18BPP: + case MCDE_PORTPIXFMT_DPI_24BPP: case MCDE_PORTPIXFMT_DSI_24BPP: return 24; default: + return 1; + } +} + +static u8 bpp2outbpp(u8 bpp) +{ + switch (bpp) { + case 16: + return MCDE_CRA1_OUTBPP_16BPP; + case 18: + return MCDE_CRA1_OUTBPP_18BPP; + case 24: + return MCDE_CRA1_OUTBPP_24BPP; + default: return 0; } } +static u8 portfmt2cdwin(enum mcde_port_pix_fmt pix_fmt) +{ + switch (pix_fmt) { + case MCDE_PORTPIXFMT_DPI_16BPP_C1: + return MCDE_CRA1_CDWIN_16BBP_C1; + case MCDE_PORTPIXFMT_DPI_16BPP_C2: + return MCDE_CRA1_CDWIN_16BBP_C2; + case MCDE_PORTPIXFMT_DPI_18BPP_C1: + return MCDE_CRA1_CDWIN_18BBP_C1; + case MCDE_PORTPIXFMT_DPI_18BPP_C2: + return MCDE_CRA1_CDWIN_18BBP_C2; + case MCDE_PORTPIXFMT_DPI_24BPP: + return MCDE_CRA1_CDWIN_24BBP; + + case MCDE_PORTPIXFMT_DPI_16BPP_C3: + /* 16 BPP C3 not supported by HW */ + dev_warn(&mcde_dev->dev, "DPI 16 BPP C3 not supported\n"); + /* intentional fall through */ + default: + /* only DPI formats are relevant */ + return 0; + } +} + +static u32 get_output_fifo_size(enum mcde_fifo fifo) +{ + u32 ret = 1; /* Avoid div by zero */ + + switch (fifo) { + case MCDE_FIFO_A: + case MCDE_FIFO_B: + ret = MCDE_FIFO_AB_SIZE; + break; + case MCDE_FIFO_C0: + case MCDE_FIFO_C1: + ret = MCDE_FIFO_C0C1_SIZE; + break; + default: + dev_vdbg(&mcde_dev->dev, "Unsupported fifo"); + break; + } + return ret; +} + static u8 get_dsi_formid(const struct mcde_port *port) { if (port->ifc == DSI_VIDEO_MODE && port->link == 0) @@ -193,73 +512,131 @@ static u8 get_dsi_formid(const struct mcde_port *port) return 0; } -#define DSI_UNIT_INTERVAL_0 0x9 + +void wait_for_overlay(struct mcde_ovly_state *ovly) +{ + /* ovly not used in u-boot */ +} + +void wait_for_channel(struct mcde_chnl_state *chnl) +{ + /* ovly not used in u-boot */ +} static int update_channel_static_registers(struct mcde_chnl_state *chnl) { + const struct chnl_config *cfg = chnl->cfg; const struct mcde_port *port = &chnl->port; - int i = 0; - u8 idx = 2 * port->link + port->ifc; - u8 lnk = port->link; - if (!cpu_is_u8500v2()) { + if (hardware_version == MCDE_CHIP_VERSION_3_0_5) { /* Fifo & muxing */ - mcde_wfld(MCDE_CONF0, SWAP_A_C0_V1, TRUE); - mcde_wfld(MCDE_CR, FABMUX_V1, FALSE); - - if (port->ifc == DSI_VIDEO_MODE && port->link == 0) - mcde_wfld(MCDE_CR, DSIVID0_EN_V1, TRUE); - else if (port->ifc == DSI_VIDEO_MODE && port->link == 1) - mcde_wfld(MCDE_CR, DSIVID1_EN_V1, TRUE); - else if (port->ifc == DSI_VIDEO_MODE && port->link == 2) - mcde_wfld(MCDE_CR, DSIVID2_EN_V1, TRUE); - else if (port->ifc == DSI_CMD_MODE && port->link == 0) - mcde_wfld(MCDE_CR, DSICMD0_EN_V1, TRUE); - else if (port->ifc == DSI_CMD_MODE && port->link == 1) - mcde_wfld(MCDE_CR, DSICMD1_EN_V1, TRUE); - else if (port->ifc == DSI_CMD_MODE && port->link == 2) - mcde_wfld(MCDE_CR, DSICMD2_EN_V1, TRUE); - - mcde_wreg(MCDE_CTRLC0, MCDE_CTRLC0_FIFOWTRMRK(0xa0)); + if (cfg->swap_a_c0_set) + mcde_wfld(MCDE_CONF0, SWAP_A_C0_V1, cfg->swap_a_c0); + if (cfg->swap_b_c1_set) + mcde_wfld(MCDE_CONF0, SWAP_B_C1_V1, cfg->swap_b_c1); + if (cfg->fabmux_set) + mcde_wfld(MCDE_CR, FABMUX_V1, cfg->fabmux); + if (cfg->f01mux_set) + mcde_wfld(MCDE_CR, F01MUX_V1, cfg->f01mux); + + if (port->type == MCDE_PORTTYPE_DPI) { + if (port->link == 0) + mcde_wfld(MCDE_CR, DPIA_EN_V1, true); + else if (port->link == 1) + mcde_wfld(MCDE_CR, DPIB_EN_V1, true); + } else if (port->type == MCDE_PORTTYPE_DSI) { + if (port->ifc == DSI_VIDEO_MODE && port->link == 0) + mcde_wfld(MCDE_CR, DSIVID0_EN_V1, true); + else if (port->ifc == DSI_VIDEO_MODE && port->link == 1) + mcde_wfld(MCDE_CR, DSIVID1_EN_V1, true); + else if (port->ifc == DSI_VIDEO_MODE && port->link == 2) + mcde_wfld(MCDE_CR, DSIVID2_EN_V1, true); + else if (port->ifc == DSI_CMD_MODE && port->link == 0) + mcde_wfld(MCDE_CR, DSICMD0_EN_V1, true); + else if (port->ifc == DSI_CMD_MODE && port->link == 1) + mcde_wfld(MCDE_CR, DSICMD1_EN_V1, true); + else if (port->ifc == DSI_CMD_MODE && port->link == 2) + mcde_wfld(MCDE_CR, DSICMD2_EN_V1, true); + } + + if (chnl->fifo == MCDE_FIFO_C0) + mcde_wreg(MCDE_CTRLC0, MCDE_CTRLC0_FIFOWTRMRK( + get_output_fifo_size(MCDE_FIFO_C0))); + else if (chnl->fifo == MCDE_FIFO_C1) + mcde_wreg(MCDE_CTRLC1, MCDE_CTRLC1_FIFOWTRMRK( + get_output_fifo_size(MCDE_FIFO_C1))); + else if (port->update_auto_trig && + (port->sync_src == MCDE_SYNCSRC_TE0)) + mcde_wreg(MCDE_CTRLC0, MCDE_CTRLC0_FIFOWTRMRK( + get_output_fifo_size(MCDE_FIFO_C0))); + else if (port->update_auto_trig && + (port->sync_src == MCDE_SYNCSRC_TE1)) + mcde_wreg(MCDE_CTRLC1, MCDE_CTRLC1_FIFOWTRMRK( + get_output_fifo_size(MCDE_FIFO_C1))); } else { switch (chnl->fifo) { case MCDE_FIFO_A: mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id * - MCDE_CHNL0MUXING_V2_GROUPOFFSET, - MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_A)); - mcde_wfld(MCDE_CTRLA, FORMTYPE, - MCDE_CTRLA_FORMTYPE_DSI); - mcde_wfld(MCDE_CTRLA, FORMID, - get_dsi_formid(port)); - mcde_wfld(MCDE_CTRLA, FIFOWTRMRK, 0x280); + MCDE_CHNL0MUXING_V2_GROUPOFFSET, + MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_A)); + if (port->type == MCDE_PORTTYPE_DPI) { + mcde_wfld(MCDE_CTRLA, FORMTYPE, + MCDE_CTRLA_FORMTYPE_DPITV); + mcde_wfld(MCDE_CTRLA, FORMID, port->link); + mcde_wfld(MCDE_CTRLA, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_A)); + } else if (port->type == MCDE_PORTTYPE_DSI) { + mcde_wfld(MCDE_CTRLA, FORMTYPE, + MCDE_CTRLA_FORMTYPE_DSI); + mcde_wfld(MCDE_CTRLA, FORMID, + get_dsi_formid(port)); + mcde_wfld(MCDE_CTRLA, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_A)); + } break; case MCDE_FIFO_B: mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id * - MCDE_CHNL0MUXING_V2_GROUPOFFSET, - MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_B)); - mcde_wfld(MCDE_CTRLB, FORMTYPE, - MCDE_CTRLB_FORMTYPE_DSI); - mcde_wfld(MCDE_CTRLB, FORMID, - get_dsi_formid(port)); - mcde_wfld(MCDE_CTRLB, FIFOWTRMRK, 0x280); + MCDE_CHNL0MUXING_V2_GROUPOFFSET, + MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_B)); + if (port->type == MCDE_PORTTYPE_DPI) { + mcde_wfld(MCDE_CTRLB, FORMTYPE, + MCDE_CTRLB_FORMTYPE_DPITV); + mcde_wfld(MCDE_CTRLB, FORMID, port->link); + mcde_wfld(MCDE_CTRLB, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_B)); + } else if (port->type == MCDE_PORTTYPE_DSI) { + mcde_wfld(MCDE_CTRLB, FORMTYPE, + MCDE_CTRLB_FORMTYPE_DSI); + mcde_wfld(MCDE_CTRLB, FORMID, + get_dsi_formid(port)); + mcde_wfld(MCDE_CTRLB, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_B)); + } + break; case MCDE_FIFO_C0: mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id * MCDE_CHNL0MUXING_V2_GROUPOFFSET, MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_C0)); + if (port->type == MCDE_PORTTYPE_DPI) + return -EINVAL; mcde_wfld(MCDE_CTRLC0, FORMTYPE, MCDE_CTRLC0_FORMTYPE_DSI); mcde_wfld(MCDE_CTRLC0, FORMID, get_dsi_formid(port)); - mcde_wfld(MCDE_CTRLC0, FIFOWTRMRK, 0xa0); + mcde_wfld(MCDE_CTRLC0, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_C0)); break; case MCDE_FIFO_C1: mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id * MCDE_CHNL0MUXING_V2_GROUPOFFSET, MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_C1)); + if (port->type == MCDE_PORTTYPE_DPI) + return -EINVAL; mcde_wfld(MCDE_CTRLC1, FORMTYPE, MCDE_CTRLC1_FORMTYPE_DSI); mcde_wfld(MCDE_CTRLC1, FORMID, get_dsi_formid(port)); - mcde_wfld(MCDE_CTRLC1, FIFOWTRMRK, 0xa0); + mcde_wfld(MCDE_CTRLC1, FIFOWTRMRK, + get_output_fifo_size(MCDE_FIFO_C1)); break; default: return -EINVAL; @@ -267,115 +644,189 @@ static int update_channel_static_registers(struct mcde_chnl_state *chnl) } /* Formatter */ - dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, LINK_EN, TRUE); - dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, TRUE); - dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, READ_EN, TRUE); - dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, TRUE); - dsi_wreg(lnk, DSI_MCTL_DPHY_STATIC, - DSI_MCTL_DPHY_STATIC_UI_X4(DSI_UNIT_INTERVAL_0)); - dsi_wreg(lnk, DSI_DPHY_LANES_TRIM, - DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_ENUM(0_90)); - dsi_wreg(lnk, DSI_MCTL_DPHY_TIMEOUT, - DSI_MCTL_DPHY_TIMEOUT_CLK_DIV(0xf) | - DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL(0x3fff) | - DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL(0x3fff)); - dsi_wreg(lnk, DSI_MCTL_MAIN_PHY_CTL, - DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME(0xf) | - DSI_MCTL_MAIN_PHY_CTL_LANE2_EN(TRUE) | - DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS(FALSE)); - dsi_wreg(lnk, DSI_MCTL_ULPOUT_TIME, - DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME(1) | - DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME(1)); - dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_MODE, FALSE); - dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_PRI, port->ifc == DSI_CMD_MODE); - dsi_wfld(lnk, DSI_CMD_MODE_CTL, TE_TIMEOUT, 0x3ff); - dsi_wreg(lnk, DSI_MCTL_MAIN_EN, - DSI_MCTL_MAIN_EN_PLL_START(TRUE) | - DSI_MCTL_MAIN_EN_CKLANE_EN(TRUE) | - DSI_MCTL_MAIN_EN_DAT1_EN(TRUE) | - DSI_MCTL_MAIN_EN_DAT2_EN(port->phy.dsi.num_data_lanes - == 2) | - DSI_MCTL_MAIN_EN_IF1_EN(port->ifc == DSI_VIDEO_MODE) | - DSI_MCTL_MAIN_EN_IF2_EN(port->ifc == DSI_CMD_MODE)); - while (dsi_rfld(lnk, DSI_MCTL_MAIN_STS, CLKLANE_READY) == 0 || - dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT1_READY) == 0 || - dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT2_READY) == 0) { - mdelay(1); - if (i++ == 10) { - printf("DSI lane not ready (link=%d)!\n", lnk); - return -EINVAL; + if (port->type == MCDE_PORTTYPE_DSI) { + int i = 0; + u8 idx = 2 * port->link + port->ifc; + u8 lnk = port->link; + + dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, LINK_EN, true); + dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, true); + dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, READ_EN, true); + dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, true); + + if (hardware_version == MCDE_CHIP_VERSION_3_0_5) { + if (port->phy.dsi.data_lanes_swap) { + dev_warn(&mcde_dev->dev, + "DSI %d data lane remap not available!\n", + lnk); + return -EINVAL; + } + } else + dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, DLX_REMAP_EN, + port->phy.dsi.data_lanes_swap); + + dsi_wreg(lnk, DSI_MCTL_DPHY_STATIC, + DSI_MCTL_DPHY_STATIC_UI_X4(port->phy.dsi.ui)); + dsi_wreg(lnk, DSI_DPHY_LANES_TRIM, + DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_ENUM(0_90)); + dsi_wreg(lnk, DSI_MCTL_DPHY_TIMEOUT, + DSI_MCTL_DPHY_TIMEOUT_CLK_DIV(0xf) | + DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL(0x3fff) | + DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL(0x3fff)); + dsi_wreg(lnk, DSI_MCTL_MAIN_PHY_CTL, + DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME(0xf) | + DSI_MCTL_MAIN_PHY_CTL_LANE2_EN(true) | + DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS( + port->phy.dsi.clk_cont)); + dsi_wreg(lnk, DSI_MCTL_ULPOUT_TIME, + DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME(1) | + DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME(1)); + dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_MODE, false); + dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_PRI, port->ifc == 1); + dsi_wreg(lnk, DSI_MCTL_MAIN_EN, + DSI_MCTL_MAIN_EN_PLL_START(true) | + DSI_MCTL_MAIN_EN_CKLANE_EN(true) | + DSI_MCTL_MAIN_EN_DAT1_EN(true) | + DSI_MCTL_MAIN_EN_DAT2_EN(port->phy.dsi.num_data_lanes + == 2) | + DSI_MCTL_MAIN_EN_IF1_EN(port->ifc == DSI_VIDEO_MODE) | + DSI_MCTL_MAIN_EN_IF2_EN(port->ifc == DSI_CMD_MODE)); + while (dsi_rfld(lnk, DSI_MCTL_MAIN_STS, CLKLANE_READY) == 0 || + dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT1_READY) == 0 || + dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT2_READY) == 0) { + mdelay(1); + if (i++ == 10) { + dev_warn(&mcde_dev->dev, + "DSI lane not ready (link=%d)!\n", lnk); + return -EINVAL; + } } - } - - mcde_wreg(MCDE_DSIVID0CONF0 + - idx * MCDE_DSIVID0CONF0_GROUPOFFSET, - MCDE_DSIVID0CONF0_BLANKING(0) | - MCDE_DSIVID0CONF0_VID_MODE(0) | - MCDE_DSIVID0CONF0_CMD8(TRUE) | - MCDE_DSIVID0CONF0_BIT_SWAP(FALSE) | - MCDE_DSIVID0CONF0_BYTE_SWAP(FALSE) | - MCDE_DSIVID0CONF0_DCSVID_NOTGEN(TRUE)); - if (port->ifc == DSI_VIDEO_MODE) - dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF1_ID, - port->phy.dsi.virt_id); - else if (port->ifc == DSI_CMD_MODE) - dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF2_ID, - port->phy.dsi.virt_id); + mcde_wreg(MCDE_DSIVID0CONF0 + + idx * MCDE_DSIVID0CONF0_GROUPOFFSET, + MCDE_DSIVID0CONF0_BLANKING(0) | + MCDE_DSIVID0CONF0_VID_MODE( + port->mode == MCDE_PORTMODE_VID) | + MCDE_DSIVID0CONF0_CMD8(true) | + MCDE_DSIVID0CONF0_BIT_SWAP(false) | + MCDE_DSIVID0CONF0_BYTE_SWAP(false) | + MCDE_DSIVID0CONF0_DCSVID_NOTGEN(true)); + + if (port->mode == MCDE_PORTMODE_CMD) { + if (port->ifc == DSI_VIDEO_MODE) + dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF1_ID, + port->phy.dsi.virt_id); + else if (port->ifc == DSI_CMD_MODE) + dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF2_ID, + port->phy.dsi.virt_id); + } + } - mcde_wfld(MCDE_CRC, SYCEN0, TRUE); - mcde_wreg(MCDE_VSCRC0, - MCDE_VSCRC0_VSPMIN(1) | - MCDE_VSCRC0_VSPMAX(0xff)); + mcde_wfld(MCDE_CR, MCDEEN, true); - mcde_wfld(MCDE_CR, MCDEEN, TRUE); - dbg_printk("Static registers setup, chnl=%d\n", chnl->id); + dev_vdbg(&mcde_dev->dev, "Static registers setup, chnl=%d\n", chnl->id); return 0; } static void update_overlay_registers(u8 idx, struct ovly_regs *regs, - u16 update_x, u16 update_y, u16 update_w, u16 update_h) + struct mcde_port *port, enum mcde_fifo fifo, + u16 update_x, u16 update_y, u16 update_w, + u16 update_h, u16 stride, u8 interlaced) { u32 lmrgn = (regs->cropx + update_x) * regs->bits_per_pixel; - u32 tmrgn = (regs->cropy + update_y) * regs->ljinc; + u32 tmrgn = (regs->cropy + update_y) * stride; u32 ppl = regs->ppl - update_x; u32 lpf = regs->lpf - update_y; + u32 ljinc = stride; + u32 pixelfetchwtrmrklevel; + u8 nr_of_bufs = 1; + u32 fifo_size; + /* TODO: disable if everything clipped */ if (!regs->enabled) { u32 temp; temp = mcde_rreg(MCDE_OVL0CR + idx * MCDE_OVL0CR_GROUPOFFSET); mcde_wreg(MCDE_OVL0CR + idx * MCDE_OVL0CR_GROUPOFFSET, (temp & ~MCDE_OVL0CR_OVLEN_MASK) | - MCDE_OVL0CR_OVLEN(FALSE)); + MCDE_OVL0CR_OVLEN(false)); return; } - mcde_wreg(MCDE_EXTSRC0A0 + idx * MCDE_EXTSRC0A0_GROUPOFFSET, - regs->baseaddress0); - mcde_wreg(MCDE_EXTSRC0A1 + idx * MCDE_EXTSRC0A1_GROUPOFFSET, - regs->baseaddress1); + /* + * TODO: Preferably most of this is done in some apply function instead + * of every update. Problem is however that at overlay apply + * there is no port type info available (and the question is + * whether it is appropriate to add a port type there). + * Note that lpf has a dependency on update_y. + */ + if (port->type == MCDE_PORTTYPE_DPI && port->phy.dpi.tv_mode) + /* REVIEW: Why not for DSI? enable in regs? */ + regs->col_conv = MCDE_OVL0CR_COLCCTRL_ENABLED_NO_SAT; + else if (port->type == MCDE_PORTTYPE_DSI) { + if (port->pixel_format == MCDE_PORTPIXFMT_DSI_YCBCR422) + regs->col_conv = MCDE_OVL0CR_COLCCTRL_ENABLED_NO_SAT; + else + regs->col_conv = MCDE_OVL0CR_COLCCTRL_DISABLED; + if (interlaced) { + nr_of_bufs = 2; + lpf = lpf / 2; + ljinc *= 2; + } + } + + fifo_size = get_output_fifo_size(fifo); +#ifdef CONFIG_AV8100_SDTV + /* TODO: check if these watermark levels work for HDMI as well. */ + pixelfetchwtrmrklevel = MCDE_PIXFETCH_SMALL_WTRMRKLVL; +#else + if ((fifo == MCDE_FIFO_A || fifo == MCDE_FIFO_B) && + regs->ppl >= fifo_size * 2) + pixelfetchwtrmrklevel = MCDE_PIXFETCH_LARGE_WTRMRKLVL; + else + pixelfetchwtrmrklevel = MCDE_PIXFETCH_MEDIUM_WTRMRKLVL; +#endif /* CONFIG_AV8100_SDTV */ + + if (regs->reset_buf_id) { + u32 sel_mod = MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL; + if (port->update_auto_trig && port->type == MCDE_PORTTYPE_DSI) { + switch (port->sync_src) { + case MCDE_SYNCSRC_OFF: + sel_mod = MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL; + break; + case MCDE_SYNCSRC_TE0: + case MCDE_SYNCSRC_TE1: + default: + sel_mod = MCDE_EXTSRC0CR_SEL_MOD_AUTO_TOGGLE; + } + } else if (port->type == MCDE_PORTTYPE_DPI) { + sel_mod = port->update_auto_trig ? + MCDE_EXTSRC0CR_SEL_MOD_AUTO_TOGGLE : + MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL; + } + + regs->reset_buf_id = false; mcde_wreg(MCDE_EXTSRC0CONF + idx * MCDE_EXTSRC0CONF_GROUPOFFSET, - MCDE_EXTSRC0CONF_BUF_ID(regs->buf_id) | - MCDE_EXTSRC0CONF_BUF_NB(2) | + MCDE_EXTSRC0CONF_BUF_ID(0) | + MCDE_EXTSRC0CONF_BUF_NB(nr_of_bufs) | MCDE_EXTSRC0CONF_PRI_OVLID(idx) | MCDE_EXTSRC0CONF_BPP(regs->bpp) | MCDE_EXTSRC0CONF_BGR(regs->bgr) | MCDE_EXTSRC0CONF_BEBO(regs->bebo) | - MCDE_EXTSRC0CONF_BEPO(FALSE)); + MCDE_EXTSRC0CONF_BEPO(false)); mcde_wreg(MCDE_EXTSRC0CR + idx * MCDE_EXTSRC0CR_GROUPOFFSET, - MCDE_EXTSRC0CR_SEL_MOD_ENUM(SOFTWARE_SEL) | + MCDE_EXTSRC0CR_SEL_MOD(sel_mod) | MCDE_EXTSRC0CR_MULTIOVL_CTRL_ENUM(PRIMARY) | - MCDE_EXTSRC0CR_FS_DIV_DISABLE(FALSE) | - MCDE_EXTSRC0CR_FORCE_FS_DIV(FALSE)); + MCDE_EXTSRC0CR_FS_DIV_DISABLE(false) | + MCDE_EXTSRC0CR_FORCE_FS_DIV(false)); mcde_wreg(MCDE_OVL0CR + idx * MCDE_OVL0CR_GROUPOFFSET, - MCDE_OVL0CR_OVLEN(TRUE) | - MCDE_OVL0CR_COLCCTRL_ENUM(DISABLED) | - MCDE_OVL0CR_CKEYGEN(FALSE) | - MCDE_OVL0CR_ALPHAPMEN(TRUE) | - MCDE_OVL0CR_OVLF(FALSE) | - MCDE_OVL0CR_OVLR(FALSE) | - MCDE_OVL0CR_OVLB(FALSE) | + MCDE_OVL0CR_OVLEN(true) | + MCDE_OVL0CR_COLCCTRL(regs->col_conv) | + MCDE_OVL0CR_CKEYGEN(false) | + MCDE_OVL0CR_ALPHAPMEN(true) | + MCDE_OVL0CR_OVLF(false) | + MCDE_OVL0CR_OVLR(false) | + MCDE_OVL0CR_OVLB(false) | MCDE_OVL0CR_FETCH_ROPC(0) | MCDE_OVL0CR_STBPRIO(0) | MCDE_OVL0CR_BURSTSIZE_ENUM(HW_8W) | @@ -387,12 +838,12 @@ static void update_overlay_registers(u8 idx, struct ovly_regs *regs, MCDE_OVL0CONF_LPF(lpf)); mcde_wreg(MCDE_OVL0CONF2 + idx * MCDE_OVL0CONF2_GROUPOFFSET, MCDE_OVL0CONF2_BP_ENUM(PER_PIXEL_ALPHA) | - MCDE_OVL0CONF2_ALPHAVALUE(128) | /* TODO: Allow setting? */ + MCDE_OVL0CONF2_ALPHAVALUE(0x80) | MCDE_OVL0CONF2_OPQ(regs->opq) | MCDE_OVL0CONF2_PIXOFF(lmrgn & 63) | MCDE_OVL0CONF2_PIXELFETCHERWATERMARKLEVEL(32)); mcde_wreg(MCDE_OVL0LJINC + idx * MCDE_OVL0LJINC_GROUPOFFSET, - regs->ljinc); + ljinc); mcde_wreg(MCDE_OVL0CROP + idx * MCDE_OVL0CROP_GROUPOFFSET, MCDE_OVL0CROP_TMRGN(tmrgn) | MCDE_OVL0CROP_LMRGN(lmrgn >> 6)); @@ -401,52 +852,225 @@ static void update_overlay_registers(u8 idx, struct ovly_regs *regs, MCDE_OVL0COMP_CH_ID(regs->ch_id) | MCDE_OVL0COMP_YPOS(regs->ypos) | MCDE_OVL0COMP_Z(regs->z)); - dbg_printk("Overlay registers setup, idx=%d\n", idx); + } + dev_vdbg(&mcde_dev->dev, "Overlay registers setup, idx=%d\n", idx); } -void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, - struct mcde_port *port) +static void update_overlay_address_registers(u8 idx, struct ovly_regs *regs) { - u8 idx = chnl_id; - if (!regs->floen) { - mcde_wfld(MCDE_CRC, C1EN, FALSE); + mcde_wreg(MCDE_EXTSRC0A0 + idx * MCDE_EXTSRC0A0_GROUPOFFSET, + regs->baseaddress0); + mcde_wreg(MCDE_EXTSRC0A1 + idx * MCDE_EXTSRC0A1_GROUPOFFSET, + regs->baseaddress1); +} + +#define MCDE_FLOWEN_MAX_TRIAL 6 + +static void disable_channel(struct mcde_chnl_state *chnl) +{ + int i; + const struct mcde_port *port = &chnl->port; + + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + + if (port->type == MCDE_PORTTYPE_DSI) + dsi_wfld(port->link, DSI_MCTL_MAIN_PHY_CTL, CLK_CONTINUOUS, + false); + + switch (chnl->id) { + case MCDE_CHNL_A: + mcde_wfld(MCDE_CRA0, FLOEN, false); + wait_for_channel(chnl); + for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) { + msleep(1); + if (!mcde_rfld(MCDE_CRA0, FLOEN)) { + dev_vdbg(&mcde_dev->dev, + "Flow (A) disable after >= %d ms\n", i); + goto break_switch; + } + } + dev_warn(&mcde_dev->dev, "%s: channel A timeout\n", __func__); + break; + case MCDE_CHNL_B: + mcde_wfld(MCDE_CRB0, FLOEN, false); + wait_for_channel(chnl); + for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) { + msleep(1); + if (!mcde_rfld(MCDE_CRB0, FLOEN)) { + dev_vdbg(&mcde_dev->dev, + "Flow (B) disable after >= %d ms\n", i); + goto break_switch; + } + } + dev_warn(&mcde_dev->dev, "%s: channel B timeout\n", __func__); + break; + case MCDE_CHNL_C0: + mcde_wfld(MCDE_CRC, C1EN, false); if (!mcde_rfld(MCDE_CRC, C2EN)) - mcde_wfld(MCDE_CRC, FLOEN, FALSE); + mcde_wfld(MCDE_CRC, FLOEN, false); + wait_for_channel(chnl); + break; + case MCDE_CHNL_C1: + mcde_wfld(MCDE_CRC, C2EN, false); + if (!mcde_rfld(MCDE_CRC, C1EN)) + mcde_wfld(MCDE_CRC, FLOEN, false); + wait_for_channel(chnl); + break; + } +break_switch: + chnl->continous_running = false; +#undef MCDE_FLOWEN_MAX_TRIAL +} + +static void enable_channel(struct mcde_chnl_state *chnl) +{ + const struct mcde_port *port = &chnl->port; + + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + + if (port->type == MCDE_PORTTYPE_DSI) + dsi_wfld(port->link, DSI_MCTL_MAIN_PHY_CTL, CLK_CONTINUOUS, + port->phy.dsi.clk_cont); + + switch (chnl->id) { + case MCDE_CHNL_A: + mcde_wfld(MCDE_CRA0, FLOEN, true); + break; + case MCDE_CHNL_B: + mcde_wfld(MCDE_CRB0, FLOEN, true); + break; + case MCDE_CHNL_C0: + mcde_wfld(MCDE_CRC, POWEREN, true); + mcde_wfld(MCDE_CRC, FLOEN, true); + mcde_wfld(MCDE_CRC, C1EN, true); + break; + case MCDE_CHNL_C1: + mcde_wfld(MCDE_CRC, POWEREN, true); + mcde_wfld(MCDE_CRC, FLOEN, true); + mcde_wfld(MCDE_CRC, C2EN, true); + break; } +} + +/* TODO get from register */ +#define MCDE_CLK_FREQ_MHZ 160 + +void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, + struct mcde_port *port, enum mcde_fifo fifo, + struct mcde_video_mode *video_mode) +{ + u8 idx = chnl_id; + u32 out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_FORMATTER; + u32 src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE; + + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + + print_vmode(video_mode); /* Channel */ + if (port->update_auto_trig && port->type == MCDE_PORTTYPE_DSI) { + switch (port->sync_src) { + case MCDE_SYNCSRC_TE0: + out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC0; + src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT; + break; + case MCDE_SYNCSRC_OFF: + src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE; + break; + case MCDE_SYNCSRC_TE1: + default: + out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC1; + src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT; + } + } else if (port->type == MCDE_PORTTYPE_DPI) { + src_synch = port->update_auto_trig ? + MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT : + MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE; + } + mcde_wreg(MCDE_CHNL0CONF + idx * MCDE_CHNL0CONF_GROUPOFFSET, MCDE_CHNL0CONF_PPL(regs->ppl-1) | MCDE_CHNL0CONF_LPF(regs->lpf-1)); mcde_wreg(MCDE_CHNL0STAT + idx * MCDE_CHNL0STAT_GROUPOFFSET, - MCDE_CHNL0STAT_CHNLBLBCKGND_EN(FALSE) | - MCDE_CHNL0STAT_CHNLRD(TRUE)); - mcde_wreg(MCDE_CHNL0SYNCHMOD + idx * MCDE_CHNL0SYNCHMOD_GROUPOFFSET, - MCDE_CHNL0SYNCHMOD_SRC_SYNCH_ENUM(SOFTWARE) | /* TODO: */ - MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_ENUM(FORMATTER)); + MCDE_CHNL0STAT_CHNLBLBCKGND_EN(true) | + MCDE_CHNL0STAT_CHNLRD(true)); + mcde_wreg(MCDE_CHNL0SYNCHMOD + + idx * MCDE_CHNL0SYNCHMOD_GROUPOFFSET, + MCDE_CHNL0SYNCHMOD_SRC_SYNCH(src_synch) | + MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC(out_synch_src)); mcde_wreg(MCDE_CHNL0BCKGNDCOL + idx * MCDE_CHNL0BCKGNDCOL_GROUPOFFSET, - MCDE_CHNL0BCKGNDCOL_B(255) | /* TODO: Temp */ - MCDE_CHNL0BCKGNDCOL_G(255) | - MCDE_CHNL0BCKGNDCOL_R(255)); - - mcde_wfld(MCDE_CRC, POWEREN, TRUE); - mcde_wfld(MCDE_CRC, FLOEN, TRUE); - mcde_wfld(MCDE_CRC, C1EN, TRUE); + MCDE_CHNL0BCKGNDCOL_R(video_mode->bckcol[0]) | + MCDE_CHNL0BCKGNDCOL_G(video_mode->bckcol[1]) | + MCDE_CHNL0BCKGNDCOL_B(video_mode->bckcol[2])); + + switch (chnl_id) { + case MCDE_CHNL_A: + mcde_wreg(MCDE_CRA1, + MCDE_CRA1_PCD(regs->pcd) | + MCDE_CRA1_CLKSEL(regs->clksel) | + MCDE_CRA1_CDWIN(regs->cdwin) | + MCDE_CRA1_OUTBPP(bpp2outbpp(regs->bpp)) | + MCDE_CRA1_BCD(regs->bcd) | + MCDE_CRA1_CLKTYPE(regs->internal_clk) + ); + break; + case MCDE_CHNL_B: + mcde_wreg(MCDE_CRB1, + MCDE_CRB1_PCD(regs->pcd) | + MCDE_CRB1_CLKSEL(regs->clksel) | + MCDE_CRB1_CDWIN(regs->cdwin) | + MCDE_CRB1_OUTBPP(bpp2outbpp(regs->bpp)) | + MCDE_CRB1_BCD(regs->bcd) | + MCDE_CRB1_CLKTYPE(regs->internal_clk) + ); + break; + default: + break; + } /* Formatter */ - { + if (port->type == MCDE_PORTTYPE_DSI) { u8 fidx = 2 * port->link + port->ifc; u32 temp, packet; + /* + * pkt_div is used to avoid underflow in output fifo for + * large packets + */ + u32 pkt_div = 1; + u32 dsi_delay0 = 0; + u32 screen_ppl, screen_lpf; + + screen_ppl = video_mode->xres; + screen_lpf = video_mode->yres; + + if (screen_ppl == SCREEN_PPL_HIGH) { + pkt_div = (screen_ppl - 1) / + get_output_fifo_size(fifo) + 1; + } else { + pkt_div = screen_ppl / + (get_output_fifo_size(fifo) * 2) + 1; + } + + if (video_mode->interlaced) + screen_lpf /= 2; + + /* pkt_delay_progressive = pixelclock * htot / + * (1E12 / 160E6) / pkt_div */ + dsi_delay0 = (video_mode->pixclock + 1) * + (video_mode->xres + video_mode->hbp + + video_mode->hfp) / + (1000000 / MCDE_CLK_FREQ_MHZ) / pkt_div; temp = mcde_rreg(MCDE_DSIVID0CONF0 + fidx * MCDE_DSIVID0CONF0_GROUPOFFSET); mcde_wreg(MCDE_DSIVID0CONF0 + fidx * MCDE_DSIVID0CONF0_GROUPOFFSET, (temp & ~MCDE_DSIVID0CONF0_PACKING_MASK) | MCDE_DSIVID0CONF0_PACKING(regs->dsipacking)); - packet = ((regs->ppl * regs->bpp) >> 3) + 1; /* 1==CMD8 */ + /* 1==CMD8 */ + packet = ((screen_ppl / pkt_div * regs->bpp) >> 3) + 1; mcde_wreg(MCDE_DSIVID0FRAME + fidx * MCDE_DSIVID0FRAME_GROUPOFFSET, - MCDE_DSIVID0FRAME_FRAME(packet * regs->lpf)); + MCDE_DSIVID0FRAME_FRAME(packet * pkt_div * screen_lpf)); mcde_wreg(MCDE_DSIVID0PKT + fidx * MCDE_DSIVID0PKT_GROUPOFFSET, MCDE_DSIVID0PKT_PACKET(packet)); mcde_wreg(MCDE_DSIVID0SYNC + @@ -459,13 +1083,78 @@ void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, MCDE_DSIVID0CMDW_CMDW_CONTINUE(DCS_CMD_WRITE_CONTINUE)); mcde_wreg(MCDE_DSIVID0DELAY0 + fidx * MCDE_DSIVID0DELAY0_GROUPOFFSET, - MCDE_DSIVID0DELAY0_INTPKTDEL(0)); + MCDE_DSIVID0DELAY0_INTPKTDEL(dsi_delay0)); mcde_wreg(MCDE_DSIVID0DELAY1 + fidx * MCDE_DSIVID0DELAY1_GROUPOFFSET, MCDE_DSIVID0DELAY1_TEREQDEL(0) | MCDE_DSIVID0DELAY1_FRAMESTARTDEL(0)); } - dbg_printk("Channel registers setup, chnl=%d\n", chnl_id); + debug("Channel registers setup, chnl=%d\n", chnl_id); +} + +void mcde_chnl_set_update_area(struct mcde_chnl_state *chnl, + u16 x, u16 y, u16 w, u16 h) +{ + if (!chnl->inuse) { + debug("%s: channel in use chnl=%p\n", __func__, chnl); + return; + } + + chnl->update_x = x; + chnl->update_y = y; + chnl->update_w = w; + chnl->update_h = h; +} + +/* DSI */ + +int mcde_dsi_dcs_write(struct mcde_port *port, u8 cmd, u8* data, int len) +{ + int i; + u32 wrdat[4] = { 0, 0, 0, 0 }; + u32 settings; + u8 link = port->link; + u8 virt_id = port->phy.dsi.virt_id; + + debug("Entering %s\n", __func__); + if (len > MCDE_MAX_DCS_WRITE) + return -EINVAL; + + wrdat[0] = cmd; + for (i = 1; i <= len; i++) + wrdat[i>>2] |= ((u32)data[i-1] << ((i & 3) * 8)); + + settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(WRITE) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(len > 1) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(len+1) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(true); + if (len == 0) + settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( + DCS_SHORT_WRITE_0); + else if (len == 1) + settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( + DCS_SHORT_WRITE_1); + else + settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( + DCS_LONG_WRITE); + + dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings); + dsi_wreg(link, DSI_DIRECT_CMD_WRDAT0, wrdat[0]); + if (len > 3) + dsi_wreg(link, DSI_DIRECT_CMD_WRDAT1, wrdat[1]); + if (len > 7) + dsi_wreg(link, DSI_DIRECT_CMD_WRDAT2, wrdat[2]); + if (len > 11) + dsi_wreg(link, DSI_DIRECT_CMD_WRDAT3, wrdat[3]); + dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); + dsi_wreg(link, DSI_DIRECT_CMD_SEND, true); + mdelay(10); + + dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0); + dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); + + return 0; } /* MCDE channels */ @@ -475,109 +1164,251 @@ struct mcde_chnl_state *mcde_chnl_get(enum mcde_chnl chnl_id, int i; struct mcde_chnl_state *chnl = NULL; + debug("%s: Entering chnl_id=%d\n", __func__, chnl_id); /* Allocate channel */ for (i = 0; i < ARRAY_SIZE(channels); i++) { if (chnl_id == channels[i].id) chnl = &channels[i]; } if (!chnl) { - printf("Invalid channel, chnl=%d\n", chnl_id); + dev_dbg(&mcde_dev->dev, "Invalid channel, chnl=%d\n", chnl_id); return ERR_PTR(-EINVAL); } if (chnl->inuse) { - printf("Channel in use, chnl=%d\n", chnl_id); + dev_dbg(&mcde_dev->dev, "Channel in use, chnl=%d\n", chnl_id); return ERR_PTR(-EBUSY); } chnl->port = *port; chnl->fifo = fifo; - if (update_channel_static_registers(chnl) < 0) return ERR_PTR(-EINVAL); - chnl->synchronized_update = FALSE; + chnl->synchronized_update = false; + chnl->pix_fmt = port->pixel_format; chnl->update_x = 0; chnl->update_y = 0; chnl->update_w = 0; chnl->update_h = 0; mcde_chnl_apply(chnl); - chnl->inuse = TRUE; + chnl->inuse = true; + debug("%s: Leaving chnl=%p\n", __func__, (void *)chnl); return chnl; } -void mcde_chnl_set_update_area(struct mcde_chnl_state *chnl, - u16 x, u16 y, u16 w, u16 h) +void mcde_chnl_set_pixel_format(struct mcde_chnl_state *chnl, + enum mcde_port_pix_fmt pix_fmt) { - if (!chnl->inuse) + if (!chnl->inuse) { + debug("%s: channel in use ovly=%p\n", __func__, chnl); return; + } - chnl->update_x = x; - chnl->update_y = y; - chnl->update_w = w; - chnl->update_h = h; + chnl->pix_fmt = pix_fmt; } -void mcde_chnl_set_pixel_format(struct mcde_chnl_state *chnl, - enum mcde_port_pix_fmt pix_fmt) +int mcde_chnl_enable_synchronized_update(struct mcde_chnl_state *chnl, + u8 enable) { if (!chnl->inuse) - return; + return -EINVAL; + chnl->synchronized_update = enable; + return 0; +} - chnl->pix_fmt = pix_fmt; +int mcde_chnl_set_power_mode(struct mcde_chnl_state *chnl, + enum mcde_display_power_mode power_mode) +{ + if (!chnl->inuse) + return -EINVAL; + + chnl->power_mode = power_mode; + return 0; } void mcde_chnl_apply(struct mcde_chnl_state *chnl) { - t_bool enable; - if (!chnl->inuse) + u8 enable; + debug("Entering %s\n", __func__); + if (!chnl->inuse) { + debug("%s: channel not in use chnl=%p\n", __func__, chnl); return; + } - enable = chnl->update_w > 0 && chnl->update_h > 0; + enable = (chnl->update_w > 0) && (chnl->update_h > 0); chnl->regs.floen = enable; chnl->regs.ppl = chnl->update_w; chnl->regs.lpf = chnl->update_h; chnl->regs.bpp = portfmt2bpp(chnl->pix_fmt); - chnl->regs.dsipacking = portfmt2dsipacking(chnl->pix_fmt); + chnl->regs.synchronized_update = chnl->synchronized_update; + if (chnl->port.type == MCDE_PORTTYPE_DSI) { + chnl->regs.clksel = MCDE_CRA1_CLKSEL_166MHZ; + chnl->regs.dsipacking = portfmt2dsipacking(chnl->pix_fmt); + } else if (chnl->port.type == MCDE_PORTTYPE_DPI) { + if (chnl->port.phy.dpi.tv_mode) { + chnl->regs.internal_clk = false; + if (chnl->id == MCDE_CHNL_A) + chnl->regs.clksel = MCDE_CRA1_CLKSEL_EXT_TV1; + else + chnl->regs.clksel = MCDE_CRA1_CLKSEL_EXT_TV2; + } else { + chnl->regs.internal_clk = true; + chnl->regs.clksel = MCDE_CRA1_CLKSEL_LCD; + chnl->regs.cdwin = portfmt2cdwin(chnl->pix_fmt); + chnl->regs.bcd = (chnl->port.phy.dpi.clock_div < 2); + if (!chnl->regs.bcd) + chnl->regs.pcd = + chnl->port.phy.dpi.clock_div - 2; + } + dpi_video_mode_apply(chnl); + } chnl->transactionid++; - dbg_printk("Channel applied, chnl=%d\n", chnl->id); + + dev_vdbg(&mcde_dev->dev, "Channel applied, chnl=%d\n", chnl->id); + return; } -void mcde_chnl_update(struct mcde_chnl_state *chnl) +static void chnl_update_registers(struct mcde_chnl_state *chnl) { - struct mcde_ovly_state *ovly; + /* REVIEW: Move content to update_channel_register */ + /* and remove this one */ + if (chnl->port.type == MCDE_PORTTYPE_DPI) + update_dpi_registers(chnl->id, &chnl->tv_regs); + if (chnl->id == MCDE_CHNL_A || chnl->id == MCDE_CHNL_B) + update_col_registers(chnl->id, &chnl->col_regs); + update_channel_registers(chnl->id, &chnl->regs, &chnl->port, + chnl->fifo, &chnl->vmode); + + chnl->transactionid_regs = chnl->transactionid; +} - if (!chnl->inuse || !chnl->regs.floen) - return; +static void chnl_update_continous(struct mcde_chnl_state *chnl) +{ + debug("%s: Entering\n", __func__); + if (!chnl->continous_running) { + if (chnl->transactionid_regs < chnl->transactionid) + chnl_update_registers(chnl); + if (chnl->port.sync_src == MCDE_SYNCSRC_TE0) + mcde_wfld(MCDE_CRC, SYCEN0, true); + else if (chnl->port.sync_src == MCDE_SYNCSRC_TE1) + mcde_wfld(MCDE_CRC, SYCEN1, true); + chnl->continous_running = true; + /* + * For main and secondary display, + * FLOWEN has to be set before a SOFTWARE TRIG + * Otherwise not overlay interrupt is triggerd + */ + enable_channel(chnl); + if (chnl->port.type == MCDE_PORTTYPE_DSI && + chnl->port.sync_src == MCDE_SYNCSRC_OFF) { + /* + * not used in u-boot + mod_timer(&chnl->auto_sync_timer, + jiffies + + msecs_to_jiffies(MCDE_AUTO_SYNC_WATCHDOG * 1000)); + */ + } + } +} +static void chnl_update_non_continous(struct mcde_chnl_state *chnl) +{ + debug("%s\n", __func__); /* Commit settings to registers */ - ovly = chnl->ovly0; - if (ovly->transactionid_regs < ovly->transactionid || - chnl->transactionid_regs < chnl->transactionid) { - update_overlay_registers(ovly->idx, &ovly->regs, - chnl->regs.x, chnl->regs.y, - chnl->regs.ppl, chnl->regs.lpf); - ovly->transactionid_regs = ovly->transactionid; + wait_for_channel(chnl); + chnl_update_registers(chnl); + /* + * For main and secondary display, + * FLOWEN has to be set before a SOFTWARE TRIG + * Otherwise not overlay interrupt is triggerd + * However FLOWEN must not be triggered before SOFTWARE TRIG + * if rotation is enabled + */ + enable_channel(chnl); + /* TODO: look at port sync source and synched_update */ + if (chnl->regs.synchronized_update && + chnl->power_mode == MCDE_DISPLAY_PM_ON) { + if (chnl->port.type == MCDE_PORTTYPE_DSI && + chnl->port.sync_src == MCDE_SYNCSRC_BTA) { + while (dsi_rfld(chnl->port.link, DSI_CMD_MODE_STS, + CSM_RUNNING)) + udelay(100); + /*dsi_te_request(chnl); not for u-boot */ + } + } else { + mcde_wreg(MCDE_CHNL0SYNCHSW + + chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET, + MCDE_CHNL0SYNCHSW_SW_TRIG(true)); + dev_vdbg(&mcde_dev->dev, "Channel update (no sync), chnl=%d\n", + chnl->id); } - ovly = chnl->ovly1; - if (ovly && ( - ovly->transactionid_regs < ovly->transactionid || - chnl->transactionid_regs < chnl->transactionid)) { - update_overlay_registers(ovly->idx, &ovly->regs, - chnl->regs.x, chnl->regs.y, - chnl->regs.ppl, chnl->regs.lpf); + if (chnl->power_mode == MCDE_DISPLAY_PM_ON) + enable_channel(chnl); +} + +static void chnl_update_overlay(struct mcde_chnl_state *chnl, + struct mcde_ovly_state *ovly) +{ + debug("%s: chnl=%p ovly=%p\n", __func__, chnl, ovly); + if (!ovly) + return; + if (!ovly || (ovly->transactionid_regs >= ovly->transactionid && + chnl->transactionid_regs >= chnl->transactionid)) + return; + + update_overlay_address_registers(ovly->idx, &ovly->regs); + if (ovly->regs.reset_buf_id) { + if (!chnl->continous_running) + wait_for_overlay(ovly); + + update_overlay_registers(ovly->idx, &ovly->regs, &chnl->port, + chnl->fifo, chnl->regs.x, chnl->regs.y, + chnl->regs.ppl, chnl->regs.lpf, ovly->stride, + chnl->vmode.interlaced); + ovly->transactionid_regs = ovly->transactionid; + } else if (chnl->continous_running) { ovly->transactionid_regs = ovly->transactionid; + wait_for_overlay(ovly); } - if (chnl->transactionid_regs < chnl->transactionid) { - update_channel_registers(chnl->id, &chnl->regs, &chnl->port); - chnl->transactionid_regs = chnl->transactionid; +} + +int mcde_chnl_update(struct mcde_chnl_state *chnl, + struct mcde_rectangle *update_area) +{ + debug("%s: chnl=%p rect=(%d, %d, %d, %d)\n", __func__, chnl, + update_area->x, update_area->y, update_area->w, update_area->h); + + /* TODO: lock & make wait->trig async */ + if (!chnl->inuse || !update_area + || (update_area->w == 0 && update_area->h == 0)) { + return -EINVAL; } - if (chnl->regs.floen) - mcde_wreg(MCDE_CHNL0SYNCHSW + - chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET, - MCDE_CHNL0SYNCHSW_SW_TRIG(TRUE)); - dbg_printk("Channel updated, chnl=%d\n", chnl->id); + chnl->regs.x = update_area->x; + chnl->regs.y = update_area->y; + /* TODO Crop against video_mode.xres and video_mode.yres */ + chnl->regs.ppl = update_area->w; + chnl->regs.lpf = update_area->h; + if (chnl->port.type == MCDE_PORTTYPE_DPI && + chnl->port.phy.dpi.tv_mode) { + chnl->regs.ppl -= 2 * MCDE_CONFIG_TVOUT_HBORDER; + /* subtract double borders, ie. per field */ + chnl->regs.lpf -= 4 * MCDE_CONFIG_TVOUT_VBORDER; + } else if (chnl->port.type == MCDE_PORTTYPE_DSI && + chnl->vmode.interlaced) + chnl->regs.lpf /= 2; + + chnl_update_overlay(chnl, chnl->ovly0); + chnl_update_overlay(chnl, chnl->ovly1); + + if (chnl->port.update_auto_trig) + chnl_update_continous(chnl); + else + chnl_update_non_continous(chnl); + + dev_vdbg(&mcde_dev->dev, "Channel updated, chnl=%d\n", chnl->id); + return 0; } /* MCDE overlays */ @@ -585,18 +1416,23 @@ struct mcde_ovly_state *mcde_ovly_get(struct mcde_chnl_state *chnl) { struct mcde_ovly_state *ovly; - if (!chnl->inuse) + if (!chnl->inuse) { + debug("%s: channel in use ovly=%p\n", __func__, chnl); return ERR_PTR(-EINVAL); + } if (!chnl->ovly0->inuse) ovly = chnl->ovly0; else if (chnl->ovly1 && !chnl->ovly1->inuse) ovly = chnl->ovly1; - else + else { + debug("%s: overlay in use ovly=%p\n", __func__, ovly); ovly = ERR_PTR(-EBUSY); + } + if (!IS_ERR(ovly)) { - ovly->inuse = TRUE; + ovly->inuse = true; ovly->paddr = 0; ovly->stride = 0; ovly->pix_fmt = MCDE_OVLYPIXFMT_RGB565; @@ -615,8 +1451,10 @@ struct mcde_ovly_state *mcde_ovly_get(struct mcde_chnl_state *chnl) void mcde_ovly_set_source_buf(struct mcde_ovly_state *ovly, u32 paddr) { - if (!ovly->inuse) + if (!ovly->inuse) { + debug("%s: overlay not in use ovly=%p\n", __func__, ovly); return; + } ovly->paddr = paddr; } @@ -624,8 +1462,10 @@ void mcde_ovly_set_source_buf(struct mcde_ovly_state *ovly, u32 paddr) void mcde_ovly_set_source_info(struct mcde_ovly_state *ovly, u32 stride, enum mcde_ovly_pix_fmt pix_fmt) { - if (!ovly->inuse) + if (!ovly->inuse) { + debug("%s: overlay not in use ovly=%p\n", __func__, ovly); return; + } ovly->stride = stride; ovly->pix_fmt = pix_fmt; @@ -634,8 +1474,10 @@ void mcde_ovly_set_source_info(struct mcde_ovly_state *ovly, void mcde_ovly_set_source_area(struct mcde_ovly_state *ovly, u16 x, u16 y, u16 w, u16 h) { - if (!ovly->inuse) + if (!ovly->inuse) { + debug("%s: overlay not in use ovly=%p\n", __func__, ovly); return; + } ovly->src_x = x; ovly->src_y = y; @@ -645,116 +1487,76 @@ void mcde_ovly_set_source_area(struct mcde_ovly_state *ovly, void mcde_ovly_set_dest_pos(struct mcde_ovly_state *ovly, u16 x, u16 y, u8 z) { - if (!ovly->inuse) + if (!ovly->inuse) { + debug("%s: overlay not in use ovly=%p\n", __func__, ovly); return; + } ovly->dst_x = x; ovly->dst_y = y; ovly->dst_z = z; } -void mcde_ovly_apply(struct mcde_ovly_state *ovly) +void mcde_ovly_set_pixfmt(struct mcde_ovly_state *ovly, + enum mcde_ovly_pix_fmt fmt) { - if (!ovly->inuse) + if (!ovly->inuse) { + debug("%s: overlay in use ovly=%p\n", __func__, ovly); return; + } + + ovly->pix_fmt = fmt; +} +void mcde_ovly_apply(struct mcde_ovly_state *ovly) +{ + if (!ovly->inuse) { + debug("%s: overlay not in use ovly=%p\n", __func__, ovly); + return; + } ovly->regs.ch_id = ovly->chnl->id; ovly->regs.enabled = ovly->paddr != 0; - if (ovly->regs.buf_id) - ovly->regs.baseaddress0 = ovly->paddr; - else - ovly->regs.baseaddress1 = ovly->paddr; - ovly->regs.buf_id = !ovly->regs.buf_id; + ovly->regs.baseaddress0 = ovly->paddr; + ovly->regs.baseaddress1 = ovly->paddr + ovly->stride; + /* TODO set to true if interlaced */ + ovly->regs.reset_buf_id = !ovly->chnl->continous_running; switch (ovly->pix_fmt) { case MCDE_OVLYPIXFMT_RGB565: ovly->regs.bits_per_pixel = 16; ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_RGB565; - ovly->regs.bgr = FALSE; - ovly->regs.bebo = FALSE; - ovly->regs.opq = TRUE; + ovly->regs.bgr = false; + ovly->regs.bebo = false; + ovly->regs.opq = true; break; case MCDE_OVLYPIXFMT_RGB888: ovly->regs.bits_per_pixel = 24; ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_RGB888; - ovly->regs.bgr = FALSE; - ovly->regs.bebo = FALSE; - ovly->regs.opq = TRUE; + ovly->regs.bgr = false; + ovly->regs.bebo = false; + ovly->regs.opq = true; break; default: break; } ovly->regs.ppl = ovly->w; ovly->regs.lpf = ovly->h; - ovly->regs.ljinc = ovly->stride; ovly->regs.cropx = ovly->src_x; ovly->regs.cropy = ovly->src_y; ovly->regs.xpos = ovly->dst_x; ovly->regs.ypos = ovly->dst_y; - ovly->regs.z = ovly->dst_z > 0; /* 0 or 1 */ + ovly->regs.z = ovly->dst_z > 0; ovly->transactionid = ++ovly->chnl->transactionid; - dbg_printk("Overlay applied, chnl=%d\n", ovly->chnl->id); + dev_vdbg(&mcde_dev->dev, "Overlay applied, chnl=%d\n", ovly->chnl->id); } -/* DSI */ -int mcde_dsi_dcs_write(struct mcde_port *port, u8 cmd, u8* data, int len) -{ - int i; - u32 wrdat[4] = { 0, 0, 0, 0 }; - u32 settings; - u8 link = port->link; - u8 virt_id = port->phy.dsi.virt_id; - - if (len > MCDE_MAX_DCS_WRITE) - return -EINVAL; - - wrdat[0] = cmd; - for (i = 1; i <= len; i++) - wrdat[i>>2] |= (data[i-1] << (i & 3)); - - settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(WRITE) | - DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(len > 1) | - DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) | - DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(len+1) | - DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(TRUE); - if (len == 0) - settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( - DCS_SHORT_WRITE_0); - else if (len == 1) - settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( - DCS_SHORT_WRITE_1); - else - settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( - DCS_LONG_WRITE); - - dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings); - dsi_wreg(link, DSI_DIRECT_CMD_WRDAT0, wrdat[0]); - if (len > 3) - dsi_wreg(link, DSI_DIRECT_CMD_WRDAT1, wrdat[1]); - if (len > 7) - dsi_wreg(link, DSI_DIRECT_CMD_WRDAT2, wrdat[2]); - if (len > 11) - dsi_wreg(link, DSI_DIRECT_CMD_WRDAT3, wrdat[3]); - dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); - dsi_wreg(link, DSI_DIRECT_CMD_SEND, TRUE); - mdelay(10); - - dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0); - dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); - return 0; -} - -/* -* Used by MCDE to setup all necessary PRCMU registers -*/ - /* Level shifter and clamp control registers */ #define PRCM_MMIP_LS_CLAMP_SET (U8500_PRCMU_BASE + 0x420) #define PRCM_MMIP_LS_CLAMP_CLR (U8500_PRCMU_BASE + 0x424) -/* PRCMU clock/PLL/reset registers */ #define PRCM_PLLDSI_FREQ (U8500_PRCMU_BASE + 0x500) #define PRCM_PLLDSI_ENABLE (U8500_PRCMU_BASE + 0x504) +#define PRCM_YYCLKEN0_MGT_SET (U8500_PRCMU_BASE + 0x510) #define PRCM_LCDCLK_MGT (U8500_PRCMU_BASE + 0x044) #define PRCM_MCDECLK_MGT (U8500_PRCMU_BASE + 0x064) #define PRCM_HDMICLK_MGT (U8500_PRCMU_BASE + 0x058) @@ -774,6 +1576,10 @@ int mcde_dsi_dcs_write(struct mcde_port *port, u8 cmd, u8* data, int len) /* Miscellaneous unit registers */ #define PRCM_DSI_SW_RESET (U8500_PRCMU_BASE + 0x324) +/* + * Used by MCDE to setup all necessary PRCMU registers + */ + #define PRCMU_CLAMP_DSS_DSIPLL 0x00600C00 #define PRCMU_RESET_DSS 0x0000000C #define PRCMU_ENABLE_DSS_MEM 0x00200000 @@ -782,22 +1588,38 @@ int mcde_dsi_dcs_write(struct mcde_port *port, u8 cmd, u8* data, int len) #define PRCMU_UNCLAMP_DSS_DSIPLL 0x00600C00 #define PRCMU_POWER_ON_DSI 0x00008000 +/* PRCMU clock/PLL/reset registers */ +#define PRCMU_CLK_PLL_DIV_SHIFT 0 +#define PRCMU_CLK_PLL_SW_SHIFT 5 +#define PRCMU_CLK_EN (1 << 8) + +#define PRCMU_DSI_CLOCK_SETTING 0x00000148 +#define PRCMU_LCDCLKEN 0x20000 /* bit 17 */ + #define PRCMU_MCDE_CLOCK_SETTING 0x00000125 +/* + * from linux prcmu-db8500.c: + * Set DPI clock to 50000000 Hz + */ +#define PRCMU_DPI_CLOCK_SETTING (PRCMU_CLK_EN | \ + (2 << PRCMU_CLK_PLL_SW_SHIFT) | \ + (8 << PRCMU_CLK_PLL_DIV_SHIFT)) + +#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000F00 +#define PRCMU_PLLDSI_FREQ_SETTING 0x00020123 #define PRCMU_ENABLE_PLLDSI 0x00000001 #define PRCMU_RELEASE_RESET_DSS 0x0000400C #define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000202 +#define PRCMU_ENABLE_ESCAPE_CLOCK 0x07030101 #define PRCMU_DSI_RESET_SW 0x00000007 -#define PRCMU_DSI_CLOCK_SETTING 0x00000148 -#define PRCMU_PLLDSI_FREQ_SETTING 0x00020123 -#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000F00 -#define PRCMU_ENABLE_ESCAPE_CLOCK 0x07030101 +#define PRCMU_MCDE_DELAY 2 -#define PRCMU_MCDE_DELAY 2 void mcde_enable_dss(void) { u32 temp; + debug("Entering %s\n", __func__); /* Clamp DSS out, DSIPLL in/out, (why not DSS input?) */ writel(PRCMU_CLAMP_DSS_DSIPLL, PRCM_MMIP_LS_CLAMP_SET); mdelay(PRCMU_MCDE_DELAY); @@ -829,6 +1651,8 @@ void mcde_enable_dss(void) /* PLLDIV=14, PLLSW=2, CLKEN=1 */ writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT); mdelay(PRCMU_MCDE_DELAY); + writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT); + mdelay(PRCMU_MCDE_DELAY); /* D=43, N=1, R=4, SELDIV2=0 */ writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ); @@ -850,28 +1674,142 @@ void mcde_enable_dss(void) mdelay(PRCMU_MCDE_DELAY); } -int mcde_init(u8 num_data_lanes) +void update_mcde_registers(struct mcde_platform_data *pdata) +{ + /* Setup output muxing */ + mcde_wreg(MCDE_CONF0, + MCDE_CONF0_IFIFOCTRLWTRMRKLVL(7) | + MCDE_CONF0_OUTMUX0(pdata->outmux[0]) | + MCDE_CONF0_OUTMUX1(pdata->outmux[1]) | + MCDE_CONF0_OUTMUX2(pdata->outmux[2]) | + MCDE_CONF0_OUTMUX3(pdata->outmux[3]) | + MCDE_CONF0_OUTMUX4(pdata->outmux[4]) | + pdata->syncmux); + + /* Enable channel VCMP interrupts */ + mcde_wreg(MCDE_IMSCPP, + MCDE_IMSCPP_VCMPAIM(true) | + MCDE_IMSCPP_VCMPBIM(true) | + MCDE_IMSCPP_VCMPC0IM(true) | + MCDE_IMSCPP_VCMPC1IM(true)); + + /* Enable overlay fetch done interrupts */ + mcde_wfld(MCDE_IMSCOVL, OVLFDIM, 0x3f); + + /* Setup sync pulse length */ + mcde_wreg(MCDE_VSCRC0, + MCDE_VSCRC0_VSPMIN(1) | + MCDE_VSCRC0_VSPMAX(0xff)); + mcde_wreg(MCDE_VSCRC1, + MCDE_VSCRC1_VSPMIN(1) | + MCDE_VSCRC1_VSPMAX(0xff)); +} + +int mcde_probe(u8 num_dsilinks, struct mcde_platform_data *pdata) { + int ret = 0; int i; - for (i = 0; i < ARRAY_SIZE(channels); i++) { - channels[i].ovly0->chnl = &channels[i]; - if (channels[i].ovly1) - channels[i].ovly1->chnl = &channels[i]; - } + u8 major_version; + u8 minor_version; + u8 development_version; + + printf("MCDE subsystem init begin\n"); - dsiio = malloc(num_data_lanes * sizeof(*dsiio)); - if (!dsiio) { - printf("%s: Failed to malloc dsiio\n", __func__); + if (!pdata) { + dev_dbg(&pdev->dev, "No platform data\n"); return -EINVAL; } + if (num_dsilinks > 0) { + dsiio = malloc(num_dsilinks * sizeof(*dsiio)); + if (!dsiio) { + ret = -ENOMEM; + printf("%s: Failed to malloc dsiio\n", __func__); + goto failed_dsi_alloc; + } + + mcdeio = (u8 *)CFG_MCDE_BASE; + debug("MCDE iomap: 0x%.8X\n", (u32)mcdeio); + for (i = 0; i < num_dsilinks; i++) { + dsiio[i] = (u8 *)(CFG_DSI_BASE + i*0x1000); + debug("MCDE DSI%d iomap: 0x%.8X\n", i, (u32)dsiio[i]); + } + } - mcdeio = (u8 *)CFG_MCDE_BASE; - dbg_printk("MCDE iomap: 0x%.8X\n", (u32)mcdeio); - for (i = 0; i < num_data_lanes; i++) { - dsiio[i] = (u8 *)(CFG_DSI_BASE + i*0x1000); - dbg_printk("MCDE DSI%d iomap: 0x%.8X\n", i, (u32)dsiio[i]); + mcde_enable_dss(); + + update_mcde_registers(pdata); + + major_version = MCDE_REG2VAL(MCDE_PID, MAJOR_VERSION, + mcde_rreg(MCDE_PID)); + minor_version = MCDE_REG2VAL(MCDE_PID, MINOR_VERSION, + mcde_rreg(MCDE_PID)); + development_version = MCDE_REG2VAL(MCDE_PID, DEVELOPMENT_VERSION, + mcde_rreg(MCDE_PID)); + + dev_info(&mcde_dev->dev, "MCDE HW revision %u.%u.%u.%u\n", + major_version, minor_version, development_version, + mcde_rfld(MCDE_PID, METALFIX_VERSION)); + + if (major_version == 3 && minor_version == 0 && + development_version >= 8) { + hardware_version = MCDE_CHIP_VERSION_3_0_8; + dev_info(&mcde_dev->dev, "V2 HW\n"); + } else if (major_version == 3 && minor_version == 0 && + development_version >= 5) { + hardware_version = MCDE_CHIP_VERSION_3_0_5; + dev_info(&mcde_dev->dev, "V1 HW\n"); + } else { + debug("Unsupported HW version\n"); + ret = ENODEV; + goto failed_hardware_version; } + + printf("MCDE subsystem init done\n"); return 0; + +failed_hardware_version: + free(dsiio); + dsiio = NULL; + +failed_dsi_alloc: + return ret; +} + +void mcde_init(void) +{ + int i; + int j; + + debug("%s\n", __func__); + /* + * clear and init static data + * link channel->overlay + */ + for (i = 0, j = 0; i < ARRAY_SIZE(channels); i++) { + memset(&channels[i], 0, sizeof(struct mcde_chnl_state)); + if (j < 6) + channels[i].ovly0 = &overlays[j++]; + if (j < 6) + channels[i].ovly1 = &overlays[j++]; + } + channels[0].id = MCDE_CHNL_A; + channels[1].id = MCDE_CHNL_B; + channels[2].id = MCDE_CHNL_C0; + channels[3].id = MCDE_CHNL_C1; + + for (i = 0; i < ARRAY_SIZE(overlays); i++) { + memset(&overlays[i], 0, sizeof(struct mcde_ovly_state)); + overlays[i].idx = i; + } + + /* link overlay->channel */ + for (i = 0; i < ARRAY_SIZE(channels); i++) { + channels[i].ovly0->chnl = &channels[i]; + if (channels[i].ovly1) + channels[i].ovly1->chnl = &channels[i]; + } + + return; } void mcde_exit(void) @@ -881,3 +1819,4 @@ void mcde_exit(void) dsiio = NULL; } } + |