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. 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? > + { 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, 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? > +}; > > -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. > + 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? > + 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. > - 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. > + > + 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... Thanks Guennadi