static void mdp_set_vsync(unsigned long data) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; struct msm_fb_panel_data *pdata = NULL; pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; vsync_mfd = mfd; init_timer(&mfd->vsync_resync_timer); if ((pdata) && (pdata->set_vsync_notifier == NULL)) return; if ((mfd->panel_info.lcd.vsync_enable) && (mfd->panel_power_on) && (!mfd->vsync_handler_pending)) { mfd->vsync_handler_pending = TRUE; if (!queue_work(mdp_vsync_wq, &mfd->vsync_resync_worker)) { MSM_FB_INFO ("mdp_set_vsync: can't queue_work! -> needs to increase vsync_resync_timer_duration\n"); } } else { MSM_FB_DEBUG ("mdp_set_vsync failed! EN:%d PWR:%d PENDING:%d\n", mfd->panel_info.lcd.vsync_enable, mfd->panel_power_on, mfd->vsync_handler_pending); } mutex_lock(&vsync_timer_lock); if (!timer_shutdown_flag) { mfd->vsync_resync_timer.function = mdp_set_vsync; mfd->vsync_resync_timer.data = data; mfd->vsync_resync_timer.expires = jiffies + mfd->panel_info.lcd.vsync_notifier_period; add_timer(&mfd->vsync_resync_timer); } mutex_unlock(&vsync_timer_lock); }
void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd) { DISP_LOCAL_LOG_EMERG("DISP mdp_pipe_kickoff S\n"); /* complete all the writes before starting */ wmb(); /* kick off PPP engine */ if (term == MDP_PPP_TERM) { if (mdp_debug[MDP_PPP_BLOCK]) jiffies_to_timeval(jiffies, &mdp_ppp_timeval); /* let's turn on PPP block */ mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_ON, FALSE); mdp_enable_irq(term); INIT_COMPLETION(mdp_ppp_comp); mdp_ppp_waiting = TRUE; outpdw(MDP_BASE + 0x30, 0x1000); wait_for_completion_killable(&mdp_ppp_comp); mdp_disable_irq(term); if (mdp_debug[MDP_PPP_BLOCK]) { struct timeval now; jiffies_to_timeval(jiffies, &now); mdp_ppp_timeval.tv_usec = now.tv_usec - mdp_ppp_timeval.tv_usec; MSM_FB_DEBUG("MDP-PPP: %d\n", (int)mdp_ppp_timeval.tv_usec); } } else if (term == MDP_DMA2_TERM) { if (mdp_debug[MDP_DMA2_BLOCK]) { MSM_FB_DEBUG("MDP-DMA2: %d\n", (int)mdp_dma2_timeval.tv_usec); jiffies_to_timeval(jiffies, &mdp_dma2_timeval); } /* DMA update timestamp */ mdp_dma2_last_update_time = ktime_get_real(); /* let's turn on DMA2 block */ #if 0 mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); #endif #ifdef CONFIG_FB_MSM_MDP22 outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x0044, 0x0);/* start DMA */ #else mdp_lut_enable(); #ifdef CONFIG_FB_MSM_MDP40 outpdw(MDP_BASE + 0x000c, 0x0); /* start DMA */ #else outpdw(MDP_BASE + 0x0044, 0x0); /* start DMA */ #endif #endif #ifdef CONFIG_FB_MSM_MDP40 } else if (term == MDP_DMA_S_TERM) { mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE); outpdw(MDP_BASE + 0x0010, 0x0); /* start DMA */ } else if (term == MDP_DMA_E_TERM) { mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE); outpdw(MDP_BASE + 0x0014, 0x0); /* start DMA */ } else if (term == MDP_OVERLAY0_TERM) { mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE); mdp_lut_enable(); outpdw(MDP_BASE + 0x0004, 0); } else if (term == MDP_OVERLAY1_TERM) { mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE); mdp_lut_enable(); outpdw(MDP_BASE + 0x0008, 0); } #else }
static void mdp_dma2_update_lcd(struct msm_fb_data_type *mfd) { MDPIBUF *iBuf = &mfd->ibuf; #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT)\ || !defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) int mddi_dest = FALSE; int cmd_mode = FALSE; #endif uint32 outBpp = iBuf->bpp; uint32 dma2_cfg_reg; uint8 *src; #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT)\ || !defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) uint32 mddi_ld_param; uint16 mddi_vdo_packet_reg; #endif #ifndef CONFIG_FB_MSM_MDP303 struct msm_fb_panel_data *pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; #endif uint32 ystride = mfd->fbi->fix.line_length; #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT)\ || !defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) uint32 mddi_pkt_desc; #endif dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; #ifdef CONFIG_FB_MSM_MDP22 dma2_cfg_reg |= DMA_PACK_TIGHT; #endif #ifdef CONFIG_FB_MSM_MDP30 /* * Software workaround: On 7x25/7x27, the MDP will not * respond if dma_w is 1 pixel. Set the update width to * 2 pixels and adjust the x offset if needed. */ if (iBuf->dma_w == 1) { iBuf->dma_w = 2; if (iBuf->dma_x == (iBuf->ibuf_width - 2)) iBuf->dma_x--; } #endif #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT)\ || !defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) if (mfd->fb_imgType == MDP_BGR_565) dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; else if (mfd->fb_imgType == MDP_RGBA_8888) dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; else dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; if (outBpp == 4) { dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN; dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; } if (outBpp == 2) dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; mddi_ld_param = 0; mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; if ((mfd->panel_info.type == MDDI_PANEL) || (mfd->panel_info.type == EXT_MDDI_PANEL)) { dma2_cfg_reg |= DMA_OUT_SEL_MDDI; mddi_dest = TRUE; if (mfd->panel_info.type == MDDI_PANEL) { mdp_total_vdopkts++; if (mfd->panel_info.pdest == DISPLAY_1) { dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; mddi_ld_param = 0; #ifdef MDDI_HOST_WINDOW_WORKAROUND mddi_window_adjust(mfd, iBuf->dma_x, iBuf->dma_w - 1, iBuf->dma_y, iBuf->dma_h - 1); #endif } else { dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY; mddi_ld_param = 1; #ifdef MDDI_HOST_WINDOW_WORKAROUND mddi_window_adjust(mfd, iBuf->dma_x, iBuf->dma_w - 1, iBuf->dma_y, iBuf->dma_h - 1); #endif } } else { dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL; mddi_ld_param = 2; } #ifdef CONFIG_FB_MSM_MDP303 } else if (mfd->panel_info.type == MIPI_CMD_PANEL) { cmd_mode = TRUE; dma2_cfg_reg |= DMA_OUT_SEL_DSI_CMD; #endif } else { if (mfd->panel_info.pdest == DISPLAY_1) { dma2_cfg_reg |= DMA_AHBM_LCD_SEL_PRIMARY; outp32(MDP_EBI2_LCD0, mfd->data_port_phys); } else { dma2_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY; outp32(MDP_EBI2_LCD1, mfd->data_port_phys); } } #else dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN; dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; dma2_cfg_reg |= DMA_OUT_SEL_DSI_CMD; #endif src = (uint8 *) iBuf->buf; /* starting input address */ src += iBuf->dma_x * outBpp + iBuf->dma_y * ystride; mdp_curr_dma2_update_width = iBuf->dma_w; mdp_curr_dma2_update_height = iBuf->dma_h; /* MDP cmd block enable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); #ifdef CONFIG_FB_MSM_MDP22 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0184, (iBuf->dma_h << 16 | iBuf->dma_w)); MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0188, src); MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x018C, ystride); #else #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT) if (cmd_mode) MDP_OUTP(MDP_BASE + 0x90004, (mfd->panel_info.yres << 16 | mfd->panel_info.xres)); else MDP_OUTP(MDP_BASE + 0x90004, (iBuf->dma_h << 16 | iBuf->dma_w)); #else MDP_OUTP(MDP_BASE + 0x90004, (mfd->panel_info.yres << 16 | mfd->panel_info.xres)); #endif MDP_OUTP(MDP_BASE + 0x90008, src); MDP_OUTP(MDP_BASE + 0x9000c, ystride); #endif #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT)\ || !defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) if (mfd->panel_info.bpp == 18) { mddi_pkt_desc = MDDI_VDO_PACKET_DESC; dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; } else if (mfd->panel_info.bpp == 24) { mddi_pkt_desc = MDDI_VDO_PACKET_DESC_24; dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 24BPP */ DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; } else { mddi_pkt_desc = MDDI_VDO_PACKET_DESC_16; dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; } #else dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 24BPP */ DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; #endif #ifndef CONFIG_FB_MSM_MDP303 if (mddi_dest) { #ifdef CONFIG_FB_MSM_MDP22 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0194, (iBuf->dma_y << 16) | iBuf->dma_x); MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0, mddi_ld_param); MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4, (mddi_pkt_desc << 16) | mddi_vdo_packet_reg); #else MDP_OUTP(MDP_BASE + 0x90010, (iBuf->dma_y << 16) | iBuf->dma_x); MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); MDP_OUTP(MDP_BASE + 0x00094, (mddi_pkt_desc << 16) | mddi_vdo_packet_reg); #endif } else { /* setting EBI2 LCDC write window */ pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, iBuf->dma_h); } #else #if !defined(CONFIG_FB_MSM_MIPI_ORISE_CMD_FWVGA_PT) if (mfd->panel_info.type == MIPI_CMD_PANEL) { #else { /* dma_p = 0, dma_s = 1 */ MDP_OUTP(MDP_BASE + 0xF1000, 0x10); /* enable dsi trigger on dma_p */ MDP_OUTP(MDP_BASE + 0xF1004, 0x01); #endif } #endif /* dma2 config register */ #ifdef MDP_HW_VSYNC MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) { uint32 start_y; if (vsync_start_y_adjust <= iBuf->dma_y) start_y = iBuf->dma_y - vsync_start_y_adjust; else start_y = (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust - iBuf->dma_y); /* * MDP VSYNC clock must be On by now so, we don't have to * re-enable it */ MDP_OUTP(MDP_BASE + 0x210, start_y); MDP_OUTP(MDP_BASE + 0x20c, 1); /* enable prim vsync */ } else { MDP_OUTP(MDP_BASE + 0x20c, 0); /* disable prim vsync */ } #else #ifdef CONFIG_FB_MSM_MDP22 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0180, dma2_cfg_reg); #else MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); #endif #endif /* MDP_HW_VSYNC */ /* MDP cmd block disable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); } static ktime_t vt = { 0 }; int mdp_usec_diff_threshold = 100; int mdp_expected_usec_wait; enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht) { struct msm_fb_data_type *mfd = NULL; mfd = container_of(ht, struct msm_fb_data_type, dma_hrtimer); mdp_pipe_kickoff(MDP_DMA2_TERM, mfd); if (msm_fb_debug_enabled) { ktime_t t; int usec_diff; int actual_wait; t = ktime_get_real(); actual_wait = ktime_to_us(ktime_sub(t, vt)); usec_diff = actual_wait - mdp_expected_usec_wait; if ((mdp_usec_diff_threshold < usec_diff) || (usec_diff < 0)) MSM_FB_DEBUG ("HRT Diff = %d usec Exp=%d usec Act=%d usec\n", usec_diff, mdp_expected_usec_wait, actual_wait); } return HRTIMER_NORESTART; } #ifdef CONFIG_FB_MSM_MDP303 static int busy_wait_cnt; void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) { unsigned long flag; int need_wait = 0; #ifdef DSI_CLK_CTRL mod_timer(&dsi_clock_timer, jiffies + HZ); /* one second */ #endif spin_lock_irqsave(&mdp_spin_lock, flag); #ifdef DSI_CLK_CTRL spin_lock_bh(&dsi_clk_lock); if (mipi_dsi_clk_on == 0) mipi_dsi_turn_on_clks(); spin_unlock_bh(&dsi_clk_lock); #endif if (mfd->dma->busy == TRUE) { if (busy_wait_cnt == 0) INIT_COMPLETION(mfd->dma->comp); busy_wait_cnt++; need_wait++; } spin_unlock_irqrestore(&mdp_spin_lock, flag); if (need_wait) { /* wait until DMA finishes the current job */ /* FIH-SW-MM-VH-DISPLAY-JB01*[ */ if (!wait_for_completion_timeout(&mfd->dma->comp, 1*HZ)) { printk(KERN_ALERT "[DISPLAY] %s: Wait DMA finish timeout!\n", __func__); mfd->dma->busy= FALSE; mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); complete(&mfd->dma->comp); } /* FIH-SW-MM-VH-DISPLAY-JB01*] */ } }
void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state, boolean isr) { boolean mdp_all_blocks_off = TRUE; int i; unsigned long flag; spin_lock_irqsave(&mdp_spin_lock, flag); if (MDP_BLOCK_POWER_ON == state) { mdp_block_power_cnt[block]++; if (MDP_DMA2_BLOCK == block) mdp_in_processing = TRUE; } else { mdp_block_power_cnt[block]--; if (mdp_block_power_cnt[block] < 0) { ////////////////////////////////////////////////////////////////// // Master has to serve a request to power off MDP always // It also has a timer to power off. // So, in case of timer expires first and DMA2 finishes later, // master has to power off two times // // There shouldn't be multiple power-off request for other blocks ////////////////////////////////////////////////////////////////// if (block != MDP_MASTER_BLOCK) { MSM_FB_INFO ("mdp_block_power_cnt[block=%d] multiple power-off request\n", block); } mdp_block_power_cnt[block] = 0; } if (MDP_DMA2_BLOCK == block) mdp_in_processing = FALSE; } spin_unlock_irqrestore(&mdp_spin_lock, flag); ///////////////////////////////////////////////////////////////// // If it's in isr, we send our request to workqueue. // Otherwise, processing happens in the current context ///////////////////////////////////////////////////////////////// if (isr) { //////////////////////////////////////////////////// //checking all blocks power state //////////////////////////////////////////////////// for (i = 0; i < MDP_MAX_BLOCK; i++) { if (mdp_block_power_cnt[i] > 0) mdp_all_blocks_off = FALSE; } //////////////////////////////////////////////////// if ((mdp_all_blocks_off) && (mdp_current_clk_on)) { //send workqueue to turn off mdp power queue_delayed_work(mdp_pipe_ctrl_wq, &mdp_pipe_ctrl_worker, mdp_timer_duration); } } else { down(&mdp_pipe_ctrl_mutex); //////////////////////////////////////////////////// //checking all blocks power state //////////////////////////////////////////////////// for (i = 0; i < MDP_MAX_BLOCK; i++) { if (mdp_block_power_cnt[i] > 0) mdp_all_blocks_off = FALSE; } //////////////////////////////////////////////////// //find out whether a delayable work item is currently pending if (delayed_work_pending(&mdp_pipe_ctrl_worker)) { // try to cancel the current work *** // if it fails to stop (which means del_timer can't delete it // from the list, it's about to expire and run), we have to // let it run. queue_delayed_work won't accept the next job // which is same as queue_delayed_work(mdp_timer_duration = 0) cancel_delayed_work(&mdp_pipe_ctrl_worker); } if ((mdp_all_blocks_off) && (mdp_current_clk_on)) { if (block == MDP_MASTER_BLOCK) { mdp_current_clk_on = FALSE; // turn off MDP clk if (mdp_clk != NULL) { clk_disable(mdp_clk); disable_irq(INT_MDP); MSM_FB_DEBUG("MDP CLK OFF\n"); } } else { //send workqueue to turn off mdp power queue_delayed_work(mdp_pipe_ctrl_wq, &mdp_pipe_ctrl_worker, mdp_timer_duration); } } else if ((!mdp_all_blocks_off) && (!mdp_current_clk_on)) { mdp_current_clk_on = TRUE; // turn on MDP clk if (mdp_clk != NULL) { enable_irq(INT_MDP); clk_enable(mdp_clk); MSM_FB_DEBUG("MDP CLK ON\n"); } } up(&mdp_pipe_ctrl_mutex); } }