diff options
Diffstat (limited to 'drivers/video/mcde/mcde_hw.c')
| -rw-r--r-- | drivers/video/mcde/mcde_hw.c | 170 |
1 files changed, 141 insertions, 29 deletions
diff --git a/drivers/video/mcde/mcde_hw.c b/drivers/video/mcde/mcde_hw.c index 585b22f9111..4653f148b7f 100644 --- a/drivers/video/mcde/mcde_hw.c +++ b/drivers/video/mcde/mcde_hw.c @@ -32,6 +32,7 @@ static void disable_channel(struct mcde_chnl_state *chnl); static void watchdog_auto_sync_timer_function(unsigned long arg); +static int _mcde_chnl_enable(struct mcde_chnl_state *chnl); static int _mcde_chnl_apply(struct mcde_chnl_state *chnl); static void disable_flow(struct mcde_chnl_state *chnl); static void enable_channel(struct mcde_chnl_state *chnl); @@ -654,7 +655,8 @@ static void dpi_video_mode_apply(struct mcde_chnl_state *chnl) chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P_BE; else chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P; - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) chnl->tv_regs.inv_clk = true; else { chnl->tv_regs.dho = MCDE_CONFIG_TVOUT_HBORDER; @@ -720,7 +722,8 @@ static void update_dpi_registers(enum mcde_chnl chnl_id, struct tv_regs *regs) /* Horizontal timing registers */ if (!regs->sel_mode_tv || - hardware_version == MCDE_CHIP_VERSION_3_0_8) { + hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) { mcde_wreg(MCDE_TVLBALWA + idx * MCDE_TVLBALWA_GROUPOFFSET, MCDE_TVLBALWA_LBW(regs->hsw) | MCDE_TVLBALWA_ALW(regs->alw)); @@ -898,14 +901,16 @@ static inline void mcde_handle_vcmp(struct mcde_chnl_state *chnl, u32 int_fld) chnl->port.sync_src == MCDE_SYNCSRC_OFF && chnl->port.type == MCDE_PORTTYPE_DSI && chnl->continous_running) { - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) enable_channel(chnl); mcde_wreg(MCDE_CHNL0SYNCHSW + chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET, MCDE_CHNL0SYNCHSW_SW_TRIG(true)); - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) disable_flow(chnl); mod_timer(&chnl->auto_sync_timer, jiffies + @@ -1025,7 +1030,8 @@ static irqreturn_t mcde_irq_handler(int irq, void *dev) irq_status = dsi_rfld(i, DSI_DIRECT_CMD_STS_FLAG, TE_RECEIVED_FLAG); if (irq_status) { - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) disable_flow(chnl_from_dsi); dsi_wreg(i, DSI_DIRECT_CMD_STS_CLR, DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR(true)); @@ -1037,7 +1043,8 @@ static irqreturn_t mcde_irq_handler(int irq, void *dev) irq_status = dsi_rfld(i, DSI_CMD_MODE_STS_FLAG, ERR_NO_TE_FLAG); if (irq_status) { - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) disable_flow(chnl_from_dsi); dsi_wreg(i, DSI_CMD_MODE_STS_CLR, DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR(true)); @@ -1130,7 +1137,8 @@ static int update_channel_static_registers(struct mcde_chnl_state *chnl) (port->sync_src == MCDE_SYNCSRC_TE1)) mcde_wreg(MCDE_CTRLC1, MCDE_CTRLC1_FIFOWTRMRK( get_output_fifo_size(MCDE_FIFO_C1))); - } else if (hardware_version == MCDE_CHIP_VERSION_3_0_8) { + } else if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) { switch (chnl->fifo) { case MCDE_FIFO_A: mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id * @@ -1480,6 +1488,18 @@ static void chnl_ovly_pixel_format_apply(struct mcde_chnl_state *chnl, mcde_chnl_col_convert_apply(chnl, &chnl->rgb_2_ycbcr); else mcde_chnl_col_convert_apply(chnl, &crycb_2_ycbcr); + } else if (port->type == MCDE_PORTTYPE_DPI) { + /* Note: YUV is not support port pixel format for DPI */ + if (ovly->pix_fmt != MCDE_OVLYPIXFMT_YCbCr422) { + /* standard case: DPI: RGB -> RGB */ + regs->col_conv = MCDE_OVL0CR_COLCCTRL_DISABLED; + } else { + /* DPI: YUV -> RGB */ + regs->col_conv = + MCDE_OVL0CR_COLCCTRL_ENABLED_SAT; + mcde_chnl_col_convert_apply(chnl, + &chnl->ycbcr_2_rgb); + } } } @@ -1611,7 +1631,8 @@ static void do_softwaretrig(struct mcde_chnl_state *chnl) * However FLOWEN must not be triggered before SOFTWARE TRIG * if rotation is enabled */ - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) enable_channel(chnl); else if ((!is_channel_enabled(chnl) && !chnl->regs.roten) || chnl->power_mode != MCDE_DISPLAY_PM_ON) @@ -1621,7 +1642,8 @@ static void do_softwaretrig(struct mcde_chnl_state *chnl) chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET, MCDE_CHNL0SYNCHSW_SW_TRIG(true)); - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) disable_flow(chnl); else enable_channel(chnl); @@ -2116,17 +2138,18 @@ static int enable_mcde_hw(void) return 0; } +#define DSI_WRITE_CMD_TIMEOUT 1000 + /* DSI */ static int mcde_dsi_direct_cmd_write(struct mcde_chnl_state *chnl, bool dcs, u8 cmd, u8 *data, int len) { - int i; + int i, ret = 0; u32 wrdat[4] = { 0, 0, 0, 0 }; u32 settings; u8 link = chnl->port.link; u8 virt_id = chnl->port.phy.dsi.virt_id; - u32 ok; - u32 error; + u32 counter = DSI_WRITE_CMD_TIMEOUT; if (len > MCDE_MAX_DSI_DIRECT_CMD_WRITE || chnl->port.type != MCDE_PORTTYPE_DSI) @@ -2134,6 +2157,7 @@ static int mcde_dsi_direct_cmd_write(struct mcde_chnl_state *chnl, mutex_lock(&mcde_hw_lock); + _mcde_chnl_enable(chnl); if (enable_mcde_hw()) { mutex_unlock(&mcde_hw_lock); return -EINVAL; @@ -2193,21 +2217,35 @@ static int mcde_dsi_direct_cmd_write(struct mcde_chnl_state *chnl, 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_CMD_MODE_STS_CLR, ~0); dsi_wreg(link, DSI_DIRECT_CMD_SEND, true); - /* TODO: irq wait and error check */ - mdelay(10); + /* loop will normally run zero or one time until WRITE_COMPLETED */ + while (!dsi_rfld(link, DSI_DIRECT_CMD_STS, WRITE_COMPLETED) + && --counter) + cpu_relax(); - ok = dsi_rreg(link, DSI_DIRECT_CMD_STS); - error = dsi_rreg(link, DSI_CMD_MODE_STS); - dev_vdbg(&mcde_dev->dev, "DSI Write ok %x error %x\n", ok, error); + if (!counter) { + dev_err(&mcde_dev->dev, + "%s: DSI write cmd 0x%x timeout on DSI link %u!\n", + __func__, cmd, link); + ret = -ETIME; + } else { + /* inform if >100 loops before command completion */ + if (counter < (DSI_WRITE_CMD_TIMEOUT-DSI_WRITE_CMD_TIMEOUT/10)) + dev_vdbg(&mcde_dev->dev, + "%s: %u loops for DSI command %x completion\n", + __func__, (DSI_WRITE_CMD_TIMEOUT - counter), + cmd); - dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0); - dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); + dev_vdbg(&mcde_dev->dev, "DSI Write ok %x error %x\n", + dsi_rreg(link, DSI_DIRECT_CMD_STS_FLAG), + dsi_rreg(link, DSI_CMD_MODE_STS_FLAG)); + } mutex_unlock(&mcde_hw_lock); - return 0; + return ret; } int mcde_dsi_generic_write(struct mcde_chnl_state *chnl, u8* para, int len) @@ -2220,7 +2258,8 @@ int mcde_dsi_dcs_write(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int len) return mcde_dsi_direct_cmd_write(chnl, true, cmd, data, len); } -int mcde_dsi_dcs_read(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int *len) +int mcde_dsi_dcs_read(struct mcde_chnl_state *chnl, + u8 cmd, u32 *data, int *len) { int ret = 0; u8 link = chnl->port.link; @@ -2234,6 +2273,7 @@ int mcde_dsi_dcs_read(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int *len) mutex_lock(&mcde_hw_lock); + _mcde_chnl_enable(chnl); if (enable_mcde_hw()) { mutex_unlock(&mcde_hw_lock); return -EINVAL; @@ -2288,6 +2328,61 @@ int mcde_dsi_dcs_read(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int *len) return ret; } +/* + * Set Maximum Return Packet size is a command that specifies the + * maximum size of the payload transmitted from peripheral back to + * the host processor. + * + * During power-on or reset sequence, the Maximum Return Packet Size + * is set to a default value of one. In order to be able to use + * mcde_dsi_dcs_read for reading more than 1 byte at a time, this + * parameter should be set by the host processor to the desired value + * in the initialization routine before commencing normal operation. + */ +int mcde_dsi_set_max_pkt_size(struct mcde_chnl_state *chnl, int size) +{ + u32 settings; + u8 link = chnl->port.link; + u8 virt_id = chnl->port.phy.dsi.virt_id; + + if (chnl->port.type != MCDE_PORTTYPE_DSI) + return -EINVAL; + + if (size > 4) + return -EINVAL; + + mutex_lock(&mcde_hw_lock); + + if (enable_mcde_hw()) { + mutex_unlock(&mcde_hw_lock); + return -EINVAL; + } + + /* + * Set Maximum Return Packet Size is a four-byte command packet + * (including ECC) that specifies the maximum size of the payload. + * The order of bytes is: + * Data ID, two-byte value for maximum return packet size, + * followed by the ECC byte. + */ + + settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(WRITE) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(1) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(size) | + DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(true); + settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM( + SET_MAX_PKT_SIZE); + dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings); + dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); + dsi_wreg(link, DSI_DIRECT_CMD_SEND, true); + + dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0); + dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0); + + mutex_unlock(&mcde_hw_lock); + return 0; +} static void dsi_te_poll_req(struct mcde_chnl_state *chnl) { @@ -2562,7 +2657,8 @@ static void chnl_update_continous(struct mcde_chnl_state *chnl, if (chnl->port.sync_src == MCDE_SYNCSRC_TE0) { mcde_wfld(MCDE_CRC, SYCEN0, true); } else if (chnl->port.sync_src == MCDE_SYNCSRC_TE1) { - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) { + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) { mcde_wfld(MCDE_VSCRC1, VSSEL, 1); mcde_wfld(MCDE_CRC, SYCEN1, true); } else { @@ -2582,7 +2678,8 @@ static void chnl_update_continous(struct mcde_chnl_state *chnl, if (chnl->port.type == MCDE_PORTTYPE_DSI && chnl->port.sync_src == MCDE_SYNCSRC_OFF) { chnl->disable_software_trig = false; - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) { + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) { mcde_wreg(MCDE_CHNL0SYNCHSW + chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET, MCDE_CHNL0SYNCHSW_SW_TRIG(true)); @@ -2607,7 +2704,8 @@ static void chnl_update_non_continous(struct mcde_chnl_state *chnl) chnl->power_mode == MCDE_DISPLAY_PM_ON) { if (chnl->port.type == MCDE_PORTTYPE_DSI && chnl->port.sync_src == MCDE_SYNCSRC_BTA) { - if (hardware_version == MCDE_CHIP_VERSION_3_0_8) + if (hardware_version == MCDE_CHIP_VERSION_3_0_8 || + hardware_version == MCDE_CHIP_VERSION_4_0_4) enable_channel(chnl); wait_while_dsi_running(chnl->port.link); dsi_te_request(chnl); @@ -2633,6 +2731,8 @@ static void chnl_update_overlay(struct mcde_chnl_state *chnl, chnl->fifo, chnl->regs.x, chnl->regs.y, chnl->regs.ppl, chnl->regs.lpf, ovly->stride, chnl->vmode.interlaced, chnl->rotation); + if (chnl->id == MCDE_CHNL_A || chnl->id == MCDE_CHNL_B) + update_col_registers(chnl->id, &chnl->col_regs); } } @@ -2678,6 +2778,13 @@ static int _mcde_chnl_update(struct mcde_chnl_state *chnl, return 0; } +static int _mcde_chnl_enable(struct mcde_chnl_state *chnl) +{ + dev_vdbg(&mcde_dev->dev, "%s\n", __func__); + chnl->enabled = true; + return 0; +} + /* API entry points */ /* MCDE channels */ struct mcde_chnl_state *mcde_chnl_get(enum mcde_chnl chnl_id, @@ -2928,7 +3035,7 @@ void mcde_chnl_enable(struct mcde_chnl_state *chnl) dev_vdbg(&mcde_dev->dev, "%s\n", __func__); mutex_lock(&mcde_hw_lock); - chnl->enabled = true; + _mcde_chnl_enable(chnl); mutex_unlock(&mcde_hw_lock); dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__); @@ -3209,8 +3316,6 @@ static int init_clocks_and_power(struct platform_device *pdev) } else { dev_dbg(&pdev->dev, "%s: No regulator id supplied\n", __func__); - ret = -EINVAL; - goto regulator_vana_err; } #endif @@ -3421,7 +3526,13 @@ static int __devinit mcde_probe(struct platform_device *pdev) development_version >= 4) { hardware_version = MCDE_CHIP_VERSION_1_0_4; mcde_dynamic_power_management = false; + num_channels = 2; + num_overlays = 3; dev_info(&mcde_dev->dev, "V1_U5500 HW\n"); + } else if (major_version == 4 && minor_version == 0 && + development_version >= 4) { + hardware_version = MCDE_CHIP_VERSION_4_0_4; + dev_info(&mcde_dev->dev, "V2_U5500 HW\n"); } else { dev_err(&mcde_dev->dev, "Unsupported HW version\n"); ret = -ENOTSUPP; @@ -3458,6 +3569,7 @@ static int __devinit mcde_probe(struct platform_device *pdev) init_timer(&channels[i].auto_sync_timer); channels[i].auto_sync_timer.function = watchdog_auto_sync_timer_function; + init_timer(&channels[i].dsi_te_timer); channels[i].dsi_te_timer.function = dsi_te_timer_function; @@ -3467,7 +3579,6 @@ static int __devinit mcde_probe(struct platform_device *pdev) return 0; failed_hardware_version: - free_irq(mcde_irq, &pdev->dev); failed_request_irq: disable_mcde_hw(true); failed_enable_clocks: @@ -3492,6 +3603,8 @@ failed_overlays_alloc: kfree(channels); channels = NULL; failed_channels_alloc: + num_channels = 0; + num_overlays = 0; return ret; } @@ -3570,7 +3683,6 @@ static struct platform_driver mcde_driver = { int __init mcde_init(void) { mutex_init(&mcde_hw_lock); - return platform_driver_register(&mcde_driver); } |
