static void omap1_cam_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; BUG_ON(icd != pcdev->icd); suspend_capture(pcdev); disable_capture(pcdev); sensor_reset(pcdev, true); /* disable and release system clocks */ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz; CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); clk_disable(pcdev->clk); pcdev->icd = NULL; dev_dbg(icd->dev.parent, "OMAP1 Camera driver detached from camera %d\n", icd->devnum); }
static void omap1_cam_clock_stop(struct soc_camera_host *ici) { struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; suspend_capture(pcdev); disable_capture(pcdev); sensor_reset(pcdev, true); /* disable and release system clocks */ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz; CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); clk_disable(pcdev->clk); }
static irqreturn_t cam_isr(int irq, void *data) { struct omap1_cam_dev *pcdev = data; struct device *dev = pcdev->icd->dev.parent; struct omap1_cam_buf *buf = pcdev->active; u32 it_status; unsigned long flags; it_status = CAM_READ(pcdev, IT_STATUS); if (!it_status) return IRQ_NONE; spin_lock_irqsave(&pcdev->lock, flags); if (WARN_ON(!buf)) { dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", __func__, it_status); suspend_capture(pcdev); disable_capture(pcdev); goto out; } if (unlikely(it_status & FIFO_FULL)) { dev_warn(dev, "%s: FIFO overflow\n", __func__); } else if (it_status & V_DOWN) { /* end of video frame watchdog */ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { /* * In CONTIG mode, the watchdog is disabled with * successful DMA end of block interrupt, and reenabled * on next frame start. If we get here, there is nothing * to check, we must be out of sync. */ } else { if (buf->sgcount == 2) { /* * If exactly 2 sgbufs from the next sglist have * been programmed into the DMA engine (the * first one already transferred into the DMA * runtime register set, the second one still * in the programming set), then we are in sync. */ goto out; } } dev_notice(dev, "%s: unexpected end of video frame\n", __func__); } else if (it_status & V_UP) { u32 mode; if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { /* * In CONTIG mode, we need this interrupt every frame * in oredr to reenable our end of frame watchdog. */ mode = CAM_READ_CACHE(pcdev, MODE); } else { /* * In SG mode, the below enabled end of frame watchdog * is kept on permanently, so we can turn this one shot * setup off. */ mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP; } if (!(mode & EN_V_DOWN)) { /* (re)enable end of frame watchdog interrupt */ mode |= EN_V_DOWN; } CAM_WRITE(pcdev, MODE, mode); goto out; } else { dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", __func__, it_status); goto out; } videobuf_done(pcdev, VIDEOBUF_ERROR); out: spin_unlock_irqrestore(&pcdev->lock, flags); return IRQ_HANDLED; }
static void dma_isr(int channel, unsigned short status, void *data) { struct omap1_cam_dev *pcdev = data; struct omap1_cam_buf *buf = pcdev->active; unsigned long flags; spin_lock_irqsave(&pcdev->lock, flags); if (WARN_ON(!buf)) { suspend_capture(pcdev); disable_capture(pcdev); goto out; } if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { /* * In CONTIG mode, assume we have just managed to collect the * whole frame, hopefully before our end of frame watchdog is * triggered. Then, all we have to do is disabling the watchdog * for this frame, and calling videobuf_done() with success * indicated. */ CAM_WRITE(pcdev, MODE, CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN); videobuf_done(pcdev, VIDEOBUF_DONE); } else { /* * In SG mode, we have to process every sgbuf from the current * sglist, one after another. */ if (buf->sgbuf) { /* * Current sglist not completed yet, try fetching next * sgbuf, hopefully putting it into the DMA programming * register set, making it ready for next DMA * autoreinitialization. */ try_next_sgbuf(pcdev->dma_ch, buf); if (buf->sgbuf) goto out; /* * No more sgbufs left in the current sglist. This * doesn't mean that the whole videobuffer is already * complete, but only that the last sgbuf from the * current sglist is about to be filled. It will be * ready on next DMA interrupt, signalled with the * buf->sgbuf set back to NULL. */ if (buf->result != VIDEOBUF_ERROR) { /* * Video frame collected without errors so far, * we can prepare for collecting a next one * as soon as DMA gets autoreinitialized * after the current (last) sgbuf is completed. */ buf = prepare_next_vb(pcdev); if (!buf) goto out; try_next_sgbuf(pcdev->dma_ch, buf); goto out; } } /* end of videobuf */ videobuf_done(pcdev, buf->result); } out: spin_unlock_irqrestore(&pcdev->lock, flags); }
static void videobuf_done(struct omap1_cam_dev *pcdev, enum videobuf_state result) { struct omap1_cam_buf *buf = pcdev->active; struct videobuf_buffer *vb; struct device *dev = pcdev->icd->dev.parent; if (WARN_ON(!buf)) { suspend_capture(pcdev); disable_capture(pcdev); return; } if (result == VIDEOBUF_ERROR) suspend_capture(pcdev); vb = &buf->vb; if (waitqueue_active(&vb->done)) { if (!pcdev->ready && result != VIDEOBUF_ERROR) { /* * No next buffer has been entered into the DMA * programming register set on time (could be done only * while the previous DMA interurpt was processed, not * later), so the last DMA block, be it a whole buffer * if in CONTIG or its last sgbuf if in SG mode, is * about to be reused by the just autoreinitialized DMA * engine, and overwritten with next frame data. Best we * can do is stopping the capture as soon as possible, * hopefully before the next frame start. */ suspend_capture(pcdev); } vb->state = result; do_gettimeofday(&vb->ts); if (result != VIDEOBUF_ERROR) vb->field_count++; wake_up(&vb->done); /* shift in next buffer */ buf = pcdev->ready; pcdev->active = buf; pcdev->ready = NULL; if (!buf) { /* * No next buffer was ready on time (see above), so * indicate error condition to force capture restart or * stop, depending on next buffer already queued or not. */ result = VIDEOBUF_ERROR; prepare_next_vb(pcdev); buf = pcdev->ready; pcdev->active = buf; pcdev->ready = NULL; } } else if (pcdev->ready) { /* * In both CONTIG and SG mode, the DMA engine has possibly * been already autoreinitialized with the preprogrammed * pcdev->ready buffer. We can either accept this fact * and just swap the buffers, or provoke an error condition * and restart capture. The former seems less intrusive. */ dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n", __func__); pcdev->active = pcdev->ready; if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { /* * In SG mode, we have to make sure that the buffer we * are putting back into the pcdev->ready is marked * fresh. */ buf->sgbuf = NULL; } pcdev->ready = buf; buf = pcdev->active; } else { /* * No next buffer has been entered into * the DMA programming register set on time. */ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { /* * In CONTIG mode, the DMA engine has already been * reinitialized with the current buffer. Best we can do * is not touching it. */ dev_dbg(dev, "%s: nobody waiting on videobuf, reuse it\n", __func__); } else { /* * In SG mode, the DMA engine has just been * autoreinitialized with the last sgbuf from the * current list. Restart capture in order to transfer * next frame start into the first sgbuf, not the last * one. */ if (result != VIDEOBUF_ERROR) { suspend_capture(pcdev); result = VIDEOBUF_ERROR; } } } if (!buf) { dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__); disable_capture(pcdev); return; } if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { /* * In CONTIG mode, the current buffer parameters had already * been entered into the DMA programming register set while the * buffer was fetched with prepare_next_vb(), they may have also * been transferred into the runtime set and already active if * the DMA still running. */ } else { /* In SG mode, extra steps are required */ if (result == VIDEOBUF_ERROR) /* make sure we (re)use sglist from start on error */ buf->sgbuf = NULL; /* * In any case, enter the next sgbuf parameters into the DMA * programming register set. They will be used either during * nearest DMA autoreinitialization or, in case of an error, * on DMA startup below. */ try_next_sgbuf(pcdev->dma_ch, buf); } if (result == VIDEOBUF_ERROR) { dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n", __func__); start_capture(pcdev); /* * In SG mode, the above also resulted in the next sgbuf * parameters being entered into the DMA programming register * set, making them ready for next DMA autoreinitialization. */ } /* * Finally, try fetching next buffer. * In CONTIG mode, it will also enter it into the DMA programming * register set, making it ready for next DMA autoreinitialization. */ prepare_next_vb(pcdev); }