/* drivers/video/msm_fb/mdp.c * * MSM MDP Interface (used by framebuffer core) * * Copyright (C) 2007 QUALCOMM Incorporated * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 "mdp_hw.h" #include "mdp_ppp.h" #include struct class *mdp_class; #ifdef CONFIG_MSM_HDMI /* Used to report LCDC underflows */ void reportUnderflow(void); #endif #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); static unsigned int mdp_irq_mask; static unsigned int mdp_dma_timer_enable = 0; struct clk *mdp_clk_to_disable_later = 0; static struct mdp_blit_req *timeout_req; #ifdef CONFIG_FB_MSM_OVERLAY extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx); extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req, struct file **pp_src_file); extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y); #include "mdp4.h" #endif static void mdp_do_standby_timer(unsigned long data) { struct mdp_info *mdp = (struct mdp_info *) data; if (!mdp_irq_mask) { clk_set_rate(mdp->ebi1_clk, 0); mdp->state |= MDP_STATE_STANDBY; } else { mod_timer(&mdp->standby_timer, jiffies + msecs_to_jiffies(200)); } } static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { BUG_ON(!mask); /* if the mask bits are already set return an error, this interrupt * is already enabled */ if (mdp_irq_mask & mask) { pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask); return -1; } /* if the mdp irq is not already enabled enable it */ if (!mdp_irq_mask) { clk_enable(mdp->clk); enable_irq(mdp->irq); if (mdp->state & MDP_STATE_STANDBY) { #ifdef CONFIG_MSM_MDP40 clk_set_rate(mdp->ebi1_clk, 153000000); #else clk_set_rate(mdp->ebi1_clk, 128000000); #endif mdp->state &= ~MDP_STATE_STANDBY; } else { del_timer_sync(&mdp->standby_timer); barrier(); } } /* clear out any previous irqs for the requested mask*/ mdp_writel(mdp, mask, MDP_INTR_CLEAR); /* update the irq mask to reflect the fact that the interrupt is * enabled */ mdp_irq_mask |= mask; mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE); return 0; } static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { unsigned long flags=0; int ret; spin_lock_irqsave(&mdp->lock, flags); ret = locked_enable_mdp_irq(mdp, mask); spin_unlock_irqrestore(&mdp->lock, flags); return ret; } static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { /* this interrupt is already disabled! */ if (!(mdp_irq_mask & mask)) { printk(KERN_ERR "mdp irq already off %x %x\n", mdp_irq_mask, mask); return -1; } /* update the irq mask to reflect the fact that the interrupt is * disabled */ mdp_irq_mask &= ~(mask); mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE); /* if no one is waiting on the interrupt, disable it */ if (!mdp_irq_mask) { disable_irq_nosync(mdp->irq); if (mdp->clk) clk_disable(mdp->clk); if (!(mdp->state & MDP_STATE_STANDBY)) mod_timer(&mdp->standby_timer, jiffies + msecs_to_jiffies(200)); } return 0; } int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { unsigned long irq_flags=0; int ret; spin_lock_irqsave(&mdp->lock, irq_flags); ret = locked_disable_mdp_irq(mdp, mask); spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } static irqreturn_t mdp_isr(int irq, void *data) { uint32_t status; unsigned long irq_flags=0; struct mdp_info *mdp = data; int i; spin_lock_irqsave(&mdp->lock, irq_flags); status = mdp_readl(mdp, MDP_INTR_STATUS); mdp_writel(mdp, status, MDP_INTR_CLEAR); // pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status, // mdp_irq_mask); if (mdp_dma_timer_enable) { del_timer_sync(&mdp->dma_timer); mdp_dma_timer_enable = 0; } #ifdef CONFIG_MSM_HDMI if (status & MDP_LCDC_UNDERFLOW) { pr_err("%s: LCDC Underflow\n", __func__); reportUnderflow(); } #endif status &= ~0x10000; // Cotulla status &= mdp_irq_mask; #ifdef CONFIG_MSM_MDP40 if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL) status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE); } #endif for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { struct mdp_out_interface *out_if = &mdp->out_if[i]; if (status & out_if->dma_mask) { if (out_if->dma_cb) { out_if->dma_cb->func(out_if->dma_cb); out_if->dma_cb = NULL; } wake_up(&out_if->dma_waitqueue); } if (status & out_if->irq_mask) { out_if->irq_cb->func(out_if->irq_cb); out_if->irq_cb = NULL; } } #ifndef CONFIG_MSM_MDP40 mdp_ppp_handle_isr(mdp, status); #endif if (status) locked_disable_mdp_irq(mdp, status); spin_unlock_irqrestore(&mdp->lock, irq_flags); return IRQ_HANDLED; } static void mdp_do_dma_timer(unsigned long data) { uint32_t status; struct mdp_info *mdp = (struct mdp_info *) data; unsigned long irq_flags=0; int i; spin_lock_irqsave(&mdp->lock, irq_flags); status = mdp_readl(mdp, MDP_INTR_STATUS); mdp_writel(mdp, mdp_irq_mask, MDP_INTR_CLEAR); for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { struct mdp_out_interface *out_if = &mdp->out_if[i]; if (mdp_irq_mask & out_if->dma_mask) { if (out_if->dma_cb) { out_if->dma_cb->func(out_if->dma_cb); out_if->dma_cb = NULL; } wake_up(&out_if->dma_waitqueue); } if (mdp_irq_mask & out_if->irq_mask) { out_if->irq_cb->func(out_if->irq_cb); out_if->irq_cb = NULL; } } locked_disable_mdp_irq(mdp, mdp_irq_mask); spin_unlock_irqrestore(&mdp->lock, irq_flags); } static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask) { uint32_t ret; unsigned long irq_flags=0; spin_lock_irqsave(&mdp->lock, irq_flags); ret = mdp_irq_mask & mask; spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } void mdp_dump_blit(struct mdp_blit_req *req) { pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, req->src.width, req->src.height, req->src.format, req->src.offset, req->src.memory_id); pr_info("%s: dst: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, req->dst.width, req->dst.height, req->dst.format, req->dst.offset, req->dst.memory_id); pr_info("%s: src_rect: x=%d y=%d w=%d h=%d\n", __func__, req->src_rect.x, req->src_rect.y, req->src_rect.w, req->src_rect.h); pr_info("%s: dst_rect: x=%d y=%d w=%d h=%d\n", __func__, req->dst_rect.x, req->dst_rect.y, req->dst_rect.w, req->dst_rect.h); pr_info("%s: alpha=0x%08x\n", __func__, req->alpha); pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask); pr_info("%s: flags=%08x\n", __func__, req->flags); } int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) { int ret = 0; unsigned long irq_flags=0; // pr_info("%s: WAITING for 0x%x\n", __func__, mask); wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); spin_lock_irqsave(&mdp->lock, irq_flags); if (mdp_irq_mask & mask) { locked_disable_mdp_irq(mdp, mask); pr_warning("%s: timeout waiting for mdp to complete 0x%x\n", __func__, mask); if(timeout_req) mdp_dump_blit(timeout_req); ret = -ETIMEDOUT; } else { // pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask); } spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) { #define MDP_MAX_TIMEOUTS 20 static int timeout_count; struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); unsigned int mask = 0; wait_queue_head_t *wq; switch (interface) { case MSM_MDDI_PMDH_INTERFACE: case MSM_MDDI_EMDH_INTERFACE: case MSM_LCDC_INTERFACE: case MSM_TV_INTERFACE: BUG_ON(!mdp->out_if[interface].registered); mask = mdp->out_if[interface].dma_mask; wq = &mdp->out_if[interface].dma_waitqueue; break; default: pr_err("%s: Unknown interface %d\n", __func__, interface); BUG(); } if (mdp_wait(mdp, mask, wq) == -ETIMEDOUT) timeout_count++; else timeout_count = 0; if (timeout_count > MDP_MAX_TIMEOUTS) { printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n", MDP_MAX_TIMEOUTS); BUG(); } } /* static int mdp_ppp_wait(struct mdp_info *mdp) { return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); } */ #ifndef CONFIG_MSM_MDP40 static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y) { struct mdp_info *mdp = priv; uint32_t dma2_cfg; uint32_t video_packet_parameter = 0; uint16_t ld_param = 1; dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; dma2_cfg |= mdp->dma_format; #if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 if (mdp->dma_format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) #else if (mdp->dma_format == DMA_IBUF_FORMAT_XRGB8888) #endif dma2_cfg |= DMA_PACK_PATTERN_BGR; else dma2_cfg |= DMA_PACK_PATTERN_RGB; dma2_cfg |= DMA_OUT_SEL_MDDI; dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; dma2_cfg |= DMA_DITHER_EN; if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; } else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; } /* setup size, address, and stride */ mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE); mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR); mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE); /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY); mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) { mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3, MDP_MDDI_PARAM); } else { mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, MDP_MDDI_PARAM); } mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG); mdp_writel(mdp, 0, MDP_DMA_S_START); } static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y) { struct mdp_info *mdp = priv; uint32_t dma2_cfg = 0; uint32_t video_packet_parameter = 0; uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ #if !defined(CONFIG_MSM_MDP30) dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; #endif dma2_cfg |= mdp->dma_format; #if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 if (mdp->dma_format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) #else if (mdp->dma_format == DMA_IBUF_FORMAT_XRGB8888) #endif dma2_cfg |= DMA_PACK_PATTERN_BGR; else dma2_cfg |= DMA_PACK_PATTERN_RGB; dma2_cfg |= DMA_OUT_SEL_MDDI; dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; #if !defined(CONFIG_MSM_MDP30) dma2_cfg |= DMA_DITHER_EN; #endif if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; } else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; } #if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302) writel(height << 16 | width, mdp->base + 0x90004); writel(addr, mdp->base + 0x90008); writel(stride, mdp->base + 0x9000c); /* set y & x offset and MDDI transaction parameters */ writel(y << 16 | x, mdp->base + 0x90010); writel(ld_param, mdp->base + 0x00090); writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, mdp->base + 0x00094); writel(dma2_cfg, mdp->base + 0x90000); /* start DMA2 */ writel(0, mdp->base + 0x0044); #elif defined(CONFIG_MSM_MDP22) /* setup size, address, and stride */ mdp_writel(mdp, (height << 16) | (width), MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); /* start DMA2 */ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); #else /* setup size, address, and stride */ mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, MDP_MDDI_PARAM); mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); mdp_writel(mdp, 0, MDP_DMA_P_START); #endif } #endif /* ifndef CONFIG_MSM_MDP40 */ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); struct mdp_out_interface *out_if; unsigned long flags; if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES || !mdp->out_if[interface].registered) { pr_err("%s: Unknown interface: %d\n", __func__, interface); BUG(); } out_if = &mdp->out_if[interface]; spin_lock_irqsave(&mdp->lock, flags); if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) { /* something wrong in dma, workaround it */ mdp_dma_timer_enable = 1; pr_err("%s: busy\n", __func__); } out_if->dma_cb = callback; out_if->dma_start(out_if->priv, addr, stride, width, height, x, y); if (mdp_dma_timer_enable) mod_timer(&mdp->dma_timer, jiffies + msecs_to_jiffies(17)); spin_unlock_irqrestore(&mdp->lock, flags); } void mdp_configure_dma(struct mdp_device *mdp_dev) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); uint32_t dma_cfg; if (!mdp->dma_config_dirty) return; dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG); dma_cfg &= ~DMA_IBUF_FORMAT_MASK; dma_cfg &= ~DMA_PACK_PATTERN_MASK; dma_cfg |= (mdp->dma_format | mdp->dma_pack_pattern); mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG); mdp->dma_config_dirty = false; return; } int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp) { switch (bpp) { case 16: case 24: case 32: break; default: return -EINVAL; } return 0; } void mdp_set_panel_size(struct mdp_device *mdp_dev, int width, int height) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); mdp->mdp_dev.width = width; mdp->mdp_dev.height = height; } int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB; switch (bpp) { case 16: format = DMA_IBUF_FORMAT_RGB565; pack_pattern = DMA_PACK_PATTERN_RGB; break; #if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 case 24: case 32: format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888; break; #else case 24: format = DMA_IBUF_FORMAT_RGB888; pack_pattern = DMA_PACK_PATTERN_BGR; break; case 32: format = DMA_IBUF_FORMAT_XRGB8888; pack_pattern = DMA_PACK_PATTERN_BGR; break; #endif default: return -EINVAL; } if (format != mdp->dma_format || pack_pattern != mdp->dma_pack_pattern) { mdp->dma_format = format; mdp->dma_pack_pattern = pack_pattern; mdp->dma_config_dirty = true; } return 0; } int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, struct mdp_blit_req *req) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); timeout_req = req; return mdp_ppp_blit(mdp, fb, req); } void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); disp_id &= 0xf; mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43); } /* used by output interface drivers like mddi and lcdc */ int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, void *private_data, uint32_t dma_mask, mdp_dma_start_func_t dma_start) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); unsigned long flags=0; int ret = 0; if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { pr_err("%s: invalid interface (%d)\n", __func__, interface); return -EINVAL; } spin_lock_irqsave(&mdp->lock, flags); if (mdp->out_if[interface].registered) { pr_err("%s: interface (%d) already registered\n", __func__, interface); ret = -EINVAL; goto done; } init_waitqueue_head(&mdp->out_if[interface].dma_waitqueue); mdp->out_if[interface].registered = 1; mdp->out_if[interface].priv = private_data; mdp->out_if[interface].dma_mask = dma_mask; mdp->out_if[interface].dma_start = dma_start; mdp->out_if[interface].dma_cb = NULL; done: spin_unlock_irqrestore(&mdp->lock, flags); return ret; } int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, uint32_t mask, struct msmfb_callback *cb) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); unsigned long flags=0; int ret = 0; if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { pr_err("%s: invalid interface (%d)\n", __func__, interface); BUG(); } else if (!mdp->out_if[interface].registered) { pr_err("%s: interface (%d) not registered\n", __func__, interface); BUG(); } spin_lock_irqsave(&mdp->lock, flags); if (mask) { ret = locked_enable_mdp_irq(mdp, mask); if (ret) { pr_err("%s: busy\n", __func__); goto done; } mdp->out_if[interface].irq_mask = mask; mdp->out_if[interface].irq_cb = cb; } else { locked_disable_mdp_irq(mdp, mask); mdp->out_if[interface].irq_mask = 0; mdp->out_if[interface].irq_cb = NULL; } done: spin_unlock_irqrestore(&mdp->lock, flags); return ret; } int register_mdp_client(struct class_interface *cint) { if (!mdp_class) { pr_err("mdp: no mdp_class when registering mdp client\n"); return -ENODEV; } cint->class = mdp_class; return class_interface_register(cint); } int mdp_probe(struct platform_device *pdev) { struct resource *resource; int ret = -EINVAL; struct mdp_info *mdp; struct msm_mdp_platform_data *pdata = pdev->dev.platform_data; resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!resource) { pr_err("mdp: can not get mdp mem resource!\n"); return -ENOMEM; } mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL); if (!mdp) return -ENOMEM; spin_lock_init(&mdp->lock); mdp->irq = platform_get_irq(pdev, 0); if (mdp->irq < 0) { pr_err("mdp: can not get mdp irq\n"); ret = mdp->irq; goto error_get_irq; } mdp->base = ioremap(resource->start, resource->end - resource->start); if (mdp->base == 0) { printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n"); ret = -ENOMEM; goto error_ioremap; } mdp->mdp_dev.dma = mdp_dma; mdp->mdp_dev.dma_wait = mdp_dma_wait; mdp->mdp_dev.blit = mdp_blit; #ifdef CONFIG_FB_MSM_OVERLAY mdp->mdp_dev.overlay_get = mdp4_overlay_get; mdp->mdp_dev.overlay_set = mdp4_overlay_set; mdp->mdp_dev.overlay_unset = mdp4_overlay_unset; mdp->mdp_dev.overlay_play = mdp4_overlay_play; #endif mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; mdp->mdp_dev.set_output_format = mdp_set_output_format; mdp->mdp_dev.set_panel_size = mdp_set_panel_size; mdp->mdp_dev.check_output_format = mdp_check_output_format; mdp->mdp_dev.configure_dma = mdp_configure_dma; mdp->enable_irq = enable_mdp_irq; mdp->disable_irq = disable_mdp_irq; if (pdata == NULL || pdata->overrides == 0) mdp->mdp_dev.overrides = 0; else if(pdata->overrides) mdp->mdp_dev.overrides = pdata->overrides; if (pdata == NULL || pdata->color_format == 0) mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565; else if(pdata->color_format) mdp->mdp_dev.color_format = pdata->color_format; #ifdef CONFIG_MSM_MDP40 if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE | MDP_DMA_S_DONE, mdp4_mddi_overlay); } else { ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE, mdp4_mddi_overlay); } #else if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) { ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, mdp_dma_to_mddi); } else if (pdata->dma_channel == MDP_DMA_S) { ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE, mdp_dmas_to_mddi); } #endif if (ret) goto error_mddi_pmdh_register; mdp->clk = clk_get(&pdev->dev, "mdp_clk"); if (IS_ERR(mdp->clk)) { printk(KERN_INFO "mdp: failed to get mdp clk"); ret = PTR_ERR(mdp->clk); goto error_get_mdp_clk; } mdp->ebi1_clk = clk_get(NULL, "ebi1_clk"); if (IS_ERR(mdp->ebi1_clk)) { pr_err("mdp: failed to get ebi1 clk\n"); ret = PTR_ERR(mdp->ebi1_clk); goto error_get_ebi1_clk; } ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); if (ret) goto error_request_irq; disable_irq(mdp->irq); clk_enable(mdp->clk); mdp_clk_to_disable_later = mdp->clk; #ifdef CONFIG_MSM_MDP40 //MDP_DISP_INTF_SEL if (mdp_readl(mdp, 0xc0000)) mdp_writel(mdp, 0x8, 0x0038); else mdp_writel(mdp, 0xa, 0x0038); //mddi //FIXME: should select mddi or lcdc interface //mdp_writel(mdp, 0x8, 0x0038); //lcdc #endif #ifdef CONFIG_MSM_MDP40 mdp4_hw_init(mdp); #else mdp_hw_init(mdp); #endif #if defined CONFIG_MSM_MDP302 /* enable the tearing check in MDP */ if(pdata != NULL && pdata->tearing_check) mdp_check_tearing(mdp, pdata); #endif /* register mdp device */ mdp->mdp_dev.dev.parent = &pdev->dev; mdp->mdp_dev.dev.class = mdp_class; dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id); /* if you can remove the platform device you'd have to implement * this: mdp_dev.release = mdp_class; */ ret = device_register(&mdp->mdp_dev.dev); if (ret) goto error_device_register; setup_timer(&mdp->standby_timer, mdp_do_standby_timer, (unsigned long )mdp); setup_timer(&mdp->dma_timer, mdp_do_dma_timer, (unsigned long )mdp); pr_info("%s: initialized\n", __func__); return 0; error_device_register: free_irq(mdp->irq, mdp); error_request_irq: clk_put(mdp->ebi1_clk); error_get_ebi1_clk: clk_put(mdp->clk); error_get_mdp_clk: error_mddi_pmdh_register: iounmap(mdp->base); error_ioremap: error_get_irq: kfree(mdp); return ret; } static struct platform_driver msm_mdp_driver = { .probe = mdp_probe, .driver = {.name = "msm_mdp"}, }; static int __init mdp_lateinit(void) { if (mdp_clk_to_disable_later) clk_disable(mdp_clk_to_disable_later); return 0; } static int __init mdp_init(void) { mdp_class = class_create(THIS_MODULE, "msm_mdp"); if (IS_ERR(mdp_class)) { printk(KERN_ERR "Error creating mdp class\n"); return PTR_ERR(mdp_class); } return platform_driver_register(&msm_mdp_driver); } subsys_initcall(mdp_init); late_initcall(mdp_lateinit);