/* * The following two functions absolutely depend on the fact, that * there can be only one camera on mx2 camera sensor interface */ static int mx2_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; int ret; u32 csicr1; if (pcdev->icd) return -EBUSY; ret = clk_enable(pcdev->clk_csi); if (ret < 0) return ret; csicr1 = CSICR1_MCLKEN; if (mx27_camera_emma(pcdev)) { csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | CSICR1_RXFF_LEVEL(0); } else if (cpu_is_mx27()) csicr1 |= CSICR1_SOF_INTEN | CSICR1_RXFF_LEVEL(2); pcdev->csicr1 = csicr1; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); pcdev->icd = icd; dev_info(icd->parent, "Camera driver attached to camera %d\n", icd->devnum); return 0; }
static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { unsigned long flags; clk_disable(pcdev->clk_csi); writel(0, pcdev->base_csi + CSICR1); if (mx27_camera_emma(pcdev)) { writel(0, pcdev->base_emma + PRP_CNTL); } else if (cpu_is_mx25()) { spin_lock_irqsave(&pcdev->lock, flags); pcdev->fb1_active = NULL; pcdev->fb2_active = NULL; writel(0, pcdev->base_csi + CSIDMASA_FB1); writel(0, pcdev->base_csi + CSIDMASA_FB2); spin_unlock_irqrestore(&pcdev->lock, flags); } }
static int mx2_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long common_flags; int ret; int bytesperline; u32 csicr1 = pcdev->csicr1; ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); if (!ret) { common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS); if (!common_flags) { dev_warn(icd->parent, "Flags incompatible: camera 0x%x, host 0x%x\n", cfg.flags, MX2_BUS_FLAGS); return -EINVAL; } } else if (ret != -ENOIOCTLCMD) { return ret; } else { common_flags = MX2_BUS_FLAGS; } if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; else common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; } if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_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(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_HSYNC_POL; if (pcdev->platform_flags & MX2_CAMERA_SWAP16) csicr1 |= CSICR1_SWAP16_EN; if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) csicr1 |= CSICR1_EXT_VSYNC; if (pcdev->platform_flags & MX2_CAMERA_CCIR) csicr1 |= CSICR1_CCIR_EN; if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) csicr1 |= CSICR1_CCIR_MODE; if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) csicr1 |= CSICR1_GCLK_MODE; if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) csicr1 |= CSICR1_INV_DATA; if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB) csicr1 |= CSICR1_PACK_DIR; pcdev->csicr1 = csicr1; bytesperline = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); if (bytesperline < 0) return bytesperline; if (mx27_camera_emma(pcdev)) { ret = mx27_camera_emma_prp_reset(pcdev); if (ret) return ret; if (pcdev->discard_buffer) dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, pcdev->discard_buffer, pcdev->discard_buffer_dma); /* * I didn't manage to properly enable/disable the prp * on a per frame basis during running transfers, * thus we allocate a buffer here and use it to * discard frames when no buffer is available. * Feel free to work on this ;) */ pcdev->discard_size = icd->user_height * bytesperline; pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, pcdev->discard_size, &pcdev->discard_buffer_dma, GFP_KERNEL); if (!pcdev->discard_buffer) return -ENOMEM; mx27_camera_emma_buf_init(icd, bytesperline); } else if (cpu_is_mx25()) { writel((bytesperline * icd->user_height) >> 2, pcdev->base_csi + CSIRXCNT); writel((bytesperline << 16) | icd->user_height, pcdev->base_csi + CSIIMAG_PARA); } writel(pcdev->csicr1, pcdev->base_csi + CSICR1); return 0; }
static void mx2_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->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); spin_lock_irqsave(&pcdev->lock, flags); vb->state = VIDEOBUF_QUEUED; list_add_tail(&vb->queue, &pcdev->capture); if (mx27_camera_emma(pcdev)) { goto out; #ifdef CONFIG_MACH_MX27 } else if (cpu_is_mx27()) { int ret; if (pcdev->active == NULL) { ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ); if (ret) { vb->state = VIDEOBUF_ERROR; wake_up(&vb->done); goto out; } vb->state = VIDEOBUF_ACTIVE; pcdev->active = buf; } #endif } else { /* cpu_is_mx25() */ u32 csicr3, dma_inten = 0; if (pcdev->fb1_active == NULL) { writel(videobuf_to_dma_contig(vb), pcdev->base_csi + CSIDMASA_FB1); pcdev->fb1_active = buf; dma_inten = CSICR1_FB1_DMA_INTEN; } else if (pcdev->fb2_active == NULL) { writel(videobuf_to_dma_contig(vb), pcdev->base_csi + CSIDMASA_FB2); pcdev->fb2_active = buf; dma_inten = CSICR1_FB2_DMA_INTEN; } if (dma_inten) { list_del(&vb->queue); vb->state = VIDEOBUF_ACTIVE; csicr3 = readl(pcdev->base_csi + CSICR3); /* Reflash DMA */ writel(csicr3 | CSICR3_DMA_REFLASH_RFF, pcdev->base_csi + CSICR3); /* clear & enable interrupts */ writel(dma_inten, pcdev->base_csi + CSISR); pcdev->csicr1 |= dma_inten; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); /* enable DMA */ csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1); writel(csicr3, pcdev->base_csi + CSICR3); } } out: spin_unlock_irqrestore(&pcdev->lock, flags); }
static int mx2_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx2_camera_dev *pcdev = ici->priv; unsigned long camera_flags, common_flags; int ret = 0; int bytesperline; u32 csicr1 = pcdev->csicr1; camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, MX2_BUS_FLAGS); if (!common_flags) return -EINVAL; if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; else common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; } if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_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; if (common_flags & SOCAM_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) csicr1 |= CSICR1_INV_PCLK; if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_HSYNC_POL; if (pcdev->platform_flags & MX2_CAMERA_SWAP16) csicr1 |= CSICR1_SWAP16_EN; if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) csicr1 |= CSICR1_EXT_VSYNC; if (pcdev->platform_flags & MX2_CAMERA_CCIR) csicr1 |= CSICR1_CCIR_EN; if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) csicr1 |= CSICR1_CCIR_MODE; if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) csicr1 |= CSICR1_GCLK_MODE; if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) csicr1 |= CSICR1_INV_DATA; if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB) csicr1 |= CSICR1_PACK_DIR; pcdev->csicr1 = csicr1; bytesperline = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); if (bytesperline < 0) return bytesperline; if (mx27_camera_emma(pcdev)) { ret = mx27_camera_emma_prp_reset(pcdev); if (ret) return ret; if (pcdev->discard_buffer) dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, pcdev->discard_buffer, pcdev->discard_buffer_dma); /* * I didn't manage to properly enable/disable the prp * on a per frame basis during running transfers, * thus we allocate a buffer here and use it to * discard frames when no buffer is available. * Feel free to work on this ;) */ pcdev->discard_size = icd->user_height * bytesperline; pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, pcdev->discard_size, &pcdev->discard_buffer_dma, GFP_KERNEL); if (!pcdev->discard_buffer) return -ENOMEM; mx27_camera_emma_buf_init(icd, bytesperline); } else if (cpu_is_mx25()) { writel((bytesperline * icd->user_height) >> 2, pcdev->base_csi + CSIRXCNT); writel((bytesperline << 16) | icd->user_height, pcdev->base_csi + CSIIMAG_PARA); } writel(pcdev->csicr1, pcdev->base_csi + CSICR1); return 0; }