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 sensor_reset(struct omap1_cam_dev *pcdev, bool reset) { /* apply/release camera sensor reset if requested by platform data */ if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH) CAM_WRITE(pcdev, GPIO, reset); else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW) CAM_WRITE(pcdev, GPIO, !reset); }
/* * The following two functions absolutely depend on the fact, that * there can be only one camera on OMAP1 camera sensor interface */ static int omap1_cam_add_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; if (pcdev->icd) return -EBUSY; clk_enable(pcdev->clk); /* setup sensor clock */ ctrlclock = CAM_READ(pcdev, CTRLCLOCK); ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); ctrlclock &= ~FOSCMOD_MASK; switch (pcdev->camexclk) { case 6000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz; break; case 8000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN; break; case 9600000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN; break; case 12000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz; break; case 24000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN; default: break; } CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN); /* enable internal clock */ ctrlclock |= MCLK_EN; CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); sensor_reset(pcdev, false); pcdev->icd = icd; dev_dbg(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n", icd->devnum); return 0; }
static void suspend_capture(struct omap1_cam_dev *pcdev) { u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); omap_stop_dma(pcdev->dma_ch); }
/* * The following two functions absolutely depend on the fact, that * there can be only one camera on OMAP1 camera sensor interface */ static int omap1_cam_clock_start(struct soc_camera_host *ici) { struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; clk_enable(pcdev->clk); /* setup sensor clock */ ctrlclock = CAM_READ(pcdev, CTRLCLOCK); ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN); CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); ctrlclock &= ~FOSCMOD_MASK; switch (pcdev->camexclk) { case 6000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz; break; case 8000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN; break; case 9600000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN; break; case 12000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz; break; case 24000000: ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN; default: break; } CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN); /* enable internal clock */ ctrlclock |= MCLK_EN; CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); sensor_reset(pcdev, false); return 0; }
static void omap1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct omap1_cam_dev *pcdev = ici->priv; struct omap1_cam_buf *buf; u32 mode; list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_QUEUED; if (pcdev->active) { /* * Capture in progress, so don't touch pcdev->ready even if * empty. Since the transfer of the DMA programming register set * content to the DMA working register set is done automatically * by the DMA hardware, this can pretty well happen while we * are keeping the lock here. Leave fetching it from the queue * to be done when a next DMA interrupt occures instead. */ return; } WARN_ON(pcdev->ready); buf = prepare_next_vb(pcdev); if (WARN_ON(!buf)) return; pcdev->active = buf; pcdev->ready = NULL; dev_dbg(icd->dev.parent, "%s: capture not active, setup FIFO, start DMA\n", __func__); mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK; mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT; CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA); if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { /* * In SG mode, the above prepare_next_vb() didn't actually * put anything into the DMA programming register set, * so we have to do it now, before activating DMA. */ try_next_sgbuf(pcdev->dma_ch, buf); } start_capture(pcdev); }
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 void start_capture(struct omap1_cam_dev *pcdev) { struct omap1_cam_buf *buf = pcdev->active; u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN; if (WARN_ON(!buf)) return; /* * Enable start of frame interrupt, which we will use for activating * our end of frame watchdog when capture actually starts. */ mode |= EN_V_UP; if (unlikely(ctrlclock & LCLK_EN)) /* stop pixel clock before FIFO reset */ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); /* reset FIFO */ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO); omap_start_dma(pcdev->dma_ch); if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { /* * In SG mode, it's a good moment for fetching next sgbuf * from the current sglist and, if available, already putting * its parameters into the DMA programming register set. */ try_next_sgbuf(pcdev->dma_ch, buf); } /* (re)enable pixel clock */ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN); /* release FIFO reset */ CAM_WRITE(pcdev, MODE, mode); }
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 disable_capture(struct omap1_cam_dev *pcdev) { u32 mode = CAM_READ_CACHE(pcdev, MODE); CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA)); }
static int omap1_cam_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct omap1_cam_dev *pcdev = ici->priv; struct device *dev = icd->dev.parent; const struct soc_camera_format_xlate *xlate; const struct soc_mbus_pixelfmt *fmt; unsigned long camera_flags, common_flags; u32 ctrlclock, mode; int ret; camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, SOCAM_BUS_FLAGS); if (!common_flags) return -EINVAL; /* Make choices, possibly based on platform configuration */ if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { if (!pcdev->pdata || pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; else common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; } ret = icd->ops->set_bus_param(icd, common_flags); if (ret < 0) return ret; ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); if (common_flags & SOCAM_PCLK_SAMPLE_RISING) { dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); ctrlclock |= POLCLK; } else { dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n"); ctrlclock &= ~POLCLK; } CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); /* select bus endianess */ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); fmt = xlate->host_fmt; mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA); if (fmt->order == SOC_MBUS_ORDER_LE) { dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n"); CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD); } else { dev_dbg(dev, "MODE_REG |= ORDERCAMD\n"); CAM_WRITE(pcdev, MODE, mode | ORDERCAMD); } return 0; }
static int omap1_cam_set_bus_param(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; u32 pixfmt = icd->current_fmt->host_fmt->fourcc; const struct soc_camera_format_xlate *xlate; const struct soc_mbus_pixelfmt *fmt; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long common_flags; u32 ctrlclock, mode; int ret; ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); if (!ret) { common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS); if (!common_flags) { dev_warn(dev, "Flags incompatible: camera 0x%x, host 0x%x\n", cfg.flags, SOCAM_BUS_FLAGS); return -EINVAL; } } else if (ret != -ENOIOCTLCMD) { return ret; } else { common_flags = SOCAM_BUS_FLAGS; } /* Make choices, possibly based on platform configuration */ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (!pcdev->pdata || pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; else common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; } cfg.flags = common_flags; ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); if (ret < 0 && ret != -ENOIOCTLCMD) { dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) { dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); ctrlclock |= POLCLK; } else { dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n"); ctrlclock &= ~POLCLK; } CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); /* select bus endianness */ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); fmt = xlate->host_fmt; mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA); if (fmt->order == SOC_MBUS_ORDER_LE) { dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n"); CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD); } else { dev_dbg(dev, "MODE_REG |= ORDERCAMD\n"); CAM_WRITE(pcdev, MODE, mode | ORDERCAMD); } return 0; }