static inline void pv_clk_enable(struct pv_dev *dev) { if (clk_enable(dev->apb_clk)) pv_err("failed to enable apb clk\n"); if (clk_enable(dev->pix_clk)) pv_err("failed to enable pix clk\n"); }
int check_pv_state(int event, struct pv_config_t *config) { u32 pv_base, stat, istat, ien; struct pv_dev *dev; if (!config) return -EINVAL; dev = container_of(config, struct pv_dev, vid_config); pv_base = dev->base_addr; istat = readl(pv_base + REG_PV_INTSTAT); stat = readl(pv_base + REG_PV_STAT); ien = readl(pv_base + REG_PV_INTEN); pv_err("event=%d state=%d istat=0x%x stat=0x%x ien=0x%x\n", event, dev->state, istat, stat, ien); if (istat & ien) { writel(istat, pv_base + REG_PV_INTSTAT); return 0; } else { return -1; } }
int pv_init(struct pv_init_t *init, struct pv_config_t **config) { struct pv_dev *dev; int ret; if (!init || (init->id >= MAX_PV_INST) || !init->irq #ifdef PV_HAS_CLK || !init->apb_clk_name || !init->pix_clk_name #endif || !init->base_addr || !init->err_cb || !init->eof_cb) { pv_err("Invalid input parameters\n"); ret = -EINVAL; goto done; } if (g_pv_init[init->id]) { pv_err("All instances of PV have already been initialised\n"); ret = -EPERM; goto done; } dev = kzalloc(sizeof(struct pv_dev), GFP_KERNEL); if (!dev) { pv_err("couldn't allocate memory for pv_data\n"); ret = -ENOMEM; goto done; } ret = request_irq(init->irq, pv_isr, IRQF_TRIGGER_HIGH, "PV", dev); if (ret < 0) { pv_err("failed to get irq\n"); goto fail; } irq_set_affinity(init->irq, cpumask_of(1)); #ifdef PV_HAS_CLK dev->apb_clk = clk_get(NULL, init->apb_clk_name); if (IS_ERR(dev->apb_clk)) { pv_err("failed to get %s\n", init->apb_clk_name); ret = PTR_ERR(dev->apb_clk); goto fail; } dev->pix_clk = clk_get(NULL, init->pix_clk_name); if (IS_ERR(dev->pix_clk)) { pv_err("failed to get %s\n", init->pix_clk_name); ret = PTR_ERR(dev->pix_clk); goto fail; } #endif dev->id = init->id; dev->irq = init->irq; dev->base_addr = init->base_addr; dev->err_cb = init->err_cb; dev->eof_cb = init->eof_cb; INIT_WORK(&dev->err_work, err_cb); INIT_WORK(&dev->eof_work, eof_cb); dev->state = PV_INIT_DONE; if (!g_display_enabled) { printk("%s:%d\n", __func__, __LINE__); dev->state = PV_INIT_DONE; } else { pv_clk_enable(dev); printk("enabling pv isr in init\n"); #ifdef INT_4_LONG_PKT writel(VFP_START, dev->base_addr + REG_PV_INTEN); //workaround1 #endif dev->state = PV_ENABLED; } g_pv_init[init->id] = true; *config = &dev->vid_config; ret = 0; goto done; fail: free_irq(init->irq, NULL); kfree(dev); done: return ret; }
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); 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) { 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_RESUME_STREAM: if (PV_STOPPED == dev->state) { writel((readl(pv_base + REG_PV_INTEN) & ~VFP_END), pv_base + REG_PV_INTEN); 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); if (!vid_config->cont) { 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_PAUSE_STREAM_SYNC: if (PV_ENABLED == dev->state) { unsigned long flags; if (vid_config->cont) { writel(readl(pv_base + REG_PV_VC) & ~VIDEN, pv_base + REG_PV_VC); spin_lock_irqsave(&lock, flags); dev->state = PV_STOPPING; spin_unlock_irqrestore(&lock, flags); } } /* In case of !cont mode, PV would be in stopping state */ if (PV_STOPPING == dev->state) { unsigned long flags; int retry = 1000; /*50ms */ while (retry > 0) { if (readl(pv_base + REG_PV_STAT) & VID_IDLE_STAT) break; usleep_range(50, 60); retry--; } if (retry <= 0) pv_err("PV_PAUSE_STREAM_SYNC fail in PV_STOPPING\n"); spin_lock_irqsave(&lock, flags); dev->state = PV_STOPPED; spin_unlock_irqrestore(&lock, flags); } ret = 0; break; case PV_STOP_EOF_ASYNC: 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; }