📄 omap24xxcam.c
字号:
/* stop any DMA transfers in progress */ omap24xxcam_dma_stop(cam, csr); spin_lock_irqsave(&cam->sg_lock, irqflags); if (cam->free_sgdma < NUM_SG_DMA) { sgslot = (cam->next_sgdma + cam->free_sgdma) % NUM_SG_DMA; sgdma = cam->sgdma + sgslot; if (sgdma->next_sglist != 0) { /* This DMA transfer was in progress, so abort it. */ dma_callback_t callback = sgdma->callback; void *arg = sgdma->arg; cam->free_sgdma++; if (callback) { /* leave interrupts masked */ spin_unlock(&cam->sg_lock); (*callback)(cam, csr, arg); spin_lock(&cam->sg_lock); } } } spin_unlock_irqrestore(&cam->sg_lock, irqflags);}/* This routine is not needed at the moment, but we'll keep it around just in * case. */#if 0/* Stop the DMA controller, aborting any DMA transfers in progress. Any queued * scatter-gather DMA transactions are cancelled. The DMA controller will be * idle after this routine completes, and the scatter-gather DMA queue will * be empty. csr is the completion status passed to the completion routines * for any DMA transfers that are aborted. It should have at least one error * bit set. */static voidomap24xxcam_sg_dma_stop(struct omap24xxcam_device *cam, unsigned long csr){ unsigned long irqflags; int queued_sgdma, sgslot; struct sgdma_state *sgdma; dma_callback_t callback; void *arg; /* stop any DMA transfers in progress */ omap24xxcam_dma_stop(cam, csr); spin_lock_irqsave(&cam->sg_lock, irqflags); queued_sgdma = NUM_SG_DMA - cam->free_sgdma; sgslot = (cam->next_sgdma + cam->free_sgdma) % NUM_SG_DMA; while (queued_sgdma > 0) { sgdma = cam->sgdma + sgslot; callback = sgdma->callback; arg = sgdma->arg; cam->free_sgdma++; sgdma->queued_sglist = 0; if (callback) { /* leave interrupts masked */ spin_unlock(&cam->sg_lock); (*callback)(cam, csr, arg); spin_lock(&cam->sg_lock); } queued_sgdma--; sgslot = (sgslot + 1) % NUM_SG_DMA; } spin_unlock_irqrestore(&cam->sg_lock, irqflags);}#endif /* if 0 *//* Register a routine to be called once immediately after a DMA transfer is * started. The purpose of this is to allow the camera interface to be * started only after a DMA transaction has been queued in order to avoid * DMA overruns. The registered callback routine will only be called one * time and then discarded. Only one callback routine may be registered at a * time. * Returns zero if successful, or a non-zero error code if a different callback * routine has already been registered. */static intomap24xxcam_dma_notify(struct omap24xxcam_device *cam, void (*callback)(struct omap24xxcam_device *cam)){ unsigned long irqflags; spin_lock_irqsave(&cam->dma_lock, irqflags); if (cam->dma_notify && (cam->dma_notify != callback)) { spin_unlock_irqrestore(&cam->dma_lock, irqflags); return -EBUSY; } cam->dma_notify = callback; spin_unlock_irqrestore(&cam->dma_lock, irqflags); return 0; }/* Camera DMA interrupt service routine. */static voidomap24xxcam_dma_isr(struct omap24xxcam_device *cam){ int dmach, i; dma_callback_t callback; void *arg; unsigned long csr; const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; spin_lock(&cam->dma_lock); if (cam->free_dmach == NUM_CAMDMA_CHANNELS) { /* A camera DMA interrupt occurred while all channels are idle, * so we'll acknowledge the interrupt in the IRQSTATUS register * and exit. */#ifdef CONFIG_OMAP24XX_VIRTIO printk("m");#endif for ( i = 0; i< 4; ++i) { csr = camdma_reg_in(cam, CAMDMA_CSR(i)); /* ack interrupt in CSR */ camdma_reg_out(cam, CAMDMA_CSR(i), csr); } camdma_reg_out(cam, CAMDMA_IRQSTATUS_L0, 0xffffffff); spin_unlock(&cam->dma_lock); return; } while (cam->free_dmach < NUM_CAMDMA_CHANNELS) { //udelay(2);#ifdef CONFIG_OMAP24XX_VIRTIO printk("k");#endif dmach = (cam->next_dmach + cam->free_dmach) % NUM_CAMDMA_CHANNELS; if (camdma_reg_in(cam, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE) { /* This buffer hasn't finished yet, so we're done. */ break; } csr = camdma_reg_in(cam, CAMDMA_CSR(dmach)); /* ack interrupt in CSR */ camdma_reg_out(cam, CAMDMA_CSR(dmach), csr); /* ack interrupt in IRQSTATUS */ camdma_reg_out(cam, CAMDMA_IRQSTATUS_L0, (1 << dmach)); if (csr & csr_error) { /* A DMA error occurred, so stop all DMA transfers in * progress. */ spin_unlock(&cam->dma_lock); omap24xxcam_dma_stop(cam, csr); return; } else { callback = cam->camdma[dmach].callback; arg = cam->camdma[dmach].arg; cam->free_dmach++; if (callback) { spin_unlock(&cam->dma_lock); (*callback)(cam, csr, arg); spin_lock(&cam->dma_lock); } } } spin_unlock(&cam->dma_lock); omap24xxcam_sg_dma_process(cam);}/* Shutdown the camera DMA driver and controller. */static voidomap24xxcam_dma_exit(struct omap24xxcam_device *cam){ int ch; /* Mask all DMA interrupts */ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0); /* disable all DMA channels */ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) camdma_reg_out(cam, CAMDMA_CCR(ch), 0);}/* Initialize the camera DMA driver. */static voidomap24xxcam_dma_init(struct omap24xxcam_device *cam){ int ch, sg; /* group all channels on DMA IRQ0 and unmask irq */ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0xffffffff); camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0); spin_lock_init(&cam->dma_lock); cam->free_dmach = NUM_CAMDMA_CHANNELS; cam->next_dmach = 0; for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) { cam->camdma[ch].callback = NULL; cam->camdma[ch].arg = NULL; } spin_lock_init(&cam->sg_lock); cam->free_sgdma = NUM_SG_DMA; cam->next_sgdma = 0; for (sg = 0; sg < NUM_SG_DMA; sg++) { cam->sgdma[sg].sglen = 0; cam->sgdma[sg].next_sglist = 0; cam->sgdma[sg].queued_sglist = 0; cam->sgdma[sg].csr = 0; cam->sgdma[sg].callback = NULL; cam->sgdma[sg].arg = NULL; }}/* -------------------------------------------------------------------------- *//* Callback routine for overlay DMA completion. We just start another DMA * transfer unless overlay has been turned off. */static voidomap24xxcam_overlay_callback(struct omap24xxcam_device *cam, unsigned long csr, void *arg){ int err; unsigned long irqflags; const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; spin_lock_irqsave(&cam->overlay_lock, irqflags); if (cam->overlay_cnt > 0) --cam->overlay_cnt; if (!cam->previewing || (csr & csr_error)) { spin_unlock_irqrestore(&cam->overlay_lock, irqflags); return; } sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys; sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage; while (cam->overlay_cnt < 2) { err = omap24xxcam_sg_dma_queue(cam, &cam->overlay_sglist, 1, omap24xxcam_overlay_callback, NULL); if (err) break; ++cam->overlay_cnt; } spin_unlock_irqrestore(&cam->overlay_lock, irqflags);}/* Begin DMA'ing into the camera overlay framebuffer. We queue up two DMA * transfers so that all frames will be captured. */static voidomap24xxcam_start_overlay_dma(struct omap24xxcam_device *cam){ int err; unsigned long irqflags; if (!cam->previewing) return; if (cam->pix.sizeimage > cam->overlay_size) return; spin_lock_irqsave(&cam->overlay_lock, irqflags); sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys; sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage; while (cam->overlay_cnt < 2) { err = omap24xxcam_sg_dma_queue(cam, &cam->overlay_sglist, 1, omap24xxcam_overlay_callback, NULL); if (err) break; ++cam->overlay_cnt; } spin_unlock_irqrestore(&cam->overlay_lock, irqflags);}/* Enable the camera core interface. */static voidomap24xxcam_cc_enable(struct omap24xxcam_device *cam){ /* Get the CC_CTRL register value for the current capture format. */ omap24xxcam_sensor_cc_ctrl(cam); /* program the camera interface DMA packet size */ cc_reg_out(cam, CC_CTRL_DMA, CC_CTRL_DMA_EN | (DMA_THRESHOLD/4 - 1)); /* enable camera core error interrupts */ cc_reg_out(cam, CC_IRQENABLE, CC_IRQENABLE_FW_ERR_IRQ | CC_IRQENABLE_FSC_ERR_IRQ | CC_IRQENABLE_SSC_ERR_IRQ | CC_IRQENABLE_FIFO_OF_IRQ); /* enable the camera interface */ cc_reg_out(cam, CC_CTRL, cam->cc_ctrl | CC_CTRL_CC_EN);}/* Error recovery for DMA errors and camera interface errors. */static voidomap24xxcam_error_handler(struct omap24xxcam_device *cam){ /* Get the CC_CTRL register value for the current capture format. */ omap24xxcam_sensor_cc_ctrl(cam); /* Disable and reset the camera interface. */ cc_reg_out(cam, CC_CTRL, cam->cc_ctrl & ~(CC_CTRL_CC_EN | CC_CTRL_CC_FRAME_TRIG)); cc_reg_out(cam, CC_CTRL, (cam->cc_ctrl | CC_CTRL_CC_RST) & ~(CC_CTRL_CC_EN | CC_CTRL_CC_FRAME_TRIG)); /* Stop the DMA controller and frame sync scatter-gather DMA. */ omap24xxcam_sg_dma_sync(cam, CAMDMA_CSR_TRANS_ERR);#if 1 /* Reset and re-initialize the entire camera subsystem. * Resetting the camera FIFO via the CC_RST bit in the CC_CTRL * register is supposed to be sufficient to recover from a * camera interface error, but it doesn't seem to be enough. If * we only do that then subsequent image captures are out of sync * by either one or two times DMA_THRESHOLD bytes. Resetting and * re-initializing the entire camera subsystem prevents the problem * with frame synchronization. Calling cam_init() from an ISR is * undesirable since it waits an indeterminate amount of time for the * camera subsystem reset to complete. If we ever figure out exactly * what needs to be reset to recover from a camera interface error, * maybe we can replace this global reset with something less drastic. */ cam_init(cam); omap24xxcam_set_xclk(cam); /* group all channels on DMA IRQ0 and unmask irq */ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0xffffffff); camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0); camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0);#endif if (cam->previewing || cam->streaming) { /* Preview or capture is in progress, so we need to register * our routine to restart the camera interface the next time a * DMA transfer is queued. */ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable); } /* Restart overlay DMA if preview is enabled. */ omap24xxcam_start_overlay_dma(cam); /* Restart the scatter-gather DMA queue. */ omap24xxcam_sg_dma_process(cam);}/* Interrupt service routine for camera core interrupts. */static voidomap24xxcam_cc_isr(struct omap24xxcam_device *cam){ unsigned long cc_irqstatus; const unsigned long cc_irqstatus_err = CC_IRQSTATUS_FW_ERR_IRQ | CC_IRQSTATUS_FSC_ERR_IRQ | CC_IRQSTATUS_SSC_ERR_IRQ | CC_IRQSTATUS_FIFO_OF_IRQ; cc_irqstatus = cc_reg_in(cam, CC_IRQSTATUS); cc_reg_out(cam, CC_IRQSTATUS, cc_irqstatus); if (cc_irqstatus & cc_irqstatus_err) omap24xxcam_error_handler(cam);}static irqreturn_tomap24xxcam_isr(int irq, void *arg, struct pt_regs *regs){ struct omap24xxcam_device *cam = (struct omap24xxcam_device *) arg; unsigned long irqstatus; unsigned int irqhandled = 0; irqstatus = cam_reg_in(cam, CAM_IRQSTATUS); if (irqstatus & (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1 | CAM_IRQSTATUS_DMA_IRQ0)) { omap24xxcam_dma_isr(cam); irqhandled = 1; } if (irqstatus & CAM_IRQSTATUS_CC_IRQ) { omap24xxcam_cc_isr(cam); irqhandled = 1; } if (irqstatus & CAM_IRQSTATUS_MMU_IRQ) printk(KERN_ERR CAM_NAME ": Unhandled camera MMU interrupt!\n"); return IRQ_RETVAL(irqhandled);}static voidomap24xxcam_adjust_xclk(struct omap24xxcam_device *cam){ unsigned long divisor; if (cam->xclk > cam->mclk) cam->xclk = cam->mclk; divisor = cam->mclk/cam->xclk; if (cam->xclk*divisor < cam->mclk) divisor += 1; if (divisor > 30) divisor = 30; cam->xclk = cam->mclk/divisor;}/* -------------------------------------------------------------------------- *//* This routine is called from interrupt context when a scatter-gather DMA * transfer of a videobuf_buffer completes. */static voidomap24xxcam_vbq_complete(struct omap24xxcam_device *cam, unsigned long csr, void *arg){ struct videobuf_buffer *vb = (struct videobuf_buffer *) arg; const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; spin_lock(&cam->vbq_lock); do_gettimeofday(&vb->ts); vb->field_count = cam->field_count; cam->field_count += 2; if (csr & csr_error) { vb->state = STATE_ERROR; omap24xxcam_error_handler(cam); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -