On 03/12/2017 05:44 PM, Guennadi Liakhovetski wrote: > Hi Hans, > > Thanks for the patch. Why hasn't this patch been CCed to Josh Wu? Is he > still at Atmel? Adding to CC to check. To the best of my knowledge Josh no longer works for Atmel/Microchip, and Songjun took over. > > A general comment: this patch doesn't only remove soc-camera dependencies, > it also changes the code and the behaviour. I would prefer to break that > down in multiple patches, or remove this driver completely and add a new > one. I'll provide some comments, but of course I cannot make an extensive > review of a 1200-line of change patch without knowing the hardware and the > set ups well enough. > > On Sat, 11 Mar 2017, Hans Verkuil wrote: > >> From: Hans Verkuil <hans.verkuil@xxxxxxxxx> >> >> This patch converts the atmel-isi driver from a soc-camera driver to a driver >> that is stand-alone. >> >> Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> >> --- >> drivers/media/platform/soc_camera/Kconfig | 3 +- >> drivers/media/platform/soc_camera/atmel-isi.c | 1209 +++++++++++++++---------- >> 2 files changed, 714 insertions(+), 498 deletions(-) >> >> diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig >> index 86d74788544f..a37ec91b026e 100644 >> --- a/drivers/media/platform/soc_camera/Kconfig >> +++ b/drivers/media/platform/soc_camera/Kconfig >> @@ -29,9 +29,8 @@ config VIDEO_SH_MOBILE_CEU >> >> config VIDEO_ATMEL_ISI >> tristate "ATMEL Image Sensor Interface (ISI) support" >> - depends on VIDEO_DEV && SOC_CAMERA >> + depends on VIDEO_V4L2 && OF && HAS_DMA >> depends on ARCH_AT91 || COMPILE_TEST >> - depends on HAS_DMA >> select VIDEOBUF2_DMA_CONTIG >> ---help--- >> This module makes the ATMEL Image Sensor Interface available >> diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c >> index 46de657c3e6d..a6d60c2e207d 100644 >> --- a/drivers/media/platform/soc_camera/atmel-isi.c >> +++ b/drivers/media/platform/soc_camera/atmel-isi.c >> @@ -22,18 +22,22 @@ >> #include <linux/platform_device.h> >> #include <linux/pm_runtime.h> >> #include <linux/slab.h> >> - >> -#include <media/soc_camera.h> >> -#include <media/drv-intf/soc_mediabus.h> >> +#include <linux/of.h> >> + >> +#include <linux/videodev2.h> >> +#include <media/v4l2-ctrls.h> >> +#include <media/v4l2-device.h> >> +#include <media/v4l2-dev.h> >> +#include <media/v4l2-ioctl.h> >> +#include <media/v4l2-event.h> >> #include <media/v4l2-of.h> >> #include <media/videobuf2-dma-contig.h> >> +#include <media/v4l2-image-sizes.h> >> >> #include "atmel-isi.h" >> >> -#define MAX_BUFFER_NUM 32 >> #define MAX_SUPPORT_WIDTH 2048 >> #define MAX_SUPPORT_HEIGHT 2048 >> -#define VID_LIMIT_BYTES (16 * 1024 * 1024) >> #define MIN_FRAME_RATE 15 >> #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) >> >> @@ -65,9 +69,37 @@ struct frame_buffer { >> struct list_head list; >> }; >> >> +struct isi_graph_entity { >> + struct device_node *node; >> + >> + struct v4l2_async_subdev asd; >> + struct v4l2_subdev *subdev; >> +}; >> + >> +/* >> + * struct isi_format - ISI media bus format information >> + * @fourcc: Fourcc code for this format >> + * @mbus_code: V4L2 media bus format code. >> + * @bpp: Bytes per pixel (when stored in memory) >> + * @swap: Byte swap configuration value >> + * @support: Indicates format supported by subdev >> + * @skip: Skip duplicate format supported by subdev >> + */ >> +struct isi_format { >> + u32 fourcc; >> + u32 mbus_code; >> + u8 bpp; >> + u32 swap; >> + >> + bool support; >> + bool skip; >> +}; >> + >> + >> struct atmel_isi { >> /* Protects the access of variables shared with the ISR */ >> - spinlock_t lock; >> + spinlock_t irqlock; >> + struct device *dev; >> void __iomem *regs; >> >> int sequence; >> @@ -76,7 +108,7 @@ struct atmel_isi { >> struct fbd *p_fb_descriptors; >> dma_addr_t fb_descriptors_phys; >> struct list_head dma_desc_head; >> - struct isi_dma_desc dma_desc[MAX_BUFFER_NUM]; >> + struct isi_dma_desc dma_desc[VIDEO_MAX_FRAME]; >> bool enable_preview_path; >> >> struct completion complete; >> @@ -90,9 +122,22 @@ struct atmel_isi { >> struct list_head video_buffer_list; >> struct frame_buffer *active; >> >> - struct soc_camera_host soc_host; >> + struct v4l2_device v4l2_dev; >> + struct video_device *vdev; >> + struct v4l2_async_notifier notifier; >> + struct isi_graph_entity entity; >> + struct v4l2_format fmt; >> + >> + struct isi_format **user_formats; >> + unsigned int num_user_formats; >> + const struct isi_format *current_fmt; >> + >> + struct mutex lock; >> + struct vb2_queue queue; >> }; >> >> +#define notifier_to_isi(n) container_of(n, struct atmel_isi, notifier) >> + >> static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val) >> { >> writel(val, isi->regs + reg); >> @@ -102,107 +147,46 @@ static u32 isi_readl(struct atmel_isi *isi, u32 reg) >> return readl(isi->regs + reg); >> } >> >> -static u32 setup_cfg2_yuv_swap(struct atmel_isi *isi, >> - const struct soc_camera_format_xlate *xlate) >> -{ >> - if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUYV) { >> - /* all convert to YUYV */ >> - switch (xlate->code) { >> - case MEDIA_BUS_FMT_VYUY8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_3; >> - case MEDIA_BUS_FMT_UYVY8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_2; >> - case MEDIA_BUS_FMT_YVYU8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_1; >> - } >> - } else if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_RGB565) { >> - /* >> - * Preview path is enabled, it will convert UYVY to RGB format. >> - * But if sensor output format is not UYVY, we need to set >> - * YCC_SWAP_MODE to convert it as UYVY. >> - */ >> - switch (xlate->code) { >> - case MEDIA_BUS_FMT_VYUY8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_1; >> - case MEDIA_BUS_FMT_YUYV8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_2; >> - case MEDIA_BUS_FMT_YVYU8_2X8: >> - return ISI_CFG2_YCC_SWAP_MODE_3; >> - } >> - } >> - >> - /* >> - * By default, no swap for the codec path of Atmel ISI. So codec >> - * output is same as sensor's output. >> - * For instance, if sensor's output is YUYV, then codec outputs YUYV. >> - * And if sensor's output is UYVY, then codec outputs UYVY. >> - */ >> - return ISI_CFG2_YCC_SWAP_DEFAULT; >> -} >> +static struct isi_format isi_formats[] = { > > This isn't a const array, you're modifying it during initialisation. Are > we sure there aren't going to be any SoCs with more than one ISI? When that happens, this should be changed at that time. I think it is unlikely since as I understand it ISI has been replaced by ISC (atmel-isc). > >> + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, >> + 2, ISI_CFG2_YCC_SWAP_DEFAULT, false }, > > This struct has 6 elements and is defined almost a 100 lines above this > initialisation. I'd find using explicit field names easier to read, Moved down and use field names. > especially since you only initialise 5 out of 6 fields explicitly. You > also explicitly set the fifth field everywhere to false and leave the > sixth field implicitly to false. > >> + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YVYU8_2X8, >> + 2, ISI_CFG2_YCC_SWAP_MODE_1, false }, >> + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_UYVY8_2X8, >> + 2, ISI_CFG2_YCC_SWAP_MODE_2, false }, >> + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_VYUY8_2X8, >> + 2, ISI_CFG2_YCC_SWAP_MODE_3, false }, >> + { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_YUYV8_2X8, >> + 2, ISI_CFG2_YCC_SWAP_MODE_2, false }, > > Why are you dropping other conversions to RGB565? Were they wrong? There was a reason, but I have forgotten it :-( I'll have to retest the other three YUYV combinations. > >> +}; >> >> -static void configure_geometry(struct atmel_isi *isi, u32 width, >> - u32 height, const struct soc_camera_format_xlate *xlate) >> +static void configure_geometry(struct atmel_isi *isi) >> { >> - u32 cfg2, psize; >> - u32 fourcc = xlate->host_fmt->fourcc; >> + u32 cfg2 = 0, psize; > > Superfluous initialisation of cfg2. Fixed. > >> + u32 fourcc = isi->current_fmt->fourcc; >> >> isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 || >> fourcc == V4L2_PIX_FMT_RGB32; >> >> /* According to sensor's output format to set cfg2 */ >> - switch (xlate->code) { >> - default: >> - /* Grey */ >> - case MEDIA_BUS_FMT_Y8_1X8: >> - cfg2 = ISI_CFG2_GRAYSCALE | ISI_CFG2_COL_SPACE_YCbCr; >> - break; >> - /* YUV */ >> - case MEDIA_BUS_FMT_VYUY8_2X8: >> - case MEDIA_BUS_FMT_UYVY8_2X8: >> - case MEDIA_BUS_FMT_YVYU8_2X8: >> - case MEDIA_BUS_FMT_YUYV8_2X8: >> - cfg2 = ISI_CFG2_COL_SPACE_YCbCr | >> - setup_cfg2_yuv_swap(isi, xlate); >> - break; >> - /* RGB, TODO */ >> - } >> + cfg2 = isi->current_fmt->swap; >> >> isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); >> /* Set width */ >> - cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & >> + cfg2 |= ((isi->fmt.fmt.pix.width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & >> ISI_CFG2_IM_HSIZE_MASK; >> /* Set height */ >> - cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) >> + cfg2 |= ((isi->fmt.fmt.pix.height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) >> & ISI_CFG2_IM_VSIZE_MASK; >> isi_writel(isi, ISI_CFG2, cfg2); >> >> /* No down sampling, preview size equal to sensor output size */ >> - psize = ((width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) & >> + psize = ((isi->fmt.fmt.pix.width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) & >> ISI_PSIZE_PREV_HSIZE_MASK; >> - psize |= ((height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) & >> + psize |= ((isi->fmt.fmt.pix.height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) & >> ISI_PSIZE_PREV_VSIZE_MASK; >> isi_writel(isi, ISI_PSIZE, psize); >> isi_writel(isi, ISI_PDECF, ISI_PDECF_NO_SAMPLING); >> - >> - return; >> -} >> - >> -static bool is_supported(struct soc_camera_device *icd, >> - const u32 pixformat) >> -{ >> - switch (pixformat) { >> - /* YUV, including grey */ >> - case V4L2_PIX_FMT_GREY: >> - case V4L2_PIX_FMT_YUYV: >> - case V4L2_PIX_FMT_UYVY: >> - case V4L2_PIX_FMT_YVYU: >> - case V4L2_PIX_FMT_VYUY: >> - /* RGB */ >> - case V4L2_PIX_FMT_RGB565: >> - return true; >> - default: >> - return false; >> - } >> } >> >> static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) >> @@ -214,6 +198,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) >> list_del_init(&buf->list); >> vbuf->vb2_buf.timestamp = ktime_get_ns(); >> vbuf->sequence = isi->sequence++; >> + vbuf->field = V4L2_FIELD_NONE; >> vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); >> } >> >> @@ -247,7 +232,7 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id) >> u32 status, mask, pending; >> irqreturn_t ret = IRQ_NONE; >> >> - spin_lock(&isi->lock); >> + spin_lock(&isi->irqlock); >> >> status = isi_readl(isi, ISI_STATUS); >> mask = isi_readl(isi, ISI_INTMASK); >> @@ -267,7 +252,7 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id) >> ret = atmel_isi_handle_streaming(isi); >> } >> >> - spin_unlock(&isi->lock); >> + spin_unlock(&isi->irqlock); >> return ret; >> } >> >> @@ -305,26 +290,21 @@ static int queue_setup(struct vb2_queue *vq, >> unsigned int *nbuffers, unsigned int *nplanes, >> unsigned int sizes[], struct device *alloc_devs[]) >> { >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vq); >> unsigned long size; >> >> - size = icd->sizeimage; >> + size = isi->fmt.fmt.pix.sizeimage; >> >> - if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM) >> - *nbuffers = MAX_BUFFER_NUM; >> - >> - if (size * *nbuffers > VID_LIMIT_BYTES) >> - *nbuffers = VID_LIMIT_BYTES / size; >> + /* Make sure the image size is large enough. */ >> + if (*nplanes) >> + return sizes[0] < size ? -EINVAL : 0; >> >> *nplanes = 1; >> sizes[0] = size; >> >> - isi->sequence = 0; >> isi->active = NULL; >> >> - dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__, >> + dev_dbg(isi->dev, "%s, count=%d, size=%ld\n", __func__, >> *nbuffers, size); >> >> return 0; >> @@ -344,17 +324,15 @@ static int buffer_init(struct vb2_buffer *vb) >> static int buffer_prepare(struct vb2_buffer *vb) >> { >> struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); >> struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); >> unsigned long size; >> struct isi_dma_desc *desc; >> >> - size = icd->sizeimage; >> + size = isi->fmt.fmt.pix.sizeimage; >> >> if (vb2_plane_size(vb, 0) < size) { >> - dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n", >> + dev_err(isi->dev, "%s data will not fit into plane (%lu < %lu)\n", >> __func__, vb2_plane_size(vb, 0), size); >> return -EINVAL; >> } >> @@ -363,7 +341,7 @@ static int buffer_prepare(struct vb2_buffer *vb) >> >> if (!buf->p_dma_desc) { >> if (list_empty(&isi->dma_desc_head)) { >> - dev_err(icd->parent, "Not enough dma descriptors.\n"); >> + dev_err(isi->dev, "Not enough dma descriptors.\n"); >> return -EINVAL; >> } else { >> /* Get an available descriptor */ >> @@ -387,9 +365,7 @@ static int buffer_prepare(struct vb2_buffer *vb) >> static void buffer_cleanup(struct vb2_buffer *vb) >> { >> struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); >> struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); >> >> /* This descriptor is available now and we add to head list */ >> @@ -409,7 +385,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) >> /* Check if already in a frame */ >> if (!isi->enable_preview_path) { >> if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { >> - dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n"); >> + dev_err(isi->dev, "Already in frame handling.\n"); >> return; >> } >> >> @@ -443,13 +419,11 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) >> static void buffer_queue(struct vb2_buffer *vb) >> { >> struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); >> struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); >> unsigned long flags = 0; >> >> - spin_lock_irqsave(&isi->lock, flags); >> + spin_lock_irqsave(&isi->irqlock, flags); >> list_add_tail(&buf->list, &isi->video_buffer_list); >> >> if (isi->active == NULL) { >> @@ -457,60 +431,83 @@ static void buffer_queue(struct vb2_buffer *vb) >> if (vb2_is_streaming(vb->vb2_queue)) >> start_dma(isi, buf); >> } >> - spin_unlock_irqrestore(&isi->lock, flags); >> + spin_unlock_irqrestore(&isi->irqlock, flags); >> } >> >> static int start_streaming(struct vb2_queue *vq, unsigned int count) >> { >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vq); >> + struct frame_buffer *buf, *node; >> int ret; >> >> - pm_runtime_get_sync(ici->v4l2_dev.dev); >> + /* Enable stream on the sub device */ >> + ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1); >> + if (ret && ret != -ENOIOCTLCMD) { >> + dev_err(isi->dev, "stream on failed in subdev\n"); >> + goto err_start_stream; >> + } >> + >> + pm_runtime_get_sync(isi->dev); >> >> /* Reset ISI */ >> ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); >> if (ret < 0) { >> - dev_err(icd->parent, "Reset ISI timed out\n"); >> - pm_runtime_put(ici->v4l2_dev.dev); >> - return ret; >> + dev_err(isi->dev, "Reset ISI timed out\n"); >> + goto err_reset; >> } >> /* Disable all interrupts */ >> isi_writel(isi, ISI_INTDIS, (u32)~0UL); >> >> - configure_geometry(isi, icd->user_width, icd->user_height, >> - icd->current_fmt); >> + isi->sequence = 0; >> + configure_geometry(isi); >> >> - spin_lock_irq(&isi->lock); >> + spin_lock_irq(&isi->irqlock); >> /* Clear any pending interrupt */ >> isi_readl(isi, ISI_STATUS); >> >> - if (count) >> - start_dma(isi, isi->active); >> - spin_unlock_irq(&isi->lock); >> + start_dma(isi, isi->active); > > Isn't this also a change in behaviour? Starting DMA also if no buffers > have been queued yet? I now set 'q->min_buffers_needed = 2' so this can never be called with less than 2 buffers queued. 'min_buffers_needed' didn't exist when this code was written, but now we can use it. > >> + spin_unlock_irq(&isi->irqlock); >> >> return 0; >> + >> +err_reset: >> + pm_runtime_put(isi->dev); >> + v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); >> + >> +err_start_stream: >> + spin_lock_irq(&isi->irqlock); >> + isi->active = NULL; >> + /* Release all active buffers */ >> + list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { >> + list_del_init(&buf->list); >> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); >> + } >> + spin_unlock_irq(&isi->irqlock); >> + >> + return ret; >> } >> >> /* abort streaming and wait for last buffer */ >> static void stop_streaming(struct vb2_queue *vq) >> { >> - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> + struct atmel_isi *isi = vb2_get_drv_priv(vq); >> struct frame_buffer *buf, *node; >> int ret = 0; >> unsigned long timeout; >> >> - spin_lock_irq(&isi->lock); >> + /* Disable stream on the sub device */ >> + ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); >> + if (ret && ret != -ENOIOCTLCMD) >> + dev_err(isi->dev, "stream off failed in subdev\n"); >> + >> + spin_lock_irq(&isi->irqlock); >> isi->active = NULL; >> /* Release all active buffers */ >> list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { >> list_del_init(&buf->list); >> vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); >> } >> - spin_unlock_irq(&isi->lock); >> + spin_unlock_irq(&isi->irqlock); >> >> if (!isi->enable_preview_path) { >> timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ; >> @@ -520,7 +517,7 @@ static void stop_streaming(struct vb2_queue *vq) >> msleep(1); >> >> if (time_after(jiffies, timeout)) >> - dev_err(icd->parent, >> + dev_err(isi->dev, >> "Timeout waiting for finishing codec request\n"); >> } >> >> @@ -531,9 +528,9 @@ static void stop_streaming(struct vb2_queue *vq) >> /* Disable ISI and wait for it is done */ >> ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); >> if (ret < 0) >> - dev_err(icd->parent, "Disable ISI timed out\n"); >> + dev_err(isi->dev, "Disable ISI timed out\n"); >> >> - pm_runtime_put(ici->v4l2_dev.dev); >> + pm_runtime_put(isi->dev); >> } >> >> static const struct vb2_ops isi_video_qops = { >> @@ -548,380 +545,257 @@ static const struct vb2_ops isi_video_qops = { >> .wait_finish = vb2_ops_wait_finish, >> }; >> >> -/* ------------------------------------------------------------------ >> - SOC camera operations for the device >> - ------------------------------------------------------------------*/ >> -static int isi_camera_init_videobuf(struct vb2_queue *q, >> - struct soc_camera_device *icd) >> +static int isi_g_fmt_vid_cap(struct file *file, void *priv, >> + struct v4l2_format *fmt) >> { >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> + struct atmel_isi *isi = video_drvdata(file); >> >> - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; >> - q->io_modes = VB2_MMAP; >> - q->drv_priv = icd; >> - q->buf_struct_size = sizeof(struct frame_buffer); >> - q->ops = &isi_video_qops; >> - q->mem_ops = &vb2_dma_contig_memops; >> - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; >> - q->lock = &ici->host_lock; >> - q->dev = ici->v4l2_dev.dev; >> + *fmt = isi->fmt; >> >> - return vb2_queue_init(q); >> + return 0; >> } >> >> -static int isi_camera_set_fmt(struct soc_camera_device *icd, >> - struct v4l2_format *f) >> +static struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, >> + unsigned int fourcc) >> { >> - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); >> - const struct soc_camera_format_xlate *xlate; >> - struct v4l2_pix_format *pix = &f->fmt.pix; >> + unsigned int num_formats = isi->num_user_formats; >> + struct isi_format *fmt; >> + unsigned int i; >> + >> + for (i = 0; i < num_formats; i++) { >> + fmt = isi->user_formats[i]; >> + if (fmt->fourcc == fourcc) >> + return fmt; >> + } >> + >> + return NULL; >> +} >> + >> +static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, >> + struct isi_format **current_fmt) >> +{ >> + struct isi_format *isi_fmt; >> + struct v4l2_pix_format *pixfmt = &f->fmt.pix; >> + struct v4l2_subdev_pad_config pad_cfg; >> struct v4l2_subdev_format format = { >> - .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> + .which = V4L2_SUBDEV_FORMAT_TRY, >> }; >> - struct v4l2_mbus_framefmt *mf = &format.format; >> int ret; >> >> - /* check with atmel-isi support format, if not support use YUYV */ >> - if (!is_supported(icd, pix->pixelformat)) >> - pix->pixelformat = V4L2_PIX_FMT_YUYV; >> - >> - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); >> - if (!xlate) { >> - dev_warn(icd->parent, "Format %x not found\n", >> - pix->pixelformat); >> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) >> return -EINVAL; >> - } >> >> - dev_dbg(icd->parent, "Plan to set format %dx%d\n", >> - pix->width, pix->height); >> + isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat); >> + if (!isi_fmt) { >> + isi_fmt = isi->user_formats[isi->num_user_formats - 1]; >> + pixfmt->pixelformat = isi_fmt->fourcc; >> + } >> >> - mf->width = pix->width; >> - mf->height = pix->height; >> - mf->field = pix->field; >> - mf->colorspace = pix->colorspace; >> - mf->code = xlate->code; >> + /* Limit to Atmel ISC hardware capabilities */ >> + if (pixfmt->width > MAX_SUPPORT_WIDTH) >> + pixfmt->width = MAX_SUPPORT_WIDTH; >> + if (pixfmt->height > MAX_SUPPORT_HEIGHT) >> + pixfmt->height = MAX_SUPPORT_HEIGHT; >> >> - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); >> + v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); >> + ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, >> + &pad_cfg, &format); >> if (ret < 0) >> return ret; >> >> - if (mf->code != xlate->code) >> - return -EINVAL; >> + v4l2_fill_pix_format(pixfmt, &format.format); >> >> - pix->width = mf->width; >> - pix->height = mf->height; >> - pix->field = mf->field; >> - pix->colorspace = mf->colorspace; >> - icd->current_fmt = xlate; >> + pixfmt->field = V4L2_FIELD_NONE; >> + pixfmt->bytesperline = pixfmt->width * isi_fmt->bpp; >> + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; >> >> - dev_dbg(icd->parent, "Finally set format %dx%d\n", >> - pix->width, pix->height); >> + if (current_fmt) >> + *current_fmt = isi_fmt; >> >> - return ret; >> + return 0; >> } >> >> -static int isi_camera_try_fmt(struct soc_camera_device *icd, >> - struct v4l2_format *f) >> +static int isi_set_fmt(struct atmel_isi *isi, struct v4l2_format *f) >> { >> - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); >> - const struct soc_camera_format_xlate *xlate; >> - struct v4l2_pix_format *pix = &f->fmt.pix; >> - struct v4l2_subdev_pad_config pad_cfg; >> struct v4l2_subdev_format format = { >> - .which = V4L2_SUBDEV_FORMAT_TRY, >> + .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> }; >> - struct v4l2_mbus_framefmt *mf = &format.format; >> - u32 pixfmt = pix->pixelformat; >> + struct isi_format *current_fmt; >> int ret; >> >> - /* check with atmel-isi support format, if not support use YUYV */ >> - if (!is_supported(icd, pix->pixelformat)) >> - pix->pixelformat = V4L2_PIX_FMT_YUYV; >> - >> - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); >> - if (pixfmt && !xlate) { >> - dev_warn(icd->parent, "Format %x not found\n", pixfmt); >> - return -EINVAL; >> - } >> - >> - /* limit to Atmel ISI hardware capabilities */ >> - if (pix->height > MAX_SUPPORT_HEIGHT) >> - pix->height = MAX_SUPPORT_HEIGHT; >> - if (pix->width > MAX_SUPPORT_WIDTH) >> - pix->width = MAX_SUPPORT_WIDTH; >> - >> - /* limit to sensor capabilities */ >> - mf->width = pix->width; >> - mf->height = pix->height; >> - mf->field = pix->field; >> - mf->colorspace = pix->colorspace; >> - mf->code = xlate->code; >> + ret = isi_try_fmt(isi, f, ¤t_fmt); >> + if (ret) >> + return ret; >> >> - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); >> + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, >> + current_fmt->mbus_code); >> + ret = v4l2_subdev_call(isi->entity.subdev, pad, >> + set_fmt, NULL, &format); >> if (ret < 0) >> return ret; >> >> - pix->width = mf->width; >> - pix->height = mf->height; >> - pix->colorspace = mf->colorspace; >> + isi->fmt = *f; >> + isi->current_fmt = current_fmt; >> >> - switch (mf->field) { >> - case V4L2_FIELD_ANY: >> - pix->field = V4L2_FIELD_NONE; >> - break; >> - case V4L2_FIELD_NONE: >> - break; >> - default: >> - dev_err(icd->parent, "Field type %d unsupported.\n", >> - mf->field); >> - ret = -EINVAL; >> - } >> - >> - return ret; >> + return 0; >> } >> >> -static const struct soc_mbus_pixelfmt isi_camera_formats[] = { >> - { >> - .fourcc = V4L2_PIX_FMT_YUYV, >> - .name = "Packed YUV422 16 bit", >> - .bits_per_sample = 8, >> - .packing = SOC_MBUS_PACKING_2X8_PADHI, >> - .order = SOC_MBUS_ORDER_LE, >> - .layout = SOC_MBUS_LAYOUT_PACKED, >> - }, >> - { >> - .fourcc = V4L2_PIX_FMT_RGB565, >> - .name = "RGB565", >> - .bits_per_sample = 8, >> - .packing = SOC_MBUS_PACKING_2X8_PADHI, >> - .order = SOC_MBUS_ORDER_LE, >> - .layout = SOC_MBUS_LAYOUT_PACKED, >> - }, >> -}; >> - >> -/* This will be corrected as we get more formats */ >> -static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) >> +static int isi_s_fmt_vid_cap(struct file *file, void *priv, >> + struct v4l2_format *f) >> { >> - return fmt->packing == SOC_MBUS_PACKING_NONE || >> - (fmt->bits_per_sample == 8 && >> - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || >> - (fmt->bits_per_sample > 8 && >> - fmt->packing == SOC_MBUS_PACKING_EXTEND16); >> + struct atmel_isi *isi = video_drvdata(file); >> + >> + if (vb2_is_streaming(&isi->queue)) >> + return -EBUSY; >> + >> + return isi_set_fmt(isi, f); >> } >> >> -#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \ >> - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ >> - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ >> - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ >> - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ >> - V4L2_MBUS_PCLK_SAMPLE_RISING | \ >> - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ >> - V4L2_MBUS_DATA_ACTIVE_HIGH) >> - >> -static int isi_camera_try_bus_param(struct soc_camera_device *icd, >> - unsigned char buswidth) >> +static int isi_try_fmt_vid_cap(struct file *file, void *priv, >> + struct v4l2_format *f) >> { >> - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; >> - unsigned long common_flags; >> - int ret; >> - >> - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); >> - if (!ret) { >> - common_flags = soc_mbus_config_compatible(&cfg, >> - ISI_BUS_PARAM); > > As far as I understand the bus configuration selection is now performed > based on fixed platform data or device tree parameters. Correct. > >> - if (!common_flags) { >> - dev_warn(icd->parent, >> - "Flags incompatible: camera 0x%x, host 0x%x\n", >> - cfg.flags, ISI_BUS_PARAM); >> - return -EINVAL; >> - } >> - } else if (ret != -ENOIOCTLCMD) { >> - return ret; >> - } >> + struct atmel_isi *isi = video_drvdata(file); >> >> - if ((1 << (buswidth - 1)) & isi->width_flags) >> - return 0; >> - return -EINVAL; >> + return isi_try_fmt(isi, f, NULL); >> } >> >> - >> -static int isi_camera_get_formats(struct soc_camera_device *icd, >> - unsigned int idx, >> - struct soc_camera_format_xlate *xlate) >> +static int isi_enum_fmt_vid_cap(struct file *file, void *priv, >> + struct v4l2_fmtdesc *f) >> { >> - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); >> - int formats = 0, ret, i, n; >> - /* sensor format */ >> - struct v4l2_subdev_mbus_code_enum code = { >> - .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> - .index = idx, >> - }; >> - /* soc camera host format */ >> - const struct soc_mbus_pixelfmt *fmt; >> + struct atmel_isi *isi = video_drvdata(file); >> >> - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); >> - if (ret < 0) >> - /* No more formats */ >> - return 0; >> - >> - fmt = soc_mbus_get_fmtdesc(code.code); >> - if (!fmt) { >> - dev_err(icd->parent, >> - "Invalid format code #%u: %d\n", idx, code.code); >> - return 0; >> - } >> + if (f->index >= isi->num_user_formats) >> + return -EINVAL; >> >> - /* This also checks support for the requested bits-per-sample */ >> - ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample); >> - if (ret < 0) { >> - dev_err(icd->parent, >> - "Fail to try the bus parameters.\n"); >> - return 0; >> - } >> + f->pixelformat = isi->user_formats[f->index]->fourcc; >> + return 0; >> +} >> >> - switch (code.code) { >> - case MEDIA_BUS_FMT_UYVY8_2X8: >> - case MEDIA_BUS_FMT_VYUY8_2X8: >> - case MEDIA_BUS_FMT_YUYV8_2X8: >> - case MEDIA_BUS_FMT_YVYU8_2X8: >> - n = ARRAY_SIZE(isi_camera_formats); >> - formats += n; >> - for (i = 0; xlate && i < n; i++, xlate++) { >> - xlate->host_fmt = &isi_camera_formats[i]; >> - xlate->code = code.code; >> - dev_dbg(icd->parent, "Providing format %s using code %d\n", >> - xlate->host_fmt->name, xlate->code); >> - } >> - break; >> - default: >> - if (!isi_camera_packing_supported(fmt)) >> - return 0; >> - if (xlate) >> - dev_dbg(icd->parent, >> - "Providing format %s in pass-through mode\n", >> - fmt->name); >> - } >> +static int isi_querycap(struct file *file, void *priv, >> + struct v4l2_capability *cap) >> +{ >> + strlcpy(cap->driver, "atmel-isi", sizeof(cap->driver)); >> + strlcpy(cap->card, "Atmel Image Sensor Interface", sizeof(cap->card)); >> + strlcpy(cap->bus_info, "platform:isi", sizeof(cap->bus_info)); >> + return 0; >> +} >> >> - /* Generic pass-through */ >> - formats++; >> - if (xlate) { >> - xlate->host_fmt = fmt; >> - xlate->code = code.code; >> - xlate++; >> - } >> +static int isi_enum_input(struct file *file, void *priv, >> + struct v4l2_input *i) >> +{ >> + if (i->index != 0) >> + return -EINVAL; >> >> - return formats; >> + i->type = V4L2_INPUT_TYPE_CAMERA; >> + strlcpy(i->name, "Camera", sizeof(i->name)); >> + return 0; >> } >> >> -static int isi_camera_add_device(struct soc_camera_device *icd) >> +static int isi_g_input(struct file *file, void *priv, unsigned int *i) >> { >> - dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n", >> - icd->devnum); >> + *i = 0; >> + return 0; >> +} >> >> +static int isi_s_input(struct file *file, void *priv, unsigned int i) >> +{ >> + if (i > 0) >> + return -EINVAL; >> return 0; >> } >> >> -static void isi_camera_remove_device(struct soc_camera_device *icd) >> +static int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) >> { >> - dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n", >> - icd->devnum); >> + struct atmel_isi *isi = video_drvdata(file); >> + >> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) >> + return -EINVAL; >> + >> + a->parm.capture.readbuffers = 2; >> + return v4l2_subdev_call(isi->entity.subdev, video, g_parm, a); >> } >> >> -static unsigned int isi_camera_poll(struct file *file, poll_table *pt) >> +static int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) >> { >> - struct soc_camera_device *icd = file->private_data; >> + struct atmel_isi *isi = video_drvdata(file); >> >> - return vb2_poll(&icd->vb2_vidq, file, pt); >> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) >> + return -EINVAL; >> + >> + a->parm.capture.readbuffers = 2; >> + return v4l2_subdev_call(isi->entity.subdev, video, s_parm, a); >> } >> >> -static int isi_camera_querycap(struct soc_camera_host *ici, >> - struct v4l2_capability *cap) >> +static int isi_enum_framesizes(struct file *file, void *fh, >> + struct v4l2_frmsizeenum *fsize) >> { >> - strcpy(cap->driver, "atmel-isi"); >> - strcpy(cap->card, "Atmel Image Sensor Interface"); >> - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; >> - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; >> + struct atmel_isi *isi = video_drvdata(file); >> + const struct isi_format *isi_fmt; >> + struct v4l2_subdev_frame_size_enum fse = { >> + .index = fsize->index, >> + .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> + }; >> + int ret; >> + >> + isi_fmt = find_format_by_fourcc(isi, fsize->pixel_format); >> + if (!isi_fmt) >> + return -EINVAL; >> + >> + fse.code = isi_fmt->mbus_code; >> + >> + ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, >> + NULL, &fse); >> + if (ret) >> + return ret; >> + >> + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; >> + fsize->discrete.width = fse.max_width; >> + fsize->discrete.height = fse.max_height; >> >> return 0; >> } >> >> -static int isi_camera_set_bus_param(struct soc_camera_device *icd) >> +static int isi_enum_frameintervals(struct file *file, void *fh, >> + struct v4l2_frmivalenum *fival) >> { >> - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); >> - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); >> - struct atmel_isi *isi = ici->priv; >> - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; >> - unsigned long common_flags; >> + struct atmel_isi *isi = video_drvdata(file); >> + const struct isi_format *isi_fmt; >> + struct v4l2_subdev_frame_interval_enum fie = { >> + .index = fival->index, >> + .width = fival->width, >> + .height = fival->height, >> + .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> + }; >> int ret; >> - u32 cfg1 = 0; >> >> - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); >> - if (!ret) { >> - common_flags = soc_mbus_config_compatible(&cfg, >> - ISI_BUS_PARAM); >> - if (!common_flags) { >> - dev_warn(icd->parent, >> - "Flags incompatible: camera 0x%x, host 0x%x\n", >> - cfg.flags, ISI_BUS_PARAM); >> - return -EINVAL; >> - } >> - } else if (ret != -ENOIOCTLCMD) { >> + isi_fmt = find_format_by_fourcc(isi, fival->pixel_format); >> + if (!isi_fmt) >> + return -EINVAL; >> + >> + fie.code = isi_fmt->mbus_code; >> + >> + ret = v4l2_subdev_call(isi->entity.subdev, pad, >> + enum_frame_interval, NULL, &fie); >> + if (ret) >> return ret; >> - } else { >> - common_flags = ISI_BUS_PARAM; >> - } >> - dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n", >> - cfg.flags, ISI_BUS_PARAM, common_flags); >> - >> - /* Make choises, based on platform preferences */ >> - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && >> - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { >> - if (isi->pdata.hsync_act_low) >> - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; >> - else >> - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; >> - } >> >> - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && >> - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { >> - if (isi->pdata.vsync_act_low) >> - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; >> - else >> - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; >> - } >> + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; >> + fival->discrete = fie.interval; >> >> - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && >> - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { >> - if (isi->pdata.pclk_act_falling) >> - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; >> - else >> - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; >> - } >> + return 0; >> +} >> >> - 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; >> - } >> +static void isi_camera_set_bus_param(struct atmel_isi *isi) >> +{ >> + u32 cfg1 = 0; >> >> /* set bus param for ISI */ >> - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) >> + if (isi->pdata.hsync_act_low) >> cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; >> - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) >> + if (isi->pdata.vsync_act_low) >> cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; >> - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) >> + if (isi->pdata.pclk_act_falling) >> cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; >> - >> - dev_dbg(icd->parent, "vsync active %s, hsync active %s, sampling on pix clock %s edge\n", >> - common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? "low" : "high", >> - common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? "low" : "high", >> - common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING ? "falling" : "rising"); >> - >> if (isi->pdata.has_emb_sync) >> cfg1 |= ISI_CFG1_EMB_SYNC; >> if (isi->pdata.full_mode) >> @@ -930,50 +804,19 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) >> cfg1 |= ISI_CFG1_THMASK_BEATS_16; >> >> /* Enable PM and peripheral clock before operate isi registers */ >> - pm_runtime_get_sync(ici->v4l2_dev.dev); >> + pm_runtime_get_sync(isi->dev); >> >> isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); >> isi_writel(isi, ISI_CFG1, cfg1); >> >> - pm_runtime_put(ici->v4l2_dev.dev); >> - >> - return 0; >> + pm_runtime_put(isi->dev); >> } >> >> -static struct soc_camera_host_ops isi_soc_camera_host_ops = { >> - .owner = THIS_MODULE, >> - .add = isi_camera_add_device, >> - .remove = isi_camera_remove_device, >> - .set_fmt = isi_camera_set_fmt, >> - .try_fmt = isi_camera_try_fmt, >> - .get_formats = isi_camera_get_formats, >> - .init_videobuf2 = isi_camera_init_videobuf, >> - .poll = isi_camera_poll, >> - .querycap = isi_camera_querycap, >> - .set_bus_param = isi_camera_set_bus_param, >> -}; >> - >> /* -----------------------------------------------------------------------*/ >> -static int atmel_isi_remove(struct platform_device *pdev) >> -{ >> - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); >> - struct atmel_isi *isi = container_of(soc_host, >> - struct atmel_isi, soc_host); >> - >> - soc_camera_host_unregister(soc_host); >> - dma_free_coherent(&pdev->dev, >> - sizeof(struct fbd) * MAX_BUFFER_NUM, >> - isi->p_fb_descriptors, >> - isi->fb_descriptors_phys); >> - pm_runtime_disable(&pdev->dev); >> - >> - return 0; >> -} >> - >> static int atmel_isi_parse_dt(struct atmel_isi *isi, >> struct platform_device *pdev) >> { >> - struct device_node *np= pdev->dev.of_node; >> + struct device_node *np = pdev->dev.of_node; >> struct v4l2_of_endpoint ep; >> int err; >> >> @@ -1021,13 +864,335 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi, >> return 0; >> } >> >> +static int isi_open(struct file *file) >> +{ >> + struct atmel_isi *isi = video_drvdata(file); >> + struct v4l2_subdev *sd = isi->entity.subdev; >> + int ret; >> + >> + if (mutex_lock_interruptible(&isi->lock)) >> + return -ERESTARTSYS; >> + >> + ret = v4l2_fh_open(file); >> + if (ret < 0) >> + goto unlock; >> + >> + if (!v4l2_fh_is_singular_file(file)) >> + goto fh_rel; >> + >> + ret = v4l2_subdev_call(sd, core, s_power, 1); >> + if (ret < 0 && ret != -ENOIOCTLCMD) >> + goto fh_rel; >> + >> + ret = isi_set_fmt(isi, &isi->fmt); >> + if (ret) >> + v4l2_subdev_call(sd, core, s_power, 0); >> +fh_rel: >> + if (ret) >> + v4l2_fh_release(file); >> +unlock: >> + mutex_unlock(&isi->lock); >> + return ret; >> +} >> + >> +static int isi_release(struct file *file) >> +{ >> + struct atmel_isi *isi = video_drvdata(file); >> + struct v4l2_subdev *sd = isi->entity.subdev; >> + bool fh_singular; >> + int ret; >> + >> + mutex_lock(&isi->lock); >> + >> + fh_singular = v4l2_fh_is_singular_file(file); >> + >> + ret = _vb2_fop_release(file, NULL); >> + >> + if (fh_singular) >> + v4l2_subdev_call(sd, core, s_power, 0); >> + >> + mutex_unlock(&isi->lock); >> + >> + return ret; >> +} >> + >> +static const struct v4l2_ioctl_ops isi_ioctl_ops = { >> + .vidioc_querycap = isi_querycap, >> + >> + .vidioc_try_fmt_vid_cap = isi_try_fmt_vid_cap, >> + .vidioc_g_fmt_vid_cap = isi_g_fmt_vid_cap, >> + .vidioc_s_fmt_vid_cap = isi_s_fmt_vid_cap, >> + .vidioc_enum_fmt_vid_cap = isi_enum_fmt_vid_cap, >> + >> + .vidioc_enum_input = isi_enum_input, >> + .vidioc_g_input = isi_g_input, >> + .vidioc_s_input = isi_s_input, >> + >> + .vidioc_g_parm = isi_g_parm, >> + .vidioc_s_parm = isi_s_parm, >> + .vidioc_enum_framesizes = isi_enum_framesizes, >> + .vidioc_enum_frameintervals = isi_enum_frameintervals, >> + >> + .vidioc_reqbufs = vb2_ioctl_reqbufs, >> + .vidioc_create_bufs = vb2_ioctl_create_bufs, >> + .vidioc_querybuf = vb2_ioctl_querybuf, >> + .vidioc_qbuf = vb2_ioctl_qbuf, >> + .vidioc_dqbuf = vb2_ioctl_dqbuf, >> + .vidioc_expbuf = vb2_ioctl_expbuf, >> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, >> + .vidioc_streamon = vb2_ioctl_streamon, >> + .vidioc_streamoff = vb2_ioctl_streamoff, >> + >> + .vidioc_log_status = v4l2_ctrl_log_status, >> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, >> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, >> +}; >> + >> +static const struct v4l2_file_operations isi_fops = { >> + .owner = THIS_MODULE, >> + .unlocked_ioctl = video_ioctl2, >> + .open = isi_open, >> + .release = isi_release, >> + .poll = vb2_fop_poll, >> + .mmap = vb2_fop_mmap, >> + .read = vb2_fop_read, >> +}; >> + >> +static int isi_set_default_fmt(struct atmel_isi *isi) >> +{ >> + struct v4l2_format f = { >> + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, >> + .fmt.pix = { >> + .width = VGA_WIDTH, >> + .height = VGA_HEIGHT, >> + .field = V4L2_FIELD_NONE, >> + .pixelformat = isi->user_formats[0]->fourcc, >> + }, >> + }; >> + int ret; >> + >> + ret = isi_try_fmt(isi, &f, NULL); >> + if (ret) >> + return ret; >> + isi->current_fmt = isi->user_formats[0]; >> + isi->fmt = f; >> + return 0; >> +} >> + >> +static struct isi_format *find_format_by_code(unsigned int code, int *index) >> +{ >> + struct isi_format *fmt = &isi_formats[0]; >> + unsigned int i; >> + >> + for (i = 0; i < ARRAY_SIZE(isi_formats); i++) { >> + if (fmt->mbus_code == code && !fmt->support && !fmt->skip) { >> + *index = i; >> + return fmt; >> + } >> + >> + fmt++; >> + } >> + >> + return NULL; >> +} >> + >> +static int isi_formats_init(struct atmel_isi *isi) >> +{ >> + struct isi_format *fmt; >> + struct v4l2_subdev *subdev = isi->entity.subdev; >> + int num_fmts = 0, i, j; >> + struct v4l2_subdev_mbus_code_enum mbus_code = { >> + .which = V4L2_SUBDEV_FORMAT_ACTIVE, >> + }; >> + >> + fmt = &isi_formats[0]; > > Superfluous too. Fixed. > >> + >> + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, >> + NULL, &mbus_code)) { >> + fmt = find_format_by_code(mbus_code.code, &i); > > find_format_by_code() assigns i, but it is never used. > >> + if (!fmt) { >> + mbus_code.index++; >> + continue; >> + } >> + >> + fmt->support = true; >> + for (i = 0; i < ARRAY_SIZE(isi_formats); i++) >> + if (isi_formats[i].fourcc == fmt->fourcc && >> + !isi_formats[i].support) >> + isi_formats[i].skip = true; >> + num_fmts++; >> + } > > Hm, how about > > +static int isi_formats_init(struct atmel_isi *isi) > +{ > + struct isi_format *isi_fmts[ARRAY_SIZE(isi_formats)]; > + unsigned int n_fmts = 0, i, j; > ... > + > + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, > + NULL, &mbus_code)) { > + for (i = 0; i < ARRAY_SIZE(isi_formats); i++) > + if (isi_formats[i].mbus_code == mbus_code.code) { > + /* Code supported, have we got this fourcc yet? */ > + for (j = 0; j < n_fmts; j++) > + if (isi_fmts[j]->fourcc == > + isi_formats[i].fourcc) > + /* Already available */ > + break; > + if (j == n_fmts) > + /* new */ > + isi_fmts[n_fmts++] = isi_formats + i; > + } > + mbus_code.index++; > + } > + > + devm_kcalloc(dev, n_fmts, ...); > ... > > Without calling .enum_mbus_code() multiple times for the same index, > without .skip, without .support, without overwriting static data... Yes, much better. Thanks! This change supercedes some of my comments I made above since they no longer apply. Regards, Hans > > Thanks > Guennadi >