/* Copyright (c) 2009-2012, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mdp.h" #include "msm_fb.h" #include "mdp4.h" static int mddi_state; #define TOUT_PERIOD HZ /* 1 second */ #define MS_100 (HZ/10) /* 100 ms */ static int vsync_start_y_adjust = 4; #define MAX_CONTROLLER 1 #define VSYNC_EXPIRE_TICK 8 static struct vsycn_ctrl { struct device *dev; int inited; int update_ndx; int expire_tick; int blt_wait; u32 ov_koff; u32 ov_done; u32 dmap_koff; u32 dmap_done; uint32 rdptr_intr_tot; uint32 rdptr_sirq_tot; atomic_t suspend; int wait_vsync_cnt; int blt_change; int blt_free; int blt_end; int uevent; struct mutex update_lock; struct completion ov_comp; struct completion dmap_comp; struct completion vsync_comp; spinlock_t spin_lock; struct msm_fb_data_type *mfd; struct mdp4_overlay_pipe *base_pipe; struct vsync_update vlist[2]; int vsync_enabled; int clk_enabled; int clk_control; int new_update; ktime_t vsync_time; struct work_struct vsync_work; struct work_struct clk_work; } vsync_ctrl_db[MAX_CONTROLLER]; static void vsync_irq_enable(int intr, int term) { unsigned long flag; spin_lock_irqsave(&mdp_spin_lock, flag); /* no need to clear other interrupts for comamnd mode */ mdp_intr_mask |= intr; outp32(MDP_INTR_ENABLE, mdp_intr_mask); mdp_enable_irq(term); spin_unlock_irqrestore(&mdp_spin_lock, flag); } static void vsync_irq_disable(int intr, int term) { unsigned long flag; spin_lock_irqsave(&mdp_spin_lock, flag); /* no need to clrear other interrupts for comamnd mode */ mdp_intr_mask &= ~intr; outp32(MDP_INTR_ENABLE, mdp_intr_mask); mdp_disable_irq_nosync(term); spin_unlock_irqrestore(&mdp_spin_lock, flag); } static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe) { uint32 off, addr; int bpp; char *overlay_base; if (pipe->ov_blt_addr == 0) return; bpp = 3; /* overlay ouput is RGB888 */ off = 0; if (pipe->ov_cnt & 0x01) off = pipe->src_height * pipe->src_width * bpp; addr = pipe->ov_blt_addr + off; /* overlay 0 */ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ outpdw(overlay_base + 0x000c, addr); outpdw(overlay_base + 0x001c, addr); } static void mdp4_mddi_blt_dmap_update(struct mdp4_overlay_pipe *pipe) { uint32 off, addr; int bpp; if (pipe->ov_blt_addr == 0) return; bpp = 3; /* overlay ouput is RGB888 */ off = 0; if (pipe->dmap_cnt & 0x01) off = pipe->src_height * pipe->src_width * bpp; addr = pipe->dma_blt_addr + off; /* dmap */ MDP_OUTP(MDP_BASE + 0x90008, addr); } static void mdp4_mddi_wait4dmap(int cndx); static void mdp4_mddi_wait4ov(int cndx); static void mdp4_mddi_do_blt(struct msm_fb_data_type *mfd, int enable) { unsigned long flags; int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; int need_wait; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); if (mfd->ov0_wb_buf->write_addr == 0) { pr_err("%s: no blt_base assigned\n", __func__); return; } spin_lock_irqsave(&vctrl->spin_lock, flags); if (enable && pipe->ov_blt_addr == 0) { vctrl->blt_change++; if (vctrl->dmap_koff != vctrl->dmap_done) { INIT_COMPLETION(vctrl->dmap_comp); need_wait = 1; } } else if (enable == 0 && pipe->ov_blt_addr) { vctrl->blt_change++; if (vctrl->ov_koff != vctrl->dmap_done) { INIT_COMPLETION(vctrl->dmap_comp); need_wait = 1; } } spin_unlock_irqrestore(&vctrl->spin_lock, flags); if (need_wait) mdp4_mddi_wait4dmap(0); spin_lock_irqsave(&vctrl->spin_lock, flags); if (enable && pipe->ov_blt_addr == 0) { pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr; pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr; pipe->ov_cnt = 0; pipe->dmap_cnt = 0; vctrl->ov_koff = vctrl->dmap_koff; vctrl->ov_done = vctrl->dmap_done; vctrl->blt_free = 0; vctrl->blt_wait = 0; vctrl->blt_end = 0; mdp4_stat.blt_mddi++; } else if (enable == 0 && pipe->ov_blt_addr) { pipe->ov_blt_addr = 0; pipe->dma_blt_addr = 0; vctrl->blt_end = 1; vctrl->blt_free = 4; /* 4 commits to free wb buf */ } pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__, vctrl->blt_change, enable, (int)pipe->ov_blt_addr); spin_unlock_irqrestore(&vctrl->spin_lock, flags); } /* * mdp4_mddi_do_update: * called from thread context */ void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) { struct vsycn_ctrl *vctrl; struct vsync_update *vp; struct mdp4_overlay_pipe *pp; int undx; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; if (atomic_read(&vctrl->suspend) > 0) return; mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; vp = &vctrl->vlist[undx]; pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */ pr_debug("%s: vndx=%d pipe_ndx=%d expire=%x pid=%d\n", __func__, undx, pipe->pipe_ndx, vctrl->expire_tick, current->pid); *pp = *pipe; /* clone it */ vp->update_cnt++; mutex_unlock(&vctrl->update_lock); mdp4_stat.overlay_play[pipe->mixer_num]++; } static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe); int mdp4_mddi_pipe_commit(void) { int i, undx; int mixer = 0; struct vsycn_ctrl *vctrl; struct vsync_update *vp; struct mdp4_overlay_pipe *pipe; struct mdp4_overlay_pipe *real_pipe; unsigned long flags; int need_dmap_wait = 0; int need_ov_wait = 0; int cnt = 0; vctrl = &vsync_ctrl_db[0]; mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; vp = &vctrl->vlist[undx]; pipe = vctrl->base_pipe; mixer = pipe->mixer_num; if (vp->update_cnt == 0) { mutex_unlock(&vctrl->update_lock); return cnt; } vctrl->update_ndx++; vctrl->update_ndx &= 0x01; vp->update_cnt = 0; /* reset */ if (vctrl->blt_free) { vctrl->blt_free--; if (vctrl->blt_free == 0) mdp4_free_writeback_buf(vctrl->mfd, mixer); } mutex_unlock(&vctrl->update_lock); /* free previous committed iommu back to pool */ mdp4_overlay_iommu_unmap_freelist(mixer); spin_lock_irqsave(&vctrl->spin_lock, flags); if (pipe->ov_blt_addr) { /* Blt */ if (vctrl->blt_wait) need_dmap_wait = 1; if (vctrl->ov_koff != vctrl->ov_done) { INIT_COMPLETION(vctrl->ov_comp); need_ov_wait = 1; } } else { /* direct out */ if (vctrl->dmap_koff != vctrl->dmap_done) { INIT_COMPLETION(vctrl->dmap_comp); pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n", __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id()); need_dmap_wait = 1; } } spin_unlock_irqrestore(&vctrl->spin_lock, flags); if (need_dmap_wait) { pr_debug("%s: wait4dmap\n", __func__); mdp4_mddi_wait4dmap(0); } if (need_ov_wait) { pr_debug("%s: wait4ov\n", __func__); mdp4_mddi_wait4ov(0); } if (pipe->ov_blt_addr) { if (vctrl->blt_end) { vctrl->blt_end = 0; pipe->ov_blt_addr = 0; pipe->dma_blt_addr = 0; } } if (vctrl->blt_change) { mdp4_overlayproc_cfg(pipe); mdp4_overlay_dmap_xy(pipe); vctrl->blt_change = 0; } pipe = vp->plist; for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { if (pipe->pipe_used) { cnt++; real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx); if (real_pipe && real_pipe->pipe_used) { /* pipe not unset */ mdp4_overlay_vsync_commit(pipe); } /* free previous iommu to freelist * which will be freed at next * pipe_commit */ mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0); pipe->pipe_used = 0; /* clear */ } } mdp4_mixer_stage_commit(mixer); pipe = vctrl->base_pipe; spin_lock_irqsave(&vctrl->spin_lock, flags); if (pipe->ov_blt_addr) { mdp4_mddi_blt_ov_update(pipe); pipe->ov_cnt++; vctrl->ov_koff++; vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); } else { vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); vctrl->dmap_koff++; } pr_debug("%s: kickoff\n", __func__); /* kickoff overlay engine */ mdp4_stat.kickoff_ov0++; outpdw(MDP_BASE + 0x0004, 0); mb(); /* make sure kickoff ececuted */ spin_unlock_irqrestore(&vctrl->spin_lock, flags); mdp4_stat.overlay_commit[pipe->mixer_num]++; return cnt; } void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; struct vsycn_ctrl *vctrl; unsigned long flags; int clk_set_on = 0; int cndx = 0; vctrl = &vsync_ctrl_db[cndx]; pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__, vctrl->clk_enabled, vctrl->vsync_enabled, enable); mutex_lock(&vctrl->update_lock); if (vctrl->vsync_enabled == enable) { mutex_unlock(&vctrl->update_lock); return; } vctrl->vsync_enabled = enable; if (enable) { if (vctrl->clk_enabled == 0) { pr_debug("%s: SET_CLK_ON\n", __func__); mdp_clk_ctrl(1); vctrl->clk_enabled = 1; clk_set_on = 1; } spin_lock_irqsave(&vctrl->spin_lock, flags); vctrl->clk_control = 0; vctrl->expire_tick = 0; vctrl->uevent = 1; vctrl->new_update = 1; if (clk_set_on) { vsync_irq_enable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); } spin_unlock_irqrestore(&vctrl->spin_lock, flags); mdp4_overlay_update_mddi(mfd); } else { spin_lock_irqsave(&vctrl->spin_lock, flags); vctrl->clk_control = 1; vctrl->uevent = 0; if (vctrl->clk_enabled) vctrl->expire_tick = VSYNC_EXPIRE_TICK; spin_unlock_irqrestore(&vctrl->spin_lock, flags); } mutex_unlock(&vctrl->update_lock); } void mdp4_mddi_wait4vsync(int cndx, long long *vtime) { struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; unsigned long flags; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; if (atomic_read(&vctrl->suspend) > 0) { *vtime = -1; return; } spin_lock_irqsave(&vctrl->spin_lock, flags); if (vctrl->wait_vsync_cnt == 0) INIT_COMPLETION(vctrl->vsync_comp); vctrl->wait_vsync_cnt++; spin_unlock_irqrestore(&vctrl->spin_lock, flags); wait_for_completion(&vctrl->vsync_comp); mdp4_stat.wait4vsync0++; *vtime = ktime_to_ns(vctrl->vsync_time); } static void mdp4_mddi_wait4dmap(int cndx) { struct vsycn_ctrl *vctrl; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; if (atomic_read(&vctrl->suspend) > 0) return; wait_for_completion(&vctrl->dmap_comp); } static void mdp4_mddi_wait4ov(int cndx) { struct vsycn_ctrl *vctrl; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; if (atomic_read(&vctrl->suspend) > 0) return; wait_for_completion(&vctrl->ov_comp); } /* * primary_rdptr_isr: * called from interrupt context */ static void primary_rdptr_isr(int cndx) { struct vsycn_ctrl *vctrl; vctrl = &vsync_ctrl_db[cndx]; pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id()); vctrl->rdptr_intr_tot++; vctrl->vsync_time = ktime_get(); spin_lock(&vctrl->spin_lock); if (vctrl->uevent) schedule_work(&vctrl->vsync_work); if (vctrl->wait_vsync_cnt) { complete(&vctrl->vsync_comp); vctrl->wait_vsync_cnt = 0; } if (vctrl->expire_tick) { vctrl->expire_tick--; if (vctrl->expire_tick == 0) schedule_work(&vctrl->clk_work); } spin_unlock(&vctrl->spin_lock); } void mdp4_dmap_done_mddi(int cndx) { struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; int diff; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; /* blt enabled */ spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); vctrl->dmap_done++; diff = vctrl->ov_done - vctrl->dmap_done; pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id()); complete_all(&vctrl->dmap_comp); if (diff <= 0) { if (vctrl->blt_wait) vctrl->blt_wait = 0; spin_unlock(&vctrl->spin_lock); return; } /* kick dmap */ mdp4_mddi_blt_dmap_update(pipe); pipe->dmap_cnt++; mdp4_stat.kickoff_dmap++; vctrl->dmap_koff++; vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ mb(); /* make sure kickoff executed */ spin_unlock(&vctrl->spin_lock); } /* * mdp4_overlay0_done_mddi: called from isr */ void mdp4_overlay0_done_mddi(int cndx) { struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; int diff; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); vctrl->ov_done++; complete_all(&vctrl->ov_comp); diff = vctrl->ov_done - vctrl->dmap_done; pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id()); if (pipe->ov_blt_addr == 0) { /* blt disabled */ spin_unlock(&vctrl->spin_lock); return; } if (diff > 1) { /* * two overlay_done and none dmap_done yet * let dmap_done kickoff dmap * and put pipe_commit to wait */ vctrl->blt_wait = 1; pr_debug("%s: blt_wait set\n", __func__); spin_unlock(&vctrl->spin_lock); return; } mdp4_mddi_blt_dmap_update(pipe); pipe->dmap_cnt++; mdp4_stat.kickoff_dmap++; vctrl->dmap_koff++; vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ mb(); /* make sure kickoff executed */ spin_unlock(&vctrl->spin_lock); } static void clk_ctrl_work(struct work_struct *work) { struct vsycn_ctrl *vctrl = container_of(work, typeof(*vctrl), clk_work); unsigned long flags; mutex_lock(&vctrl->update_lock); if (vctrl->clk_control && vctrl->clk_enabled) { pr_debug("%s: SET_CLK_OFF\n", __func__); mdp_clk_ctrl(0); spin_lock_irqsave(&vctrl->spin_lock, flags); vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); vctrl->clk_enabled = 0; vctrl->clk_control = 0; spin_unlock_irqrestore(&vctrl->spin_lock, flags); } mutex_unlock(&vctrl->update_lock); } static void send_vsync_work(struct work_struct *work) { struct vsycn_ctrl *vctrl = container_of(work, typeof(*vctrl), vsync_work); char buf[64]; char *envp[2]; snprintf(buf, sizeof(buf), "VSYNC=%llu", ktime_to_ns(vctrl->vsync_time)); envp[0] = buf; envp[1] = NULL; kobject_uevent_env(&vctrl->dev->kobj, KOBJ_CHANGE, envp); } void mdp4_mddi_rdptr_init(int cndx) { struct vsycn_ctrl *vctrl; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; if (vctrl->inited) return; vctrl->inited = 1; vctrl->update_ndx = 0; mutex_init(&vctrl->update_lock); init_completion(&vctrl->ov_comp); init_completion(&vctrl->dmap_comp); init_completion(&vctrl->vsync_comp); spin_lock_init(&vctrl->spin_lock); INIT_WORK(&vctrl->vsync_work, send_vsync_work); INIT_WORK(&vctrl->clk_work, clk_ctrl_work); } void mdp4_primary_rdptr(void) { primary_rdptr_isr(0); } void mdp4_overlay_mddi_state_set(int state) { unsigned long flag; spin_lock_irqsave(&mdp_spin_lock, flag); mddi_state = state; spin_unlock_irqrestore(&mdp_spin_lock, flag); } int mdp4_overlay_mddi_state_get(void) { return mddi_state; } static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) { /* * The adreno GPU hardware requires that the pitch be aligned to * 32 pixels for color buffers, so for the cases where the GPU * is writing directly to fb0, the framebuffer pitch * also needs to be 32 pixel aligned */ if (fb_index == 0) return ALIGN(xres, 32) * bpp; else return xres * bpp; } void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe, int which) { uint32 start_y, data, tear_en; tear_en = (1 << which); if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) { if (vsync_start_y_adjust <= pipe->dst_y) start_y = pipe->dst_y - vsync_start_y_adjust; else start_y = (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust - pipe->dst_y); if (which == 0) MDP_OUTP(MDP_BASE + 0x210, start_y); /* primary */ else MDP_OUTP(MDP_BASE + 0x214, start_y); /* secondary */ data = inpdw(MDP_BASE + 0x20c); data |= tear_en; MDP_OUTP(MDP_BASE + 0x20c, data); } else { data = inpdw(MDP_BASE + 0x20c); data &= ~tear_en; MDP_OUTP(MDP_BASE + 0x20c, data); } } void mdp4_mddi_base_swap(int cndx, struct mdp4_overlay_pipe *pipe) { struct vsycn_ctrl *vctrl; if (cndx >= MAX_CONTROLLER) { pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } vctrl = &vsync_ctrl_db[cndx]; vctrl->base_pipe = pipe; } static void mdp4_overlay_setup_pipe_addr(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe) { MDPIBUF *iBuf = &mfd->ibuf; struct fb_info *fbi; int bpp; uint8 *src; /* whole screen for base layer */ src = (uint8 *) iBuf->buf; fbi = mfd->fbi; if (pipe->is_3d) { bpp = fbi->var.bits_per_pixel / 8; pipe->src_height = pipe->src_height_3d; pipe->src_width = pipe->src_width_3d; pipe->src_h = pipe->src_height_3d; pipe->src_w = pipe->src_width_3d; pipe->dst_h = pipe->src_height_3d; pipe->dst_w = pipe->src_width_3d; pipe->srcp0_ystride = msm_fb_line_length(0, pipe->src_width, bpp); } else { /* 2D */ pipe->src_height = fbi->var.yres; pipe->src_width = fbi->var.xres; pipe->src_h = fbi->var.yres; pipe->src_w = fbi->var.xres; pipe->dst_h = fbi->var.yres; pipe->dst_w = fbi->var.xres; pipe->srcp0_ystride = fbi->fix.line_length; } pipe->src_y = 0; pipe->src_x = 0; pipe->dst_y = 0; pipe->dst_x = 0; pipe->srcp0_addr = (uint32)src; } void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd) { int ptype; uint32 mddi_ld_param; uint16 mddi_vdo_packet_reg; struct mdp4_overlay_pipe *pipe; uint32 data; int ret; int cndx = 0; struct vsycn_ctrl *vctrl; if (mfd->key != MFD_KEY) return; vctrl = &vsync_ctrl_db[cndx]; if (vctrl->base_pipe == NULL) { ptype = mdp4_overlay_format2type(mfd->fb_imgType); if (ptype < 0) pr_err("%s: format2type failed\n", __func__); pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); if (pipe == NULL) { pr_err("%s: pipe_alloc failed\n", __func__); return; } pipe->pipe_used++; pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; pipe->mixer_num = MDP4_MIXER0; pipe->src_format = mfd->fb_imgType; mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI); ret = mdp4_overlay_format2pipe(pipe); if (ret < 0) pr_err("%s: format2type failed\n", __func__); vctrl->base_pipe = pipe; /* keep it */ mdp4_init_writeback_buf(mfd, MDP4_MIXER0); pipe->ov_blt_addr = 0; pipe->dma_blt_addr = 0; } else { pipe = vctrl->base_pipe; } MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */ mddi_ld_param = 0; mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; if (mdp_hw_revision == MDP4_REVISION_V2_1) { data = inpdw(MDP_BASE + 0x0028); data &= ~0x0300; /* bit 8, 9, MASTER4 */ if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */ data |= 0x0200; else data |= 0x0100; MDP_OUTP(MDP_BASE + 0x00028, data); } if (mfd->panel_info.type == MDDI_PANEL) { if (mfd->panel_info.pdest == DISPLAY_1) mddi_ld_param = 0; else mddi_ld_param = 1; } else { mddi_ld_param = 2; } MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); if (mfd->panel_info.bpp == 24) MDP_OUTP(MDP_BASE + 0x00094, (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); else if (mfd->panel_info.bpp == 16) MDP_OUTP(MDP_BASE + 0x00094, (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); else MDP_OUTP(MDP_BASE + 0x00094, (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); MDP_OUTP(MDP_BASE + 0x00098, 0x01); mdp4_overlay_setup_pipe_addr(mfd, pipe); mdp4_overlay_rgb_setup(pipe); mdp4_overlay_reg_flush(pipe, 1); mdp4_mixer_stage_up(pipe, 0); mdp4_overlayproc_cfg(pipe); mdp4_overlay_dmap_xy(pipe); mdp4_overlay_dmap_cfg(mfd, 0); mdp4_mixer_stage_commit(pipe->mixer_num); wmb(); } void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd) { mdp4_mddi_do_blt(mfd, 1); } void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd) { mdp4_mddi_do_blt(mfd, 0); } void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req) { mdp4_mddi_do_blt(mfd, req->enable); } int mdp4_mddi_on(struct platform_device *pdev) { int ret = 0; int cndx = 0; struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; pr_debug("%s+:\n", __func__); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); vctrl = &vsync_ctrl_db[cndx]; vctrl->mfd = mfd; vctrl->dev = mfd->fbi->dev; mdp_clk_ctrl(1); mdp4_overlay_update_mddi(mfd); mdp_clk_ctrl(0); mdp4_iommu_attach(); atomic_set(&vctrl->suspend, 0); pr_debug("%s-:\n", __func__); return ret; } int mdp4_mddi_off(struct platform_device *pdev) { int ret = 0; int cndx = 0; struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; pr_debug("%s+:\n", __func__); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; if (pipe == NULL) { pr_err("%s: NO base pipe\n", __func__); return ret; } atomic_set(&vctrl->suspend, 1); /* sanity check, free pipes besides base layer */ mdp4_overlay_unset_mixer(pipe->mixer_num); mdp4_mixer_stage_down(pipe, 1); mdp4_overlay_pipe_free(pipe); vctrl->base_pipe = NULL; if (vctrl->clk_enabled) { /* * in case of suspend, vsycn_ctrl off is not * received from frame work which left clock on * then, clock need to be turned off here */ mdp_clk_ctrl(0); } vctrl->clk_enabled = 0; vctrl->vsync_enabled = 0; vctrl->clk_control = 0; vctrl->expire_tick = 0; vctrl->uevent = 0; vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); pr_debug("%s-:\n", __func__); /* * footswitch off * this will casue all mdp register * to be reset to default * after footswitch on later */ return ret; } void mdp_mddi_overlay_suspend(struct msm_fb_data_type *mfd) { int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; /* dis-engage rgb0 from mixer0 */ if (pipe) { if (mfd->ref_cnt == 0) { /* adb stop */ if (pipe->pipe_type == OVERLAY_TYPE_BF) mdp4_overlay_borderfill_stage_down(pipe); /* pipe == rgb1 */ mdp4_overlay_unset_mixer(pipe->mixer_num); vctrl->base_pipe = NULL; } else { mdp4_mixer_stage_down(pipe, 1); mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 1); } } } void mdp4_mddi_overlay(struct msm_fb_data_type *mfd) { int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; unsigned long flags; long long xx; vctrl = &vsync_ctrl_db[cndx]; if (!mfd->panel_power_on) return; pipe = vctrl->base_pipe; if (pipe == NULL) { pr_err("%s: NO base pipe\n", __func__); return; } mutex_lock(&vctrl->update_lock); if (!vctrl->clk_enabled) { pr_err("%s: mdp clocks disabled\n", __func__); mutex_unlock(&vctrl->update_lock); return; } mutex_unlock(&vctrl->update_lock); spin_lock_irqsave(&vctrl->spin_lock, flags); if (vctrl->expire_tick) { /* * in the middle of shutting clocks down * delay to allow pan display to go through */ vctrl->expire_tick = VSYNC_EXPIRE_TICK; } spin_unlock_irqrestore(&vctrl->spin_lock, flags); if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) { mdp4_mddi_vsync_enable(mfd, pipe, 0); mdp4_overlay_setup_pipe_addr(mfd, pipe); mdp4_mddi_pipe_queue(0, pipe); } mdp4_overlay_mdp_perf_upd(mfd, 1); mutex_lock(&mfd->dma->ov_mutex); mdp4_mddi_pipe_commit(); mutex_unlock(&mfd->dma->ov_mutex); mdp4_mddi_wait4vsync(0, &xx); mdp4_overlay_mdp_perf_upd(mfd, 0); } int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor) { struct msm_fb_data_type *mfd = info->par; mutex_lock(&mfd->dma->ov_mutex); if (mfd && mfd->panel_power_on) { mdp_hw_cursor_update(info, cursor); } mutex_unlock(&mfd->dma->ov_mutex); return 0; }