/* * 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; }
/* * 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 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 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; }