static irqreturn_t pv_isr(int irq, void *dev_data) { u32 pv_base, irq_stat; struct pv_dev *dev = dev_data; u32 schedule_wk = 0; pv_base = dev->base_addr; irq_stat = readl(pv_base + REG_PV_INTSTAT); if (irq_stat & VFP_END) { #if 0 if (!config->cont) { /*Since the PV will be disabled at the frame boundary, this will not be required. In case ASIC verification turns out to be negative, then we need to enable this*/ pv_info("stopping PV\n"); writel((readl(pv_base + REG_PV_C) | FIFOCLRC) & ~PVEN, pv_base + REG_PV_C); writel(readl(pv_base + REG_PV_VC) & ~VIDEN, pv_base + REG_PV_VC); } #else if (PV_STOPPING == dev->state) { /*Change PV state to Stopped*/ dev->state = PV_STOPPED; schedule_wk = 1; } #endif writel(VFP_END, pv_base + REG_PV_INTSTAT); irq_stat = irq_stat & ~VFP_END; //disable_irq(dev->irq); } if (irq_stat & OF_UF) { dev->irq_stat |= OF_UF; schedule_work(&dev->err_work); writel(HVS_UF | HVS_OF | PV_UF, pv_base + REG_PV_STAT); writel(OF_UF, pv_base + REG_PV_INTSTAT); irq_stat = irq_stat & ~OF_UF; } if (irq_stat) { writel(irq_stat, pv_base + REG_PV_INTSTAT); } if (schedule_wk) schedule_work(&dev->eof_work); return IRQ_HANDLED; }
int pv_change_state(int event, struct pv_config_t *vid_config) { u32 pv_base; struct pv_dev *dev; int ret = -EINVAL; if (!vid_config) return -EINVAL; dev = container_of(vid_config, struct pv_dev, vid_config); pv_base = dev->base_addr; switch (event) { case PV_VID_CONFIG: #if 0 int cnt = 100; while ((PV_STOPPING == dev->state) && cnt-- ) { if (readl(pv_base + REG_PV_STAT) & VID_IDLE_STAT) dev->state = PV_STOPPED; else usleep_range(1000, 1100); } #endif if ((PV_STOPPED == dev->state) || (PV_INIT_DONE == dev->state)) { pv_clk_enable(dev); ret = pv_vid_config(vid_config); pv_clk_disable(dev); dev->state = PV_CONFIGURED; ret = 0; } else { return -EBUSY; } break; case PV_START: if ((PV_STOPPED == dev->state) || (PV_CONFIGURED == dev->state)) { pv_clk_enable(dev); writel((readl(pv_base + REG_PV_INTEN) & ~VFP_END), pv_base + REG_PV_INTEN); #ifdef INT_4_LONG_PKT writel(VFP_START, (dev->base_addr + REG_PV_INTEN));//workaround 1 #endif writel(readl(pv_base + REG_PV_C) | PVEN, pv_base + REG_PV_C); writel(readl(pv_base + REG_PV_VC) | VIDEN, pv_base + REG_PV_VC); //enable_irq(dev->irq); if (!vid_config->cont) { printk("single shot enabled!!!\n"); writel((readl(pv_base + REG_PV_INTEN) | VFP_END), pv_base + REG_PV_INTEN); dev->state = PV_STOPPING; } else { dev->state = PV_ENABLED; } ret = 0; } else { return -EBUSY; } break; case PV_STOP_EOF: if (PV_ENABLED == dev->state) { unsigned long flags; if (vid_config->cont) { spin_lock_irqsave(&lock, flags); writel(readl(pv_base + REG_PV_VC) & ~VIDEN, pv_base + REG_PV_VC); writel((readl(pv_base + REG_PV_INTEN) | VFP_END), pv_base + REG_PV_INTEN); dev->state = PV_STOPPING; spin_unlock_irqrestore(&lock, flags); } else { spin_lock_irqsave(&lock, flags); /* Stop after the currrent frame is txferd. Need to find out if there would be an interrupt at VFP_END in single-shot/cmd mode. + VFP_END will be enabled for !cont mode.*/ writel((readl(pv_base + REG_PV_INTEN) | VFP_END), pv_base + REG_PV_INTEN); dev->state = PV_STOPPING; spin_unlock_irqrestore(&lock, flags); } ret = 0; } else { return -EBUSY; } break; case PV_STOP_IMM: if ((PV_STOPPING == dev->state) || (PV_ENABLED == dev->state)) { unsigned long flags; spin_lock_irqsave(&lock, flags); writel(readl(pv_base + REG_PV_VC) & ~VIDEN, pv_base + REG_PV_VC); writel(readl(pv_base + REG_PV_C) & ~PVEN, pv_base + REG_PV_C); /* Todo: Clear FIFO? */ //disable_irq(dev->irq); pv_clk_disable(dev); dev->state = PV_STOPPED; spin_unlock_irqrestore(&lock, flags); ret = 0; } else { return -EBUSY; } break; case PV_RESET: /* Need to reset the PV block and take it to hardware reset state*/ break; default: pv_info("Invalid option %d\n", dev->state); break; } return ret; }