diff options
Diffstat (limited to 'drivers/media/platform')
92 files changed, 6886 insertions, 1836 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index d9b872b9285a..421f53188c6c 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -56,7 +56,7 @@ config VIDEO_VIU config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST select VIDEO_ADV7180 select VIDEOBUF_DMA_CONTIG @@ -90,6 +90,7 @@ config VIDEO_OMAP3 select ARM_DMA_USE_IOMMU select OMAP_IOMMU select VIDEOBUF2_DMA_CONTIG + select MFD_SYSCON ---help--- Driver for an OMAP 3 camera controller. @@ -117,6 +118,7 @@ source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" +source "drivers/media/platform/xilinx/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 3ec154742083..8f855616c237 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -48,4 +48,6 @@ obj-y += omap/ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ +obj-$(CONFIG_VIDEO_XILINX) += xilinx/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig index 7b023a76e32e..42d9c186710a 100644 --- a/drivers/media/platform/am437x/Kconfig +++ b/drivers/media/platform/am437x/Kconfig @@ -1,6 +1,6 @@ config VIDEO_AM437X_VPFE tristate "TI AM437x VPFE video capture driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on SOC_AM43XX || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG help diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 56a5cb0d2152..a30cc2f7e4f1 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1645,6 +1645,7 @@ static int vpfe_enum_size(struct file *file, void *priv, fse.index = fsize->index; fse.pad = 0; fse.code = mbus.code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse); if (ret) return -EINVAL; @@ -1700,11 +1701,16 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe, { struct vpfe_config *cfg = vpfe->cfg; struct vpfe_subdev_info *sdinfo; + struct i2c_client *client; + struct i2c_client *curr_client; int i, j = 0; + curr_client = v4l2_get_subdevdata(vpfe->current_subdev->sd); for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { sdinfo = &cfg->sub_devs[i]; - if (!strcmp(sdinfo->name, vpfe->current_subdev->name)) { + client = v4l2_get_subdevdata(sdinfo->sd); + if (client->addr == curr_client->addr && + client->adapter->nr == client->adapter->nr) { if (vpfe->current_input >= 1) return -1; *app_input_index = j + vpfe->current_input; @@ -2296,20 +2302,10 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { - sdinfo = &vpfe->cfg->sub_devs[i]; - - if (!strcmp(sdinfo->name, subdev->name)) { + if (vpfe->cfg->asd[i]->match.of.node == asd[i].match.of.node) { + sdinfo = &vpfe->cfg->sub_devs[i]; vpfe->sd[i] = subdev; - vpfe_info(vpfe, - "v4l2 sub device %s registered\n", - subdev->name); - vpfe->sd[i]->grp_id = - sdinfo->grp_id; - /* update tvnorms from the sub devices */ - for (j = 0; j < 1; j++) - vpfe->video_dev->tvnorms |= - sdinfo->inputs[j].std; - + vpfe->sd[i]->grp_id = sdinfo->grp_id; found = true; break; } @@ -2320,6 +2316,8 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, return -EINVAL; } + vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std; + /* setup the supported formats & indexes */ for (j = 0, i = 0; ; ++j) { struct vpfe_fmt *fmt; @@ -2327,6 +2325,7 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &mbus_code); if (ret) @@ -2390,9 +2389,9 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) INIT_LIST_HEAD(&vpfe->dma_queue); - vdev = vpfe->video_dev; + vdev = &vpfe->video_dev; strlcpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name)); - vdev->release = video_device_release; + vdev->release = video_device_release_empty; vdev->fops = &vpfe_fops; vdev->ioctl_ops = &vpfe_ioctl_ops; vdev->v4l2_dev = &vpfe->v4l2_dev; @@ -2400,7 +2399,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) vdev->queue = q; vdev->lock = &vpfe->lock; video_set_drvdata(vdev, vpfe); - err = video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&vpfe->video_dev, VFL_TYPE_GRABBER, -1); if (err) { vpfe_err(vpfe, "Unable to register video device.\n"); @@ -2425,7 +2424,7 @@ static int vpfe_async_complete(struct v4l2_async_notifier *notifier) static struct vpfe_config * vpfe_get_pdata(struct platform_device *pdev) { - struct device_node *endpoint = NULL, *rem = NULL; + struct device_node *endpoint = NULL; struct v4l2_of_endpoint bus_cfg; struct vpfe_subdev_info *sdinfo; struct vpfe_config *pdata; @@ -2443,6 +2442,8 @@ vpfe_get_pdata(struct platform_device *pdev) return NULL; for (i = 0; ; i++) { + struct device_node *rem; + endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, endpoint); if (!endpoint) @@ -2497,14 +2498,17 @@ vpfe_get_pdata(struct platform_device *pdev) goto done; } - strncpy(sdinfo->name, rem->name, sizeof(sdinfo->name)); - pdata->asd[i] = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_async_subdev), GFP_KERNEL); + if (!pdata->asd[i]) { + of_node_put(rem); + pdata = NULL; + goto done; + } + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; pdata->asd[i]->match.of.node = rem; - of_node_put(endpoint); of_node_put(rem); } @@ -2513,7 +2517,6 @@ vpfe_get_pdata(struct platform_device *pdev) done: of_node_put(endpoint); - of_node_put(rem); return NULL; } @@ -2561,17 +2564,11 @@ static int vpfe_probe(struct platform_device *pdev) return -EINVAL; } - vpfe->video_dev = video_device_alloc(); - if (!vpfe->video_dev) { - dev_err(&pdev->dev, "Unable to allocate video device\n"); - return -ENOMEM; - } - ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); if (ret) { vpfe_err(vpfe, "Unable to register v4l2 device.\n"); - goto probe_out_video_release; + return ret; } /* set the driver data in platform device */ @@ -2609,9 +2606,6 @@ static int vpfe_probe(struct platform_device *pdev) probe_out_v4l2_unregister: v4l2_device_unregister(&vpfe->v4l2_dev); -probe_out_video_release: - if (!video_is_registered(vpfe->video_dev)) - video_device_release(vpfe->video_dev); return ret; } @@ -2628,7 +2622,7 @@ static int vpfe_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&vpfe->notifier); v4l2_device_unregister(&vpfe->v4l2_dev); - video_unregister_device(vpfe->video_dev); + video_unregister_device(&vpfe->video_dev); return 0; } diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 0f557352313d..5bfb35649a39 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -83,7 +83,6 @@ struct vpfe_route { }; struct vpfe_subdev_info { - char name[32]; /* Sub device group id */ int grp_id; /* inputs available at the sub device */ @@ -223,7 +222,7 @@ struct vpfe_ccdc { struct vpfe_device { /* V4l2 specific parameters */ /* Identifies video device for this channel */ - struct video_device *video_dev; + struct video_device video_dev; /* sub devices */ struct v4l2_subdev **sd; /* vpfe cfg */ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 8f6698668ecf..6a437f86dcdc 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -44,7 +44,6 @@ #include <media/blackfin/ppi.h> #define CAPTURE_DRV_NAME "bfin_capture" -#define BCAP_MIN_NUM_BUF 2 struct bcap_format { char *desc; @@ -65,7 +64,7 @@ struct bcap_device { /* v4l2 control handler */ struct v4l2_ctrl_handler ctrl_handler; /* device node data */ - struct video_device *video_dev; + struct video_device video_dev; /* sub device instance */ struct v4l2_subdev *sd; /* capture config */ @@ -104,12 +103,8 @@ struct bcap_device { struct completion comp; /* prepare to stop */ bool stop; -}; - -struct bcap_fh { - struct v4l2_fh fh; - /* indicates whether this file handle is doing IO */ - bool io_allowed; + /* vb2 buffer sequence counter */ + unsigned sequence; }; static const struct bcap_format bcap_formats[] = { @@ -201,90 +196,6 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) bcap_dev->sensor_formats = NULL; } -static int bcap_open(struct file *file) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct video_device *vfd = bcap_dev->video_dev; - struct bcap_fh *bcap_fh; - - if (!bcap_dev->sd) { - v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n"); - return -ENODEV; - } - - bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL); - if (!bcap_fh) { - v4l2_err(&bcap_dev->v4l2_dev, - "unable to allocate memory for file handle object\n"); - return -ENOMEM; - } - - v4l2_fh_init(&bcap_fh->fh, vfd); - - /* store pointer to v4l2_fh in private_data member of file */ - file->private_data = &bcap_fh->fh; - v4l2_fh_add(&bcap_fh->fh); - bcap_fh->io_allowed = false; - return 0; -} - -static int bcap_release(struct file *file) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; - struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); - - /* if this instance is doing IO */ - if (bcap_fh->io_allowed) - vb2_queue_release(&bcap_dev->buffer_queue); - - file->private_data = NULL; - v4l2_fh_del(&bcap_fh->fh); - v4l2_fh_exit(&bcap_fh->fh); - kfree(bcap_fh); - return 0; -} - -static int bcap_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&bcap_dev->mutex)) - return -ERESTARTSYS; - ret = vb2_mmap(&bcap_dev->buffer_queue, vma); - mutex_unlock(&bcap_dev->mutex); - return ret; -} - -#ifndef CONFIG_MMU -static unsigned long bcap_get_unmapped_area(struct file *file, - unsigned long addr, - unsigned long len, - unsigned long pgoff, - unsigned long flags) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - return vb2_get_unmapped_area(&bcap_dev->buffer_queue, - addr, - len, - pgoff, - flags); -} -#endif - -static unsigned int bcap_poll(struct file *file, poll_table *wait) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - unsigned int res; - - mutex_lock(&bcap_dev->mutex); - res = vb2_poll(&bcap_dev->buffer_queue, file, wait); - mutex_unlock(&bcap_dev->mutex); - return res; -} - static int bcap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int *nbuffers, unsigned int *nplanes, @@ -292,37 +203,32 @@ static int bcap_queue_setup(struct vb2_queue *vq, { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); - if (*nbuffers < BCAP_MIN_NUM_BUF) - *nbuffers = BCAP_MIN_NUM_BUF; + if (fmt && fmt->fmt.pix.sizeimage < bcap_dev->fmt.sizeimage) + return -EINVAL; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2; *nplanes = 1; - sizes[0] = bcap_dev->fmt.sizeimage; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : bcap_dev->fmt.sizeimage; alloc_ctxs[0] = bcap_dev->alloc_ctx; return 0; } -static int bcap_buffer_init(struct vb2_buffer *vb) -{ - struct bcap_buffer *buf = to_bcap_vb(vb); - - INIT_LIST_HEAD(&buf->list); - return 0; -} - static int bcap_buffer_prepare(struct vb2_buffer *vb) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - struct bcap_buffer *buf = to_bcap_vb(vb); - unsigned long size; + unsigned long size = bcap_dev->fmt.sizeimage; - size = bcap_dev->fmt.sizeimage; if (vb2_plane_size(vb, 0) < size) { v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n", vb2_plane_size(vb, 0), size); return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(vb, 0, size); + + vb->v4l2_buf.field = bcap_dev->fmt.field; return 0; } @@ -353,14 +259,16 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); struct ppi_if *ppi = bcap_dev->ppi; + struct bcap_buffer *buf, *tmp; struct ppi_params params; + dma_addr_t addr; int ret; /* enable streamon on the sub device */ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1); if (ret && (ret != -ENOIOCTLCMD)) { v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n"); - return ret; + goto err; } /* set ppi params */ @@ -399,7 +307,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, "Error in setting ppi params\n"); - return ret; + goto err; } /* attach ppi DMA irq handler */ @@ -407,12 +315,34 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, "Error in attaching interrupt handler\n"); - return ret; + goto err; } + bcap_dev->sequence = 0; + reinit_completion(&bcap_dev->comp); bcap_dev->stop = false; + + /* get the next frame from the dma queue */ + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + /* remove buffer from the dma queue */ + list_del_init(&bcap_dev->cur_frm->list); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + /* update DMA address */ + ppi->ops->update_addr(ppi, (unsigned long)addr); + /* enable ppi */ + ppi->ops->start(ppi); + return 0; + +err: + list_for_each_entry_safe(buf, tmp, &bcap_dev->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + + return ret; } static void bcap_stop_streaming(struct vb2_queue *vq) @@ -431,6 +361,9 @@ static void bcap_stop_streaming(struct vb2_queue *vq) "stream off failed in subdev\n"); /* release all active buffers */ + if (bcap_dev->cur_frm) + vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); + while (!list_empty(&bcap_dev->dma_queue)) { bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); @@ -441,7 +374,6 @@ static void bcap_stop_streaming(struct vb2_queue *vq) static struct vb2_ops bcap_video_qops = { .queue_setup = bcap_queue_setup, - .buf_init = bcap_buffer_init, .buf_prepare = bcap_buffer_prepare, .buf_cleanup = bcap_buffer_cleanup, .buf_queue = bcap_buffer_queue, @@ -451,57 +383,6 @@ static struct vb2_ops bcap_video_qops = { .stop_streaming = bcap_stop_streaming, }; -static int bcap_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req_buf) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct vb2_queue *vq = &bcap_dev->buffer_queue; - struct v4l2_fh *fh = file->private_data; - struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); - - if (vb2_is_busy(vq)) - return -EBUSY; - - bcap_fh->io_allowed = true; - - return vb2_reqbufs(vq, req_buf); -} - -static int bcap_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - return vb2_querybuf(&bcap_dev->buffer_queue, buf); -} - -static int bcap_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; - struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); - - if (!bcap_fh->io_allowed) - return -EBUSY; - - return vb2_qbuf(&bcap_dev->buffer_queue, buf); -} - -static int bcap_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; - struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); - - if (!bcap_fh->io_allowed) - return -EBUSY; - - return vb2_dqbuf(&bcap_dev->buffer_queue, - buf, file->f_flags & O_NONBLOCK); -} - static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; @@ -517,6 +398,7 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); ppi->err = false; } else { + vb->v4l2_buf.sequence = bcap_dev->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, @@ -543,62 +425,14 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int bcap_streamon(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct bcap_fh *fh = file->private_data; - struct ppi_if *ppi = bcap_dev->ppi; - dma_addr_t addr; - int ret; - - if (!fh->io_allowed) - return -EBUSY; - - /* call streamon to start streaming in videobuf */ - ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type); - if (ret) - return ret; - - /* if dma queue is empty, return error */ - if (list_empty(&bcap_dev->dma_queue)) { - v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n"); - ret = -EINVAL; - goto err; - } - - /* get the next frame from the dma queue */ - bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - /* remove buffer from the dma queue */ - list_del_init(&bcap_dev->cur_frm->list); - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); - /* update DMA address */ - ppi->ops->update_addr(ppi, (unsigned long)addr); - /* enable ppi */ - ppi->ops->start(ppi); - - return 0; -err: - vb2_streamoff(&bcap_dev->buffer_queue, buf_type); - return ret; -} - -static int bcap_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - struct bcap_fh *fh = file->private_data; - - if (!fh->io_allowed) - return -EBUSY; - - return vb2_streamoff(&bcap_dev->buffer_queue, buf_type); -} - static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; return v4l2_subdev_call(bcap_dev->sd, video, querystd, std); } @@ -606,6 +440,11 @@ static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; *std = bcap_dev->std; return 0; @@ -614,8 +453,13 @@ static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; int ret; + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; + if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; @@ -631,6 +475,11 @@ static int bcap_enum_dv_timings(struct file *file, void *priv, struct v4l2_enum_dv_timings *timings) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) + return -ENODATA; timings->pad = 0; @@ -642,6 +491,11 @@ static int bcap_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) + return -ENODATA; return v4l2_subdev_call(bcap_dev->sd, video, query_dv_timings, timings); @@ -651,6 +505,11 @@ static int bcap_g_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) + return -ENODATA; *timings = bcap_dev->dv_timings; return 0; @@ -660,7 +519,13 @@ static int bcap_s_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_input input; int ret; + + input = bcap_dev->cfg->inputs[bcap_dev->cur_input]; + if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS)) + return -ENODATA; + if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; @@ -881,12 +746,14 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_g_dv_timings = bcap_g_dv_timings, .vidioc_query_dv_timings = bcap_query_dv_timings, .vidioc_enum_dv_timings = bcap_enum_dv_timings, - .vidioc_reqbufs = bcap_reqbufs, - .vidioc_querybuf = bcap_querybuf, - .vidioc_qbuf = bcap_qbuf, - .vidioc_dqbuf = bcap_dqbuf, - .vidioc_streamon = bcap_streamon, - .vidioc_streamoff = bcap_streamoff, + .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_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_parm = bcap_g_parm, .vidioc_s_parm = bcap_s_parm, .vidioc_log_status = bcap_log_status, @@ -894,14 +761,14 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { static struct v4l2_file_operations bcap_fops = { .owner = THIS_MODULE, - .open = bcap_open, - .release = bcap_release, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .mmap = bcap_mmap, + .mmap = vb2_fop_mmap, #ifndef CONFIG_MMU - .get_unmapped_area = bcap_get_unmapped_area, + .get_unmapped_area = vb2_fop_get_unmapped_area, #endif - .poll = bcap_poll + .poll = vb2_fop_poll }; static int bcap_probe(struct platform_device *pdev) @@ -942,27 +809,20 @@ static int bcap_probe(struct platform_device *pdev) goto err_free_ppi; } - vfd = video_device_alloc(); - if (!vfd) { - ret = -ENOMEM; - v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); - goto err_cleanup_ctx; - } - + vfd = &bcap_dev->video_dev; /* initialize field of video device */ - vfd->release = video_device_release; + vfd->release = video_device_release_empty; vfd->fops = &bcap_fops; vfd->ioctl_ops = &bcap_ioctl_ops; vfd->tvnorms = 0; vfd->v4l2_dev = &bcap_dev->v4l2_dev; strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); - bcap_dev->video_dev = vfd; ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev); if (ret) { v4l2_err(pdev->dev.driver, "Unable to register v4l2 device\n"); - goto err_release_vdev; + goto err_cleanup_ctx; } v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); @@ -978,13 +838,14 @@ static int bcap_probe(struct platform_device *pdev) /* initialize queue */ q = &bcap_dev->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = bcap_dev; q->buf_struct_size = sizeof(struct bcap_buffer); q->ops = &bcap_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &bcap_dev->mutex; + q->min_buffers_needed = 1; ret = vb2_queue_init(q); if (ret) @@ -997,15 +858,16 @@ static int bcap_probe(struct platform_device *pdev) INIT_LIST_HEAD(&bcap_dev->dma_queue); vfd->lock = &bcap_dev->mutex; + vfd->queue = q; /* register video device */ - ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, "Unable to register video device\n"); goto err_free_handler; } - video_set_drvdata(bcap_dev->video_dev, bcap_dev); + video_set_drvdata(&bcap_dev->video_dev, bcap_dev); v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n", video_device_node_name(vfd)); @@ -1083,15 +945,11 @@ static int bcap_probe(struct platform_device *pdev) } return 0; err_unreg_vdev: - video_unregister_device(bcap_dev->video_dev); - bcap_dev->video_dev = NULL; + video_unregister_device(&bcap_dev->video_dev); err_free_handler: v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); err_unreg_v4l2: v4l2_device_unregister(&bcap_dev->v4l2_dev); -err_release_vdev: - if (bcap_dev->video_dev) - video_device_release(bcap_dev->video_dev); err_cleanup_ctx: vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); err_free_ppi: @@ -1108,7 +966,7 @@ static int bcap_remove(struct platform_device *pdev) struct bcap_device, v4l2_dev); bcap_free_sensor_formats(bcap_dev); - video_unregister_device(bcap_dev->video_dev); + video_unregister_device(&bcap_dev->video_dev); v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); v4l2_device_unregister(v4l2_dev); vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile index 25ce15561695..834e504bf085 100644 --- a/drivers/media/platform/coda/Makefile +++ b/drivers/media/platform/coda/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -I$(src) + coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o obj-$(CONFIG_VIDEO_CODA) += coda.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 856b542b35b9..d0430071d2ee 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -15,6 +15,7 @@ #include <linux/clk.h> #include <linux/irqreturn.h> #include <linux/kernel.h> +#include <linux/log2.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/slab.h> @@ -29,13 +30,18 @@ #include <media/videobuf2-vmalloc.h> #include "coda.h" +#define CREATE_TRACE_POINTS +#include "trace.h" +#define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA7_PS_BUF_SIZE 0x28000 #define CODA9_PS_SAVE_SIZE (512 * 1024) #define CODA_DEFAULT_GAMMA 4096 #define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ +static void coda_free_bitstream_buffer(struct coda_ctx *ctx); + static inline int coda_is_initialized(struct coda_dev *dev) { return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0; @@ -84,15 +90,21 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd) coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); + trace_coda_bit_run(ctx, cmd); + coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); } static int coda_command_sync(struct coda_ctx *ctx, int cmd) { struct coda_dev *dev = ctx->dev; + int ret; coda_command_async(ctx, cmd); - return coda_wait_timeout(dev); + ret = coda_wait_timeout(dev); + trace_coda_bit_done(ctx); + + return ret; } int coda_hw_reset(struct coda_ctx *ctx) @@ -177,10 +189,6 @@ static int coda_bitstream_queue(struct coda_ctx *ctx, if (n < src_size) return -ENOSPC; - dma_sync_single_for_device(&ctx->dev->plat_dev->dev, - ctx->bitstream.paddr, ctx->bitstream.size, - DMA_TO_DEVICE); - src_buf->v4l2_buf.sequence = ctx->qsequence++; return 0; @@ -214,7 +222,7 @@ static bool coda_bitstream_try_queue(struct coda_ctx *ctx, return true; } -void coda_fill_bitstream(struct coda_ctx *ctx) +void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) { struct vb2_buffer *src_buf; struct coda_buffer_meta *meta; @@ -235,9 +243,12 @@ void coda_fill_bitstream(struct coda_ctx *ctx) if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && !coda_jpeg_check_buffer(ctx, src_buf)) { v4l2_err(&ctx->dev->v4l2_dev, - "dropping invalid JPEG frame\n"); + "dropping invalid JPEG frame %d\n", + ctx->qsequence); src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, streaming ? + VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); continue; } @@ -262,6 +273,8 @@ void coda_fill_bitstream(struct coda_ctx *ctx) ctx->bitstream_fifo.kfifo.mask; list_add_tail(&meta->list, &ctx->buffer_meta_list); + + trace_coda_bit_queue(ctx, src_buf, meta); } v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); @@ -297,6 +310,14 @@ static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) p[index ^ 1] = value; } +static inline int coda_alloc_context_buf(struct coda_ctx *ctx, + struct coda_aux_buf *buf, size_t size, + const char *name) +{ + return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); +} + + static void coda_free_framebuffers(struct coda_ctx *ctx) { int i; @@ -377,6 +398,7 @@ static void coda_free_context_buffers(struct coda_ctx *ctx) coda_free_aux_buf(dev, &ctx->psbuf); if (dev->devtype->product != CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); + coda_free_aux_buf(dev, &ctx->parabuf); } static int coda_alloc_context_buffers(struct coda_ctx *ctx, @@ -386,57 +408,42 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, size_t size; int ret; + if (!ctx->parabuf.vaddr) { + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, + CODA_PARA_BUF_SIZE, "parabuf"); + if (ret < 0) + return ret; + } + if (dev->devtype->product == CODA_DX6) return 0; - if (ctx->psbuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n"); - return -EBUSY; - } - if (ctx->slicebuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n"); - return -EBUSY; - } - if (ctx->workbuf.vaddr) { - v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n"); - ret = -EBUSY; - return -ENOMEM; - } - - if (q_data->fourcc == V4L2_PIX_FMT_H264) { + if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { /* worst case slice size */ size = (DIV_ROUND_UP(q_data->width, 16) * DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %d byte slice buffer", - ctx->slicebuf.size); - return ret; - } + if (ret < 0) + goto err; } - if (dev->devtype->product == CODA_7541) { + if (!ctx->psbuf.vaddr && dev->devtype->product == CODA_7541) { ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate psmem buffer"); + if (ret < 0) goto err; - } } - size = dev->devtype->workbuf_size; - if (dev->devtype->product == CODA_960 && - q_data->fourcc == V4L2_PIX_FMT_H264) - size += CODA9_PS_SAVE_SIZE; - ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %d byte context buffer", - ctx->workbuf.size); - goto err; + if (!ctx->workbuf.vaddr) { + size = dev->devtype->workbuf_size; + if (dev->devtype->product == CODA_960 && + q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, + "workbuf"); + if (ret < 0) + goto err; } return 0; @@ -709,6 +716,27 @@ err_clk_per: * Encoder context operations */ +static int coda_encoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + } else { + coda_free_context_buffers(ctx); + } + + return 0; +} + static int coda_start_encoding(struct coda_ctx *ctx) { struct coda_dev *dev = ctx->dev; @@ -725,11 +753,6 @@ static int coda_start_encoding(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_fourcc = q_data_dst->fourcc; - /* Allocate per-instance buffers */ - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); bitstream_size = q_data_dst->sizeimage; @@ -1227,6 +1250,8 @@ static int coda_prepare_encode(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.axi_sram_use, CODA7_REG_BIT_AXI_SRAM_USE); + trace_coda_enc_pic_run(ctx, src_buf); + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); return 0; @@ -1241,6 +1266,8 @@ static void coda_finish_encode(struct coda_ctx *ctx) src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + trace_coda_enc_pic_done(ctx, dst_buf); + /* Get results from the coda */ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); @@ -1311,7 +1338,6 @@ static void coda_seq_end_work(struct work_struct *work) ctx->bitstream.vaddr, ctx->bitstream.size); coda_free_framebuffers(ctx); - coda_free_context_buffers(ctx); mutex_unlock(&dev->coda_mutex); mutex_unlock(&ctx->buffer_mutex); @@ -1322,11 +1348,13 @@ static void coda_bit_release(struct coda_ctx *ctx) mutex_lock(&ctx->buffer_mutex); coda_free_framebuffers(ctx); coda_free_context_buffers(ctx); + coda_free_bitstream_buffer(ctx); mutex_unlock(&ctx->buffer_mutex); } const struct coda_context_ops coda_bit_encode_ops = { .queue_init = coda_encoder_queue_init, + .reqbufs = coda_encoder_reqbufs, .start_streaming = coda_start_encoding, .prepare_run = coda_prepare_encode, .finish_run = coda_finish_encode, @@ -1338,6 +1366,65 @@ const struct coda_context_ops coda_bit_encode_ops = { * Decoder context operations */ +static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + if (ctx->bitstream.vaddr) + return 0; + + ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); + ctx->bitstream.vaddr = dma_alloc_writecombine( + &ctx->dev->plat_dev->dev, ctx->bitstream.size, + &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&ctx->dev->v4l2_dev, + "failed to allocate bitstream ringbuffer"); + return -ENOMEM; + } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + return 0; +} + +static void coda_free_bitstream_buffer(struct coda_ctx *ctx) +{ + if (ctx->bitstream.vaddr == NULL) + return; + + dma_free_writecombine(&ctx->dev->plat_dev->dev, ctx->bitstream.size, + ctx->bitstream.vaddr, ctx->bitstream.paddr); + ctx->bitstream.vaddr = NULL; + kfifo_init(&ctx->bitstream_fifo, NULL, 0); +} + +static int coda_decoder_reqbufs(struct coda_ctx *ctx, + struct v4l2_requestbuffers *rb) +{ + struct coda_q_data *q_data_src; + int ret; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return 0; + + if (rb->count) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + ret = coda_alloc_bitstream_buffer(ctx, q_data_src); + if (ret < 0) { + coda_free_context_buffers(ctx); + return ret; + } + } else { + coda_free_bitstream_buffer(ctx); + coda_free_context_buffers(ctx); + } + + return 0; +} + static int __coda_start_decoding(struct coda_ctx *ctx) { struct coda_q_data *q_data_src, *q_data_dst; @@ -1356,11 +1443,6 @@ static int __coda_start_decoding(struct coda_ctx *ctx) src_fourcc = q_data_src->fourcc; dst_fourcc = q_data_dst->fourcc; - /* Allocate per-instance buffers */ - ret = coda_alloc_context_buffers(ctx, q_data_src); - if (ret < 0) - return ret; - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); /* Update coda bitstream read and write pointers from kfifo */ @@ -1579,7 +1661,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) /* Try to copy source buffer contents into the bitstream ringbuffer */ mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx); + coda_fill_bitstream(ctx, true); mutex_unlock(&ctx->bitstream_mutex); if (coda_get_bitstream_payload(ctx) < 512 && @@ -1675,6 +1757,8 @@ static int coda_prepare_decode(struct coda_ctx *ctx) /* Clear decode success flag */ coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); + trace_coda_dec_pic_run(ctx, meta); + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); return 0; @@ -1704,7 +1788,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) * by up to 512 bytes */ if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { - if (coda_get_bitstream_payload(ctx) >= CODA_MAX_FRAME_SIZE - 512) + if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512) kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); } @@ -1835,6 +1919,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) } mutex_unlock(&ctx->bitstream_mutex); + trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]); + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; if (val == 0) ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; @@ -1874,6 +1960,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) dst_buf->v4l2_buf.timecode = meta->timecode; dst_buf->v4l2_buf.timestamp = meta->timestamp; + trace_coda_dec_rot_done(ctx, meta, dst_buf); + switch (q_data_dst->fourcc) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -1906,6 +1994,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) const struct coda_context_ops coda_bit_decode_ops = { .queue_init = coda_decoder_queue_init, + .reqbufs = coda_decoder_reqbufs, .start_streaming = coda_start_decoding, .prepare_run = coda_prepare_decode, .finish_run = coda_finish_decode, @@ -1931,6 +2020,8 @@ irqreturn_t coda_irq_handler(int irq, void *data) return IRQ_HANDLED; } + trace_coda_bit_done(ctx); + if (ctx->aborting) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "task has been aborted\n"); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6f32e6d6b156..8e6fe0200117 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -46,7 +46,6 @@ #define CODADX6_MAX_INSTANCES 4 #define CODA_MAX_FORMATS 4 -#define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) #define MIN_W 176 @@ -696,6 +695,26 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, return coda_s_fmt(ctx, &f_cap); } +static int coda_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); + if (ret) + return ret; + + /* + * Allow to allocate instance specific per-context buffers, such as + * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. + */ + if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) + return ctx->ops->reqbufs(ctx, rb); + + return 0; +} + static int coda_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -841,7 +860,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_reqbufs = coda_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, .vidioc_qbuf = coda_qbuf, @@ -1173,7 +1192,7 @@ static void coda_buf_queue(struct vb2_buffer *vb) mutex_lock(&ctx->bitstream_mutex); v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); if (vb2_is_streaming(vb->vb2_queue)) - coda_fill_bitstream(ctx); + coda_fill_bitstream(ctx, true); mutex_unlock(&ctx->bitstream_mutex); } else { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); @@ -1215,8 +1234,9 @@ void coda_free_aux_buf(struct coda_dev *dev, buf->vaddr, buf->paddr); buf->vaddr = NULL; buf->size = 0; + debugfs_remove(buf->dentry); + buf->dentry = NULL; } - debugfs_remove(buf->dentry); } static int coda_start_streaming(struct vb2_queue *q, unsigned int count) @@ -1232,9 +1252,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (q_data_src->fourcc == V4L2_PIX_FMT_H264 || (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && ctx->dev->devtype->product == CODA_7541)) { - /* copy the buffers that where queued before streamon */ + /* copy the buffers that were queued before streamon */ mutex_lock(&ctx->bitstream_mutex); - coda_fill_bitstream(ctx); + coda_fill_bitstream(ctx, false); mutex_unlock(&ctx->bitstream_mutex); if (coda_get_bitstream_payload(ctx) < 512) { @@ -1262,12 +1282,23 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (!(ctx->streamon_out & ctx->streamon_cap)) return 0; + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if ((q_data_src->width != q_data_dst->width && + round_up(q_data_src->width, 16) != q_data_dst->width) || + (q_data_src->height != q_data_dst->height && + round_up(q_data_src->height, 16) != q_data_dst->height)) { + v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", + q_data_src->width, q_data_src->height, + q_data_dst->width, q_data_dst->height); + ret = -EINVAL; + goto err; + } + /* Allow BIT decoder device_run with no new buffers queued */ if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); ctx->gopcounter = ctx->params.gop_size - 1; - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc, q_data_dst->fourcc); @@ -1308,6 +1339,9 @@ static void coda_stop_streaming(struct vb2_queue *q) struct coda_ctx *ctx = vb2_get_drv_priv(q); struct coda_dev *dev = ctx->dev; struct vb2_buffer *buf; + bool stop; + + stop = ctx->streamon_out && ctx->streamon_cap; if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, @@ -1332,7 +1366,7 @@ static void coda_stop_streaming(struct vb2_queue *q) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); } - if (!ctx->streamon_out && !ctx->streamon_cap) { + if (stop) { struct coda_buffer_meta *meta; if (ctx->ops->seq_end_work) { @@ -1457,7 +1491,7 @@ static const struct v4l2_ctrl_ops coda_ctrl_ops = { static void coda_encode_ctrls(struct coda_ctx *ctx) { v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0); + V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, @@ -1541,6 +1575,13 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; vq->lock = &ctx->dev->dev_mutex; + /* One way to indicate end-of-stream for coda is to set the + * bytesused == 0. However by default videobuf2 handles bytesused + * equal to 0 as a special case and changes its value to the size + * of the buffer. Set the allow_zero_bytesused flag, so + * that videobuf2 will keep the value of bytesused intact. + */ + vq->allow_zero_bytesused = 1; return vb2_queue_init(vq); } @@ -1621,6 +1662,11 @@ static int coda_open(struct file *file) set_bit(idx, &dev->instance_mask); name = kasprintf(GFP_KERNEL, "context%d", idx); + if (!name) { + ret = -ENOMEM; + goto err_coda_name_init; + } + ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); kfree(name); @@ -1682,28 +1728,6 @@ static int coda_open(struct file *file) ctx->fh.ctrl_handler = &ctx->ctrls; - if (ctx->use_bit) { - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, - CODA_PARA_BUF_SIZE, "parabuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); - goto err_dma_alloc; - } - } - if (ctx->use_bit && ctx->inst_type == CODA_INST_DECODER) { - ctx->bitstream.size = CODA_MAX_FRAME_SIZE; - ctx->bitstream.vaddr = dma_alloc_writecombine( - &dev->plat_dev->dev, ctx->bitstream.size, - &ctx->bitstream.paddr, GFP_KERNEL); - if (!ctx->bitstream.vaddr) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate bitstream ringbuffer"); - ret = -ENOMEM; - goto err_dma_writecombine; - } - } - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); mutex_init(&ctx->bitstream_mutex); mutex_init(&ctx->buffer_mutex); INIT_LIST_HEAD(&ctx->buffer_meta_list); @@ -1717,12 +1741,6 @@ static int coda_open(struct file *file) return 0; -err_dma_writecombine: - if (ctx->dev->devtype->product == CODA_DX6) - coda_free_aux_buf(dev, &ctx->workbuf); - coda_free_aux_buf(dev, &ctx->parabuf); -err_dma_alloc: - v4l2_ctrl_handler_free(&ctx->ctrls); err_ctrls_setup: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_ctx_init: @@ -1735,6 +1753,7 @@ err_pm_get: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->idx, &dev->instance_mask); +err_coda_name_init: err_coda_max: kfree(ctx); return ret; @@ -1764,14 +1783,9 @@ static int coda_release(struct file *file) list_del(&ctx->list); coda_unlock(ctx); - if (ctx->bitstream.vaddr) { - dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size, - ctx->bitstream.vaddr, ctx->bitstream.paddr); - } if (ctx->dev->devtype->product == CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); - coda_free_aux_buf(dev, &ctx->parabuf); v4l2_ctrl_handler_free(&ctx->ctrls); clk_disable_unprepare(dev->clk_ahb); clk_disable_unprepare(dev->clk_per); @@ -1901,8 +1915,7 @@ static int coda_register_device(struct coda_dev *dev, int i) if (i >= dev->devtype->num_vdevs) return -EINVAL; - snprintf(vfd->name, sizeof(vfd->name), "%s", - dev->devtype->vdevs[i]->name); + strlcpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); vfd->fops = &coda_fops; vfd->ioctl_ops = &coda_ioctl_ops; vfd->release = video_device_release_empty, @@ -1933,10 +1946,8 @@ static void coda_fw_callback(const struct firmware *fw, void *context) /* allocate auxiliary per-device code buffer for the BIT processor */ ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate code buffer\n"); + if (ret < 0) goto put_pm; - } /* Copy the whole firmware image to the code buffer */ memcpy(dev->codebuf.vaddr, fw->data, fw->size); @@ -2174,20 +2185,16 @@ static int coda_probe(struct platform_device *pdev) ret = coda_alloc_aux_buf(dev, &dev->workbuf, dev->devtype->workbuf_size, "workbuf", dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate work buffer\n"); + if (ret < 0) goto err_v4l2_register; - } } if (dev->devtype->tempbuf_size) { ret = coda_alloc_aux_buf(dev, &dev->tempbuf, dev->devtype->tempbuf_size, "tempbuf", dev->debugfs_root); - if (ret < 0) { - dev_err(&pdev->dev, "failed to allocate temp buffer\n"); + if (ret < 0) goto err_v4l2_register; - } } dev->iram.size = dev->devtype->iram_size; diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c index 8fa3e353f9e2..11e734bc2cbd 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/coda/coda-jpeg.c @@ -13,6 +13,7 @@ #include <linux/swab.h> #include "coda.h" +#include "trace.h" #define SOI_MARKER 0xffd8 #define EOI_MARKER 0xffd9 diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 0c35cd5032ff..6a5c8f6c688e 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -12,6 +12,9 @@ * (at your option) any later version. */ +#ifndef __CODA_H__ +#define __CODA_H__ + #include <linux/debugfs.h> #include <linux/irqreturn.h> #include <linux/mutex.h> @@ -26,7 +29,6 @@ #include "coda_regs.h" #define CODA_MAX_FRAMEBUFFERS 8 -#define CODA_MAX_FRAME_SIZE 0x100000 #define FMO_SLICE_SAVE_BUF_SIZE (32) enum { @@ -178,6 +180,7 @@ struct coda_ctx; struct coda_context_ops { int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); + int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb); int (*start_streaming)(struct coda_ctx *ctx); int (*prepare_run)(struct coda_ctx *ctx); void (*finish_run)(struct coda_ctx *ctx); @@ -249,13 +252,6 @@ int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, size_t size, const char *name, struct dentry *parent); void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf); -static inline int coda_alloc_context_buf(struct coda_ctx *ctx, - struct coda_aux_buf *buf, size_t size, - const char *name) -{ - return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); -} - int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, @@ -263,7 +259,7 @@ int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, int coda_hw_reset(struct coda_ctx *ctx); -void coda_fill_bitstream(struct coda_ctx *ctx); +void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming); void coda_set_gdi_regs(struct coda_ctx *ctx); @@ -284,7 +280,7 @@ const char *coda_product_name(int product); int coda_check_firmware(struct coda_dev *dev); -static inline int coda_get_bitstream_payload(struct coda_ctx *ctx) +static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) { return kfifo_len(&ctx->bitstream_fifo); } @@ -301,3 +297,5 @@ extern const struct coda_context_ops coda_bit_encode_ops; extern const struct coda_context_ops coda_bit_decode_ops; irqreturn_t coda_irq_handler(int irq, void *data); + +#endif /* __CODA_H__ */ diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h new file mode 100644 index 000000000000..d1d06cbd1f6a --- /dev/null +++ b/drivers/media/platform/coda/trace.h @@ -0,0 +1,203 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM coda + +#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __CODA_TRACE_H__ + +#include <linux/tracepoint.h> +#include <media/videobuf2-core.h> + +#include "coda.h" + +#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) + +TRACE_EVENT(coda_bit_run, + TP_PROTO(struct coda_ctx *ctx, int cmd), + + TP_ARGS(ctx, cmd), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + __field(int, cmd) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + __entry->cmd = cmd; + ), + + TP_printk("minor = %d, ctx = %d, cmd = %d", + __entry->minor, __entry->ctx, __entry->cmd) +); + +TRACE_EVENT(coda_bit_done, + TP_PROTO(struct coda_ctx *ctx), + + TP_ARGS(ctx), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) +); + +TRACE_EVENT(coda_enc_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), + + TP_ARGS(ctx, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->v4l2_buf.index; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, ctx = %d", + __entry->minor, __entry->index, __entry->ctx) +); + +TRACE_EVENT(coda_enc_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), + + TP_ARGS(ctx, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->v4l2_buf.index; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, ctx = %d", + __entry->minor, __entry->index, __entry->ctx) +); + +TRACE_EVENT(coda_bit_queue, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf, + struct coda_buffer_meta *meta), + + TP_ARGS(ctx, buf, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, index) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->index = buf->v4l2_buf.index; + __entry->start = meta->start; + __entry->end = meta->end; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->index, __entry->start, __entry->end, + __entry->ctx) +); + +TRACE_EVENT(coda_dec_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + + TP_ARGS(ctx, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->start = meta ? meta->start : 0; + __entry->end = meta ? meta->end : 0; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->start, __entry->end, __entry->ctx) +); + +TRACE_EVENT(coda_dec_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + + TP_ARGS(ctx, meta), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, start) + __field(int, end) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->start = meta->start; + __entry->end = meta->end; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", + __entry->minor, __entry->start, __entry->end, __entry->ctx) +); + +TRACE_EVENT(coda_dec_rot_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta, + struct vb2_buffer *buf), + + TP_ARGS(ctx, meta, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(int, start) + __field(int, end) + __field(int, index) + __field(int, ctx) + ), + + TP_fast_assign( + __entry->minor = ctx->fh.vdev->minor; + __entry->start = meta->start; + __entry->end = meta->end; + __entry->index = buf->v4l2_buf.index; + __entry->ctx = ctx->idx; + ), + + TP_printk("minor = %d, start = 0x%x, end = 0x%x, index = %d, ctx = %d", + __entry->minor, __entry->start, __entry->end, __entry->index, + __entry->ctx) +); + +#endif /* __CODA_TRACE_H__ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index b41bf7e822c8..ccfcf3f528d3 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1871,16 +1871,9 @@ static int vpfe_probe(struct platform_device *pdev) goto probe_free_ccdc_cfg_mem; } - /* Allocate memory for video device */ - vfd = video_device_alloc(); - if (NULL == vfd) { - ret = -ENOMEM; - v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); - goto probe_out_release_irq; - } - + vfd = &vpfe_dev->video_dev; /* Initialize field of video device */ - vfd->release = video_device_release; + vfd->release = video_device_release_empty; vfd->fops = &vpfe_fops; vfd->ioctl_ops = &vpfe_ioctl_ops; vfd->tvnorms = 0; @@ -1891,14 +1884,12 @@ static int vpfe_probe(struct platform_device *pdev) (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, (VPFE_CAPTURE_VERSION_CODE) & 0xff); - /* Set video_dev to the video device */ - vpfe_dev->video_dev = vfd; ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); if (ret) { v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); - goto probe_out_video_release; + goto probe_out_release_irq; } v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); spin_lock_init(&vpfe_dev->irqlock); @@ -1914,7 +1905,7 @@ static int vpfe_probe(struct platform_device *pdev) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "video_dev=%p\n", &vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ret = video_register_device(vpfe_dev->video_dev, + ret = video_register_device(&vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1927,7 +1918,7 @@ static int vpfe_probe(struct platform_device *pdev) /* set the driver data in platform device */ platform_set_drvdata(pdev, vpfe_dev); /* set driver private data */ - video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev); i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); num_subdevs = vpfe_cfg->num_subdevs; vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, @@ -1979,12 +1970,9 @@ static int vpfe_probe(struct platform_device *pdev) probe_sd_out: kfree(vpfe_dev->sd); probe_out_video_unregister: - video_unregister_device(vpfe_dev->video_dev); + video_unregister_device(&vpfe_dev->video_dev); probe_out_v4l2_unregister: v4l2_device_unregister(&vpfe_dev->v4l2_dev); -probe_out_video_release: - if (!video_is_registered(vpfe_dev->video_dev)) - video_device_release(vpfe_dev->video_dev); probe_out_release_irq: free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); probe_free_ccdc_cfg_mem: @@ -2007,7 +1995,7 @@ static int vpfe_remove(struct platform_device *pdev) free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); kfree(vpfe_dev->sd); v4l2_device_unregister(&vpfe_dev->v4l2_dev); - video_unregister_device(vpfe_dev->video_dev); + video_unregister_device(&vpfe_dev->video_dev); kfree(vpfe_dev); kfree(ccdc_cfg); return 0; diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index fa0a51521772..a5f548138b91 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -712,7 +712,7 @@ static int vpif_set_input( ch->vpifparams.iface = chan_cfg->vpif_if; /* update tvnorms from the sub device input info */ - ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std; + ch->video_dev.tvnorms = chan_cfg->inputs[index].input.std; return 0; } @@ -1337,7 +1337,7 @@ static int vpif_probe_complete(void) struct video_device *vdev; struct channel_obj *ch; struct vb2_queue *q; - int i, j, err, k; + int j, err, k; for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { ch = vpif_obj.dev[j]; @@ -1384,16 +1384,16 @@ static int vpif_probe_complete(void) INIT_LIST_HEAD(&common->dma_queue); /* Initialize the video_device structure */ - vdev = ch->video_dev; + vdev = &ch->video_dev; strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); - vdev->release = video_device_release; + vdev->release = video_device_release_empty; vdev->fops = &vpif_fops; vdev->ioctl_ops = &vpif_ioctl_ops; vdev->v4l2_dev = &vpif_obj.v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; vdev->queue = q; vdev->lock = &common->lock; - video_set_drvdata(ch->video_dev, ch); + video_set_drvdata(&ch->video_dev, ch); err = video_register_device(vdev, VFL_TYPE_GRABBER, (j ? 1 : 0)); if (err) @@ -1410,14 +1410,9 @@ probe_out: common = &ch->common[k]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ - video_unregister_device(ch->video_dev); + video_unregister_device(&ch->video_dev); } kfree(vpif_obj.sd); - for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { - ch = vpif_obj.dev[i]; - /* Note: does nothing if ch->video_dev == NULL */ - video_device_release(ch->video_dev); - } v4l2_device_unregister(&vpif_obj.v4l2_dev); return err; @@ -1438,13 +1433,11 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier) static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - int i, j, err; - int res_idx = 0; struct i2c_adapter *i2c_adap; - struct channel_obj *ch; - struct video_device *vfd; struct resource *res; int subdev_count; + int res_idx = 0; + int i, err; vpif_dev = &pdev->dev; @@ -1472,24 +1465,6 @@ static __init int vpif_probe(struct platform_device *pdev) res_idx++; } - for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { - /* Get the pointer to the channel object */ - ch = vpif_obj.dev[i]; - /* Allocate memory for video device */ - vfd = video_device_alloc(); - if (NULL == vfd) { - for (j = 0; j < i; j++) { - ch = vpif_obj.dev[j]; - video_device_release(ch->video_dev); - } - err = -ENOMEM; - goto vpif_unregister; - } - - /* Set video_dev to the video device */ - ch->video_dev = vfd; - } - vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; @@ -1498,7 +1473,7 @@ static __init int vpif_probe(struct platform_device *pdev) if (vpif_obj.sd == NULL) { vpif_err("unable to allocate memory for subdevice pointers\n"); err = -ENOMEM; - goto vpif_sd_error; + goto vpif_unregister; } if (!vpif_obj.config->asd_sizes) { @@ -1541,13 +1516,6 @@ static __init int vpif_probe(struct platform_device *pdev) probe_subdev_out: /* free sub devices memory */ kfree(vpif_obj.sd); - -vpif_sd_error: - for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { - ch = vpif_obj.dev[i]; - /* Note: does nothing if ch->video_dev == NULL */ - video_device_release(ch->video_dev); - } vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); @@ -1576,7 +1544,7 @@ static int vpif_remove(struct platform_device *device) common = &ch->common[VPIF_VIDEO_INDEX]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ - video_unregister_device(ch->video_dev); + video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); } return 0; diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index f65d28d38e66..8b8a663f6b22 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -92,7 +92,7 @@ struct common_obj { struct channel_obj { /* Identifies video device for this channel */ - struct video_device *video_dev; + struct video_device video_dev; /* Indicates id of the field which is being displayed */ u32 field_id; /* flag to indicate whether decoder is initialized */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 839c24de1fd8..682e5d578bf7 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -829,7 +829,7 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg, ch->sd = sd; if (chan_cfg->outputs != NULL) /* update tvnorms from the sub device output info */ - ch->video_dev->tvnorms = chan_cfg->outputs[index].output.std; + ch->video_dev.tvnorms = chan_cfg->outputs[index].output.std; return 0; } @@ -1204,16 +1204,16 @@ static int vpif_probe_complete(void) ch, &ch->video_dev); /* Initialize the video_device structure */ - vdev = ch->video_dev; + vdev = &ch->video_dev; strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); - vdev->release = video_device_release; + vdev->release = video_device_release_empty; vdev->fops = &vpif_fops; vdev->ioctl_ops = &vpif_ioctl_ops; vdev->v4l2_dev = &vpif_obj.v4l2_dev; vdev->vfl_dir = VFL_DIR_TX; vdev->queue = q; vdev->lock = &common->lock; - video_set_drvdata(ch->video_dev, ch); + video_set_drvdata(&ch->video_dev, ch); err = video_register_device(vdev, VFL_TYPE_GRABBER, (j ? 3 : 2)); if (err < 0) @@ -1227,9 +1227,7 @@ probe_out: ch = vpif_obj.dev[k]; common = &ch->common[k]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - video_unregister_device(ch->video_dev); - video_device_release(ch->video_dev); - ch->video_dev = NULL; + video_unregister_device(&ch->video_dev); } return err; } @@ -1246,13 +1244,11 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier) static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - int i, j = 0, err = 0; - int res_idx = 0; struct i2c_adapter *i2c_adap; - struct channel_obj *ch; - struct video_device *vfd; struct resource *res; int subdev_count; + int res_idx = 0; + int i, err; vpif_dev = &pdev->dev; err = initialize_vpif(); @@ -1281,25 +1277,6 @@ static __init int vpif_probe(struct platform_device *pdev) res_idx++; } - for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { - /* Get the pointer to the channel object */ - ch = vpif_obj.dev[i]; - - /* Allocate memory for video device */ - vfd = video_device_alloc(); - if (vfd == NULL) { - for (j = 0; j < i; j++) { - ch = vpif_obj.dev[j]; - video_device_release(ch->video_dev); - } - err = -ENOMEM; - goto vpif_unregister; - } - - /* Set video_dev to the video device */ - ch->video_dev = vfd; - } - vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; subdevdata = vpif_obj.config->subdevinfo; @@ -1308,7 +1285,7 @@ static __init int vpif_probe(struct platform_device *pdev) if (vpif_obj.sd == NULL) { vpif_err("unable to allocate memory for subdevice pointers\n"); err = -ENOMEM; - goto vpif_sd_error; + goto vpif_unregister; } if (!vpif_obj.config->asd_sizes) { @@ -1348,12 +1325,6 @@ static __init int vpif_probe(struct platform_device *pdev) probe_subdev_out: kfree(vpif_obj.sd); -vpif_sd_error: - for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { - ch = vpif_obj.dev[i]; - /* Note: does nothing if ch->video_dev == NULL */ - video_device_release(ch->video_dev); - } vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); @@ -1379,9 +1350,7 @@ static int vpif_remove(struct platform_device *device) common = &ch->common[VPIF_VIDEO_INDEX]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ - video_unregister_device(ch->video_dev); - - ch->video_dev = NULL; + video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); } diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index 7b21a7607674..849e0e385f18 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -100,7 +100,7 @@ struct common_obj { struct channel_obj { /* V4l2 specific parameters */ - struct video_device *video_dev; /* Identifies video device for + struct video_device video_dev; /* Identifies video device for * this channel */ u32 field_id; /* Indicates id of the field * which is being displayed */ diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 8a2fd8c33d42..cfebf292e15a 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1482,7 +1482,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, } static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct fimc_fmt *fmt; @@ -1495,7 +1495,7 @@ static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1504,7 +1504,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1536,7 +1536,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, } static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1559,7 +1559,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; return 0; } @@ -1602,7 +1602,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, } static int fimc_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1628,10 +1628,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); f = &ctx->d_frame; break; default: @@ -1657,7 +1657,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, } static int fimc_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); @@ -1675,10 +1675,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad); f = &ctx->d_frame; break; default: diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 60c744915549..5d78f5716f3b 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -112,7 +112,7 @@ static const struct media_entity_operations fimc_is_subdev_media_ops = { }; static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -125,14 +125,14 @@ static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *mf = *v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); return 0; } @@ -162,7 +162,7 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, } static void __isp_subdev_try_format(struct fimc_isp *isp, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; @@ -178,7 +178,7 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; } else { if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - format = v4l2_subdev_get_try_format(fh, + format = v4l2_subdev_get_try_format(&isp->subdev, cfg, FIMC_ISP_SD_PAD_SINK); else format = &isp->sink_fmt; @@ -197,7 +197,7 @@ static void __isp_subdev_try_format(struct fimc_isp *isp, } static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); @@ -209,10 +209,10 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, __func__, fmt->pad, mf->code, mf->width, mf->height); mutex_lock(&isp->subdev_lock); - __isp_subdev_try_format(isp, fh, fmt); + __isp_subdev_try_format(isp, cfg, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; /* Propagate format to the source pads */ @@ -223,8 +223,8 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, for (pad = FIMC_ISP_SD_PAD_SRC_FIFO; pad < FIMC_ISP_SD_PADS_NUM; pad++) { format.pad = pad; - __isp_subdev_try_format(isp, fh, &format); - mf = v4l2_subdev_get_try_format(fh, pad); + __isp_subdev_try_format(isp, cfg, &format); + mf = v4l2_subdev_get_try_format(sd, cfg, pad); *mf = format.format; } } @@ -236,7 +236,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, isp->sink_fmt = *mf; format.pad = FIMC_ISP_SD_PAD_SRC_DMA; - __isp_subdev_try_format(isp, fh, &format); + __isp_subdev_try_format(isp, cfg, &format); isp->src_fmt = format.format; __is_set_frame_size(is, &isp->src_fmt); @@ -369,7 +369,7 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt fmt; struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK); fmt.colorspace = V4L2_COLORSPACE_SRGB; fmt.code = fimc_isp_formats[0].mbus_code; @@ -378,12 +378,12 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, fmt.field = V4L2_FIELD_NONE; *format = fmt; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO); fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; *format = fmt; - format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA); + format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA); *format = fmt; return 0; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 2510f189e242..ca6261a86a5f 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -568,7 +568,7 @@ static const struct v4l2_file_operations fimc_lite_fops = { */ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct flite_drvdata *dd = fimc->dd; @@ -592,13 +592,13 @@ static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, struct v4l2_rect *rect; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sink_fmt = v4l2_subdev_get_try_format(fh, + sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, cfg, FLITE_SD_PAD_SINK); mf->code = sink_fmt->code; mf->colorspace = sink_fmt->colorspace; - rect = v4l2_subdev_get_try_crop(fh, + rect = v4l2_subdev_get_try_crop(&fimc->subdev, cfg, FLITE_SD_PAD_SINK); } else { mf->code = sink->fmt->mbus_code; @@ -1047,7 +1047,7 @@ static const struct media_entity_operations fimc_lite_subdev_media_ops = { }; static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { const struct fimc_fmt *fmt; @@ -1060,16 +1060,17 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( - struct v4l2_subdev_fh *fh, unsigned int pad) + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, unsigned int pad) { if (pad != FLITE_SD_PAD_SINK) pad = FLITE_SD_PAD_SOURCE_DMA; - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(sd, cfg, pad); } static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1077,7 +1078,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1100,7 +1101,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, } static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1122,17 +1123,17 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, return -EBUSY; } - ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt); + ffmt = fimc_lite_subdev_try_fmt(fimc, cfg, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *src_fmt; - mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad); *mf = fmt->format; if (fmt->pad == FLITE_SD_PAD_SINK) { unsigned int pad = FLITE_SD_PAD_SOURCE_DMA; - src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad); + src_fmt = __fimc_lite_subdev_get_try_fmt(sd, cfg, pad); *src_fmt = *mf; } @@ -1160,7 +1161,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, } static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1172,7 +1173,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); return 0; } @@ -1195,7 +1196,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1209,7 +1210,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, fimc_lite_try_crop(fimc, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; } else { unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 2504aa89a6f4..d74e1bec3d86 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -540,7 +540,7 @@ unlock: } static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5pcsis_formats)) @@ -568,23 +568,23 @@ static struct csis_pix_format const *s5pcsis_try_format( } static struct v4l2_mbus_framefmt *__s5pcsis_get_format( - struct csis_state *state, struct v4l2_subdev_fh *fh, + struct csis_state *state, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + return cfg ? v4l2_subdev_get_try_format(&state->sd, cfg, 0) : NULL; return &state->format; } -static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + mf = __s5pcsis_get_format(state, cfg, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -605,13 +605,13 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + mf = __s5pcsis_get_format(state, cfg, fmt->which); if (!mf) return -EINVAL; @@ -651,7 +651,7 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); format->colorspace = V4L2_COLORSPACE_JPEG; format->code = s5pcsis_formats[0].code; diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index b70c1aecca37..92d954973ccf 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -127,7 +127,7 @@ static struct deinterlace_fmt *find_format(struct v4l2_format *f) struct deinterlace_dev { struct v4l2_device v4l2_dev; - struct video_device *vfd; + struct video_device vfd; atomic_t busy; struct mutex dev_mutex; @@ -983,7 +983,7 @@ static struct video_device deinterlace_videodev = { .fops = &deinterlace_fops, .ioctl_ops = &deinterlace_ioctl_ops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, .vfl_dir = VFL_DIR_M2M, }; @@ -1026,13 +1026,7 @@ static int deinterlace_probe(struct platform_device *pdev) atomic_set(&pcdev->busy, 0); mutex_init(&pcdev->dev_mutex); - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto unreg_dev; - } - + vfd = &pcdev->vfd; *vfd = deinterlace_videodev; vfd->lock = &pcdev->dev_mutex; vfd->v4l2_dev = &pcdev->v4l2_dev; @@ -1040,12 +1034,11 @@ static int deinterlace_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; + goto unreg_dev; } video_set_drvdata(vfd, pcdev); snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name); - pcdev->vfd = vfd; v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME " Device registered as /dev/video%d\n", vfd->num); @@ -1069,11 +1062,9 @@ static int deinterlace_probe(struct platform_device *pdev) v4l2_m2m_release(pcdev->m2m_dev); err_m2m: - video_unregister_device(pcdev->vfd); + video_unregister_device(&pcdev->vfd); err_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); -rel_vdev: - video_device_release(vfd); unreg_dev: v4l2_device_unregister(&pcdev->v4l2_dev); rel_dma: @@ -1088,7 +1079,7 @@ static int deinterlace_remove(struct platform_device *pdev) v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME); v4l2_m2m_release(pcdev->m2m_dev); - video_unregister_device(pcdev->vfd); + video_unregister_device(&pcdev->vfd); v4l2_device_unregister(&pcdev->v4l2_dev); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); dma_release_channel(pcdev->dma_chan); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index dd5b1415f974..9c64b5d01c6a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1568,24 +1568,64 @@ static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, struct v4l2_frmsizeenum *sizes) { struct mcam_camera *cam = priv; + struct mcam_format_struct *f; + struct v4l2_subdev_frame_size_enum fse = { + .index = sizes->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; + f = mcam_find_format(sizes->pixel_format); + if (f->pixelformat != sizes->pixel_format) + return -EINVAL; + fse.code = f->mbus_code; mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, video, enum_framesizes, sizes); + ret = sensor_call(cam, pad, enum_frame_size, NULL, &fse); mutex_unlock(&cam->s_mutex); - return ret; + if (ret) + return ret; + if (fse.min_width == fse.max_width && + fse.min_height == fse.max_height) { + sizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; + sizes->discrete.width = fse.min_width; + sizes->discrete.height = fse.min_height; + return 0; + } + sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + sizes->stepwise.min_width = fse.min_width; + sizes->stepwise.max_width = fse.max_width; + sizes->stepwise.min_height = fse.min_height; + sizes->stepwise.max_height = fse.max_height; + sizes->stepwise.step_width = 1; + sizes->stepwise.step_height = 1; + return 0; } static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *interval) { struct mcam_camera *cam = priv; + struct mcam_format_struct *f; + struct v4l2_subdev_frame_interval_enum fie = { + .index = interval->index, + .width = interval->width, + .height = interval->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; + f = mcam_find_format(interval->pixel_format); + if (f->pixelformat != interval->pixel_format) + return -EINVAL; + fie.code = f->mbus_code; mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, video, enum_frameintervals, interval); + ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie); mutex_unlock(&cam->s_mutex); - return ret; + if (ret) + return ret; + interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; + interval->discrete = fie.interval; + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index ba2d8f973d58..17b189a81ec5 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1978,7 +1978,7 @@ static int __init omap_vout_setup_video_bufs(struct platform_device *pdev, vout->cropped_offset = 0; if (ovid->rotation_type == VOUT_ROT_VRFB) { - int static_vrfb_allocation = (vid_num == 0) ? + bool static_vrfb_allocation = (vid_num == 0) ? vid1_static_vrfb_alloc : vid2_static_vrfb_alloc; ret = omap_vout_setup_vrfb_bufs(pdev, vid_num, static_vrfb_allocation); diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index aa39306afc73..c6e252760c62 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -21,6 +21,7 @@ #include "omap_voutdef.h" #include "omap_voutlib.h" +#include "omap_vout_vrfb.h" #define OMAP_DMA_NO_DEVICE 0 diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/omap/omap_vout_vrfb.h index 4c2314839b48..c976975024df 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.h +++ b/drivers/media/platform/omap/omap_vout_vrfb.h @@ -15,7 +15,7 @@ #ifdef CONFIG_VIDEO_OMAP2_VOUT_VRFB void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout); int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, - u32 static_vrfb_allocation); + bool static_vrfb_allocation); void omap_vout_release_vrfb(struct omap_vout_device *vout); int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, unsigned int *count, unsigned int startindex); @@ -25,7 +25,7 @@ void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout); #else static inline void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }; static inline int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, - u32 static_vrfb_allocation) + bool static_vrfb_allocation) { return 0; }; static inline void omap_vout_release_vrfb(struct omap_vout_device *vout) { }; static inline int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index deca80903c3a..18d0a871747f 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -51,6 +51,7 @@ #include <linux/dma-mapping.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/omap-iommu.h> #include <linux/platform_device.h> @@ -63,6 +64,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-device.h> +#include <media/v4l2-of.h> #include "isp.h" #include "ispreg.h" @@ -85,35 +87,45 @@ static void isp_restore_ctx(struct isp_device *isp); static const struct isp_res_mapping isp_res_maps[] = { { .isp_rev = ISP_REVISION_2_0, - .map = 1 << OMAP3_ISP_IOMEM_MAIN | - 1 << OMAP3_ISP_IOMEM_CCP2 | - 1 << OMAP3_ISP_IOMEM_CCDC | - 1 << OMAP3_ISP_IOMEM_HIST | - 1 << OMAP3_ISP_IOMEM_H3A | - 1 << OMAP3_ISP_IOMEM_PREV | - 1 << OMAP3_ISP_IOMEM_RESZ | - 1 << OMAP3_ISP_IOMEM_SBL | - 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | - 1 << OMAP3_ISP_IOMEM_CSIPHY2 | - 1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, + .offset = { + /* first MMIO area */ + 0x0000, /* base, len 0x0070 */ + 0x0400, /* ccp2, len 0x01f0 */ + 0x0600, /* ccdc, len 0x00a8 */ + 0x0a00, /* hist, len 0x0048 */ + 0x0c00, /* h3a, len 0x0060 */ + 0x0e00, /* preview, len 0x00a0 */ + 0x1000, /* resizer, len 0x00ac */ + 0x1200, /* sbl, len 0x00fc */ + /* second MMIO area */ + 0x0000, /* csi2a, len 0x0170 */ + 0x0170, /* csiphy2, len 0x000c */ + }, + .syscon_offset = 0xdc, + .phy_type = ISP_PHY_TYPE_3430, }, { .isp_rev = ISP_REVISION_15_0, - .map = 1 << OMAP3_ISP_IOMEM_MAIN | - 1 << OMAP3_ISP_IOMEM_CCP2 | - 1 << OMAP3_ISP_IOMEM_CCDC | - 1 << OMAP3_ISP_IOMEM_HIST | - 1 << OMAP3_ISP_IOMEM_H3A | - 1 << OMAP3_ISP_IOMEM_PREV | - 1 << OMAP3_ISP_IOMEM_RESZ | - 1 << OMAP3_ISP_IOMEM_SBL | - 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | - 1 << OMAP3_ISP_IOMEM_CSIPHY2 | - 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | - 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | - 1 << OMAP3_ISP_IOMEM_CSIPHY1 | - 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 | - 1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, + .offset = { + /* first MMIO area */ + 0x0000, /* base, len 0x0070 */ + 0x0400, /* ccp2, len 0x01f0 */ + 0x0600, /* ccdc, len 0x00a8 */ + 0x0a00, /* hist, len 0x0048 */ + 0x0c00, /* h3a, len 0x0060 */ + 0x0e00, /* preview, len 0x00a0 */ + 0x1000, /* resizer, len 0x00ac */ + 0x1200, /* sbl, len 0x00fc */ + /* second MMIO area */ + 0x0000, /* csi2a, len 0x0170 (1st area) */ + 0x0170, /* csiphy2, len 0x000c */ + 0x01c0, /* csi2a, len 0x0040 (2nd area) */ + 0x0400, /* csi2c, len 0x0170 (1st area) */ + 0x0570, /* csiphy1, len 0x000c */ + 0x05c0, /* csi2c, len 0x0040 (2nd area) */ + }, + .syscon_offset = 0x2f0, + .phy_type = ISP_PHY_TYPE_3630, }, }; @@ -279,9 +291,20 @@ static const struct clk_init_data isp_xclk_init_data = { .num_parents = 1, }; +static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) +{ + unsigned int idx = clkspec->args[0]; + struct isp_device *isp = data; + + if (idx >= ARRAY_SIZE(isp->xclks)) + return ERR_PTR(-ENOENT); + + return isp->xclks[idx].clk; +} + static int isp_xclk_init(struct isp_device *isp) { - struct isp_platform_data *pdata = isp->pdata; + struct device_node *np = isp->dev->of_node; struct clk_init_data init; unsigned int i; @@ -311,37 +334,27 @@ static int isp_xclk_init(struct isp_device *isp) xclk->clk = clk_register(NULL, &xclk->hw); if (IS_ERR(xclk->clk)) return PTR_ERR(xclk->clk); - - if (pdata->xclks[i].con_id == NULL && - pdata->xclks[i].dev_id == NULL) - continue; - - xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL); - if (xclk->lookup == NULL) - return -ENOMEM; - - xclk->lookup->con_id = pdata->xclks[i].con_id; - xclk->lookup->dev_id = pdata->xclks[i].dev_id; - xclk->lookup->clk = xclk->clk; - - clkdev_add(xclk->lookup); } + if (np) + of_clk_add_provider(np, isp_xclk_src_get, isp); + return 0; } static void isp_xclk_cleanup(struct isp_device *isp) { + struct device_node *np = isp->dev->of_node; unsigned int i; + if (np) + of_clk_del_provider(np); + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { struct isp_xclk *xclk = &isp->xclks[i]; if (!IS_ERR(xclk->clk)) clk_unregister(xclk->clk); - - if (xclk->lookup) - clkdev_drop(xclk->lookup); } } @@ -422,7 +435,7 @@ static void isp_core_init(struct isp_device *isp, int idle) */ void omap3isp_configure_bridge(struct isp_device *isp, enum ccdc_input_entity input, - const struct isp_parallel_platform_data *pdata, + const struct isp_parallel_cfg *parcfg, unsigned int shift, unsigned int bridge) { u32 ispctrl_val; @@ -437,8 +450,8 @@ void omap3isp_configure_bridge(struct isp_device *isp, switch (input) { case CCDC_INPUT_PARALLEL: ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; - ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; - shift += pdata->data_lane_shift * 2; + ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; + shift += parcfg->data_lane_shift * 2; break; case CCDC_INPUT_CSI2A: @@ -1784,58 +1797,121 @@ static void isp_unregister_entities(struct isp_device *isp) } /* - * isp_register_subdev_group - Register a group of subdevices + * isp_register_subdev - Register a sub-device * @isp: OMAP3 ISP device - * @board_info: I2C subdevs board information array + * @isp_subdev: platform data related to a sub-device * - * Register all I2C subdevices in the board_info array. The array must be - * terminated by a NULL entry, and the first entry must be the sensor. + * Register an I2C sub-device which has not been registered by other + * means (such as the Device Tree). * - * Return a pointer to the sensor media entity if it has been successfully + * Return a pointer to the sub-device if it has been successfully * registered, or NULL otherwise. */ static struct v4l2_subdev * -isp_register_subdev_group(struct isp_device *isp, - struct isp_subdev_i2c_board_info *board_info) +isp_register_subdev(struct isp_device *isp, + struct isp_platform_subdev *isp_subdev) { - struct v4l2_subdev *sensor = NULL; - unsigned int first; + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; - if (board_info->board_info == NULL) + if (isp_subdev->board_info == NULL) return NULL; - for (first = 1; board_info->board_info; ++board_info, first = 0) { - struct v4l2_subdev *subdev; - struct i2c_adapter *adapter; + adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id); + if (adapter == NULL) { + dev_err(isp->dev, + "%s: Unable to get I2C adapter %d for device %s\n", + __func__, isp_subdev->i2c_adapter_id, + isp_subdev->board_info->type); + return NULL; + } - adapter = i2c_get_adapter(board_info->i2c_adapter_id); - if (adapter == NULL) { - dev_err(isp->dev, "%s: Unable to get I2C adapter %d for " - "device %s\n", __func__, - board_info->i2c_adapter_id, - board_info->board_info->type); - continue; - } + sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, + isp_subdev->board_info, NULL); + if (sd == NULL) { + dev_err(isp->dev, "%s: Unable to register subdev %s\n", + __func__, isp_subdev->board_info->type); + return NULL; + } - subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, - board_info->board_info, NULL); - if (subdev == NULL) { - dev_err(isp->dev, "%s: Unable to register subdev %s\n", - __func__, board_info->board_info->type); - continue; - } + return sd; +} + +static int isp_link_entity( + struct isp_device *isp, struct media_entity *entity, + enum isp_interface_type interface) +{ + struct media_entity *input; + unsigned int flags; + unsigned int pad; + unsigned int i; + + /* Connect the sensor to the correct interface module. + * Parallel sensors are connected directly to the CCDC, while + * serial sensors are connected to the CSI2a, CCP2b or CSI2c + * receiver through CSIPHY1 or CSIPHY2. + */ + switch (interface) { + case ISP_INTERFACE_PARALLEL: + input = &isp->isp_ccdc.subdev.entity; + pad = CCDC_PAD_SINK; + flags = 0; + break; + + case ISP_INTERFACE_CSI2A_PHY2: + input = &isp->isp_csi2a.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; + break; + + case ISP_INTERFACE_CCP2B_PHY1: + case ISP_INTERFACE_CCP2B_PHY2: + input = &isp->isp_ccp2.subdev.entity; + pad = CCP2_PAD_SINK; + flags = 0; + break; + + case ISP_INTERFACE_CSI2C_PHY1: + input = &isp->isp_csi2c.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; + break; - if (first) - sensor = subdev; + default: + dev_err(isp->dev, "%s: invalid interface type %u\n", __func__, + interface); + return -EINVAL; + } + + /* + * Not all interfaces are available on all revisions of the + * ISP. The sub-devices of those interfaces aren't initialised + * in such a case. Check this by ensuring the num_pads is + * non-zero. + */ + if (!input->num_pads) { + dev_err(isp->dev, "%s: invalid input %u\n", entity->name, + interface); + return -EINVAL; + } + + for (i = 0; i < entity->num_pads; i++) { + if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + if (i == entity->num_pads) { + dev_err(isp->dev, "%s: no source pad in external entity\n", + __func__); + return -EINVAL; } - return sensor; + return media_entity_create_link(entity, i, input, pad, flags); } static int isp_register_entities(struct isp_device *isp) { struct isp_platform_data *pdata = isp->pdata; - struct isp_v4l2_subdevs_group *subdevs; + struct isp_platform_subdev *isp_subdev; int ret; isp->media_dev.dev = isp->dev; @@ -1892,74 +1968,31 @@ static int isp_register_entities(struct isp_device *isp) if (ret < 0) goto done; + /* + * Device Tree --- the external sub-devices will be registered + * later. The same goes for the sub-device node registration. + */ + if (isp->dev->of_node) + return 0; + /* Register external entities */ - for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) { - struct v4l2_subdev *sensor; - struct media_entity *input; - unsigned int flags; - unsigned int pad; - unsigned int i; - - sensor = isp_register_subdev_group(isp, subdevs->subdevs); - if (sensor == NULL) - continue; + for (isp_subdev = pdata ? pdata->subdevs : NULL; + isp_subdev && isp_subdev->board_info; isp_subdev++) { + struct v4l2_subdev *sd; - sensor->host_priv = subdevs; + sd = isp_register_subdev(isp, isp_subdev); - /* Connect the sensor to the correct interface module. Parallel - * sensors are connected directly to the CCDC, while serial - * sensors are connected to the CSI2a, CCP2b or CSI2c receiver - * through CSIPHY1 or CSIPHY2. + /* + * No bus information --- this is either a flash or a + * lens subdev. */ - switch (subdevs->interface) { - case ISP_INTERFACE_PARALLEL: - input = &isp->isp_ccdc.subdev.entity; - pad = CCDC_PAD_SINK; - flags = 0; - break; - - case ISP_INTERFACE_CSI2A_PHY2: - input = &isp->isp_csi2a.subdev.entity; - pad = CSI2_PAD_SINK; - flags = MEDIA_LNK_FL_IMMUTABLE - | MEDIA_LNK_FL_ENABLED; - break; - - case ISP_INTERFACE_CCP2B_PHY1: - case ISP_INTERFACE_CCP2B_PHY2: - input = &isp->isp_ccp2.subdev.entity; - pad = CCP2_PAD_SINK; - flags = 0; - break; - - case ISP_INTERFACE_CSI2C_PHY1: - input = &isp->isp_csi2c.subdev.entity; - pad = CSI2_PAD_SINK; - flags = MEDIA_LNK_FL_IMMUTABLE - | MEDIA_LNK_FL_ENABLED; - break; - - default: - dev_err(isp->dev, "%s: invalid interface type %u\n", - __func__, subdevs->interface); - ret = -EINVAL; - goto done; - } + if (!sd || !isp_subdev->bus) + continue; - for (i = 0; i < sensor->entity.num_pads; i++) { - if (sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - break; - } - if (i == sensor->entity.num_pads) { - dev_err(isp->dev, - "%s: no source pad in external entity\n", - __func__); - ret = -EINVAL; - goto done; - } + sd->host_priv = isp_subdev->bus; - ret = media_entity_create_link(&sensor->entity, i, input, pad, - flags); + ret = isp_link_entity(isp, &sd->entity, + isp_subdev->bus->interface); if (ret < 0) goto done; } @@ -1967,8 +2000,10 @@ static int isp_register_entities(struct isp_device *isp) ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); done: - if (ret < 0) + if (ret < 0) { isp_unregister_entities(isp); + v4l2_async_notifier_unregister(&isp->notifier); + } return ret; } @@ -2183,6 +2218,7 @@ static int isp_remove(struct platform_device *pdev) { struct isp_device *isp = platform_get_drvdata(pdev); + v4l2_async_notifier_unregister(&isp->notifier); isp_unregister_entities(isp); isp_cleanup_modules(isp); isp_xclk_cleanup(isp); @@ -2194,26 +2230,156 @@ static int isp_remove(struct platform_device *pdev) return 0; } -static int isp_map_mem_resource(struct platform_device *pdev, - struct isp_device *isp, - enum isp_mem_resources res) +enum isp_of_phy { + ISP_OF_PHY_PARALLEL = 0, + ISP_OF_PHY_CSIPHY1, + ISP_OF_PHY_CSIPHY2, +}; + +static int isp_of_parse_node(struct device *dev, struct device_node *node, + struct isp_async_subdev *isd) { - struct resource *mem; + struct isp_bus_cfg *buscfg = &isd->bus; + struct v4l2_of_endpoint vep; + unsigned int i; - /* request the mem region for the camera registers */ + v4l2_of_parse_endpoint(node, &vep); + + dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name, + vep.base.port); + + switch (vep.base.port) { + case ISP_OF_PHY_PARALLEL: + buscfg->interface = ISP_INTERFACE_PARALLEL; + buscfg->bus.parallel.data_lane_shift = + vep.bus.parallel.data_shift; + buscfg->bus.parallel.clk_pol = + !!(vep.bus.parallel.flags + & V4L2_MBUS_PCLK_SAMPLE_FALLING); + buscfg->bus.parallel.hs_pol = + !!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW); + buscfg->bus.parallel.vs_pol = + !!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW); + buscfg->bus.parallel.fld_pol = + !!(vep.bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW); + buscfg->bus.parallel.data_pol = + !!(vep.bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW); + break; - mem = platform_get_resource(pdev, IORESOURCE_MEM, res); + case ISP_OF_PHY_CSIPHY1: + case ISP_OF_PHY_CSIPHY2: + /* FIXME: always assume CSI-2 for now. */ + switch (vep.base.port) { + case ISP_OF_PHY_CSIPHY1: + buscfg->interface = ISP_INTERFACE_CSI2C_PHY1; + break; + case ISP_OF_PHY_CSIPHY2: + buscfg->interface = ISP_INTERFACE_CSI2A_PHY2; + break; + } + buscfg->bus.csi2.lanecfg.clk.pos = vep.bus.mipi_csi2.clock_lane; + buscfg->bus.csi2.lanecfg.clk.pol = + vep.bus.mipi_csi2.lane_polarities[0]; + dev_dbg(dev, "clock lane polarity %u, pos %u\n", + buscfg->bus.csi2.lanecfg.clk.pol, + buscfg->bus.csi2.lanecfg.clk.pos); + + for (i = 0; i < ISP_CSIPHY2_NUM_DATA_LANES; i++) { + buscfg->bus.csi2.lanecfg.data[i].pos = + vep.bus.mipi_csi2.data_lanes[i]; + buscfg->bus.csi2.lanecfg.data[i].pol = + vep.bus.mipi_csi2.lane_polarities[i + 1]; + dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i, + buscfg->bus.csi2.lanecfg.data[i].pol, + buscfg->bus.csi2.lanecfg.data[i].pos); + } - /* map the region */ - isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem); - if (IS_ERR(isp->mmio_base[res])) - return PTR_ERR(isp->mmio_base[res]); + /* + * FIXME: now we assume the CRC is always there. + * Implement a way to obtain this information from the + * sensor. Frame descriptors, perhaps? + */ + buscfg->bus.csi2.crc = 1; + break; - isp->mmio_base_phys[res] = mem->start; + default: + dev_warn(dev, "%s: invalid interface %u\n", node->full_name, + vep.base.port); + break; + } return 0; } +static int isp_of_parse_nodes(struct device *dev, + struct v4l2_async_notifier *notifier) +{ + struct device_node *node = NULL; + + notifier->subdevs = devm_kcalloc( + dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL); + if (!notifier->subdevs) + return -ENOMEM; + + while (notifier->num_subdevs < ISP_MAX_SUBDEVS && + (node = of_graph_get_next_endpoint(dev->of_node, node))) { + struct isp_async_subdev *isd; + + isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL); + if (!isd) { + of_node_put(node); + return -ENOMEM; + } + + notifier->subdevs[notifier->num_subdevs] = &isd->asd; + + if (isp_of_parse_node(dev, node, isd)) { + of_node_put(node); + return -EINVAL; + } + + isd->asd.match.of.node = of_graph_get_remote_port_parent(node); + of_node_put(node); + if (!isd->asd.match.of.node) { + dev_warn(dev, "bad remote port parent\n"); + return -EINVAL; + } + + isd->asd.match_type = V4L2_ASYNC_MATCH_OF; + notifier->num_subdevs++; + } + + return notifier->num_subdevs; +} + +static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isp_device *isp = container_of(async, struct isp_device, + notifier); + struct isp_async_subdev *isd = + container_of(asd, struct isp_async_subdev, asd); + int ret; + + ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface); + if (ret < 0) + return ret; + + isd->sd = subdev; + isd->sd->host_priv = &isd->bus; + + return ret; +} + +static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) +{ + struct isp_device *isp = container_of(async, struct isp_device, + notifier); + + return v4l2_device_register_subdev_nodes(&isp->v4l2_dev); +} + /* * isp_probe - Probe ISP platform device * @pdev: Pointer to ISP platform device @@ -2227,47 +2393,86 @@ static int isp_map_mem_resource(struct platform_device *pdev, */ static int isp_probe(struct platform_device *pdev) { - struct isp_platform_data *pdata = pdev->dev.platform_data; struct isp_device *isp; + struct resource *mem; int ret; int i, m; - if (pdata == NULL) - return -EINVAL; - isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); if (!isp) { dev_err(&pdev->dev, "could not allocate memory\n"); return -ENOMEM; } + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", + &isp->phy_type); + if (ret) + return ret; + + isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(isp->syscon)) + return PTR_ERR(isp->syscon); + + ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, + &isp->syscon_offset); + if (ret) + return ret; + + ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + if (ret < 0) + return ret; + ret = v4l2_async_notifier_register(&isp->v4l2_dev, + &isp->notifier); + if (ret) + return ret; + } else { + isp->pdata = pdev->dev.platform_data; + isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); + if (IS_ERR(isp->syscon)) + return PTR_ERR(isp->syscon); + dev_warn(&pdev->dev, + "Platform data support is deprecated! Please move to DT now!\n"); + } + isp->autoidle = autoidle; mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); isp->dev = &pdev->dev; - isp->pdata = pdata; isp->ref_count = 0; ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); if (ret) - return ret; + goto error; platform_set_drvdata(pdev, isp); /* Regulators */ - isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY1"); - isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY2"); + isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1"); + isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2"); /* Clocks * * The ISP clock tree is revision-dependent. We thus need to enable ICLK * manually to read the revision before calling __omap3isp_get(). + * + * Start by mapping the ISP MMIO area, which is in two pieces. + * The ISP IOMMU is in between. Map both now, and fill in the + * ISP revision specific portions a little later in the + * function. */ - ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); - if (ret < 0) - goto error; + for (i = 0; i < 2; i++) { + unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, i); + isp->mmio_base[map_idx] = + devm_ioremap_resource(isp->dev, mem); + if (IS_ERR(isp->mmio_base[map_idx])) + return PTR_ERR(isp->mmio_base[map_idx]); + } ret = isp_get_clocks(isp); if (ret < 0) @@ -2308,14 +2513,23 @@ static int isp_probe(struct platform_device *pdev) goto error_isp; } - for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) { - if (isp_res_maps[m].map & 1 << i) { - ret = isp_map_mem_resource(pdev, isp, i); - if (ret) - goto error_isp; - } + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) { + isp->syscon_offset = isp_res_maps[m].syscon_offset; + isp->phy_type = isp_res_maps[m].phy_type; } + for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) + isp->mmio_base[i] = + isp->mmio_base[0] + isp_res_maps[m].offset[i]; + + for (i = OMAP3_ISP_IOMEM_CSIPHY2; i < OMAP3_ISP_IOMEM_LAST; i++) + isp->mmio_base[i] = + isp->mmio_base[OMAP3_ISP_IOMEM_CSI2A_REGS1] + + isp_res_maps[m].offset[i]; + + isp->mmio_hist_base_phys = + mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST]; + /* IOMMU */ ret = isp_attach_iommu(isp); if (ret < 0) { @@ -2343,6 +2557,9 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_iommu; + isp->notifier.bound = isp_subdev_notifier_bound; + isp->notifier.complete = isp_subdev_notifier_complete; + ret = isp_register_entities(isp); if (ret < 0) goto error_modules; @@ -2378,6 +2595,11 @@ static struct platform_device_id omap3isp_id_table[] = { }; MODULE_DEVICE_TABLE(platform, omap3isp_id_table); +static const struct of_device_id omap3isp_of_table[] = { + { .compatible = "ti,omap3-isp" }, + { }, +}; + static struct platform_driver omap3isp_driver = { .probe = isp_probe, .remove = isp_remove, @@ -2385,6 +2607,7 @@ static struct platform_driver omap3isp_driver = { .driver = { .name = "omap3isp", .pm = &omap3isp_pm_ops, + .of_match_table = omap3isp_of_table, }, }; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index cfdfc8714b6b..e579943175c4 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -18,6 +18,7 @@ #define OMAP3_ISP_CORE_H #include <media/omap3isp.h> +#include <media/v4l2-async.h> #include <media/v4l2-device.h> #include <linux/clk-provider.h> #include <linux/device.h> @@ -59,8 +60,6 @@ enum isp_mem_resources { OMAP3_ISP_IOMEM_CSI2C_REGS1, OMAP3_ISP_IOMEM_CSIPHY1, OMAP3_ISP_IOMEM_CSI2C_REGS2, - OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, - OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, OMAP3_ISP_IOMEM_LAST }; @@ -93,14 +92,25 @@ enum isp_subclk_resource { /* ISP2P: OMAP 36xx */ #define ISP_REVISION_15_0 0xF0 +#define ISP_PHY_TYPE_3430 0 +#define ISP_PHY_TYPE_3630 1 + +struct regmap; + /* * struct isp_res_mapping - Map ISP io resources to ISP revision. * @isp_rev: ISP_REVISION_x_x - * @map: bitmap for enum isp_mem_resources + * @offset: register offsets of various ISP sub-blocks + * @syscon_offset: offset of the syscon register for 343x / 3630 + * (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively) + * from the syscon base address + * @phy_type: ISP_PHY_TYPE_{3430,3630} */ struct isp_res_mapping { u32 isp_rev; - u32 map; + u32 offset[OMAP3_ISP_IOMEM_LAST]; + u32 syscon_offset; + u32 phy_type; }; /* @@ -122,7 +132,6 @@ enum isp_xclk_id { struct isp_xclk { struct isp_device *isp; struct clk_hw hw; - struct clk_lookup *lookup; struct clk *clk; enum isp_xclk_id id; @@ -138,8 +147,11 @@ struct isp_xclk { * @irq_num: Currently used IRQ number. * @mmio_base: Array with kernel base addresses for ioremapped ISP register * regions. - * @mmio_base_phys: Array with physical L4 bus addresses for ISP register - * regions. + * @mmio_hist_base_phys: Physical L4 bus address for ISP hist block register + * region. + * @syscon: Regmap for the syscon register space + * @syscon_offset: Offset of the CSIPHY control register in syscon + * @phy_type: ISP_PHY_TYPE_{3430,3630} * @mapping: IOMMU mapping * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. @@ -166,6 +178,7 @@ struct isp_xclk { */ struct isp_device { struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; struct media_device media_dev; struct device *dev; u32 revision; @@ -175,7 +188,10 @@ struct isp_device { unsigned int irq_num; void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; - unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; + unsigned long mmio_hist_base_phys; + struct regmap *syscon; + u32 syscon_offset; + u32 phy_type; struct dma_iommu_mapping *mapping; @@ -209,6 +225,15 @@ struct isp_device { unsigned int sbl_resources; unsigned int subclk_resources; + +#define ISP_MAX_SUBDEVS 8 + struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS]; +}; + +struct isp_async_subdev { + struct v4l2_subdev *sd; + struct isp_bus_cfg bus; + struct v4l2_async_subdev asd; }; #define v4l2_dev_to_isp_device(dev) \ @@ -229,7 +254,7 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe); void omap3isp_configure_bridge(struct isp_device *isp, enum ccdc_input_entity input, - const struct isp_parallel_platform_data *pdata, + const struct isp_parallel_cfg *buscfg, unsigned int shift, unsigned int bridge); struct isp_device *omap3isp_get(struct isp_device *isp); diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 587489a072d5..a6a61cce43dd 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -32,7 +32,7 @@ #define CCDC_MIN_HEIGHT 32 static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which); static const unsigned int ccdc_fmts[] = { @@ -958,11 +958,11 @@ void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, /* * ccdc_config_sync_if - Set CCDC sync interface configuration * @ccdc: Pointer to ISP CCDC device. - * @pdata: Parallel interface platform data (may be NULL) + * @parcfg: Parallel interface platform data (may be NULL) * @data_size: Data size */ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, - struct isp_parallel_platform_data *pdata, + struct isp_parallel_cfg *parcfg, unsigned int data_size) { struct isp_device *isp = to_isp_device(ccdc); @@ -1000,19 +1000,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, break; } - if (pdata && pdata->data_pol) + if (parcfg && parcfg->data_pol) syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; - if (pdata && pdata->hs_pol) + if (parcfg && parcfg->hs_pol) syn_mode |= ISPCCDC_SYN_MODE_HDPOL; /* The polarity of the vertical sync signal output by the BT.656 * decoder is not documented and seems to be active low. */ - if ((pdata && pdata->vs_pol) || ccdc->bt656) + if ((parcfg && parcfg->vs_pol) || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_VDPOL; - if (pdata && pdata->fld_pol) + if (parcfg && parcfg->fld_pol) syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -1115,7 +1115,7 @@ static const u32 ccdc_sgbrg_pattern = static void ccdc_configure(struct isp_ccdc_device *ccdc) { struct isp_device *isp = to_isp_device(ccdc); - struct isp_parallel_platform_data *pdata = NULL; + struct isp_parallel_cfg *parcfg = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; @@ -1145,7 +1145,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) if (!ret) ccdc->bt656 = cfg.type == V4L2_MBUS_BT656; - pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) + parcfg = &((struct isp_bus_cfg *)sensor->host_priv) ->bus.parallel; } @@ -1175,10 +1175,10 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) else bridge = ISPCTRL_PAR_BRIDGE_DISABLE; - omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge); + omap3isp_configure_bridge(isp, ccdc->input, parcfg, shift, bridge); /* Configure the sync interface. */ - ccdc_config_sync_if(ccdc, pdata, depth_out); + ccdc_config_sync_if(ccdc, parcfg, depth_out); syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -1935,21 +1935,21 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad); else return &ccdc->formats[pad]; } static struct v4l2_rect * -__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1957,12 +1957,12 @@ __ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @pad: Pad number * @fmt: Format */ static void -ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1998,7 +1998,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, case CCDC_PAD_SOURCE_OF: pixelcode = fmt->code; field = fmt->field; - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); /* In SYNC mode the bridge converts YUV formats from 2X8 to * 1X16. In BT.656 no such conversion occurs. As we don't know @@ -2023,7 +2023,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, } /* Hardcode the output size to the crop rectangle size. */ - crop = __ccdc_get_crop(ccdc, fh, which); + crop = __ccdc_get_crop(ccdc, cfg, which); fmt->width = crop->width; fmt->height = crop->height; @@ -2040,7 +2040,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, break; case CCDC_PAD_SOURCE_VP: - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); /* The video port interface truncates the data to 10 bits. */ info = omap3isp_video_format_info(fmt->code); @@ -2112,12 +2112,12 @@ static void ccdc_try_crop(struct isp_ccdc_device *ccdc, /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2132,8 +2132,8 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, break; case CCDC_PAD_SOURCE_OF: - format = __ccdc_get_format(ccdc, fh, code->pad, - V4L2_SUBDEV_FORMAT_TRY); + format = __ccdc_get_format(ccdc, cfg, code->pad, + code->which); if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 || format->code == MEDIA_BUS_FMT_UYVY8_2X8) { @@ -2163,8 +2163,8 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __ccdc_get_format(ccdc, fh, code->pad, - V4L2_SUBDEV_FORMAT_TRY); + format = __ccdc_get_format(ccdc, cfg, code->pad, + code->which); /* A pixel code equal to 0 means that the video port doesn't * support the input format. Don't enumerate any pixel code. @@ -2183,7 +2183,7 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, } static int ccdc_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2195,7 +2195,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -2205,7 +2205,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -2215,7 +2215,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, /* * ccdc_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the output formatter @@ -2223,7 +2223,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2239,12 +2239,12 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); break; default: @@ -2257,7 +2257,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output @@ -2265,7 +2265,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2284,17 +2284,17 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); return 0; } - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); - *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + *__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r; /* Update the source format. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which); return 0; } @@ -2302,19 +2302,19 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2325,30 +2325,30 @@ static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccdc_set_format - Set the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); + ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop = __ccdc_get_crop(ccdc, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2357,16 +2357,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, &fmt->format, crop); /* Update the source formats. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, fmt->which); - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format, fmt->which); } @@ -2417,11 +2417,11 @@ static int ccdc_link_validate(struct v4l2_subdev *sd, /* We've got a parallel sensor here. */ if (ccdc->input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) + struct isp_parallel_cfg *parcfg = + &((struct isp_bus_cfg *) media_entity_to_v4l2_subdev(link->source->entity) ->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; + parallel_shift = parcfg->data_lane_shift * 2; } else { parallel_shift = 0; } @@ -2453,7 +2453,7 @@ static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccdc_set_format(sd, fh, &format); + ccdc_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index f4aedb37e41e..38e6a974c5b1 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -201,14 +201,14 @@ static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) /* * ccp2_phyif_config - Initialize CCP2 phy interface config * @ccp2: Pointer to ISP CCP2 device - * @pdata: CCP2 platform data + * @buscfg: CCP2 platform data * * Configure the CCP2 physical interface module from platform data. * * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. */ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, - const struct isp_ccp2_platform_data *pdata) + const struct isp_ccp2_cfg *buscfg) { struct isp_device *isp = to_isp_device(ccp2); u32 val; @@ -218,16 +218,16 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; /* Data/strobe physical layer */ BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, - pdata->phy_layer); + buscfg->phy_layer); BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, - pdata->strobe_clk_pol); + buscfg->strobe_clk_pol); isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); if (!(val & ISPCCP2_CTRL_MODE)) { - if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2) + if (buscfg->ccp2_mode == ISP_CCP2_MODE_CCP2) dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); - if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE) + if (buscfg->phy_layer == ISP_CCP2_PHY_DATA_STROBE) /* Strobe mode requires CCP2 */ return -EIO; } @@ -347,7 +347,7 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, */ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) { - const struct isp_v4l2_subdevs_group *pdata; + const struct isp_bus_cfg *buscfg; struct v4l2_mbus_framefmt *format; struct media_pad *pad; struct v4l2_subdev *sensor; @@ -358,20 +358,20 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - pdata = sensor->host_priv; + buscfg = sensor->host_priv; - ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); + ret = ccp2_phyif_config(ccp2, &buscfg->bus.ccp2); if (ret < 0) return ret; - ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); + ccp2_vp_config(ccp2, buscfg->bus.ccp2.vpclk_div + 1); v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); format = &ccp2->formats[CCP2_PAD_SINK]; ccp2->if_cfg.data_start = lines; - ccp2->if_cfg.crc = pdata->bus.ccp2.crc; + ccp2->if_cfg.crc = buscfg->bus.ccp2.crc; ccp2->if_cfg.format = format->code; ccp2->if_cfg.data_size = format->height; @@ -611,17 +611,17 @@ static const unsigned int ccp2_fmts[] = { /* * __ccp2_get_format - helper function for getting ccp2 format * @ccp2 : Pointer to ISP CCP2 device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad number * @which : wanted subdev format * return format structure or NULL on error */ static struct v4l2_mbus_framefmt * -__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, +__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ccp2->subdev, cfg, pad); else return &ccp2->formats[pad]; } @@ -629,13 +629,13 @@ __ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, /* * ccp2_try_format - Handle try format by pad subdev method * @ccp2 : Pointer to ISP CCP2 device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad num * @fmt : pointer to v4l2 mbus format structure * @which : wanted subdev format */ static void ccp2_try_format(struct isp_ccp2_device *ccp2, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -669,7 +669,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, * When CCP2 write to memory feature will be added this * should be changed properly. */ - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; break; @@ -682,12 +682,12 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, /* * ccp2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -702,8 +702,8 @@ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, - V4L2_SUBDEV_FORMAT_TRY); + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, + code->which); code->code = format->code; } @@ -711,7 +711,7 @@ static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, } static int ccp2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); @@ -723,7 +723,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -733,7 +733,7 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -743,17 +743,17 @@ static int ccp2_enum_frame_size(struct v4l2_subdev *sd, /* * ccp2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -764,29 +764,29 @@ static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * ccp2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * returns zero */ -static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); + format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); + ccp2_try_format(ccp2, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCP2_PAD_SINK) { - format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, + format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SOURCE, fmt->which); *format = fmt->format; - ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); + ccp2_try_format(ccp2, cfg, CCP2_PAD_SOURCE, format, fmt->which); } return 0; @@ -811,7 +811,7 @@ static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccp2_set_format(sd, fh, &format); + ccp2_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 09c686d96ae8..a78338d012b4 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -548,7 +548,8 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2) static int csi2_configure(struct isp_csi2_device *csi2) { - const struct isp_v4l2_subdevs_group *pdata; + struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); + const struct isp_bus_cfg *buscfg; struct isp_device *isp = csi2->isp; struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; struct v4l2_subdev *sensor; @@ -565,14 +566,19 @@ static int csi2_configure(struct isp_csi2_device *csi2) pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - pdata = sensor->host_priv; + buscfg = sensor->host_priv; csi2->frame_skip = 0; v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); - csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; + csi2->ctrl.vp_out_ctrl = + clamp_t(unsigned int, pipe->l3_ick / pipe->external_rate - 1, + 1, 3); + dev_dbg(isp->dev, "%s: l3_ick %lu, external_rate %u, vp_out_ctrl %u\n", + __func__, pipe->l3_ick, pipe->external_rate, + csi2->ctrl.vp_out_ctrl); csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; - csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; + csi2->ctrl.ecc_enable = buscfg->bus.csi2.crc; timing->ionum = 1; timing->force_rx_mode = 1; @@ -829,17 +835,17 @@ static const struct isp_video_operations csi2_ispvideo_ops = { */ static struct v4l2_mbus_framefmt * -__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, +__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad); else return &csi2->formats[pad]; } static void -csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, +csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -869,7 +875,7 @@ csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, * compression. */ pixelcode = fmt->code; - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); /* @@ -890,12 +896,12 @@ csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, /* * csi2_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -908,8 +914,8 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, code->code = csi2_input_fmts[code->index]; } else { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, - V4L2_SUBDEV_FORMAT_TRY); + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, + code->which); switch (code->index) { case 0: /* Passthrough sink pad code */ @@ -932,7 +938,7 @@ static int csi2_enum_mbus_code(struct v4l2_subdev *sd, } static int csi2_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); @@ -944,7 +950,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -954,7 +960,7 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + csi2_try_format(csi2, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -964,17 +970,17 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd, /* * csi2_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -985,29 +991,29 @@ static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * csi2_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); + format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); + csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CSI2_PAD_SINK) { - format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, + format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE, fmt->which); *format = fmt->format; - csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); + csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which); } return 0; @@ -1032,7 +1038,7 @@ static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - csi2_set_format(sd, fh, &format); + csi2_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index e033f2237a72..495447d66cfd 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/device.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include "isp.h" @@ -26,10 +27,11 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, enum isp_interface_type iface, bool ccp2_strobe) { - u32 reg = isp_reg_readl( - phy->isp, OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0); + u32 reg; u32 shift, mode; + regmap_read(phy->isp->syscon, phy->isp->syscon_offset, ®); + switch (iface) { default: /* Should not happen in practice, but let's keep the compiler happy. */ @@ -63,8 +65,7 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift); reg |= mode << shift; - isp_reg_writel(phy->isp, reg, - OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0); + regmap_write(phy->isp->syscon, phy->isp->syscon_offset, reg); } static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, @@ -78,16 +79,14 @@ static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, return; if (!on) { - isp_reg_writel(phy->isp, 0, - OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0); + regmap_write(phy->isp->syscon, phy->isp->syscon_offset, 0); return; } if (ccp2_strobe) csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM; - isp_reg_writel(phy->isp, csirxfe, - OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0); + regmap_write(phy->isp->syscon, phy->isp->syscon_offset, csirxfe); } /* @@ -106,10 +105,9 @@ static void csiphy_routing_cfg(struct isp_csiphy *phy, enum isp_interface_type iface, bool on, bool ccp2_strobe) { - if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL] - && on) + if (phy->isp->phy_type == ISP_PHY_TYPE_3630 && on) return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe); - if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE]) + if (phy->isp->phy_type == ISP_PHY_TYPE_3430) return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe); } @@ -168,18 +166,25 @@ static int omap3isp_csiphy_config(struct isp_csiphy *phy) { struct isp_csi2_device *csi2 = phy->csi2; struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); - struct isp_v4l2_subdevs_group *subdevs = pipe->external->host_priv; + struct isp_bus_cfg *buscfg = pipe->external->host_priv; struct isp_csiphy_lanes_cfg *lanes; int csi2_ddrclk_khz; unsigned int used_lanes = 0; unsigned int i; u32 reg; - if (subdevs->interface == ISP_INTERFACE_CCP2B_PHY1 - || subdevs->interface == ISP_INTERFACE_CCP2B_PHY2) - lanes = &subdevs->bus.ccp2.lanecfg; + if (!buscfg) { + struct isp_async_subdev *isd = + container_of(pipe->external->asd, + struct isp_async_subdev, asd); + buscfg = &isd->bus; + } + + if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1 + || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) + lanes = &buscfg->bus.ccp2.lanecfg; else - lanes = &subdevs->bus.csi2.lanecfg; + lanes = &buscfg->bus.csi2.lanecfg; /* Clock and data lanes verification */ for (i = 0; i < phy->num_data_lanes; i++) { @@ -203,8 +208,8 @@ static int omap3isp_csiphy_config(struct isp_csiphy *phy) * issue since the MPU power domain is forced on whilst the * ISP is in use. */ - csiphy_routing_cfg(phy, subdevs->interface, true, - subdevs->bus.ccp2.phy_layer); + csiphy_routing_cfg(phy, buscfg->interface, true, + buscfg->bus.ccp2.phy_layer); /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ @@ -302,11 +307,10 @@ void omap3isp_csiphy_release(struct isp_csiphy *phy) struct isp_csi2_device *csi2 = phy->csi2; struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); - struct isp_v4l2_subdevs_group *subdevs = - pipe->external->host_priv; + struct isp_bus_cfg *buscfg = pipe->external->host_priv; - csiphy_routing_cfg(phy, subdevs->interface, false, - subdevs->bus.ccp2.phy_layer); + csiphy_routing_cfg(phy, buscfg->interface, false, + buscfg->bus.ccp2.phy_layer); csiphy_power_autoswitch_enable(phy, false); csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); regulator_disable(phy->vdd); diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index b208c5417146..ccaf92f39236 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -297,7 +297,6 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) aewb->ops = &h3a_aewb_ops; aewb->priv = aewb_cfg; - aewb->dma_ch = -1; aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; aewb->isp = isp; diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index 8a83e195f3e3..92937f7eecef 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -360,7 +360,6 @@ int omap3isp_h3a_af_init(struct isp_device *isp) af->ops = &h3a_af_ops; af->priv = af_cfg; - af->dma_ch = -1; af->event_type = V4L2_EVENT_OMAP3ISP_AF; af->isp = isp; diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index ce822c34c843..7138b043a4aa 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -16,20 +16,18 @@ */ #include <linux/delay.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/omap-dmaengine.h> #include <linux/slab.h> #include <linux/uaccess.h> -#include <linux/device.h> #include "isp.h" #include "ispreg.h" #include "isphist.h" -#define OMAP24XX_DMA_NO_DEVICE 0 - #define HIST_CONFIG_DMA 1 -#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) - /* * hist_reset_mem - clear Histogram memory before start stats engine. */ @@ -62,20 +60,6 @@ static void hist_reset_mem(struct ispstat *hist) hist->wait_acc_frames = conf->num_acc_frames; } -static void hist_dma_config(struct ispstat *hist) -{ - struct isp_device *isp = hist->isp; - - hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; - hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; - hist->dma_config.frame_count = 1; - hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; - hist->dma_config.src_start = isp->mmio_base_phys[OMAP3_ISP_IOMEM_HIST] - + ISPHIST_DATA; - hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; - hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; -} - /* * hist_setup_regs - Helper function to update Histogram registers. */ @@ -176,17 +160,12 @@ static int hist_busy(struct ispstat *hist) & ISPHIST_PCR_BUSY; } -static void hist_dma_cb(int lch, u16 ch_status, void *data) +static void hist_dma_cb(void *data) { struct ispstat *hist = data; - if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { - dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", - ch_status); - omap_stop_dma(lch); - hist_reset_mem(hist); - atomic_set(&hist->buf_err, 1); - } + /* FIXME: The DMA engine API can't report transfer errors :-/ */ + isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); @@ -198,24 +177,57 @@ static void hist_dma_cb(int lch, u16 ch_status, void *data) static int hist_buf_dma(struct ispstat *hist) { dma_addr_t dma_addr = hist->active_buf->dma_addr; + struct dma_async_tx_descriptor *tx; + struct dma_slave_config cfg; + dma_cookie_t cookie; + int ret; if (unlikely(!dma_addr)) { dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); - hist_reset_mem(hist); - return STAT_NO_BUF; + goto error; } isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); omap3isp_flush(hist->isp); - hist->dma_config.dst_start = dma_addr; - hist->dma_config.elem_count = hist->buf_size / sizeof(u32); - omap_set_dma_params(hist->dma_ch, &hist->dma_config); - omap_start_dma(hist->dma_ch); + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = hist->isp->mmio_hist_base_phys + ISPHIST_DATA; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = hist->buf_size / 4; + + ret = dmaengine_slave_config(hist->dma_ch, &cfg); + if (ret < 0) { + dev_dbg(hist->isp->dev, + "hist: DMA slave configuration failed\n"); + goto error; + } + + tx = dmaengine_prep_slave_single(hist->dma_ch, dma_addr, + hist->buf_size, DMA_DEV_TO_MEM, + DMA_CTRL_ACK); + if (tx == NULL) { + dev_dbg(hist->isp->dev, + "hist: DMA slave preparation failed\n"); + goto error; + } + + tx->callback = hist_dma_cb; + tx->callback_param = hist; + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + dev_dbg(hist->isp->dev, "hist: DMA submission failed\n"); + goto error; + } + + dma_async_issue_pending(hist->dma_ch); return STAT_BUF_WAITING_DMA; + +error: + hist_reset_mem(hist); + return STAT_NO_BUF; } static int hist_buf_pio(struct ispstat *hist) @@ -272,7 +284,7 @@ static int hist_buf_process(struct ispstat *hist) if (--(hist->wait_acc_frames)) return STAT_NO_BUF; - if (HIST_USING_DMA(hist)) + if (hist->dma_ch) ret = hist_buf_dma(hist); else ret = hist_buf_pio(hist); @@ -473,18 +485,28 @@ int omap3isp_hist_init(struct isp_device *isp) hist->isp = isp; - if (HIST_CONFIG_DMA) - ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", - hist_dma_cb, hist, &hist->dma_ch); - if (ret) { - if (HIST_CONFIG_DMA) - dev_warn(isp->dev, "hist: DMA request channel failed. " - "Using PIO only.\n"); - hist->dma_ch = -1; - } else { - dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); - hist_dma_config(hist); - omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); + if (HIST_CONFIG_DMA) { + struct platform_device *pdev = to_platform_device(isp->dev); + struct resource *res; + unsigned int sig = 0; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "hist"); + if (res) + sig = res->start; + + hist->dma_ch = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, &sig, isp->dev, "hist"); + if (!hist->dma_ch) + dev_warn(isp->dev, + "hist: DMA channel request failed, using PIO\n"); + else + dev_dbg(isp->dev, "hist: using DMA channel %s\n", + dma_chan_name(hist->dma_ch)); } hist->ops = &hist_ops; @@ -493,8 +515,8 @@ int omap3isp_hist_init(struct isp_device *isp) ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); if (ret) { - if (HIST_USING_DMA(hist)) - omap_free_dma(hist->dma_ch); + if (hist->dma_ch) + dma_release_channel(hist->dma_ch); } return ret; @@ -505,7 +527,10 @@ int omap3isp_hist_init(struct isp_device *isp) */ void omap3isp_hist_cleanup(struct isp_device *isp) { - if (HIST_USING_DMA(&isp->isp_hist)) - omap_free_dma(isp->isp_hist.dma_ch); - omap3isp_stat_cleanup(&isp->isp_hist); + struct ispstat *hist = &isp->isp_hist; + + if (hist->dma_ch) + dma_release_channel(hist->dma_ch); + + omap3isp_stat_cleanup(hist); } diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index dd9eed45d853..15cb254ccc39 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -1686,21 +1686,21 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) } static struct v4l2_mbus_framefmt * -__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, +__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&prev->subdev, cfg, pad); else return &prev->formats[pad]; } static struct v4l2_rect * -__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, +__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK); + return v4l2_subdev_get_try_crop(&prev->subdev, cfg, PREV_PAD_SINK); else return &prev->crop; } @@ -1727,7 +1727,7 @@ static const unsigned int preview_output_fmts[] = { /* * preview_try_format - Validate a format * @prev: ISP preview engine - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad: pad number * @fmt: format to be validated * @which: try/active format selector @@ -1736,7 +1736,7 @@ static const unsigned int preview_output_fmts[] = { * engine limits and the format and crop rectangles on other pads. */ static void preview_try_format(struct isp_prev_device *prev, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1777,7 +1777,7 @@ static void preview_try_format(struct isp_prev_device *prev, case PREV_PAD_SOURCE: pixelcode = fmt->code; - *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which); + *fmt = *__preview_get_format(prev, cfg, PREV_PAD_SINK, which); switch (pixelcode) { case MEDIA_BUS_FMT_YUYV8_1X16: @@ -1795,7 +1795,7 @@ static void preview_try_format(struct isp_prev_device *prev, * is not supported yet, hardcode the output size to the crop * rectangle size. */ - crop = __preview_get_crop(prev, fh, which); + crop = __preview_get_crop(prev, cfg, which); fmt->width = crop->width; fmt->height = crop->height; @@ -1864,12 +1864,12 @@ static void preview_try_crop(struct isp_prev_device *prev, /* * preview_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int preview_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { switch (code->pad) { @@ -1893,7 +1893,7 @@ static int preview_enum_mbus_code(struct v4l2_subdev *sd, } static int preview_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1905,7 +1905,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + preview_try_format(prev, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -1915,7 +1915,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + preview_try_format(prev, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -1925,7 +1925,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, /* * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1933,7 +1933,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int preview_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1949,13 +1949,13 @@ static int preview_get_selection(struct v4l2_subdev *sd, sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, + format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which); preview_try_crop(prev, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__preview_get_crop(prev, fh, sel->which); + sel->r = *__preview_get_crop(prev, cfg, sel->which); break; default: @@ -1968,7 +1968,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, /* * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1976,7 +1976,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int preview_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); @@ -1995,17 +1995,17 @@ static int preview_set_selection(struct v4l2_subdev *sd, * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__preview_get_crop(prev, fh, sel->which); + sel->r = *__preview_get_crop(prev, cfg, sel->which); return 0; } - format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which); + format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which); preview_try_crop(prev, format, &sel->r); - *__preview_get_crop(prev, fh, sel->which) = sel->r; + *__preview_get_crop(prev, cfg, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which); + format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, sel->which); + preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, sel->which); return 0; } @@ -2013,17 +2013,17 @@ static int preview_set_selection(struct v4l2_subdev *sd, /* * preview_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2034,28 +2034,28 @@ static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * preview_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __preview_get_format(prev, fh, fmt->pad, fmt->which); + format = __preview_get_format(prev, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); + preview_try_format(prev, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == PREV_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __preview_get_crop(prev, fh, fmt->which); + crop = __preview_get_crop(prev, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2064,9 +2064,9 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, preview_try_crop(prev, &fmt->format, crop); /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, + format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, fmt->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, + preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, fmt->which); } @@ -2093,7 +2093,7 @@ static int preview_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - preview_set_format(sd, fh, &format); + preview_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index 2b9bc4839876..7cfb43dc0ffd 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -112,16 +112,16 @@ static const struct isprsz_coef filter_coefs = { * __resizer_get_format - helper function for getting resizer format * @res : pointer to resizer private structure * @pad : pad number - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @which : wanted subdev format * return zero */ static struct v4l2_mbus_framefmt * -__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, +__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&res->subdev, cfg, pad); else return &res->formats[pad]; } @@ -129,15 +129,15 @@ __resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, /* * __resizer_get_crop - helper function for getting resizer crop rectangle * @res : pointer to resizer private structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @which : wanted subdev crop rectangle */ static struct v4l2_rect * -__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, +__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); + return v4l2_subdev_get_try_crop(&res->subdev, cfg, RESZ_PAD_SINK); else return &res->crop.request; } @@ -1215,7 +1215,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, /* * resizer_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the sink pad. @@ -1223,7 +1223,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, * Return 0 on success or a negative error code otherwise. */ static int resizer_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1234,9 +1234,9 @@ static int resizer_get_selection(struct v4l2_subdev *sd, if (sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, sel->which); - format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format_source = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which); switch (sel->target) { @@ -1251,7 +1251,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, break; case V4L2_SEL_TGT_CROP: - sel->r = *__resizer_get_crop(res, fh, sel->which); + sel->r = *__resizer_get_crop(res, cfg, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1265,7 +1265,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, /* * resizer_set_selection - Set a selection rectangle on a pad * @sd: ISP resizer V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the sink pad. @@ -1276,7 +1276,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, * Return 0 on success or a negative error code otherwise. */ static int resizer_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1290,9 +1290,9 @@ static int resizer_set_selection(struct v4l2_subdev *sd, sel->pad != RESZ_PAD_SINK) return -EINVAL; - format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK, sel->which); - format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format_source = *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which); dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1310,7 +1310,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, * stored the mangled rectangle. */ resizer_try_crop(format_sink, &format_source, &sel->r); - *__resizer_get_crop(res, fh, sel->which) = sel->r; + *__resizer_get_crop(res, cfg, sel->which) = sel->r; resizer_calc_ratios(res, &sel->r, &format_source, &ratio); dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", @@ -1320,7 +1320,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, format_source.width, format_source.height); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = format_source; return 0; } @@ -1331,7 +1331,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, */ spin_lock_irqsave(&res->lock, flags); - *__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) = + *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) = format_source; res->ratio = ratio; @@ -1368,13 +1368,13 @@ static unsigned int resizer_max_in_width(struct isp_res_device *res) /* * resizer_try_format - Handle try format by pad subdev method * @res : ISP resizer device - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @pad : pad num * @fmt : pointer to v4l2 format structure * @which : wanted subdev format */ static void resizer_try_format(struct isp_res_device *res, - struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -1395,10 +1395,10 @@ static void resizer_try_format(struct isp_res_device *res, break; case RESZ_PAD_SOURCE: - format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); + format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, which); fmt->code = format->code; - crop = *__resizer_get_crop(res, fh, which); + crop = *__resizer_get_crop(res, cfg, which); resizer_calc_ratios(res, &crop, fmt, &ratio); break; } @@ -1410,12 +1410,12 @@ static void resizer_try_format(struct isp_res_device *res, /* * resizer_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1430,8 +1430,8 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - format = __resizer_get_format(res, fh, RESZ_PAD_SINK, - V4L2_SUBDEV_FORMAT_TRY); + format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, + code->which); code->code = format->code; } @@ -1439,7 +1439,7 @@ static int resizer_enum_mbus_code(struct v4l2_subdev *sd, } static int resizer_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_res_device *res = v4l2_get_subdevdata(sd); @@ -1451,7 +1451,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = 1; format.height = 1; - resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(res, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -1461,7 +1461,7 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, format.code = fse->code; format.width = -1; format.height = -1; - resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + resizer_try_format(res, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -1471,17 +1471,17 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd, /* * resizer_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -1492,37 +1492,37 @@ static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* * resizer_set_format - Handle set format by pads subdev method * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt : pointer to v4l2 subdev format structure * return -EINVAL or zero on success */ -static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + format = __resizer_get_format(res, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); + resizer_try_format(res, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; if (fmt->pad == RESZ_PAD_SINK) { /* reset crop rectangle */ - crop = __resizer_get_crop(res, fh, fmt->which); + crop = __resizer_get_crop(res, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; /* Propagate the format from sink to source */ - format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + format = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE, fmt->which); *format = fmt->format; - resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, + resizer_try_format(res, cfg, RESZ_PAD_SOURCE, format, fmt->which); } @@ -1573,7 +1573,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd, format.format.code = MEDIA_BUS_FMT_YUYV8_1X16; format.format.width = 4096; format.format.height = 4096; - resizer_set_format(sd, fh, &format); + resizer_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index a94e8340508f..20434e83e801 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -21,7 +21,7 @@ #include "isp.h" -#define ISP_STAT_USES_DMAENGINE(stat) ((stat)->dma_ch >= 0) +#define ISP_STAT_USES_DMAENGINE(stat) ((stat)->dma_ch != NULL) /* * MAGIC_SIZE must always be the greatest common divisor of diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index b32b29677e2c..b79380d83fcf 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -20,7 +20,6 @@ #include <linux/types.h> #include <linux/omap3isp.h> -#include <linux/omap-dma.h> #include <media/v4l2-event.h> #include "isp.h" @@ -33,6 +32,7 @@ #define STAT_NO_BUF 1 /* An error has occurred */ #define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */ +struct dma_chan; struct ispstat; struct ispstat_buffer { @@ -96,7 +96,6 @@ struct ispstat { u8 inc_config; atomic_t buf_err; enum ispstat_state_t state; /* enabling/disabling state */ - struct omap_dma_channel_params dma_config; struct isp_device *isp; void *priv; /* pointer to priv config struct */ void *recover_priv; /* pointer to recover priv configuration */ @@ -110,7 +109,7 @@ struct ispstat { u32 frame_number; u32 buf_size; u32 buf_alloc_size; - int dma_ch; + struct dma_chan *dma_ch; unsigned long event_type; struct ispstat_buffer *buf; struct ispstat_buffer *active_buf; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 3fe9047ef466..d285af18df7f 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -452,7 +452,6 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) enum isp_pipeline_state state; struct isp_buffer *buf; unsigned long flags; - struct timespec ts; spin_lock_irqsave(&video->irqlock, flags); if (WARN_ON(list_empty(&video->dmaqueue))) { @@ -465,9 +464,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) list_del(&buf->irqlist); spin_unlock_irqrestore(&video->irqlock, flags); - ktime_get_ts(&ts); - buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec; - buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); /* Do frame number propagation only if this is the output video node. * Frame number either comes from the CSI receivers or it gets @@ -524,7 +521,6 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) buf = list_first_entry(&video->dmaqueue, struct isp_buffer, irqlist); - buf->vb.state = VB2_BUF_STATE_ACTIVE; spin_unlock_irqrestore(&video->irqlock, flags); @@ -1022,7 +1018,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->entities = 0; - if (video->isp->pdata->set_constraints) + if (video->isp->pdata && video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, true); pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; @@ -1104,7 +1100,7 @@ err_set_stream: err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: - if (video->isp->pdata->set_constraints) + if (video->isp->pdata && video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, false); /* The DMA queue must be emptied here, otherwise CCDC interrupts that * will get triggered the next time the CCDC is powered up will try to @@ -1165,7 +1161,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = NULL; video->error = false; - if (video->isp->pdata->set_constraints) + if (video->isp->pdata && video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, false); media_entity_pipeline_stop(&video->video.entity); @@ -1326,14 +1322,8 @@ static unsigned int isp_video_poll(struct file *file, poll_table *wait) static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) { struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); - struct isp_video *video = video_drvdata(file); - int ret; - - mutex_lock(&video->queue_lock); - ret = vb2_mmap(&vfh->queue, vma); - mutex_unlock(&video->queue_lock); - return ret; + return vb2_mmap(&vfh->queue, vma); } static struct v4l2_file_operations isp_video_fops = { diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 54479d60cc0d..f6a61b9ceff4 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1219,7 +1219,7 @@ static const u32 camif_mbus_formats[] = { */ static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(camif_mbus_formats)) @@ -1230,14 +1230,14 @@ static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, } static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf = &fmt->format; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); fmt->format = *mf; return 0; } @@ -1297,7 +1297,7 @@ static void __camif_subdev_try_format(struct camif_dev *camif, } static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1325,7 +1325,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, __camif_subdev_try_format(camif, mf, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; mutex_unlock(&camif->lock); return 0; @@ -1364,7 +1364,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, } static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1377,7 +1377,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); return 0; } @@ -1451,7 +1451,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) } static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct camif_dev *camif = v4l2_get_subdevdata(sd); @@ -1465,7 +1465,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, __camif_try_crop(camif, &sel->r); if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; } else { unsigned long flags; unsigned int i; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 12f7452edce3..bfbf1575677c 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -621,6 +621,7 @@ static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; return ctx->subsampling; case SJPEG_EXYNOS3250: + case SJPEG_EXYNOS5420: if (ctx->subsampling > 3) return V4L2_JPEG_CHROMA_SUBSAMPLING_411; return exynos3250_decoded_subsampling[ctx->subsampling]; @@ -1142,13 +1143,13 @@ static void jpeg_bound_align_image(struct s5p_jpeg_ctx *ctx, w_step = 1 << walign; h_step = 1 << halign; - if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) { + if (ctx->jpeg->variant->hw3250_compat) { /* * Rightmost and bottommost pixels are cropped by the - * Exynos3250 JPEG IP for RGB formats, for the specific - * width and height values respectively. This assignment - * will result in v4l_bound_align_image returning dimensions - * reduced by 1 for the aforementioned cases. + * Exynos3250/compatible JPEG IP for RGB formats, for the + * specific width and height values respectively. This + * assignment will result in v4l_bound_align_image returning + * dimensions reduced by 1 for the aforementioned cases. */ if (w_step == 4 && ((width & 3) == 1)) { wmax = width; @@ -1384,12 +1385,12 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) /* * Prevent downscaling to YUV420 format by more than 2 - * for Exynos3250 SoC as it produces broken raw image + * for Exynos3250/compatible SoC as it produces broken raw image * in such cases. */ if (ct->mode == S5P_JPEG_DECODE && f_type == FMT_TYPE_CAPTURE && - ct->jpeg->variant->version == SJPEG_EXYNOS3250 && + ct->jpeg->variant->hw3250_compat && pix->pixelformat == V4L2_PIX_FMT_YUV420 && ct->scale_factor > 2) { scale_rect.width = ct->out_q.w / 2; @@ -1569,12 +1570,12 @@ static int s5p_jpeg_s_selection(struct file *file, void *fh, if (s->target == V4L2_SEL_TGT_COMPOSE) { if (ctx->mode != S5P_JPEG_DECODE) return -EINVAL; - if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) + if (ctx->jpeg->variant->hw3250_compat) ret = exynos3250_jpeg_try_downscale(ctx, rect); } else if (s->target == V4L2_SEL_TGT_CROP) { if (ctx->mode != S5P_JPEG_ENCODE) return -EINVAL; - if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) + if (ctx->jpeg->variant->hw3250_compat) ret = exynos3250_jpeg_try_crop(ctx, rect); } @@ -1604,8 +1605,9 @@ static int s5p_jpeg_adjust_subs_ctrl(struct s5p_jpeg_ctx *ctx, int *ctrl_val) case SJPEG_S5P: return 0; case SJPEG_EXYNOS3250: + case SJPEG_EXYNOS5420: /* - * The exynos3250 device can produce JPEG image only + * The exynos3250/compatible device can produce JPEG image only * of 4:4:4 subsampling when given RGB32 source image. */ if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) @@ -1624,7 +1626,7 @@ static int s5p_jpeg_adjust_subs_ctrl(struct s5p_jpeg_ctx *ctx, int *ctrl_val) } /* - * The exynos4x12 and exynos3250 devices require resulting + * The exynos4x12 and exynos3250/compatible devices require resulting * jpeg subsampling not to be lower than the input raw image * subsampling. */ @@ -1842,9 +1844,12 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; struct vb2_buffer *vb; - struct s5p_jpeg_addr jpeg_addr; + struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size, padding_bytes = 0; + jpeg_addr.cb = 0; + jpeg_addr.cr = 0; + pix_size = ctx->cap_q.w * ctx->cap_q.h; if (ctx->mode == S5P_JPEG_ENCODE) { @@ -1943,7 +1948,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; struct vb2_buffer *vb; - struct s5p_jpeg_addr jpeg_addr; + struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size; pix_size = ctx->cap_q.w * ctx->cap_q.h; @@ -2017,6 +2022,16 @@ static void exynos3250_jpeg_device_run(void *priv) exynos3250_jpeg_qtbl(jpeg->regs, 2, 1); exynos3250_jpeg_qtbl(jpeg->regs, 3, 1); + /* + * Some SoCs require setting Huffman tables before each run + */ + if (jpeg->variant->htbl_reinit) { + s5p_jpeg_set_hdctbl(jpeg->regs); + s5p_jpeg_set_hdctblg(jpeg->regs); + s5p_jpeg_set_hactbl(jpeg->regs); + s5p_jpeg_set_hactblg(jpeg->regs); + } + /* Y, Cb, Cr use Huffman table 0 */ exynos3250_jpeg_htbl_ac(jpeg->regs, 1); exynos3250_jpeg_htbl_dc(jpeg->regs, 1); @@ -2660,13 +2675,12 @@ static int s5p_jpeg_runtime_resume(struct device *dev) /* * JPEG IP allows storing two Huffman tables for each component. * We fill table 0 for each component and do this here only - * for S5PC210 and Exynos3250 SoCs. Exynos4x12 SoC requires - * programming its Huffman tables each time the encoding process - * is initialized, and thus it is accomplished in the device_run - * callback of m2m_ops. + * for S5PC210 and Exynos3250 SoCs. Exynos4x12 and Exynos542x SoC + * require programming their Huffman tables each time the encoding + * process is initialized, and thus it is accomplished in the + * device_run callback of m2m_ops. */ - if (jpeg->variant->version == SJPEG_S5P || - jpeg->variant->version == SJPEG_EXYNOS3250) { + if (!jpeg->variant->htbl_reinit) { s5p_jpeg_set_hdctbl(jpeg->regs); s5p_jpeg_set_hdctblg(jpeg->regs); s5p_jpeg_set_hactbl(jpeg->regs); @@ -2714,6 +2728,7 @@ static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { .jpeg_irq = exynos3250_jpeg_irq, .m2m_ops = &exynos3250_jpeg_m2m_ops, .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, + .hw3250_compat = 1, }; static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { @@ -2721,6 +2736,16 @@ static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { .jpeg_irq = exynos4_jpeg_irq, .m2m_ops = &exynos4_jpeg_m2m_ops, .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, + .htbl_reinit = 1, +}; + +static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = { + .version = SJPEG_EXYNOS5420, + .jpeg_irq = exynos3250_jpeg_irq, /* intentionally 3250 */ + .m2m_ops = &exynos3250_jpeg_m2m_ops, /* intentionally 3250 */ + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, /* intentionally 3250 */ + .hw3250_compat = 1, + .htbl_reinit = 1, }; static const struct of_device_id samsung_jpeg_match[] = { @@ -2736,6 +2761,9 @@ static const struct of_device_id samsung_jpeg_match[] = { }, { .compatible = "samsung,exynos4212-jpeg", .data = &exynos4_jpeg_drvdata, + }, { + .compatible = "samsung,exynos5420-jpeg", + .data = &exynos5420_jpeg_drvdata, }, {}, }; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 764b32de326b..7d9a9ed19cea 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -67,10 +67,12 @@ #define SJPEG_SUBSAMPLING_420 0x22 /* Version numbers */ - -#define SJPEG_S5P 1 -#define SJPEG_EXYNOS3250 2 -#define SJPEG_EXYNOS4 3 +enum sjpeg_version { + SJPEG_S5P, + SJPEG_EXYNOS3250, + SJPEG_EXYNOS4, + SJPEG_EXYNOS5420, +}; enum exynos4_jpeg_result { OK_ENC_OR_DEC, @@ -130,6 +132,8 @@ struct s5p_jpeg { struct s5p_jpeg_variant { unsigned int version; unsigned int fmt_ver_flag; + unsigned int hw3250_compat:1; + unsigned int htbl_reinit:1; struct v4l2_m2m_ops *m2m_ops; irqreturn_t (*jpeg_irq)(int irq, void *priv); }; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c index e8c2cad93962..0974b9a7a584 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c @@ -20,7 +20,7 @@ void exynos3250_jpeg_reset(void __iomem *regs) { - u32 reg = 0; + u32 reg = 1; int count = 1000; writel(1, regs + EXYNOS3250_SW_RESET); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index e3b8e67e005f..b5f20e722b63 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -51,18 +51,6 @@ void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) writel(reg, regs + S5P_JPGCMOD); } -void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGCMOD); - if (y16) - reg |= S5P_MODE_Y16; - else - reg &= ~S5P_MODE_Y16_MASK; - writel(reg, regs + S5P_JPGCMOD); -} - void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode) { unsigned long reg, m; @@ -208,26 +196,6 @@ void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) writel(reg, regs + S5P_JPGINTSE); } -void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg |= S5P_TIMER_INT_EN; - reg &= ~S5P_TIMER_INIT_MASK; - reg |= val & S5P_TIMER_INIT_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - -void s5p_jpeg_timer_disable(void __iomem *regs) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg &= ~S5P_TIMER_INT_EN_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - int s5p_jpeg_timer_stat(void __iomem *regs) { return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h index c11ebe86b9c9..f208fa3ed738 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h @@ -29,7 +29,6 @@ void s5p_jpeg_reset(void __iomem *regs); void s5p_jpeg_poweron(void __iomem *regs); void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode); -void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16); void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode); void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs); @@ -42,8 +41,6 @@ void s5p_jpeg_x(void __iomem *regs, unsigned int x); void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable); void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable); void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl); -void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val); -void s5p_jpeg_timer_disable(void __iomem *regs); int s5p_jpeg_timer_stat(void __iomem *regs); void s5p_jpeg_clear_timer_stat(void __iomem *regs); void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8e44a59d8ec2..8333fbc2fe96 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -833,6 +833,7 @@ static int s5p_mfc_open(struct file *file) q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; q->io_modes = VB2_MMAP; q->drv_priv = &ctx->fh; + q->lock = &dev->mfc_mutex; if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); @@ -843,6 +844,13 @@ static int s5p_mfc_open(struct file *file) ret = -ENOENT; goto err_queue_init; } + /* One way to indicate end-of-stream for MFC is to set the + * bytesused == 0. However by default videobuf2 handles bytesused + * equal to 0 as a special case and changes its value to the size + * of the buffer. Set the allow_zero_bytesused flag so that videobuf2 + * will keep the value of bytesused intact. + */ + q->allow_zero_bytesused = 1; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 15f7663dd9f5..24262bbb1a35 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -29,7 +29,7 @@ /* Offset base used to differentiate between CAPTURE and OUTPUT * while mmaping */ -#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2) +#define DST_QUEUE_OFF_BASE (1 << 30) #define MFC_BANK1_ALLOC_CTX 0 #define MFC_BANK2_ALLOC_CTX 1 diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index de2b8c69daa5..22dfb3effda8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -302,7 +302,7 @@ struct s5p_mfc_hw_ops { void (*write_info)(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs); unsigned int (*read_info)(struct s5p_mfc_ctx *ctx, - unsigned int ofs); + unsigned long ofs); int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev); int (*get_dec_y_adr)(struct s5p_mfc_dev *dev); int (*get_dspl_status)(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 0c4fcf2dfd09..b09bcd140491 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -263,15 +263,15 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { - writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs)); + writel(data, (void *)(ctx->shm.virt + ofs)); wmb(); } static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, - unsigned int ofs) + unsigned long ofs) { rmb(); - return readl((volatile void __iomem *)(ctx->shm.virt + ofs)); + return readl((void *)(ctx->shm.virt + ofs)); } static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index d826c58b5d53..cefad184fe96 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1852,17 +1852,17 @@ static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { s5p_mfc_clock_on(); - writel(data, (volatile void __iomem *)((unsigned long)ofs)); + writel(data, (void *)((unsigned long)ofs)); s5p_mfc_clock_off(); } static unsigned int -s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) +s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned long ofs) { int ret; s5p_mfc_clock_on(); - ret = readl((volatile void __iomem *)((unsigned long)ofs)); + ret = readl((void *)ofs); s5p_mfc_clock_off(); return ret; diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig index 5a1835dd65e8..697aaed42486 100644 --- a/drivers/media/platform/s5p-tv/Kconfig +++ b/drivers/media/platform/s5p-tv/Kconfig @@ -20,6 +20,7 @@ if VIDEO_SAMSUNG_S5P_TV config VIDEO_SAMSUNG_S5P_HDMI tristate "Samsung HDMI Driver" depends on VIDEO_V4L2 + depends on I2C depends on VIDEO_SAMSUNG_S5P_TV select VIDEO_SAMSUNG_S5P_HDMIPHY help diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 72d4f2e1efc0..751f3b618337 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -287,7 +287,7 @@ static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, u32 bl_width = divup(width, blk->width); u32 bl_height = divup(height, blk->height); u32 sizeimage = bl_width * bl_height * blk->size; - u16 bytesperline = bl_width * blk->size / blk->height; + u32 bytesperline = bl_width * blk->size / blk->height; plane->sizeimage += sizeimage; plane->bytesperline = max(plane->bytesperline, bytesperline); diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index a901b6248557..2554f3719b9e 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1158,6 +1158,7 @@ static int sh_veu_probe(struct platform_device *pdev) } *vdev = sh_veu_videodev; + vdev->v4l2_dev = &veu->v4l2_dev; spin_lock_init(&veu->lock); mutex_init(&veu->fop_lock); vdev->lock = &veu->fop_lock; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 261f1195b49f..dde1ccc730be 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -62,7 +62,7 @@ enum sh_vou_status { struct sh_vou_device { struct v4l2_device v4l2_dev; - struct video_device *vdev; + struct video_device vdev; atomic_t use_count; struct sh_vou_pdata *pdata; spinlock_t lock; @@ -890,7 +890,7 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); - if (std_id & ~vou_dev->vdev->tvnorms) + if (std_id & ~vou_dev->vdev.tvnorms) return -EINVAL; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, @@ -1168,10 +1168,10 @@ static int sh_vou_open(struct file *file) dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - file->private_data = vou_file; - - if (mutex_lock_interruptible(&vou_dev->fop_lock)) + if (mutex_lock_interruptible(&vou_dev->fop_lock)) { + kfree(vou_file); return -ERESTARTSYS; + } if (atomic_inc_return(&vou_dev->use_count) == 1) { int ret; /* First open */ @@ -1183,6 +1183,7 @@ static int sh_vou_open(struct file *file) pm_runtime_put(vou_dev->v4l2_dev.dev); vou_dev->status = SH_VOU_IDLE; mutex_unlock(&vou_dev->fop_lock); + kfree(vou_file); return ret; } } @@ -1192,9 +1193,11 @@ static int sh_vou_open(struct file *file) V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), - vou_dev->vdev, &vou_dev->fop_lock); + &vou_dev->vdev, &vou_dev->fop_lock); mutex_unlock(&vou_dev->fop_lock); + file->private_data = vou_file; + return 0; } @@ -1358,21 +1361,14 @@ static int sh_vou_probe(struct platform_device *pdev) goto ev4l2devreg; } - /* Allocate memory for video device */ - vdev = video_device_alloc(); - if (vdev == NULL) { - ret = -ENOMEM; - goto evdevalloc; - } - + vdev = &vou_dev->vdev; *vdev = sh_vou_video_template; if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT) vdev->tvnorms |= V4L2_STD_PAL; vdev->v4l2_dev = &vou_dev->v4l2_dev; - vdev->release = video_device_release; + vdev->release = video_device_release_empty; vdev->lock = &vou_dev->fop_lock; - vou_dev->vdev = vdev; video_set_drvdata(vdev, vou_dev); pm_runtime_enable(&pdev->dev); @@ -1406,9 +1402,7 @@ ei2cnd: ereset: i2c_put_adapter(i2c_adap); ei2cgadap: - video_device_release(vdev); pm_runtime_disable(&pdev->dev); -evdevalloc: v4l2_device_unregister(&vou_dev->v4l2_dev); ev4l2devreg: free_irq(irq, vou_dev); @@ -1435,7 +1429,7 @@ static int sh_vou_remove(struct platform_device *pdev) if (irq > 0) free_irq(irq, vou_dev); pm_runtime_disable(&pdev->dev); - video_unregister_device(vou_dev->vdev); + video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&vou_dev->v4l2_dev); iounmap(vou_dev->base); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 8526bf5c8429..c835beb2a1a8 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -843,6 +843,8 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if (isi->pdata.full_mode) cfg1 |= ISI_CFG1_FULL_MODE; + cfg1 |= ISI_CFG1_THMASK_BEATS_16; + isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); isi_writel(isi, ISI_CFG1, cfg1); diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 279ab9f6ae38..9351f64dee7b 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -977,19 +977,6 @@ static void rcar_vin_remove_device(struct soc_camera_device *icd) icd->devnum); } -/* Called with .host_lock held */ -static int rcar_vin_clock_start(struct soc_camera_host *ici) -{ - /* VIN does not have "mclk" */ - return 0; -} - -/* Called with .host_lock held */ -static void rcar_vin_clock_stop(struct soc_camera_host *ici) -{ - /* VIN does not have "mclk" */ -} - static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs) { int i; @@ -1803,8 +1790,6 @@ static struct soc_camera_host_ops rcar_vin_host_ops = { .owner = THIS_MODULE, .add = rcar_vin_add_device, .remove = rcar_vin_remove_device, - .clock_start = rcar_vin_clock_start, - .clock_stop = rcar_vin_clock_stop, .get_formats = rcar_vin_get_formats, .put_formats = rcar_vin_put_formats, .get_crop = rcar_vin_get_crop, diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index c4e7aa0ee7e1..cd93241eb497 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -380,7 +380,6 @@ static int sh_csi2_remove(struct platform_device *pdev) struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev); v4l2_async_unregister_subdev(&priv->subdev); - v4l2_device_unregister_subdev(subdev); pm_runtime_disable(&pdev->dev); return 0; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index cee7b56f8404..7bfe7665687f 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -177,6 +177,30 @@ static int __soc_camera_power_off(struct soc_camera_device *icd) return 0; } +static int soc_camera_clock_start(struct soc_camera_host *ici) +{ + int ret; + + if (!ici->ops->clock_start) + return 0; + + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + + return ret; +} + +static void soc_camera_clock_stop(struct soc_camera_host *ici) +{ + if (!ici->ops->clock_stop) + return; + + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); +} + const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) { @@ -584,9 +608,7 @@ static int soc_camera_add_device(struct soc_camera_device *icd) return -EBUSY; if (!icd->clk) { - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); + ret = soc_camera_clock_start(ici); if (ret < 0) return ret; } @@ -602,11 +624,8 @@ static int soc_camera_add_device(struct soc_camera_device *icd) return 0; eadd: - if (!icd->clk) { - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); - } + if (!icd->clk) + soc_camera_clock_stop(ici); return ret; } @@ -619,11 +638,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd) if (ici->ops->remove) ici->ops->remove(icd); - if (!icd->clk) { - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); - } + if (!icd->clk) + soc_camera_clock_stop(ici); ici->icd = NULL; } @@ -688,7 +704,8 @@ static int soc_camera_open(struct file *file) /* The camera could have been already on, try to reset */ if (sdesc->subdev_desc.reset) - sdesc->subdev_desc.reset(icd->pdev); + if (icd->control) + sdesc->subdev_desc.reset(icd->control); ret = soc_camera_add_device(icd); if (ret < 0) { @@ -1159,7 +1176,8 @@ static void scan_add_host(struct soc_camera_host *ici) /* The camera could have been already on, try to reset */ if (ssdd->reset) - ssdd->reset(icd->pdev); + if (icd->control) + ssdd->reset(icd->control); icd->parent = ici->v4l2_dev.dev; @@ -1178,7 +1196,6 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk) { struct soc_camera_device *icd = clk->priv; struct soc_camera_host *ici; - int ret; if (!icd || !icd->parent) return -ENODEV; @@ -1192,10 +1209,7 @@ static int soc_camera_clk_enable(struct v4l2_clk *clk) * If a different client is currently being probed, the host will tell * you to go */ - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); - return ret; + return soc_camera_clock_start(ici); } static void soc_camera_clk_disable(struct v4l2_clk *clk) @@ -1208,9 +1222,7 @@ static void soc_camera_clk_disable(struct v4l2_clk *clk) ici = to_soc_camera_host(icd->parent); - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); + soc_camera_clock_stop(ici); module_put(ici->ops->owner); } @@ -1364,7 +1376,7 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd, snprintf(clk_name, sizeof(clk_name), "%d-%04x", shd->i2c_adapter_id, shd->board_info->addr); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; @@ -1445,7 +1457,7 @@ static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, memcpy(&sdesc->subdev_desc, ssdd, sizeof(sdesc->subdev_desc)); if (ssdd->reset) - ssdd->reset(icd->pdev); + ssdd->reset(&client->dev); } icd->control = &client->dev; @@ -1545,7 +1557,7 @@ static int scan_async_group(struct soc_camera_host *ici, snprintf(clk_name, sizeof(clk_name), "%d-%04x", sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; @@ -1650,7 +1662,7 @@ static int soc_of_bind(struct soc_camera_host *ici, snprintf(clk_name, sizeof(clk_name), "of-%s", of_node_full_name(remote)); - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); if (IS_ERR(icd->clk)) { ret = PTR_ERR(icd->clk); goto eclkreg; @@ -1659,13 +1671,15 @@ static int soc_of_bind(struct soc_camera_host *ici, ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); if (!ret) return 0; + + v4l2_clk_unregister(icd->clk); eclkreg: icd->clk = NULL; platform_device_del(sasc->pdev); eaddpdev: platform_device_put(sasc->pdev); eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); + devm_kfree(ici->v4l2_dev.dev, info); dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); return ret; @@ -1694,7 +1708,6 @@ static void scan_of_host(struct soc_camera_host *ici) if (!i) soc_of_bind(ici, epn, ren->parent); - of_node_put(epn); of_node_put(ren); if (i) { @@ -1702,6 +1715,8 @@ static void scan_of_host(struct soc_camera_host *ici) break; } } + + of_node_put(epn); } #else @@ -1750,9 +1765,7 @@ static int soc_camera_probe(struct soc_camera_host *ici, ret = -EINVAL; goto eadd; } else { - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); + ret = soc_camera_clock_start(ici); if (ret < 0) goto eadd; @@ -1792,9 +1805,7 @@ efinish: module_put(control->driver->owner); enodrv: eadddev: - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); + soc_camera_clock_stop(ici); } eadd: if (icd->vdev) { @@ -1888,22 +1899,34 @@ static int default_enum_framesizes(struct soc_camera_device *icd, int ret; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; - __u32 pixfmt = fsize->pixel_format; - struct v4l2_frmsizeenum fsize_mbus = *fsize; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); if (!xlate) return -EINVAL; - /* map xlate-code to pixel_format, sensor only handle xlate-code*/ - fsize_mbus.pixel_format = xlate->code; + fse.code = xlate->code; - ret = v4l2_subdev_call(sd, video, enum_framesizes, &fsize_mbus); + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); if (ret < 0) return ret; - *fsize = fsize_mbus; - fsize->pixel_format = pixfmt; - + if (fse.min_width == fse.max_width && + fse.min_height == fse.max_height) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.min_width; + fsize->discrete.height = fse.min_height; + return 0; + } + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = fse.min_width; + fsize->stepwise.max_width = fse.max_width; + fsize->stepwise.min_height = fse.min_height; + fsize->stepwise.max_height = fse.max_height; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; return 0; } @@ -1920,8 +1943,6 @@ int soc_camera_host_register(struct soc_camera_host *ici) ((!ici->ops->init_videobuf || !ici->ops->reqbufs) && !ici->ops->init_videobuf2) || - !ici->ops->clock_start || - !ici->ops->clock_stop || !ici->ops->poll || !ici->v4l2_dev.dev) return -EINVAL; diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 86989d86abfa..678ed9f353cb 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -1147,12 +1147,23 @@ static int viacam_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *interval) { struct via_camera *cam = priv; + struct v4l2_subdev_frame_interval_enum fie = { + .index = interval->index, + .code = cam->mbus_code, + .width = cam->sensor_format.width, + .height = cam->sensor_format.height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; mutex_lock(&cam->lock); - ret = sensor_call(cam, video, enum_frameintervals, interval); + ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie); mutex_unlock(&cam->lock); - return ret; + if (ret) + return ret; + interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; + interval->discrete = fie.interval; + return 0; } diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index d9d844aab39b..4d6b4cc57c57 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -142,7 +142,7 @@ static struct vim2m_fmt *find_format(struct v4l2_format *f) struct vim2m_dev { struct v4l2_device v4l2_dev; - struct video_device *vfd; + struct video_device vfd; atomic_t num_inst; struct mutex dev_mutex; @@ -968,7 +968,7 @@ static struct video_device vim2m_videodev = { .fops = &vim2m_fops, .ioctl_ops = &vim2m_ioctl_ops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, }; static struct v4l2_m2m_ops m2m_ops = { @@ -996,26 +996,19 @@ static int vim2m_probe(struct platform_device *pdev) atomic_set(&dev->num_inst, 0); mutex_init(&dev->dev_mutex); - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto unreg_dev; - } - - *vfd = vim2m_videodev; + dev->vfd = vim2m_videodev; + vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; + goto unreg_dev; } video_set_drvdata(vfd, dev); snprintf(vfd->name, sizeof(vfd->name), "%s", vim2m_videodev.name); - dev->vfd = vfd; v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n", vfd->num); @@ -1033,9 +1026,7 @@ static int vim2m_probe(struct platform_device *pdev) err_m2m: v4l2_m2m_release(dev->m2m_dev); - video_unregister_device(dev->vfd); -rel_vdev: - video_device_release(vfd); + video_unregister_device(&dev->vfd); unreg_dev: v4l2_device_unregister(&dev->v4l2_dev); @@ -1049,7 +1040,7 @@ static int vim2m_remove(struct platform_device *pdev) v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME); v4l2_m2m_release(dev->m2m_dev); del_timer_sync(&dev->timer); - video_unregister_device(dev->vfd); + video_unregister_device(&dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); return 0; diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index a7e033a5d291..d33f16495dbc 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -26,6 +26,7 @@ #include <linux/vmalloc.h> #include <linux/font.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/videodev2.h> #include <linux/v4l2-dv-timings.h> #include <media/videobuf2-vmalloc.h> @@ -618,7 +619,23 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { Initialization and module stuff ------------------------------------------------------------------*/ -static int __init vivid_create_instance(int inst) +static void vivid_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev); + + vivid_free_controls(dev); + v4l2_device_unregister(&dev->v4l2_dev); + vfree(dev->scaled_line); + vfree(dev->blended_line); + vfree(dev->edid); + vfree(dev->bitmap_cap); + vfree(dev->bitmap_out); + tpg_free(&dev->tpg); + kfree(dev->query_dv_timings_qmenu); + kfree(dev); +} + +static int vivid_create_instance(struct platform_device *pdev, int inst) { static const struct v4l2_dv_timings def_dv_timings = V4L2_DV_BT_CEA_1280X720P60; @@ -646,9 +663,12 @@ static int __init vivid_create_instance(int inst) /* register v4l2_device */ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s-%03d", VIVID_MODULE_NAME, inst); - ret = v4l2_device_register(NULL, &dev->v4l2_dev); - if (ret) - goto free_dev; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + kfree(dev); + return ret; + } + dev->v4l2_dev.release = vivid_dev_release; /* start detecting feature set */ @@ -1256,15 +1276,8 @@ unreg_dev: video_unregister_device(&dev->vbi_cap_dev); video_unregister_device(&dev->vid_out_dev); video_unregister_device(&dev->vid_cap_dev); - vivid_free_controls(dev); - v4l2_device_unregister(&dev->v4l2_dev); free_dev: - vfree(dev->scaled_line); - vfree(dev->blended_line); - vfree(dev->edid); - tpg_free(&dev->tpg); - kfree(dev->query_dv_timings_qmenu); - kfree(dev); + v4l2_device_put(&dev->v4l2_dev); return ret; } @@ -1274,7 +1287,7 @@ free_dev: will succeed. This is limited to the maximum number of devices that videodev supports, which is equal to VIDEO_NUM_DEVICES. */ -static int __init vivid_init(void) +static int vivid_probe(struct platform_device *pdev) { const struct font_desc *font = find_font("VGA8x16"); int ret = 0, i; @@ -1289,7 +1302,7 @@ static int __init vivid_init(void) n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS); for (i = 0; i < n_devs; i++) { - ret = vivid_create_instance(i); + ret = vivid_create_instance(pdev, i); if (ret) { /* If some instantiations succeeded, keep driver */ if (i) @@ -1309,7 +1322,7 @@ static int __init vivid_init(void) return ret; } -static void __exit vivid_exit(void) +static int vivid_remove(struct platform_device *pdev) { struct vivid_dev *dev; unsigned i; @@ -1358,18 +1371,48 @@ static void __exit vivid_exit(void) unregister_framebuffer(&dev->fb_info); vivid_fb_release_buffers(dev); } - v4l2_device_unregister(&dev->v4l2_dev); - vivid_free_controls(dev); - vfree(dev->scaled_line); - vfree(dev->blended_line); - vfree(dev->edid); - vfree(dev->bitmap_cap); - vfree(dev->bitmap_out); - tpg_free(&dev->tpg); - kfree(dev->query_dv_timings_qmenu); - kfree(dev); + v4l2_device_put(&dev->v4l2_dev); vivid_devs[i] = NULL; } + return 0; +} + +static void vivid_pdev_release(struct device *dev) +{ +} + +static struct platform_device vivid_pdev = { + .name = "vivid", + .dev.release = vivid_pdev_release, +}; + +static struct platform_driver vivid_pdrv = { + .probe = vivid_probe, + .remove = vivid_remove, + .driver = { + .name = "vivid", + }, +}; + +static int __init vivid_init(void) +{ + int ret; + + ret = platform_device_register(&vivid_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&vivid_pdrv); + if (ret) + platform_device_unregister(&vivid_pdev); + + return ret; +} + +static void __exit vivid_exit(void) +{ + platform_driver_unregister(&vivid_pdrv); + platform_device_unregister(&vivid_pdev); } module_init(vivid_init); diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index 4b497df4b6a4..9e15aee9a52e 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -79,12 +79,14 @@ extern unsigned vivid_debug; struct vivid_fmt { const char *name; u32 fourcc; /* v4l2 format id */ - u8 depth; bool is_yuv; bool can_do_overlay; + u8 vdownsampling[TPG_MAX_PLANES]; u32 alpha_mask; u8 planes; - u32 data_offset[2]; + u8 buffers; + u32 data_offset[TPG_MAX_PLANES]; + u32 bit_depth[TPG_MAX_PLANES]; }; extern struct vivid_fmt vivid_formats[]; @@ -332,7 +334,7 @@ struct vivid_dev { u32 ycbcr_enc_out; u32 quantization_out; u32 service_set_out; - u32 bytesperline_out[2]; + unsigned bytesperline_out[TPG_MAX_PLANES]; unsigned tv_field_out; unsigned tv_audio_output; bool vbi_out_have_wss; diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 32a798f2d953..2b9070098b08 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -818,7 +818,7 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) dev->dvi_d_out = ctrl->val == V4L2_DV_TX_MODE_DVI_D; if (!vivid_is_hdmi_out(dev)) break; - if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) { + if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { if (bt->width == 720 && bt->height <= 576) dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; else diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 39a67cfae120..1727f5453f0b 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -229,14 +229,29 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top); } +static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, + unsigned p, unsigned bpl[TPG_MAX_PLANES], unsigned h) +{ + unsigned i; + void *vbuf; + + if (p == 0 || tpg_g_buffers(tpg) > 1) + return vb2_plane_vaddr(&buf->vb, p); + vbuf = vb2_plane_vaddr(&buf->vb, 0); + for (i = 0; i < p; i++) + vbuf += bpl[i] * h / tpg->vdownsampling[i]; + return vbuf; +} + static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) { bool blank = dev->must_blank[vid_cap_buf->vb.v4l2_buf.index]; struct tpg_data *tpg = &dev->tpg; struct vivid_buffer *vid_out_buf = NULL; - unsigned pixsize = tpg_g_twopixelsize(tpg, p) / 2; - unsigned img_width = dev->compose_cap.width; + unsigned vdiv = dev->fmt_out->vdownsampling[p]; + unsigned twopixsize = tpg_g_twopixelsize(tpg, p); + unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width); unsigned img_height = dev->compose_cap.height; unsigned stride_cap = tpg->bytesperline[p]; unsigned stride_out = dev->bytesperline_out[p]; @@ -255,6 +270,7 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, unsigned vid_overlay_fract_part = 0; unsigned vid_overlay_y = 0; unsigned vid_overlay_error = 0; + unsigned vid_cap_left = tpg_hdiv(tpg, p, dev->loop_vid_cap.left); unsigned vid_cap_right; bool quick; @@ -269,25 +285,29 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, vid_cap_buf->vb.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field; - voutbuf = vb2_plane_vaddr(&vid_out_buf->vb, p) + - vid_out_buf->vb.v4l2_planes[p].data_offset; - voutbuf += dev->loop_vid_out.left * pixsize + dev->loop_vid_out.top * stride_out; - vcapbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride_cap; + voutbuf = plane_vaddr(tpg, vid_out_buf, p, + dev->bytesperline_out, dev->fmt_out_rect.height); + if (p < dev->fmt_out->buffers) + voutbuf += vid_out_buf->vb.v4l2_planes[p].data_offset; + voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) + + (dev->loop_vid_out.top / vdiv) * stride_out; + vcapbuf += tpg_hdiv(tpg, p, dev->compose_cap.left) + + (dev->compose_cap.top / vdiv) * stride_cap; if (dev->loop_vid_copy.width == 0 || dev->loop_vid_copy.height == 0) { /* * If there is nothing to copy, then just fill the capture window * with black. */ - for (y = 0; y < hmax; y++, vcapbuf += stride_cap) - memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize); + for (y = 0; y < hmax / vdiv; y++, vcapbuf += stride_cap) + memcpy(vcapbuf, tpg->black_line[p], img_width); return 0; } if (dev->overlay_out_enabled && dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) { vosdbuf = dev->video_vbase; - vosdbuf += dev->loop_fb_copy.left * pixsize + + vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 + dev->loop_fb_copy.top * stride_osd; vid_overlay_int_part = dev->loop_vid_overlay.height / dev->loop_vid_overlay_cap.height; @@ -295,12 +315,12 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, dev->loop_vid_overlay_cap.height; } - vid_cap_right = dev->loop_vid_cap.left + dev->loop_vid_cap.width; + vid_cap_right = tpg_hdiv(tpg, p, dev->loop_vid_cap.left + dev->loop_vid_cap.width); /* quick is true if no video scaling is needed */ quick = dev->loop_vid_out.width == dev->loop_vid_cap.width; dev->cur_scaled_line = dev->loop_vid_out.height; - for (y = 0; y < hmax; y++, vcapbuf += stride_cap) { + for (y = 0; y < hmax; y += vdiv, vcapbuf += stride_cap) { /* osdline is true if this line requires overlay blending */ bool osdline = vosdbuf && y >= dev->loop_vid_overlay_cap.top && y < dev->loop_vid_overlay_cap.top + dev->loop_vid_overlay_cap.height; @@ -311,34 +331,34 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, */ if (y < dev->loop_vid_cap.top || y >= dev->loop_vid_cap.top + dev->loop_vid_cap.height) { - memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize); + memcpy(vcapbuf, tpg->black_line[p], img_width); continue; } /* fill the left border with black */ if (dev->loop_vid_cap.left) - memcpy(vcapbuf, tpg->black_line[p], dev->loop_vid_cap.left * pixsize); + memcpy(vcapbuf, tpg->black_line[p], vid_cap_left); /* fill the right border with black */ if (vid_cap_right < img_width) - memcpy(vcapbuf + vid_cap_right * pixsize, - tpg->black_line[p], (img_width - vid_cap_right) * pixsize); + memcpy(vcapbuf + vid_cap_right, tpg->black_line[p], + img_width - vid_cap_right); if (quick && !osdline) { - memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, + memcpy(vcapbuf + vid_cap_left, voutbuf + vid_out_y * stride_out, - dev->loop_vid_cap.width * pixsize); + tpg_hdiv(tpg, p, dev->loop_vid_cap.width)); goto update_vid_out_y; } if (dev->cur_scaled_line == vid_out_y) { - memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, - dev->scaled_line, - dev->loop_vid_cap.width * pixsize); + memcpy(vcapbuf + vid_cap_left, dev->scaled_line, + tpg_hdiv(tpg, p, dev->loop_vid_cap.width)); goto update_vid_out_y; } if (!osdline) { scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line, - dev->loop_vid_out.width, dev->loop_vid_cap.width, + tpg_hdiv(tpg, p, dev->loop_vid_out.width), + tpg_hdiv(tpg, p, dev->loop_vid_cap.width), tpg_g_twopixelsize(tpg, p)); } else { /* @@ -346,7 +366,8 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, * loop_vid_overlay rectangle. */ unsigned offset = - (dev->loop_vid_overlay.left - dev->loop_vid_copy.left) * pixsize; + ((dev->loop_vid_overlay.left - dev->loop_vid_copy.left) * + twopixsize) / 2; u8 *osd = vosdbuf + vid_overlay_y * stride_osd; scale_line(voutbuf + vid_out_y * stride_out, dev->blended_line, @@ -356,18 +377,17 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, blend_line(dev, vid_overlay_y + dev->loop_vid_overlay.top, dev->loop_vid_overlay.left, dev->blended_line + offset, osd, - dev->loop_vid_overlay.width, pixsize); + dev->loop_vid_overlay.width, twopixsize / 2); else memcpy(dev->blended_line + offset, - osd, dev->loop_vid_overlay.width * pixsize); + osd, (dev->loop_vid_overlay.width * twopixsize) / 2); scale_line(dev->blended_line, dev->scaled_line, dev->loop_vid_copy.width, dev->loop_vid_cap.width, tpg_g_twopixelsize(tpg, p)); } dev->cur_scaled_line = vid_out_y; - memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize, - dev->scaled_line, - dev->loop_vid_cap.width * pixsize); + memcpy(vcapbuf + vid_cap_left, dev->scaled_line, + tpg_hdiv(tpg, p, dev->loop_vid_cap.width)); update_vid_out_y: if (osdline) { @@ -380,21 +400,22 @@ update_vid_out_y: } vid_out_y += vid_out_int_part; vid_out_error += vid_out_fract_part; - if (vid_out_error >= dev->loop_vid_cap.height) { - vid_out_error -= dev->loop_vid_cap.height; + if (vid_out_error >= dev->loop_vid_cap.height / vdiv) { + vid_out_error -= dev->loop_vid_cap.height / vdiv; vid_out_y++; } } if (!blank) return 0; - for (; y < img_height; y++, vcapbuf += stride_cap) - memcpy(vcapbuf, tpg->contrast_line[p], img_width * pixsize); + for (; y < img_height; y += vdiv, vcapbuf += stride_cap) + memcpy(vcapbuf, tpg->contrast_line[p], img_width); return 0; } static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) { + struct tpg_data *tpg = &dev->tpg; unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; unsigned line_height = 16 / factor; bool is_tv = vivid_is_sdtv_cap(dev); @@ -427,7 +448,7 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) * standards. */ buf->vb.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; /* * The sequence counter counts frames, not fields. So divide * by two. @@ -436,27 +457,29 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) } else { buf->vb.v4l2_buf.field = dev->field_cap; } - tpg_s_field(&dev->tpg, buf->vb.v4l2_buf.field); - tpg_s_perc_fill_blank(&dev->tpg, dev->must_blank[buf->vb.v4l2_buf.index]); + tpg_s_field(tpg, buf->vb.v4l2_buf.field, + dev->field_cap == V4L2_FIELD_ALTERNATE); + tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.v4l2_buf.index]); vivid_precalc_copy_rects(dev); - for (p = 0; p < tpg_g_planes(&dev->tpg); p++) { - void *vbuf = vb2_plane_vaddr(&buf->vb, p); + for (p = 0; p < tpg_g_planes(tpg); p++) { + void *vbuf = plane_vaddr(tpg, buf, p, + tpg->bytesperline, tpg->buf_height); /* * The first plane of a multiplanar format has a non-zero * data_offset. This helps testing whether the application * correctly supports non-zero data offsets. */ - if (dev->fmt_cap->data_offset[p]) { + if (p < tpg_g_buffers(tpg) && dev->fmt_cap->data_offset[p]) { memset(vbuf, dev->fmt_cap->data_offset[p] & 0xff, dev->fmt_cap->data_offset[p]); vbuf += dev->fmt_cap->data_offset[p]; } - tpg_calc_text_basep(&dev->tpg, basep, p, vbuf); + tpg_calc_text_basep(tpg, basep, p, vbuf); if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) - tpg_fillbuffer(&dev->tpg, vivid_get_std_cap(dev), p, vbuf); + tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf); } dev->must_blank[buf->vb.v4l2_buf.index] = false; @@ -475,12 +498,12 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) (dev->field_cap == V4L2_FIELD_ALTERNATE) ? (buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ? " top" : " bottom") : ""); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); } if (dev->osd_mode == 0) { snprintf(str, sizeof(str), " %dx%d, input %d ", dev->src_rect.width, dev->src_rect.height, dev->input); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); gain = v4l2_ctrl_g_ctrl(dev->gain); mutex_lock(dev->ctrl_hdl_user_vid.lock); @@ -490,38 +513,38 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) dev->contrast->cur.val, dev->saturation->cur.val, dev->hue->cur.val); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), " autogain %d, gain %3d, alpha 0x%02x ", dev->autogain->cur.val, gain, dev->alpha->cur.val); mutex_unlock(dev->ctrl_hdl_user_vid.lock); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); mutex_lock(dev->ctrl_hdl_user_aud.lock); snprintf(str, sizeof(str), " volume %3d, mute %d ", dev->volume->cur.val, dev->mute->cur.val); mutex_unlock(dev->ctrl_hdl_user_aud.lock); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); mutex_lock(dev->ctrl_hdl_user_gen.lock); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, *dev->int64->p_cur.p_s64, dev->bitmask->cur.val); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->p_cur.p_char); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), " integer_menu %lld, value %d ", dev->int_menu->qmenu_int[dev->int_menu->cur.val], dev->int_menu->cur.val); mutex_unlock(dev->ctrl_hdl_user_gen.lock); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); if (dev->button_pressed) { dev->button_pressed--; snprintf(str, sizeof(str), " button pressed!"); - tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(tpg, basep, line++ * line_height, 16, str); } } @@ -585,6 +608,12 @@ static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf) bool quick = dev->bitmap_cap == NULL && dev->clipcount_cap == 0; int x, y, w, out_x = 0; + /* + * Overlay support is only supported for formats that have a twopixelsize + * that's >= 2. Warn and bail out if that's not the case. + */ + if (WARN_ON(pixsize == 0)) + return; if ((dev->overlay_cap_field == V4L2_FIELD_TOP || dev->overlay_cap_field == V4L2_FIELD_BOTTOM) && dev->overlay_cap_field != buf->vb.v4l2_buf.field) diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c index 4af55f18829f..caf131666e37 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -27,6 +27,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/v4l2-dv-timings.h> +#include <linux/fixp-arith.h> #include "vivid-core.h" #include "vivid-ctrls.h" @@ -423,40 +424,19 @@ int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) return 0; } -#define FIXP_FRAC (1 << 15) -#define FIXP_PI ((int)(FIXP_FRAC * 3.141592653589)) - -/* cos() from cx88 driver: cx88-dsp.c */ -static s32 fixp_cos(unsigned int x) -{ - u32 t2, t4, t6, t8; - u16 period = x / FIXP_PI; - - if (period % 2) - return -fixp_cos(x - FIXP_PI); - x = x % FIXP_PI; - if (x > FIXP_PI/2) - return -fixp_cos(FIXP_PI/2 - (x % (FIXP_PI/2))); - /* Now x is between 0 and FIXP_PI/2. - * To calculate cos(x) we use it's Taylor polinom. */ - t2 = x*x/FIXP_FRAC/2; - t4 = t2*x/FIXP_FRAC*x/FIXP_FRAC/3/4; - t6 = t4*x/FIXP_FRAC*x/FIXP_FRAC/5/6; - t8 = t6*x/FIXP_FRAC*x/FIXP_FRAC/7/8; - return FIXP_FRAC-t2+t4-t6+t8; -} - -static inline s32 fixp_sin(unsigned int x) -{ - return -fixp_cos(x + (FIXP_PI / 2)); -} +#define FIXP_N (15) +#define FIXP_FRAC (1 << FIXP_N) +#define FIXP_2PI ((int)(2 * 3.141592653589 * FIXP_FRAC)) void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) { u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned long i; unsigned long plane_size = vb2_plane_size(&buf->vb, 0); - int fixp_src_phase_step, fixp_i, fixp_q; + s32 src_phase_step; + s32 mod_phase_step; + s32 fixp_i; + s32 fixp_q; /* * TODO: Generated beep tone goes very crackly when sample rate is @@ -466,28 +446,36 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) /* calculate phase step */ #define BEEP_FREQ 1000 /* 1kHz beep */ - fixp_src_phase_step = DIV_ROUND_CLOSEST(2 * FIXP_PI * BEEP_FREQ, + src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ, dev->sdr_adc_freq); for (i = 0; i < plane_size; i += 2) { - dev->sdr_fixp_mod_phase += fixp_cos(dev->sdr_fixp_src_phase); - dev->sdr_fixp_src_phase += fixp_src_phase_step; + mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase, + FIXP_2PI) >> (31 - FIXP_N); + + dev->sdr_fixp_src_phase += src_phase_step; + dev->sdr_fixp_mod_phase += mod_phase_step / 4; /* * Transfer phases to [0 / 2xPI] in order to avoid variable * overflow and make it suitable for cosine implementation * used, which does not support negative angles. */ - while (dev->sdr_fixp_mod_phase < (0 * FIXP_PI)) - dev->sdr_fixp_mod_phase += (2 * FIXP_PI); - while (dev->sdr_fixp_mod_phase > (2 * FIXP_PI)) - dev->sdr_fixp_mod_phase -= (2 * FIXP_PI); + while (dev->sdr_fixp_mod_phase < FIXP_2PI) + dev->sdr_fixp_mod_phase += FIXP_2PI; + while (dev->sdr_fixp_mod_phase > FIXP_2PI) + dev->sdr_fixp_mod_phase -= FIXP_2PI; + + while (dev->sdr_fixp_src_phase > FIXP_2PI) + dev->sdr_fixp_src_phase -= FIXP_2PI; - while (dev->sdr_fixp_src_phase > (2 * FIXP_PI)) - dev->sdr_fixp_src_phase -= (2 * FIXP_PI); + fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); + fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); - fixp_i = fixp_cos(dev->sdr_fixp_mod_phase); - fixp_q = fixp_sin(dev->sdr_fixp_mod_phase); + /* Normalize fraction values represented with 32 bit precision + * to fixed point representation with FIXP_N bits */ + fixp_i >>= (31 - FIXP_N); + fixp_q >>= (31 - FIXP_N); /* convert 'fixp float' to u8 */ /* u8 = X * 127.5f + 127.5f; where X is float [-1.0 / +1.0] */ diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c index 34493f435d5a..cb766eb154e7 100644 --- a/drivers/media/platform/vivid/vivid-tpg.c +++ b/drivers/media/platform/vivid/vivid-tpg.c @@ -35,7 +35,10 @@ const char * const tpg_pattern_strings[] = { "100% Green", "100% Blue", "16x16 Checkers", + "2x2 Checkers", "1x1 Checkers", + "2x2 Red/Green Checkers", + "1x1 Red/Green Checkers", "Alternating Hor Lines", "Alternating Vert Lines", "One Pixel Wide Cross", @@ -120,15 +123,20 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w) tpg->max_line_width = max_w; for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) { for (plane = 0; plane < TPG_MAX_PLANES; plane++) { - unsigned pixelsz = plane ? 1 : 4; + unsigned pixelsz = plane ? 2 : 4; tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz); if (!tpg->lines[pat][plane]) return -ENOMEM; + if (plane == 0) + continue; + tpg->downsampled_lines[pat][plane] = vzalloc(max_w * 2 * pixelsz); + if (!tpg->downsampled_lines[pat][plane]) + return -ENOMEM; } } for (plane = 0; plane < TPG_MAX_PLANES; plane++) { - unsigned pixelsz = plane ? 1 : 4; + unsigned pixelsz = plane ? 2 : 4; tpg->contrast_line[plane] = vzalloc(max_w * pixelsz); if (!tpg->contrast_line[plane]) @@ -152,6 +160,10 @@ void tpg_free(struct tpg_data *tpg) for (plane = 0; plane < TPG_MAX_PLANES; plane++) { vfree(tpg->lines[pat][plane]); tpg->lines[pat][plane] = NULL; + if (plane == 0) + continue; + vfree(tpg->downsampled_lines[pat][plane]); + tpg->downsampled_lines[pat][plane] = NULL; } for (plane = 0; plane < TPG_MAX_PLANES; plane++) { vfree(tpg->contrast_line[plane]); @@ -167,14 +179,38 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) { tpg->fourcc = fourcc; tpg->planes = 1; + tpg->buffers = 1; tpg->recalc_colors = true; + tpg->interleaved = false; + tpg->vdownsampling[0] = 1; + tpg->hdownsampling[0] = 1; + tpg->hmask[0] = ~0; + tpg->hmask[1] = ~0; + tpg->hmask[2] = ~0; + switch (fourcc) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + tpg->interleaved = true; + tpg->vdownsampling[1] = 1; + tpg->hdownsampling[1] = 1; + tpg->planes = 2; + /* fall through */ + case V4L2_PIX_FMT_RGB332: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_XRGB444: + case V4L2_PIX_FMT_ARGB444: case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_XRGB555: case V4L2_PIX_FMT_ARGB555: case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_XRGB555X: + case V4L2_PIX_FMT_ARGB555X: + case V4L2_PIX_FMT_BGR666: case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB32: @@ -183,16 +219,72 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_XBGR32: case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_GREY: tpg->is_yuv = false; break; + case V4L2_PIX_FMT_YUV444: + case V4L2_PIX_FMT_YUV555: + case V4L2_PIX_FMT_YUV565: + case V4L2_PIX_FMT_YUV32: + tpg->is_yuv = true; + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + tpg->buffers = 3; + /* fall through */ + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + tpg->vdownsampling[1] = 2; + tpg->vdownsampling[2] = 2; + tpg->hdownsampling[1] = 2; + tpg->hdownsampling[2] = 2; + tpg->planes = 3; + tpg->is_yuv = true; + break; + case V4L2_PIX_FMT_YUV422P: + tpg->vdownsampling[1] = 1; + tpg->vdownsampling[2] = 1; + tpg->hdownsampling[1] = 2; + tpg->hdownsampling[2] = 2; + tpg->planes = 3; + tpg->is_yuv = true; + break; case V4L2_PIX_FMT_NV16M: case V4L2_PIX_FMT_NV61M: + tpg->buffers = 2; + /* fall through */ + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + tpg->vdownsampling[1] = 1; + tpg->hdownsampling[1] = 1; + tpg->hmask[1] = ~1; tpg->planes = 2; - /* fall-through */ + tpg->is_yuv = true; + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + tpg->buffers = 2; + /* fall through */ + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + tpg->vdownsampling[1] = 2; + tpg->hdownsampling[1] = 1; + tpg->hmask[1] = ~1; + tpg->planes = 2; + tpg->is_yuv = true; + break; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + tpg->vdownsampling[1] = 1; + tpg->hdownsampling[1] = 1; + tpg->planes = 2; + tpg->is_yuv = true; + break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: + tpg->hmask[0] = ~1; tpg->is_yuv = true; break; default: @@ -200,35 +292,75 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) } switch (fourcc) { + case V4L2_PIX_FMT_RGB332: + tpg->twopixelsize[0] = 2; + break; case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_XRGB444: + case V4L2_PIX_FMT_ARGB444: case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_XRGB555: case V4L2_PIX_FMT_ARGB555: case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_XRGB555X: + case V4L2_PIX_FMT_ARGB555X: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUV444: + case V4L2_PIX_FMT_YUV555: + case V4L2_PIX_FMT_YUV565: tpg->twopixelsize[0] = 2 * 2; break; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: tpg->twopixelsize[0] = 2 * 3; break; + case V4L2_PIX_FMT_BGR666: case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_XRGB32: case V4L2_PIX_FMT_XBGR32: case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_YUV32: tpg->twopixelsize[0] = 2 * 4; break; + case V4L2_PIX_FMT_GREY: + tpg->twopixelsize[0] = 2; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV16M: case V4L2_PIX_FMT_NV61M: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: tpg->twopixelsize[0] = 2; tpg->twopixelsize[1] = 2; break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + tpg->twopixelsize[0] = 2; + tpg->twopixelsize[1] = 2; + tpg->twopixelsize[2] = 2; + break; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + tpg->twopixelsize[0] = 2; + tpg->twopixelsize[1] = 4; + break; } return true; } @@ -267,7 +399,8 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, tpg->compose.width = width; tpg->compose.height = tpg->buf_height; for (p = 0; p < tpg->planes; p++) - tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2; + tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) / + (2 * tpg->hdownsampling[p]); tpg->recalc_square_border = true; } @@ -347,9 +480,9 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) }, }; static const int bt2020[3][3] = { - { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) }, + { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) }, { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) }, + { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) }, }; bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; unsigned y_offset = full ? 0 : 16; @@ -524,10 +657,10 @@ static void precalculate_color(struct tpg_data *tpg, int k) g <<= 4; b <<= 4; } - if (tpg->qual == TPG_QUAL_GRAY) { + if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) { /* Rec. 709 Luma function */ /* (0.2126, 0.7152, 0.0722) * (255 * 256) */ - r = g = b = ((13879 * r + 46688 * g + 4713 * b) >> 16) + (16 << 4); + r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16; } /* @@ -601,9 +734,29 @@ static void precalculate_color(struct tpg_data *tpg, int k) cb = clamp(cb, 16 << 4, 240 << 4); cr = clamp(cr, 16 << 4, 240 << 4); } - tpg->colors[k][0] = clamp(y >> 4, 1, 254); - tpg->colors[k][1] = clamp(cb >> 4, 1, 254); - tpg->colors[k][2] = clamp(cr >> 4, 1, 254); + y = clamp(y >> 4, 1, 254); + cb = clamp(cb >> 4, 1, 254); + cr = clamp(cr >> 4, 1, 254); + switch (tpg->fourcc) { + case V4L2_PIX_FMT_YUV444: + y >>= 4; + cb >>= 4; + cr >>= 4; + break; + case V4L2_PIX_FMT_YUV555: + y >>= 3; + cb >>= 3; + cr >>= 3; + break; + case V4L2_PIX_FMT_YUV565: + y >>= 3; + cb >>= 2; + cr >>= 3; + break; + } + tpg->colors[k][0] = y; + tpg->colors[k][1] = cb; + tpg->colors[k][2] = cr; } else { if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) { r = (r * 219) / 255 + (16 << 4); @@ -611,20 +764,39 @@ static void precalculate_color(struct tpg_data *tpg, int k) b = (b * 219) / 255 + (16 << 4); } switch (tpg->fourcc) { + case V4L2_PIX_FMT_RGB332: + r >>= 9; + g >>= 9; + b >>= 10; + break; case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB565X: r >>= 7; g >>= 6; b >>= 7; break; + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_XRGB444: + case V4L2_PIX_FMT_ARGB444: + r >>= 8; + g >>= 8; + b >>= 8; + break; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_XRGB555: case V4L2_PIX_FMT_ARGB555: case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_XRGB555X: + case V4L2_PIX_FMT_ARGB555X: r >>= 7; g >>= 7; b >>= 7; break; + case V4L2_PIX_FMT_BGR666: + r >>= 6; + g >>= 6; + b >>= 6; + break; default: r >>= 4; g >>= 4; @@ -665,31 +837,120 @@ static void gen_twopix(struct tpg_data *tpg, b_v = tpg->colors[color][2]; /* B or precalculated V */ switch (tpg->fourcc) { + case V4L2_PIX_FMT_GREY: + buf[0][offset] = r_y; + break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV420M: + buf[0][offset] = r_y; + if (odd) { + buf[1][0] = (buf[1][0] + g_u) / 2; + buf[2][0] = (buf[2][0] + b_v) / 2; + buf[1][1] = buf[1][0]; + buf[2][1] = buf[2][0]; + break; + } + buf[1][0] = g_u; + buf[2][0] = b_v; + break; + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YVU420M: + buf[0][offset] = r_y; + if (odd) { + buf[1][0] = (buf[1][0] + b_v) / 2; + buf[2][0] = (buf[2][0] + g_u) / 2; + buf[1][1] = buf[1][0]; + buf[2][1] = buf[2][0]; + break; + } + buf[1][0] = b_v; + buf[2][0] = g_u; + break; + + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV16M: buf[0][offset] = r_y; - buf[1][offset] = odd ? b_v : g_u; + if (odd) { + buf[1][0] = (buf[1][0] + g_u) / 2; + buf[1][1] = (buf[1][1] + b_v) / 2; + break; + } + buf[1][0] = g_u; + buf[1][1] = b_v; break; + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV61M: buf[0][offset] = r_y; - buf[1][offset] = odd ? g_u : b_v; + if (odd) { + buf[1][0] = (buf[1][0] + b_v) / 2; + buf[1][1] = (buf[1][1] + g_u) / 2; + break; + } + buf[1][0] = b_v; + buf[1][1] = g_u; + break; + + case V4L2_PIX_FMT_NV24: + buf[0][offset] = r_y; + buf[1][2 * offset] = g_u; + buf[1][2 * offset + 1] = b_v; + break; + + case V4L2_PIX_FMT_NV42: + buf[0][offset] = r_y; + buf[1][2 * offset] = b_v; + buf[1][2 * offset + 1] = g_u; break; case V4L2_PIX_FMT_YUYV: buf[0][offset] = r_y; - buf[0][offset + 1] = odd ? b_v : g_u; + if (odd) { + buf[0][1] = (buf[0][1] + g_u) / 2; + buf[0][3] = (buf[0][3] + b_v) / 2; + break; + } + buf[0][1] = g_u; + buf[0][3] = b_v; break; case V4L2_PIX_FMT_UYVY: - buf[0][offset] = odd ? b_v : g_u; buf[0][offset + 1] = r_y; + if (odd) { + buf[0][0] = (buf[0][0] + g_u) / 2; + buf[0][2] = (buf[0][2] + b_v) / 2; + break; + } + buf[0][0] = g_u; + buf[0][2] = b_v; break; case V4L2_PIX_FMT_YVYU: buf[0][offset] = r_y; - buf[0][offset + 1] = odd ? g_u : b_v; + if (odd) { + buf[0][1] = (buf[0][1] + b_v) / 2; + buf[0][3] = (buf[0][3] + g_u) / 2; + break; + } + buf[0][1] = b_v; + buf[0][3] = g_u; break; case V4L2_PIX_FMT_VYUY: - buf[0][offset] = odd ? g_u : b_v; buf[0][offset + 1] = r_y; + if (odd) { + buf[0][0] = (buf[0][0] + b_v) / 2; + buf[0][2] = (buf[0][2] + g_u) / 2; + break; + } + buf[0][0] = b_v; + buf[0][2] = g_u; + break; + case V4L2_PIX_FMT_RGB332: + buf[0][offset] = (r_y << 5) | (g_u << 2) | b_v; break; + case V4L2_PIX_FMT_YUV565: case V4L2_PIX_FMT_RGB565: buf[0][offset] = (g_u << 5) | b_v; buf[0][offset + 1] = (r_y << 3) | (g_u >> 3); @@ -698,15 +959,29 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset] = (r_y << 3) | (g_u >> 3); buf[0][offset + 1] = (g_u << 5) | b_v; break; + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_XRGB444: + alpha = 0; + /* fall through */ + case V4L2_PIX_FMT_YUV444: + case V4L2_PIX_FMT_ARGB444: + buf[0][offset] = (g_u << 4) | b_v; + buf[0][offset + 1] = (alpha & 0xf0) | r_y; + break; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_XRGB555: alpha = 0; /* fall through */ + case V4L2_PIX_FMT_YUV555: case V4L2_PIX_FMT_ARGB555: buf[0][offset] = (g_u << 5) | b_v; buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_XRGB555X: + alpha = 0; + /* fall through */ + case V4L2_PIX_FMT_ARGB555X: buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); buf[0][offset + 1] = (g_u << 5) | b_v; break; @@ -720,10 +995,17 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset + 1] = g_u; buf[0][offset + 2] = r_y; break; + case V4L2_PIX_FMT_BGR666: + buf[0][offset] = (b_v << 2) | (g_u >> 4); + buf[0][offset + 1] = (g_u << 4) | (r_y >> 2); + buf[0][offset + 2] = r_y << 6; + buf[0][offset + 3] = 0; + break; case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_XRGB32: alpha = 0; /* fall through */ + case V4L2_PIX_FMT_YUV32: case V4L2_PIX_FMT_ARGB32: buf[0][offset] = alpha; buf[0][offset + 1] = r_y; @@ -740,15 +1022,47 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset + 2] = r_y; buf[0][offset + 3] = alpha; break; + case V4L2_PIX_FMT_SBGGR8: + buf[0][offset] = odd ? g_u : b_v; + buf[1][offset] = odd ? r_y : g_u; + break; + case V4L2_PIX_FMT_SGBRG8: + buf[0][offset] = odd ? b_v : g_u; + buf[1][offset] = odd ? g_u : r_y; + break; + case V4L2_PIX_FMT_SGRBG8: + buf[0][offset] = odd ? r_y : g_u; + buf[1][offset] = odd ? g_u : b_v; + break; + case V4L2_PIX_FMT_SRGGB8: + buf[0][offset] = odd ? g_u : r_y; + buf[1][offset] = odd ? b_v : g_u; + break; + } +} + +unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line) +{ + switch (tpg->fourcc) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + return buf_line & 1; + default: + return 0; } } /* Return how many pattern lines are used by the current pattern. */ -static unsigned tpg_get_pat_lines(struct tpg_data *tpg) +static unsigned tpg_get_pat_lines(const struct tpg_data *tpg) { switch (tpg->pattern) { case TPG_PAT_CHECKERS_16X16: + case TPG_PAT_CHECKERS_2X2: case TPG_PAT_CHECKERS_1X1: + case TPG_PAT_COLOR_CHECKERS_2X2: + case TPG_PAT_COLOR_CHECKERS_1X1: case TPG_PAT_ALTERNATING_HLINES: case TPG_PAT_CROSS_1_PIXEL: case TPG_PAT_CROSS_2_PIXELS: @@ -763,14 +1077,18 @@ static unsigned tpg_get_pat_lines(struct tpg_data *tpg) } /* Which pattern line should be used for the given frame line. */ -static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line) +static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line) { switch (tpg->pattern) { case TPG_PAT_CHECKERS_16X16: return (line >> 4) & 1; case TPG_PAT_CHECKERS_1X1: + case TPG_PAT_COLOR_CHECKERS_1X1: case TPG_PAT_ALTERNATING_HLINES: return line & 1; + case TPG_PAT_CHECKERS_2X2: + case TPG_PAT_COLOR_CHECKERS_2X2: + return (line & 2) >> 1; case TPG_PAT_100_COLORSQUARES: case TPG_PAT_100_HCOLORBAR: return (line * 8) / tpg->src_height; @@ -789,7 +1107,8 @@ static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line) * Which color should be used for the given pattern line and X coordinate. * Note: x is in the range 0 to 2 * tpg->src_width. */ -static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x) +static enum tpg_color tpg_get_color(const struct tpg_data *tpg, + unsigned pat_line, unsigned x) { /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code should be modified */ @@ -836,6 +1155,15 @@ static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, uns case TPG_PAT_CHECKERS_1X1: return ((x & 1) ^ (pat_line & 1)) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; + case TPG_PAT_COLOR_CHECKERS_1X1: + return ((x & 1) ^ (pat_line & 1)) ? + TPG_COLOR_100_RED : TPG_COLOR_100_BLUE; + case TPG_PAT_CHECKERS_2X2: + return (((x >> 1) & 1) ^ (pat_line & 1)) ? + TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; + case TPG_PAT_COLOR_CHECKERS_2X2: + return (((x >> 1) & 1) ^ (pat_line & 1)) ? + TPG_COLOR_100_RED : TPG_COLOR_100_BLUE; case TPG_PAT_ALTERNATING_HLINES: return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; case TPG_PAT_ALTERNATING_VLINES: @@ -948,6 +1276,7 @@ static void tpg_calculate_square_border(struct tpg_data *tpg) static void tpg_precalculate_line(struct tpg_data *tpg) { enum tpg_color contrast; + u8 pix[TPG_MAX_PLANES][8]; unsigned pat; unsigned p; unsigned x; @@ -974,7 +1303,6 @@ static void tpg_precalculate_line(struct tpg_data *tpg) for (x = 0; x < tpg->scaled_width * 2; x += 2) { unsigned real_x = src_x; enum tpg_color color1, color2; - u8 pix[TPG_MAX_PLANES][8]; real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x; color1 = tpg_get_color(tpg, pat, real_x); @@ -1001,39 +1329,53 @@ static void tpg_precalculate_line(struct tpg_data *tpg) gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1); for (p = 0; p < tpg->planes; p++) { unsigned twopixsize = tpg->twopixelsize[p]; - u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2; + unsigned hdiv = tpg->hdownsampling[p]; + u8 *pos = tpg->lines[pat][p] + tpg_hdiv(tpg, p, x); - memcpy(pos, pix[p], twopixsize); + memcpy(pos, pix[p], twopixsize / hdiv); } } } - for (x = 0; x < tpg->scaled_width; x += 2) { - u8 pix[TPG_MAX_PLANES][8]; - gen_twopix(tpg, pix, contrast, 0); - gen_twopix(tpg, pix, contrast, 1); - for (p = 0; p < tpg->planes; p++) { - unsigned twopixsize = tpg->twopixelsize[p]; - u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2; + if (tpg->vdownsampling[tpg->planes - 1] > 1) { + unsigned pat_lines = tpg_get_pat_lines(tpg); - memcpy(pos, pix[p], twopixsize); + for (pat = 0; pat < pat_lines; pat++) { + unsigned next_pat = (pat + 1) % pat_lines; + + for (p = 1; p < tpg->planes; p++) { + unsigned w = tpg_hdiv(tpg, p, tpg->scaled_width * 2); + u8 *pos1 = tpg->lines[pat][p]; + u8 *pos2 = tpg->lines[next_pat][p]; + u8 *dest = tpg->downsampled_lines[pat][p]; + + for (x = 0; x < w; x++, pos1++, pos2++, dest++) + *dest = ((u16)*pos1 + (u16)*pos2) / 2; + } } } - for (x = 0; x < tpg->scaled_width; x += 2) { - u8 pix[TPG_MAX_PLANES][8]; - gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0); - gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1); - for (p = 0; p < tpg->planes; p++) { - unsigned twopixsize = tpg->twopixelsize[p]; - u8 *pos = tpg->black_line[p] + x * twopixsize / 2; + gen_twopix(tpg, pix, contrast, 0); + gen_twopix(tpg, pix, contrast, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->contrast_line[p]; + for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize) + memcpy(pos, pix[p], twopixsize); + } + + gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0); + gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1); + for (p = 0; p < tpg->planes; p++) { + unsigned twopixsize = tpg->twopixelsize[p]; + u8 *pos = tpg->black_line[p]; + + for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize) memcpy(pos, pix[p], twopixsize); - } } - for (x = 0; x < tpg->scaled_width * 2; x += 2) { - u8 pix[TPG_MAX_PLANES][8]; + for (x = 0; x < tpg->scaled_width * 2; x += 2) { gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0); gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1); for (p = 0; p < tpg->planes; p++) { @@ -1043,6 +1385,7 @@ static void tpg_precalculate_line(struct tpg_data *tpg) memcpy(pos, pix[p], twopixsize); } } + gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0); gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1); gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0); @@ -1052,8 +1395,8 @@ static void tpg_precalculate_line(struct tpg_data *tpg) /* need this to do rgb24 rendering */ typedef struct { u16 __; u8 _; } __packed x24; -void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], - int y, int x, char *text) +void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + int y, int x, char *text) { int line; unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; @@ -1083,24 +1426,37 @@ void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], div = 2; for (p = 0; p < tpg->planes; p++) { - /* Print stream time */ + unsigned vdiv = tpg->vdownsampling[p]; + unsigned hdiv = tpg->hdownsampling[p]; + + /* Print text */ #define PRINTSTR(PIXTYPE) do { \ PIXTYPE fg; \ PIXTYPE bg; \ memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \ memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \ \ - for (line = first; line < 16; line += step) { \ + for (line = first; line < 16; line += vdiv * step) { \ int l = tpg->vflip ? 15 - line : line; \ - PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \ - ((y * step + l) / div) * tpg->bytesperline[p] + \ - x * sizeof(PIXTYPE)); \ + PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \ + ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \ + (x / hdiv) * sizeof(PIXTYPE)); \ unsigned s; \ \ for (s = 0; s < len; s++) { \ u8 chr = font8x16[text[s] * 16 + line]; \ \ - if (tpg->hflip) { \ + if (hdiv == 2 && tpg->hflip) { \ + pos[3] = (chr & (0x01 << 6) ? fg : bg); \ + pos[2] = (chr & (0x01 << 4) ? fg : bg); \ + pos[1] = (chr & (0x01 << 2) ? fg : bg); \ + pos[0] = (chr & (0x01 << 0) ? fg : bg); \ + } else if (hdiv == 2) { \ + pos[0] = (chr & (0x01 << 7) ? fg : bg); \ + pos[1] = (chr & (0x01 << 5) ? fg : bg); \ + pos[2] = (chr & (0x01 << 3) ? fg : bg); \ + pos[3] = (chr & (0x01 << 1) ? fg : bg); \ + } else if (tpg->hflip) { \ pos[7] = (chr & (0x01 << 7) ? fg : bg); \ pos[6] = (chr & (0x01 << 6) ? fg : bg); \ pos[5] = (chr & (0x01 << 5) ? fg : bg); \ @@ -1120,7 +1476,7 @@ void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], pos[7] = (chr & (0x01 << 0) ? fg : bg); \ } \ \ - pos += tpg->hflip ? -8 : 8; \ + pos += (tpg->hflip ? -8 : 8) / hdiv; \ } \ } \ } while (0) @@ -1187,7 +1543,7 @@ void tpg_update_mv_step(struct tpg_data *tpg) } /* Map the line number relative to the crop rectangle to a frame line number */ -static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y, +static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y, unsigned field) { switch (field) { @@ -1204,7 +1560,7 @@ static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y, * Map the line number relative to the compose rectangle to a destination * buffer line number. */ -static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y, +static unsigned tpg_calc_buffer_line(const struct tpg_data *tpg, unsigned y, unsigned field) { y += tpg->compose.top; @@ -1265,6 +1621,10 @@ static void tpg_recalc(struct tpg_data *tpg) V4L2_QUANTIZATION_LIM_RANGE; break; } + } else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) { + /* R'G'B' BT.2020 is limited range */ + tpg->real_quantization = + V4L2_QUANTIZATION_LIM_RANGE; } } tpg_precalculate_colors(tpg); @@ -1283,191 +1643,388 @@ void tpg_calc_text_basep(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf) { unsigned stride = tpg->bytesperline[p]; + unsigned h = tpg->buf_height; tpg_recalc(tpg); basep[p][0] = vbuf; basep[p][1] = vbuf; + h /= tpg->vdownsampling[p]; if (tpg->field == V4L2_FIELD_SEQ_TB) - basep[p][1] += tpg->buf_height * stride / 2; + basep[p][1] += h * stride / 2; else if (tpg->field == V4L2_FIELD_SEQ_BT) - basep[p][0] += tpg->buf_height * stride / 2; + basep[p][0] += h * stride / 2; + if (p == 0 && tpg->interleaved) + tpg_calc_text_basep(tpg, basep, 1, vbuf); } -void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) +static int tpg_pattern_avg(const struct tpg_data *tpg, + unsigned pat1, unsigned pat2) { - bool is_tv = std; - bool is_60hz = is_tv && (std & V4L2_STD_525_60); - unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width; - unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width; - unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height; - unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height; + unsigned pat_lines = tpg_get_pat_lines(tpg); + + if (pat1 == (pat2 + 1) % pat_lines) + return pat2; + if (pat2 == (pat1 + 1) % pat_lines) + return pat1; + return -1; +} + +/* + * This struct contains common parameters used by both the drawing of the + * test pattern and the drawing of the extras (borders, square, etc.) + */ +struct tpg_draw_params { + /* common data */ + bool is_tv; + bool is_60hz; + unsigned twopixsize; + unsigned img_width; + unsigned stride; + unsigned hmax; + unsigned frame_line; + unsigned frame_line_next; + + /* test pattern */ + unsigned mv_hor_old; + unsigned mv_hor_new; + unsigned mv_vert_old; + unsigned mv_vert_new; + + /* extras */ unsigned wss_width; - unsigned f; - int hmax = (tpg->compose.height * tpg->perc_fill) / 100; - int h; - unsigned twopixsize = tpg->twopixelsize[p]; - unsigned img_width = tpg->compose.width * twopixsize / 2; - unsigned line_offset; - unsigned left_pillar_width = 0; - unsigned right_pillar_start = img_width; - unsigned stride = tpg->bytesperline[p]; - unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; - u8 *orig_vbuf = vbuf; + unsigned wss_random_offset; + unsigned sav_eav_f; + unsigned left_pillar_width; + unsigned right_pillar_start; +}; - /* Coarse scaling with Bresenham */ - unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height; - unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height; - unsigned src_y = 0; - unsigned error = 0; +static void tpg_fill_params_pattern(const struct tpg_data *tpg, unsigned p, + struct tpg_draw_params *params) +{ + params->mv_hor_old = + tpg_hscale_div(tpg, p, tpg->mv_hor_count % tpg->src_width); + params->mv_hor_new = + tpg_hscale_div(tpg, p, (tpg->mv_hor_count + tpg->mv_hor_step) % + tpg->src_width); + params->mv_vert_old = tpg->mv_vert_count % tpg->src_height; + params->mv_vert_new = + (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height; +} - tpg_recalc(tpg); +static void tpg_fill_params_extras(const struct tpg_data *tpg, + unsigned p, + struct tpg_draw_params *params) +{ + unsigned left_pillar_width = 0; + unsigned right_pillar_start = params->img_width; + + params->wss_width = tpg->crop.left < tpg->src_width / 2 ? + tpg->src_width / 2 - tpg->crop.left : 0; + if (params->wss_width > tpg->crop.width) + params->wss_width = tpg->crop.width; + params->wss_width = tpg_hscale_div(tpg, p, params->wss_width); + params->wss_random_offset = + params->twopixsize * prandom_u32_max(tpg->src_width / 2); - mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1; - mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1; - wss_width = tpg->crop.left < tpg->src_width / 2 ? - tpg->src_width / 2 - tpg->crop.left : 0; - if (wss_width > tpg->crop.width) - wss_width = tpg->crop.width; - wss_width = wss_width * tpg->scaled_width / tpg->src_width; - - vbuf += tpg->compose.left * twopixsize / 2; - line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width; - line_offset = (line_offset & ~1) * twopixsize / 2; if (tpg->crop.left < tpg->border.left) { left_pillar_width = tpg->border.left - tpg->crop.left; if (left_pillar_width > tpg->crop.width) left_pillar_width = tpg->crop.width; - left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width; - left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2; + left_pillar_width = tpg_hscale_div(tpg, p, left_pillar_width); } - if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) { - right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left; - right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width; - right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2; - if (right_pillar_start > img_width) - right_pillar_start = img_width; + params->left_pillar_width = left_pillar_width; + + if (tpg->crop.left + tpg->crop.width > + tpg->border.left + tpg->border.width) { + right_pillar_start = + tpg->border.left + tpg->border.width - tpg->crop.left; + right_pillar_start = + tpg_hscale_div(tpg, p, right_pillar_start); + if (right_pillar_start > params->img_width) + right_pillar_start = params->img_width; } + params->right_pillar_start = right_pillar_start; - f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + params->sav_eav_f = tpg->field == + (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); +} - for (h = 0; h < tpg->compose.height; h++) { - bool even; - bool fill_blank = false; - unsigned frame_line; - unsigned buf_line; - unsigned pat_line_old; - unsigned pat_line_new; - u8 *linestart_older; - u8 *linestart_newer; - u8 *linestart_top; - u8 *linestart_bottom; - - frame_line = tpg_calc_frameline(tpg, src_y, tpg->field); - even = !(frame_line & 1); - buf_line = tpg_calc_buffer_line(tpg, h, tpg->field); - src_y += int_part; - error += fract_part; - if (error >= tpg->compose.height) { - error -= tpg->compose.height; - src_y++; - } +static void tpg_fill_plane_extras(const struct tpg_data *tpg, + const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) +{ + unsigned twopixsize = params->twopixsize; + unsigned img_width = params->img_width; + unsigned frame_line = params->frame_line; + const struct v4l2_rect *sq = &tpg->square; + const struct v4l2_rect *b = &tpg->border; + const struct v4l2_rect *c = &tpg->crop; + + if (params->is_tv && !params->is_60hz && + frame_line == 0 && params->wss_width) { + /* + * Replace the first half of the top line of a 50 Hz frame + * with random data to simulate a WSS signal. + */ + u8 *wss = tpg->random_line[p] + params->wss_random_offset; - if (h >= hmax) { - if (hmax == tpg->compose.height) - continue; - if (!tpg->perc_fill_blank) - continue; - fill_blank = true; - } + memcpy(vbuf, wss, params->wss_width); + } + + if (tpg->show_border && frame_line >= b->top && + frame_line < b->top + b->height) { + unsigned bottom = b->top + b->height - 1; + unsigned left = params->left_pillar_width; + unsigned right = params->right_pillar_start; - if (tpg->vflip) - frame_line = tpg->src_height - frame_line - 1; - - if (fill_blank) { - linestart_older = tpg->contrast_line[p]; - linestart_newer = tpg->contrast_line[p]; - } else if (tpg->qual != TPG_QUAL_NOISE && - (frame_line < tpg->border.top || - frame_line >= tpg->border.top + tpg->border.height)) { - linestart_older = tpg->black_line[p]; - linestart_newer = tpg->black_line[p]; - } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) { - linestart_older = tpg->random_line[p] + - twopixsize * prandom_u32_max(tpg->src_width / 2); - linestart_newer = tpg->random_line[p] + - twopixsize * prandom_u32_max(tpg->src_width / 2); + if (frame_line == b->top || frame_line == b->top + 1 || + frame_line == bottom || frame_line == bottom - 1) { + memcpy(vbuf + left, tpg->contrast_line[p], + right - left); } else { - pat_line_old = tpg_get_pat_line(tpg, - (frame_line + mv_vert_old) % tpg->src_height); - pat_line_new = tpg_get_pat_line(tpg, - (frame_line + mv_vert_new) % tpg->src_height); - linestart_older = tpg->lines[pat_line_old][p] + - mv_hor_old * twopixsize / 2; - linestart_newer = tpg->lines[pat_line_new][p] + - mv_hor_new * twopixsize / 2; - linestart_older += line_offset; - linestart_newer += line_offset; + if (b->left >= c->left && + b->left < c->left + c->width) + memcpy(vbuf + left, + tpg->contrast_line[p], twopixsize); + if (b->left + b->width > c->left && + b->left + b->width <= c->left + c->width) + memcpy(vbuf + right - twopixsize, + tpg->contrast_line[p], twopixsize); } - if (is_60hz) { - linestart_top = linestart_newer; - linestart_bottom = linestart_older; - } else { - linestart_top = linestart_older; - linestart_bottom = linestart_newer; + } + if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top && + frame_line < b->top + b->height) { + memcpy(vbuf, tpg->black_line[p], params->left_pillar_width); + memcpy(vbuf + params->right_pillar_start, tpg->black_line[p], + img_width - params->right_pillar_start); + } + if (tpg->show_square && frame_line >= sq->top && + frame_line < sq->top + sq->height && + sq->left < c->left + c->width && + sq->left + sq->width >= c->left) { + unsigned left = sq->left; + unsigned width = sq->width; + + if (c->left > left) { + width -= c->left - left; + left = c->left; } + if (c->left + c->width < left + width) + width -= left + width - c->left - c->width; + left -= c->left; + left = tpg_hscale_div(tpg, p, left); + width = tpg_hscale_div(tpg, p, width); + memcpy(vbuf + left, tpg->contrast_line[p], width); + } + if (tpg->insert_sav) { + unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width / 3); + u8 *p = vbuf + offset; + unsigned vact = 0, hact = 0; + + p[0] = 0xff; + p[1] = 0; + p[2] = 0; + p[3] = 0x80 | (params->sav_eav_f << 6) | + (vact << 5) | (hact << 4) | + ((hact ^ vact) << 3) | + ((hact ^ params->sav_eav_f) << 2) | + ((params->sav_eav_f ^ vact) << 1) | + (hact ^ vact ^ params->sav_eav_f); + } + if (tpg->insert_eav) { + unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width * 2 / 3); + u8 *p = vbuf + offset; + unsigned vact = 0, hact = 1; + + p[0] = 0xff; + p[1] = 0; + p[2] = 0; + p[3] = 0x80 | (params->sav_eav_f << 6) | + (vact << 5) | (hact << 4) | + ((hact ^ vact) << 3) | + ((hact ^ params->sav_eav_f) << 2) | + ((params->sav_eav_f ^ vact) << 1) | + (hact ^ vact ^ params->sav_eav_f); + } +} - switch (tpg->field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: - if (even) - memcpy(vbuf + buf_line * stride, linestart_top, img_width); - else - memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); - break; - case V4L2_FIELD_INTERLACED_BT: - if (even) - memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); - else - memcpy(vbuf + buf_line * stride, linestart_top, img_width); - break; - case V4L2_FIELD_TOP: - memcpy(vbuf + buf_line * stride, linestart_top, img_width); - break; - case V4L2_FIELD_BOTTOM: - memcpy(vbuf + buf_line * stride, linestart_bottom, img_width); - break; - case V4L2_FIELD_NONE: - default: - memcpy(vbuf + buf_line * stride, linestart_older, img_width); - break; - } +static void tpg_fill_plane_pattern(const struct tpg_data *tpg, + const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) +{ + unsigned twopixsize = params->twopixsize; + unsigned img_width = params->img_width; + unsigned mv_hor_old = params->mv_hor_old; + unsigned mv_hor_new = params->mv_hor_new; + unsigned mv_vert_old = params->mv_vert_old; + unsigned mv_vert_new = params->mv_vert_new; + unsigned frame_line = params->frame_line; + unsigned frame_line_next = params->frame_line_next; + unsigned line_offset = tpg_hscale_div(tpg, p, tpg->crop.left); + bool even; + bool fill_blank = false; + unsigned pat_line_old; + unsigned pat_line_new; + u8 *linestart_older; + u8 *linestart_newer; + u8 *linestart_top; + u8 *linestart_bottom; + + even = !(frame_line & 1); + + if (h >= params->hmax) { + if (params->hmax == tpg->compose.height) + return; + if (!tpg->perc_fill_blank) + return; + fill_blank = true; + } - if (is_tv && !is_60hz && frame_line == 0 && wss_width) { - /* - * Replace the first half of the top line of a 50 Hz frame - * with random data to simulate a WSS signal. - */ - u8 *wss = tpg->random_line[p] + + if (tpg->vflip) { + frame_line = tpg->src_height - frame_line - 1; + frame_line_next = tpg->src_height - frame_line_next - 1; + } + + if (fill_blank) { + linestart_older = tpg->contrast_line[p]; + linestart_newer = tpg->contrast_line[p]; + } else if (tpg->qual != TPG_QUAL_NOISE && + (frame_line < tpg->border.top || + frame_line >= tpg->border.top + tpg->border.height)) { + linestart_older = tpg->black_line[p]; + linestart_newer = tpg->black_line[p]; + } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) { + linestart_older = tpg->random_line[p] + + twopixsize * prandom_u32_max(tpg->src_width / 2); + linestart_newer = tpg->random_line[p] + twopixsize * prandom_u32_max(tpg->src_width / 2); + } else { + unsigned frame_line_old = + (frame_line + mv_vert_old) % tpg->src_height; + unsigned frame_line_new = + (frame_line + mv_vert_new) % tpg->src_height; + unsigned pat_line_next_old; + unsigned pat_line_next_new; - memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2); + pat_line_old = tpg_get_pat_line(tpg, frame_line_old); + pat_line_new = tpg_get_pat_line(tpg, frame_line_new); + linestart_older = tpg->lines[pat_line_old][p] + mv_hor_old; + linestart_newer = tpg->lines[pat_line_new][p] + mv_hor_new; + + if (tpg->vdownsampling[p] > 1 && frame_line != frame_line_next) { + int avg_pat; + + /* + * Now decide whether we need to use downsampled_lines[]. + * That's necessary if the two lines use different patterns. + */ + pat_line_next_old = tpg_get_pat_line(tpg, + (frame_line_next + mv_vert_old) % tpg->src_height); + pat_line_next_new = tpg_get_pat_line(tpg, + (frame_line_next + mv_vert_new) % tpg->src_height); + + switch (tpg->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED_TB: + avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_new); + if (avg_pat < 0) + break; + linestart_older = tpg->downsampled_lines[avg_pat][p] + mv_hor_old; + linestart_newer = linestart_older; + break; + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_SEQ_TB: + avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_next_old); + if (avg_pat >= 0) + linestart_older = tpg->downsampled_lines[avg_pat][p] + + mv_hor_old; + avg_pat = tpg_pattern_avg(tpg, pat_line_new, pat_line_next_new); + if (avg_pat >= 0) + linestart_newer = tpg->downsampled_lines[avg_pat][p] + + mv_hor_new; + break; + } } + linestart_older += line_offset; + linestart_newer += line_offset; + } + if (tpg->field_alternate) { + linestart_top = linestart_bottom = linestart_older; + } else if (params->is_60hz) { + linestart_top = linestart_newer; + linestart_bottom = linestart_older; + } else { + linestart_top = linestart_older; + linestart_bottom = linestart_newer; + } + + switch (tpg->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + if (even) + memcpy(vbuf, linestart_top, img_width); + else + memcpy(vbuf, linestart_bottom, img_width); + break; + case V4L2_FIELD_INTERLACED_BT: + if (even) + memcpy(vbuf, linestart_bottom, img_width); + else + memcpy(vbuf, linestart_top, img_width); + break; + case V4L2_FIELD_TOP: + memcpy(vbuf, linestart_top, img_width); + break; + case V4L2_FIELD_BOTTOM: + memcpy(vbuf, linestart_bottom, img_width); + break; + case V4L2_FIELD_NONE: + default: + memcpy(vbuf, linestart_older, img_width); + break; } +} + +void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, + unsigned p, u8 *vbuf) +{ + struct tpg_draw_params params; + unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; + + /* Coarse scaling with Bresenham */ + unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height; + unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height; + unsigned src_y = 0; + unsigned error = 0; + unsigned h; + + tpg_recalc(tpg); + + params.is_tv = std; + params.is_60hz = std & V4L2_STD_525_60; + params.twopixsize = tpg->twopixelsize[p]; + params.img_width = tpg_hdiv(tpg, p, tpg->compose.width); + params.stride = tpg->bytesperline[p]; + params.hmax = (tpg->compose.height * tpg->perc_fill) / 100; + + tpg_fill_params_pattern(tpg, p, ¶ms); + tpg_fill_params_extras(tpg, p, ¶ms); + + vbuf += tpg_hdiv(tpg, p, tpg->compose.left); - vbuf = orig_vbuf; - vbuf += tpg->compose.left * twopixsize / 2; - src_y = 0; - error = 0; for (h = 0; h < tpg->compose.height; h++) { - unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field); - unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field); - const struct v4l2_rect *sq = &tpg->square; - const struct v4l2_rect *b = &tpg->border; - const struct v4l2_rect *c = &tpg->crop; + unsigned buf_line; + params.frame_line = tpg_calc_frameline(tpg, src_y, tpg->field); + params.frame_line_next = params.frame_line; + buf_line = tpg_calc_buffer_line(tpg, h, tpg->field); src_y += int_part; error += fract_part; if (error >= tpg->compose.height) { @@ -1475,80 +2032,61 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) src_y++; } - if (tpg->show_border && frame_line >= b->top && - frame_line < b->top + b->height) { - unsigned bottom = b->top + b->height - 1; - unsigned left = left_pillar_width; - unsigned right = right_pillar_start; + /* + * For line-interleaved formats determine the 'plane' + * based on the buffer line. + */ + if (tpg_g_interleaved(tpg)) + p = tpg_g_interleaved_plane(tpg, buf_line); - if (frame_line == b->top || frame_line == b->top + 1 || - frame_line == bottom || frame_line == bottom - 1) { - memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], - right - left); + if (tpg->vdownsampling[p] > 1) { + /* + * When doing vertical downsampling the field setting + * matters: for SEQ_BT/TB we downsample each field + * separately (i.e. lines 0+2 are combined, as are + * lines 1+3), for the other field settings we combine + * odd and even lines. Doing that for SEQ_BT/TB would + * be really weird. + */ + if (tpg->field == V4L2_FIELD_SEQ_BT || + tpg->field == V4L2_FIELD_SEQ_TB) { + unsigned next_src_y = src_y; + + if ((h & 3) >= 2) + continue; + next_src_y += int_part; + if (error + fract_part >= tpg->compose.height) + next_src_y++; + params.frame_line_next = + tpg_calc_frameline(tpg, next_src_y, tpg->field); } else { - if (b->left >= c->left && - b->left < c->left + c->width) - memcpy(vbuf + buf_line * stride + left, - tpg->contrast_line[p], twopixsize); - if (b->left + b->width > c->left && - b->left + b->width <= c->left + c->width) - memcpy(vbuf + buf_line * stride + right - twopixsize, - tpg->contrast_line[p], twopixsize); + if (h & 1) + continue; + params.frame_line_next = + tpg_calc_frameline(tpg, src_y, tpg->field); } + + buf_line /= tpg->vdownsampling[p]; } - if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top && - frame_line < b->top + b->height) { - memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width); - memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p], - img_width - right_pillar_start); - } - if (tpg->show_square && frame_line >= sq->top && - frame_line < sq->top + sq->height && - sq->left < c->left + c->width && - sq->left + sq->width >= c->left) { - unsigned left = sq->left; - unsigned width = sq->width; - - if (c->left > left) { - width -= c->left - left; - left = c->left; - } - if (c->left + c->width < left + width) - width -= left + width - c->left - c->width; - left -= c->left; - left = (left * tpg->scaled_width) / tpg->src_width; - left = (left & ~1) * twopixsize / 2; - width = (width * tpg->scaled_width) / tpg->src_width; - width = (width & ~1) * twopixsize / 2; - memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width); - } - if (tpg->insert_sav) { - unsigned offset = (tpg->compose.width / 6) * twopixsize; - u8 *p = vbuf + buf_line * stride + offset; - unsigned vact = 0, hact = 0; - - p[0] = 0xff; - p[1] = 0; - p[2] = 0; - p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) | - ((hact ^ vact) << 3) | - ((hact ^ f) << 2) | - ((f ^ vact) << 1) | - (hact ^ vact ^ f); - } - if (tpg->insert_eav) { - unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize; - u8 *p = vbuf + buf_line * stride + offset; - unsigned vact = 0, hact = 1; - - p[0] = 0xff; - p[1] = 0; - p[2] = 0; - p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) | - ((hact ^ vact) << 3) | - ((hact ^ f) << 2) | - ((f ^ vact) << 1) | - (hact ^ vact ^ f); - } + tpg_fill_plane_pattern(tpg, ¶ms, p, h, + vbuf + buf_line * params.stride); + tpg_fill_plane_extras(tpg, ¶ms, p, h, + vbuf + buf_line * params.stride); + } +} + +void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) +{ + unsigned offset = 0; + unsigned i; + + if (tpg->buffers > 1) { + tpg_fill_plane_buffer(tpg, std, p, vbuf); + return; + } + + for (i = 0; i < tpg_g_planes(tpg); i++) { + tpg_fill_plane_buffer(tpg, std, i, vbuf + offset); + offset += tpg_calc_plane_size(tpg, i); } } diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h index bd8b1c760b3f..a50cd2e2535b 100644 --- a/drivers/media/platform/vivid/vivid-tpg.h +++ b/drivers/media/platform/vivid/vivid-tpg.h @@ -41,7 +41,10 @@ enum tpg_pattern { TPG_PAT_GREEN, TPG_PAT_BLUE, TPG_PAT_CHECKERS_16X16, + TPG_PAT_CHECKERS_2X2, TPG_PAT_CHECKERS_1X1, + TPG_PAT_COLOR_CHECKERS_2X2, + TPG_PAT_COLOR_CHECKERS_1X1, TPG_PAT_ALTERNATING_HLINES, TPG_PAT_ALTERNATING_VLINES, TPG_PAT_CROSS_1_PIXEL, @@ -87,7 +90,7 @@ enum tpg_move_mode { extern const char * const tpg_aspect_strings[]; -#define TPG_MAX_PLANES 2 +#define TPG_MAX_PLANES 3 #define TPG_MAX_PAT_LINES 8 struct tpg_data { @@ -98,6 +101,7 @@ struct tpg_data { /* Scaled output frame size */ unsigned scaled_width; u32 field; + bool field_alternate; /* crop coordinates are frame-based */ struct v4l2_rect crop; /* compose coordinates are format-based */ @@ -134,7 +138,16 @@ struct tpg_data { enum tpg_pixel_aspect pix_aspect; unsigned rgb_range; unsigned real_rgb_range; + unsigned buffers; unsigned planes; + bool interleaved; + u8 vdownsampling[TPG_MAX_PLANES]; + u8 hdownsampling[TPG_MAX_PLANES]; + /* + * horizontal positions must be ANDed with this value to enforce + * correct boundaries for packed YUYV values. + */ + unsigned hmask[TPG_MAX_PLANES]; /* Used to store the colors in native format, either RGB or YUV */ u8 colors[TPG_COLOR_MAX][3]; u8 textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8]; @@ -168,6 +181,7 @@ struct tpg_data { /* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */ unsigned max_line_width; u8 *lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES]; + u8 *downsampled_lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES]; u8 *random_line[TPG_MAX_PLANES]; u8 *contrast_line[TPG_MAX_PLANES]; u8 *black_line[TPG_MAX_PLANES]; @@ -180,11 +194,15 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, u32 field); void tpg_set_font(const u8 *f); -void tpg_gen_text(struct tpg_data *tpg, +void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text); void tpg_calc_text_basep(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf); -void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf); +unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line); +void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, + unsigned p, u8 *vbuf); +void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, + unsigned p, u8 *vbuf); bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc); void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, const struct v4l2_rect *compose); @@ -323,9 +341,19 @@ static inline u32 tpg_g_quantization(const struct tpg_data *tpg) return tpg->quantization; } +static inline unsigned tpg_g_buffers(const struct tpg_data *tpg) +{ + return tpg->buffers; +} + static inline unsigned tpg_g_planes(const struct tpg_data *tpg) { - return tpg->planes; + return tpg->interleaved ? 1 : tpg->planes; +} + +static inline bool tpg_g_interleaved(const struct tpg_data *tpg) +{ + return tpg->interleaved; } static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane) @@ -333,6 +361,24 @@ static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned p return tpg->twopixelsize[plane]; } +static inline unsigned tpg_hdiv(const struct tpg_data *tpg, + unsigned plane, unsigned x) +{ + return ((x / tpg->hdownsampling[plane]) & tpg->hmask[plane]) * + tpg->twopixelsize[plane] / 2; +} + +static inline unsigned tpg_hscale(const struct tpg_data *tpg, unsigned x) +{ + return (x * tpg->scaled_width) / tpg->src_width; +} + +static inline unsigned tpg_hscale_div(const struct tpg_data *tpg, + unsigned plane, unsigned x) +{ + return tpg_hdiv(tpg, plane, tpg_hscale(tpg, x)); +} + static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane) { return tpg->bytesperline[plane]; @@ -340,7 +386,60 @@ static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned p static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl) { - tpg->bytesperline[plane] = bpl; + unsigned p; + + if (tpg->buffers > 1) { + tpg->bytesperline[plane] = bpl; + return; + } + + for (p = 0; p < tpg_g_planes(tpg); p++) { + unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0]; + + tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p]; + } +} + + +static inline unsigned tpg_g_line_width(const struct tpg_data *tpg, unsigned plane) +{ + unsigned w = 0; + unsigned p; + + if (tpg->buffers > 1) + return tpg_g_bytesperline(tpg, plane); + for (p = 0; p < tpg_g_planes(tpg); p++) { + unsigned plane_w = tpg_g_bytesperline(tpg, p); + + w += plane_w / tpg->vdownsampling[p]; + } + return w; +} + +static inline unsigned tpg_calc_line_width(const struct tpg_data *tpg, + unsigned plane, unsigned bpl) +{ + unsigned w = 0; + unsigned p; + + if (tpg->buffers > 1) + return bpl; + for (p = 0; p < tpg_g_planes(tpg); p++) { + unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0]; + + plane_w /= tpg->hdownsampling[p]; + w += plane_w / tpg->vdownsampling[p]; + } + return w; +} + +static inline unsigned tpg_calc_plane_size(const struct tpg_data *tpg, unsigned plane) +{ + if (plane >= tpg_g_planes(tpg)) + return 0; + + return tpg_g_bytesperline(tpg, plane) * tpg->buf_height / + tpg->vdownsampling[plane]; } static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h) @@ -348,9 +447,10 @@ static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h) tpg->buf_height = h; } -static inline void tpg_s_field(struct tpg_data *tpg, unsigned field) +static inline void tpg_s_field(struct tpg_data *tpg, unsigned field, bool alternate) { tpg->field = field; + tpg->field_alternate = alternate; } static inline void tpg_s_perc_fill(struct tpg_data *tpg, diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 867a29a6d18f..dab5990f45a0 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -42,20 +42,26 @@ static const struct vivid_fmt formats_ovl[] = { { .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, }, { .name = "XRGB555 (LE)", .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, }, { .name = "ARGB555 (LE)", .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, }, }; @@ -94,7 +100,7 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f unsigned sizes[], void *alloc_ctxs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned planes = tpg_g_planes(&dev->tpg); + unsigned buffers = tpg_g_buffers(&dev->tpg); unsigned h = dev->fmt_cap_rect.height; unsigned p; @@ -127,39 +133,36 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f mp = &fmt->fmt.pix_mp; /* * Check if the number of planes in the specified format match - * the number of planes in the current format. You can't mix that. + * the number of buffers in the current format. You can't mix that. */ - if (mp->num_planes != planes) + if (mp->num_planes != buffers) return -EINVAL; vfmt = vivid_get_format(dev, mp->pixelformat); - for (p = 0; p < planes; p++) { + for (p = 0; p < buffers; p++) { sizes[p] = mp->plane_fmt[p].sizeimage; - if (sizes[0] < tpg_g_bytesperline(&dev->tpg, 0) * h + + if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + vfmt->data_offset[p]) return -EINVAL; } } else { - for (p = 0; p < planes; p++) - sizes[p] = tpg_g_bytesperline(&dev->tpg, p) * h + + for (p = 0; p < buffers; p++) + sizes[p] = tpg_g_line_width(&dev->tpg, p) * h + dev->fmt_cap->data_offset[p]; } if (vq->num_buffers + *nbuffers < 2) *nbuffers = 2 - vq->num_buffers; - *nplanes = planes; + *nplanes = buffers; /* * videobuf2-vmalloc allocator is context-less so no need to set * alloc_ctxs array. */ - if (planes == 2) - dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__, - *nbuffers, sizes[0], sizes[1]); - else - dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__, - *nbuffers, sizes[0]); + dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); + for (p = 0; p < buffers; p++) + dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); return 0; } @@ -168,7 +171,7 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size; - unsigned planes = tpg_g_planes(&dev->tpg); + unsigned buffers = tpg_g_buffers(&dev->tpg); unsigned p; dprintk(dev, 1, "%s\n", __func__); @@ -184,13 +187,13 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) dev->buf_prepare_error = false; return -EINVAL; } - for (p = 0; p < planes; p++) { - size = tpg_g_bytesperline(&dev->tpg, p) * dev->fmt_cap_rect.height + + for (p = 0; p < buffers; p++) { + size = tpg_g_line_width(&dev->tpg, p) * dev->fmt_cap_rect.height + dev->fmt_cap->data_offset[p]; - if (vb2_plane_size(vb, 0) < size) { + if (vb2_plane_size(vb, p) < size) { dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n", - __func__, p, vb2_plane_size(vb, 0), size); + __func__, p, vb2_plane_size(vb, p), size); return -EINVAL; } @@ -441,7 +444,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) */ if (keep_controls || !dev->colorspace) break; - if (bt->standards & V4L2_DV_BT_STD_CEA861) { + if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { if (bt->width == 720 && bt->height <= 576) v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); else @@ -526,11 +529,11 @@ int vivid_g_fmt_vid_cap(struct file *file, void *priv, mp->colorspace = vivid_colorspace_cap(dev); mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); mp->quantization = vivid_quantization_cap(dev); - mp->num_planes = dev->fmt_cap->planes; + mp->num_planes = dev->fmt_cap->buffers; for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height + + tpg_g_line_width(&dev->tpg, p) * mp->height + dev->fmt_cap->data_offset[p]; } return 0; @@ -596,18 +599,19 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, /* This driver supports custom bytesperline values */ - /* Calculate the minimum supported bytesperline value */ - bytesperline = (mp->width * fmt->depth) >> 3; - /* Calculate the maximum supported bytesperline value */ - max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3; - mp->num_planes = fmt->planes; + mp->num_planes = fmt->buffers; for (p = 0; p < mp->num_planes; p++) { + /* Calculate the minimum supported bytesperline value */ + bytesperline = (mp->width * fmt->bit_depth[p]) >> 3; + /* Calculate the maximum supported bytesperline value */ + max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3; + if (pfmt[p].bytesperline > max_bpl) pfmt[p].bytesperline = max_bpl; if (pfmt[p].bytesperline < bytesperline) pfmt[p].bytesperline = bytesperline; - pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height + - fmt->data_offset[p]; + pfmt[p].sizeimage = tpg_calc_line_width(&dev->tpg, p, pfmt[p].bytesperline) * + mp->height + fmt->data_offset[p]; memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); } mp->colorspace = vivid_colorspace_cap(dev); @@ -627,6 +631,7 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, struct vb2_queue *q = &dev->vb_vid_cap_q; int ret = vivid_try_fmt_vid_cap(file, priv, f); unsigned factor = 1; + unsigned p; unsigned i; if (ret < 0) @@ -729,13 +734,15 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, dev->fmt_cap_rect.width = mp->width; dev->fmt_cap_rect.height = mp->height; tpg_s_buf_height(&dev->tpg, mp->height); - tpg_s_bytesperline(&dev->tpg, 0, mp->plane_fmt[0].bytesperline); - if (tpg_g_planes(&dev->tpg) > 1) - tpg_s_bytesperline(&dev->tpg, 1, mp->plane_fmt[1].bytesperline); + tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); + for (p = 0; p < tpg_g_buffers(&dev->tpg); p++) + tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline); dev->field_cap = mp->field; - tpg_s_field(&dev->tpg, dev->field_cap); + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true); + else + tpg_s_field(&dev->tpg, dev->field_cap, false); tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap); - tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); if (vivid_is_sdtv_cap(dev)) dev->tv_field_cap = mp->field; tpg_update_mv_step(&dev->tpg); @@ -1012,8 +1019,12 @@ int vivid_vid_cap_cropcap(struct file *file, void *priv, int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f) { + struct vivid_dev *dev = video_drvdata(file); const struct vivid_fmt *fmt; + if (dev->multiplanar) + return -ENOTTY; + if (f->index >= ARRAY_SIZE(formats_ovl)) return -EINVAL; @@ -1032,6 +1043,9 @@ int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_window *win = &f->fmt.win; unsigned clipcount = win->clipcount; + if (dev->multiplanar) + return -ENOTTY; + win->w.top = dev->overlay_cap_top; win->w.left = dev->overlay_cap_left; win->w.width = compose->width; @@ -1063,6 +1077,9 @@ int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_window *win = &f->fmt.win; int i, j; + if (dev->multiplanar) + return -ENOTTY; + win->w.left = clamp_t(int, win->w.left, -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width); win->w.top = clamp_t(int, win->w.top, @@ -1150,6 +1167,9 @@ int vivid_vid_cap_overlay(struct file *file, void *fh, unsigned i) { struct vivid_dev *dev = video_drvdata(file); + if (dev->multiplanar) + return -ENOTTY; + if (i && dev->fb_vbase_cap == NULL) return -EINVAL; @@ -1169,6 +1189,9 @@ int vivid_vid_cap_g_fbuf(struct file *file, void *fh, { struct vivid_dev *dev = video_drvdata(file); + if (dev->multiplanar) + return -ENOTTY; + *a = dev->fb_cap; a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING | V4L2_FBUF_CAP_LIST_CLIPPING; @@ -1185,6 +1208,9 @@ int vivid_vid_cap_s_fbuf(struct file *file, void *fh, struct vivid_dev *dev = video_drvdata(file); const struct vivid_fmt *fmt; + if (dev->multiplanar) + return -ENOTTY; + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; @@ -1202,7 +1228,7 @@ int vivid_vid_cap_s_fbuf(struct file *file, void *fh, fmt = vivid_get_format(dev, a->fmt.pixelformat); if (!fmt || !fmt->can_do_overlay) return -EINVAL; - if (a->fmt.bytesperline < (a->fmt.width * fmt->depth) / 8) + if (a->fmt.bytesperline < (a->fmt.width * fmt->bit_depth[0]) / 8) return -EINVAL; if (a->fmt.height * a->fmt.bytesperline < a->fmt.sizeimage) return -EINVAL; @@ -1332,7 +1358,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); break; case HDMI: - if (bt->standards & V4L2_DV_BT_STD_CEA861) { + if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) { if (dev->src_rect.width == 720 && dev->src_rect.height <= 576) v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M); else @@ -1552,6 +1578,65 @@ int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id) return 0; } +static void find_aspect_ratio(u32 width, u32 height, + u32 *num, u32 *denom) +{ + if (!(height % 3) && ((height * 4 / 3) == width)) { + *num = 4; + *denom = 3; + } else if (!(height % 9) && ((height * 16 / 9) == width)) { + *num = 16; + *denom = 9; + } else if (!(height % 10) && ((height * 16 / 10) == width)) { + *num = 16; + *denom = 10; + } else if (!(height % 4) && ((height * 5 / 4) == width)) { + *num = 5; + *denom = 4; + } else if (!(height % 9) && ((height * 15 / 9) == width)) { + *num = 15; + *denom = 9; + } else { /* default to 16:9 */ + *num = 16; + *denom = 9; + } +} + +static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 total_h_pixel; + u32 total_v_lines; + u32 h_freq; + + if (!v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, + NULL, NULL)) + return false; + + total_h_pixel = V4L2_DV_BT_FRAME_WIDTH(bt); + total_v_lines = V4L2_DV_BT_FRAME_HEIGHT(bt); + + h_freq = (u32)bt->pixelclock / total_h_pixel; + + if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { + if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, + bt->polarities, timings)) + return true; + } + + if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) { + struct v4l2_fract aspect_ratio; + + find_aspect_ratio(bt->width, bt->height, + &aspect_ratio.numerator, + &aspect_ratio.denominator); + if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync, + bt->polarities, aspect_ratio, timings)) + return true; + } + return false; +} + int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings) { @@ -1559,13 +1644,16 @@ int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, if (!vivid_is_hdmi_cap(dev)) return -ENODATA; - if (vb2_is_busy(&dev->vb_vid_cap_q)) - return -EBUSY; if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, - 0, NULL, NULL)) + 0, NULL, NULL) && + !valid_cvt_gtf_timings(timings)) return -EINVAL; + if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0)) return 0; + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; + dev->dv_timings_cap = *timings; vivid_update_format_cap(dev, false); return 0; @@ -1663,18 +1751,14 @@ int vidioc_enum_frameintervals(struct file *file, void *priv, return -EINVAL; if (!vivid_is_webcam(dev)) { - static const struct v4l2_fract step = { 1, 1 }; - if (fival->index) return -EINVAL; if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM) return -EINVAL; if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM) return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - fival->stepwise.min = tpf_min; - fival->stepwise.max = tpf_max; - fival->stepwise.step = step; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = dev->timeperframe_vid_cap; return 0; } diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 6bef1e6d6788..aa446271ad34 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -33,8 +33,9 @@ const struct v4l2_dv_timings_cap vivid_dv_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 25000000, 600000000, - V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT, + V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 14000000, 775000000, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED) }; @@ -46,145 +47,435 @@ struct vivid_fmt vivid_formats[] = { { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .is_yuv = true, .planes = 1, - .data_offset = { PLANE0_DATA_OFFSET, 0 }, + .buffers = 1, + .data_offset = { PLANE0_DATA_OFFSET }, }, { .name = "4:2:2, packed, UYVY", .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .is_yuv = true, .planes = 1, + .buffers = 1, }, { .name = "4:2:2, packed, YVYU", .fourcc = V4L2_PIX_FMT_YVYU, - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .is_yuv = true, .planes = 1, + .buffers = 1, }, { .name = "4:2:2, packed, VYUY", .fourcc = V4L2_PIX_FMT_VYUY, - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .is_yuv = true, + .planes = 1, + .buffers = 1, + }, + { + .name = "YUV 4:2:2 triplanar", + .fourcc = V4L2_PIX_FMT_YUV422P, + .vdownsampling = { 1, 1, 1 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 1, + }, + { + .name = "YUV 4:2:0 triplanar", + .fourcc = V4L2_PIX_FMT_YUV420, + .vdownsampling = { 1, 2, 2 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 1, + }, + { + .name = "YVU 4:2:0 triplanar", + .fourcc = V4L2_PIX_FMT_YVU420, + .vdownsampling = { 1, 2, 2 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 1, + }, + { + .name = "YUV 4:2:0 biplanar", + .fourcc = V4L2_PIX_FMT_NV12, + .vdownsampling = { 1, 2 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YVU 4:2:0 biplanar", + .fourcc = V4L2_PIX_FMT_NV21, + .vdownsampling = { 1, 2 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YUV 4:2:2 biplanar", + .fourcc = V4L2_PIX_FMT_NV16, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YVU 4:2:2 biplanar", + .fourcc = V4L2_PIX_FMT_NV61, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YUV 4:4:4 biplanar", + .fourcc = V4L2_PIX_FMT_NV24, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 16 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YVU 4:4:4 biplanar", + .fourcc = V4L2_PIX_FMT_NV42, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 16 }, + .is_yuv = true, + .planes = 2, + .buffers = 1, + }, + { + .name = "YUV555 (LE)", + .fourcc = V4L2_PIX_FMT_YUV555, /* uuuvvvvv ayyyyyuu */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x8000, + }, + { + .name = "YUV565 (LE)", + .fourcc = V4L2_PIX_FMT_YUV565, /* uuuvvvvv yyyyyuuu */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "YUV444", + .fourcc = V4L2_PIX_FMT_YUV444, /* uuuuvvvv aaaayyyy */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0xf000, + }, + { + .name = "YUV32 (LE)", + .fourcc = V4L2_PIX_FMT_YUV32, /* ayuv */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x000000ff, + }, + { + .name = "Monochrome", + .fourcc = V4L2_PIX_FMT_GREY, + .vdownsampling = { 1 }, + .bit_depth = { 8 }, .is_yuv = true, .planes = 1, + .buffers = 1, + }, + { + .name = "RGB332", + .fourcc = V4L2_PIX_FMT_RGB332, /* rrrgggbb */ + .vdownsampling = { 1 }, + .bit_depth = { 8 }, + .planes = 1, + .buffers = 1, }, { .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, .can_do_overlay = true, }, { .name = "RGB565 (BE)", .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, .can_do_overlay = true, }, { + .name = "RGB444", + .fourcc = V4L2_PIX_FMT_RGB444, /* xxxxrrrr ggggbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "XRGB444", + .fourcc = V4L2_PIX_FMT_XRGB444, /* xxxxrrrr ggggbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "ARGB444", + .fourcc = V4L2_PIX_FMT_ARGB444, /* aaaarrrr ggggbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x00f0, + }, + { .name = "RGB555 (LE)", - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb xrrrrrgg */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, .can_do_overlay = true, }, { .name = "XRGB555 (LE)", - .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, + .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb xrrrrrgg */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, .can_do_overlay = true, }, { .name = "ARGB555 (LE)", .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ - .depth = 16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, + .buffers = 1, .can_do_overlay = true, .alpha_mask = 0x8000, }, { .name = "RGB555 (BE)", - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .depth = 16, + .fourcc = V4L2_PIX_FMT_RGB555X, /* xrrrrrgg gggbbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, .planes = 1, - .can_do_overlay = true, + .buffers = 1, + }, + { + .name = "XRGB555 (BE)", + .fourcc = V4L2_PIX_FMT_XRGB555X, /* xrrrrrgg gggbbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "ARGB555 (BE)", + .fourcc = V4L2_PIX_FMT_ARGB555X, /* arrrrrgg gggbbbbb */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x0080, }, { .name = "RGB24 (LE)", .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .depth = 24, + .vdownsampling = { 1 }, + .bit_depth = { 24 }, .planes = 1, + .buffers = 1, }, { .name = "RGB24 (BE)", .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .depth = 24, + .vdownsampling = { 1 }, + .bit_depth = { 24 }, .planes = 1, + .buffers = 1, + }, + { + .name = "BGR666", + .fourcc = V4L2_PIX_FMT_BGR666, /* bbbbbbgg ggggrrrr rrxxxxxx */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, }, { .name = "RGB32 (LE)", - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .depth = 32, + .fourcc = V4L2_PIX_FMT_RGB32, /* xrgb */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, }, { .name = "RGB32 (BE)", - .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ - .depth = 32, + .fourcc = V4L2_PIX_FMT_BGR32, /* bgrx */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, }, { .name = "XRGB32 (LE)", - .fourcc = V4L2_PIX_FMT_XRGB32, /* argb */ - .depth = 32, + .fourcc = V4L2_PIX_FMT_XRGB32, /* xrgb */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, }, { .name = "XRGB32 (BE)", - .fourcc = V4L2_PIX_FMT_XBGR32, /* bgra */ - .depth = 32, + .fourcc = V4L2_PIX_FMT_XBGR32, /* bgrx */ + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, }, { .name = "ARGB32 (LE)", .fourcc = V4L2_PIX_FMT_ARGB32, /* argb */ - .depth = 32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, .alpha_mask = 0x000000ff, }, { .name = "ARGB32 (BE)", .fourcc = V4L2_PIX_FMT_ABGR32, /* bgra */ - .depth = 32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, .planes = 1, + .buffers = 1, .alpha_mask = 0xff000000, }, { - .name = "4:2:2, planar, YUV", + .name = "Bayer BG/GR", + .fourcc = V4L2_PIX_FMT_SBGGR8, /* Bayer BG/GR */ + .vdownsampling = { 1 }, + .bit_depth = { 8 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "Bayer GB/RG", + .fourcc = V4L2_PIX_FMT_SGBRG8, /* Bayer GB/RG */ + .vdownsampling = { 1 }, + .bit_depth = { 8 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "Bayer GR/BG", + .fourcc = V4L2_PIX_FMT_SGRBG8, /* Bayer GR/BG */ + .vdownsampling = { 1 }, + .bit_depth = { 8 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "Bayer RG/GB", + .fourcc = V4L2_PIX_FMT_SRGGB8, /* Bayer RG/GB */ + .vdownsampling = { 1 }, + .bit_depth = { 8 }, + .planes = 1, + .buffers = 1, + }, + { + .name = "4:2:2, biplanar, YUV", .fourcc = V4L2_PIX_FMT_NV16M, - .depth = 8, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 8 }, .is_yuv = true, .planes = 2, + .buffers = 2, .data_offset = { PLANE0_DATA_OFFSET, 0 }, }, { - .name = "4:2:2, planar, YVU", + .name = "4:2:2, biplanar, YVU", .fourcc = V4L2_PIX_FMT_NV61M, - .depth = 8, + .vdownsampling = { 1, 1 }, + .bit_depth = { 8, 8 }, .is_yuv = true, .planes = 2, + .buffers = 2, .data_offset = { 0, PLANE0_DATA_OFFSET }, }, + { + .name = "4:2:0, triplanar, YUV", + .fourcc = V4L2_PIX_FMT_YUV420M, + .vdownsampling = { 1, 2, 2 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, + { + .name = "4:2:0, triplanar, YVU", + .fourcc = V4L2_PIX_FMT_YVU420M, + .vdownsampling = { 1, 2, 2 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, + { + .name = "4:2:0, biplanar, YUV", + .fourcc = V4L2_PIX_FMT_NV12M, + .vdownsampling = { 1, 2 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 2, + }, + { + .name = "4:2:0, biplanar, YVU", + .fourcc = V4L2_PIX_FMT_NV21M, + .vdownsampling = { 1, 2 }, + .bit_depth = { 8, 8 }, + .is_yuv = true, + .planes = 2, + .buffers = 2, + }, }; -/* There are 2 multiplanar formats in the list */ -#define VIVID_MPLANAR_FORMATS 2 +/* There are 6 multiplanar formats in the list */ +#define VIVID_MPLANAR_FORMATS 6 const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) { @@ -194,7 +485,7 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) for (k = 0; k < ARRAY_SIZE(vivid_formats); k++) { fmt = &vivid_formats[k]; if (fmt->fourcc == pixelformat) - if (fmt->planes == 1 || dev->multiplanar) + if (fmt->buffers == 1 || dev->multiplanar) return fmt; } @@ -210,6 +501,13 @@ bool vivid_vid_can_loop(struct vivid_dev *dev) return false; if (dev->field_cap != dev->field_out) return false; + /* + * While this can be supported, it is just too much work + * to actually implement. + */ + if (dev->field_cap == V4L2_FIELD_SEQ_TB || + dev->field_cap == V4L2_FIELD_SEQ_BT) + return false; if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { if (!(dev->std_cap & V4L2_STD_525_60) != !(dev->std_out & V4L2_STD_525_60)) @@ -397,6 +695,9 @@ int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r) unsigned w = r->width; unsigned h = r->height; + /* sanitize w and h in case someone passes ~0 as the value */ + w &= 0xffff; + h &= 0xffff; if (!(flags & V4L2_SEL_FLAG_LE)) { w++; h++; @@ -421,8 +722,9 @@ int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r) r->top = 0; if (r->left < 0) r->left = 0; - r->left &= ~1; - r->top &= ~1; + /* sanitize left and top in case someone passes ~0 as the value */ + r->left &= 0xfffe; + r->top &= 0xfffe; if (r->left + w > MAX_WIDTH) r->left = MAX_WIDTH - w; if (r->top + h > MAX_HEIGHT) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 39ff79f6aa67..0af43dc7715c 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -36,9 +36,14 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f unsigned sizes[], void *alloc_ctxs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); - unsigned planes = dev->fmt_out->planes; + const struct vivid_fmt *vfmt = dev->fmt_out; + unsigned planes = vfmt->buffers; unsigned h = dev->fmt_out_rect.height; unsigned size = dev->bytesperline_out[0] * h; + unsigned p; + + for (p = vfmt->buffers; p < vfmt->planes; p++) + size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; if (dev->field_out == V4L2_FIELD_ALTERNATE) { /* @@ -74,21 +79,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f if (mp->num_planes != planes) return -EINVAL; sizes[0] = mp->plane_fmt[0].sizeimage; - if (planes == 2) { - sizes[1] = mp->plane_fmt[1].sizeimage; - if (sizes[0] < dev->bytesperline_out[0] * h || - sizes[1] < dev->bytesperline_out[1] * h) - return -EINVAL; - } else if (sizes[0] < size) { + if (sizes[0] < size) return -EINVAL; + for (p = 1; p < planes; p++) { + sizes[p] = mp->plane_fmt[p].sizeimage; + if (sizes[p] < dev->bytesperline_out[p] * h) + return -EINVAL; } } else { - if (planes == 2) { - sizes[0] = dev->bytesperline_out[0] * h; - sizes[1] = dev->bytesperline_out[1] * h; - } else { - sizes[0] = size; - } + for (p = 0; p < planes; p++) + sizes[p] = p ? dev->bytesperline_out[p] * h : size; } if (vq->num_buffers + *nbuffers < 2) @@ -101,12 +101,9 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f * alloc_ctxs array. */ - if (planes == 2) - dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__, - *nbuffers, sizes[0], sizes[1]); - else - dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__, - *nbuffers, sizes[0]); + dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers); + for (p = 0; p < planes; p++) + dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]); return 0; } @@ -114,7 +111,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size; - unsigned planes = dev->fmt_out->planes; + unsigned planes; unsigned p; dprintk(dev, 1, "%s\n", __func__); @@ -122,6 +119,8 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) if (WARN_ON(NULL == dev->fmt_out)) return -EINVAL; + planes = dev->fmt_out->planes; + if (dev->buf_prepare_error) { /* * Error injection: test what happens if buf_prepare() returns @@ -220,7 +219,7 @@ const struct vb2_ops vivid_vid_out_qops = { void vivid_update_format_out(struct vivid_dev *dev) { struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; - unsigned size; + unsigned size, p; switch (dev->output_type[dev->output]) { case SVID: @@ -249,7 +248,7 @@ void vivid_update_format_out(struct vivid_dev *dev) dev->field_out = V4L2_FIELD_ALTERNATE; else dev->field_out = V4L2_FIELD_NONE; - if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) { + if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { if (bt->width == 720 && bt->height <= 576) dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M; else @@ -267,9 +266,9 @@ void vivid_update_format_out(struct vivid_dev *dev) if (V4L2_FIELD_HAS_T_OR_B(dev->field_out)) dev->crop_out.height /= 2; dev->fmt_out_rect = dev->crop_out; - dev->bytesperline_out[0] = (dev->sink_rect.width * dev->fmt_out->depth) / 8; - if (dev->fmt_out->planes == 2) - dev->bytesperline_out[1] = (dev->sink_rect.width * dev->fmt_out->depth) / 8; + for (p = 0; p < dev->fmt_out->planes; p++) + dev->bytesperline_out[p] = + (dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8; } /* Map the field to something that is valid for the current output */ @@ -313,21 +312,28 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, { struct vivid_dev *dev = video_drvdata(file); struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp; + const struct vivid_fmt *fmt = dev->fmt_out; unsigned p; mp->width = dev->fmt_out_rect.width; mp->height = dev->fmt_out_rect.height; mp->field = dev->field_out; - mp->pixelformat = dev->fmt_out->fourcc; + mp->pixelformat = fmt->fourcc; mp->colorspace = dev->colorspace_out; mp->ycbcr_enc = dev->ycbcr_enc_out; mp->quantization = dev->quantization_out; - mp->num_planes = dev->fmt_out->planes; + mp->num_planes = fmt->buffers; for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = mp->plane_fmt[p].bytesperline * mp->height; } + for (p = fmt->buffers; p < fmt->planes; p++) { + unsigned stride = dev->bytesperline_out[p]; + + mp->plane_fmt[0].sizeimage += + (stride * mp->height) / fmt->vdownsampling[p]; + } return 0; } @@ -386,10 +392,10 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, /* This driver supports custom bytesperline values */ /* Calculate the minimum supported bytesperline value */ - bytesperline = (mp->width * fmt->depth) >> 3; + bytesperline = (mp->width * fmt->bit_depth[0]) >> 3; /* Calculate the maximum supported bytesperline value */ - max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3; - mp->num_planes = fmt->planes; + max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[0]) >> 3; + mp->num_planes = fmt->buffers; for (p = 0; p < mp->num_planes; p++) { if (pfmt[p].bytesperline > max_bpl) pfmt[p].bytesperline = max_bpl; @@ -398,11 +404,14 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height; memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); } + for (p = fmt->buffers; p < fmt->planes; p++) + pfmt[0].sizeimage += (pfmt[0].bytesperline * fmt->bit_depth[p]) / + (fmt->bit_depth[0] * fmt->vdownsampling[p]); mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; mp->quantization = V4L2_QUANTIZATION_DEFAULT; if (vivid_is_svid_out(dev)) { mp->colorspace = V4L2_COLORSPACE_SMPTE170M; - } else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861)) { + } else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) { mp->colorspace = V4L2_COLORSPACE_SRGB; if (dev->dvi_d_out) mp->quantization = V4L2_QUANTIZATION_LIM_RANGE; @@ -429,6 +438,7 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, struct vb2_queue *q = &dev->vb_vid_out_q; int ret = vivid_try_fmt_vid_out(file, priv, f); unsigned factor = 1; + unsigned p; if (ret < 0) return ret; @@ -524,9 +534,12 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, dev->fmt_out_rect.width = mp->width; dev->fmt_out_rect.height = mp->height; - dev->bytesperline_out[0] = mp->plane_fmt[0].bytesperline; - if (mp->num_planes > 1) - dev->bytesperline_out[1] = mp->plane_fmt[1].bytesperline; + for (p = 0; p < mp->num_planes; p++) + dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline; + for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++) + dev->bytesperline_out[p] = + (dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) / + dev->fmt_out->bit_depth[0]; dev->field_out = mp->field; if (vivid_is_svid_out(dev)) dev->tv_field_out = mp->field; @@ -1114,13 +1127,13 @@ int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, if (!vivid_is_hdmi_out(dev)) return -ENODATA; - if (vb2_is_busy(&dev->vb_vid_out_q)) - return -EBUSY; if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, 0, NULL, NULL)) return -EINVAL; if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0)) return 0; + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->dv_timings_out = *timings; vivid_update_format_out(dev); return 0; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 401e2b77a0b6..7dd763311c0f 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -183,13 +183,14 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) */ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; + struct vsp1_bru *bru = to_bru(subdev); struct v4l2_mbus_framefmt *format; if (code->pad == BRU_PAD_SINK(0)) { @@ -201,7 +202,8 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0)); + format = vsp1_entity_get_pad_format(&bru->entity, cfg, + BRU_PAD_SINK(0), code->which); code->code = format->code; } @@ -209,7 +211,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, } static int bru_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index) @@ -228,12 +230,12 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&bru->entity.subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &bru->inputs[pad].compose; default: @@ -241,18 +243,18 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, } } -static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); - fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, fmt->which); return 0; } -static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, +static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -268,7 +270,7 @@ static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, default: /* The BRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SINK(0), which); fmt->code = format->code; break; @@ -280,15 +282,15 @@ static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); struct v4l2_mbus_framefmt *format; - bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which); + bru_try_format(bru, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; @@ -296,7 +298,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, if (fmt->pad != BRU_PAD_SOURCE) { struct v4l2_rect *compose; - compose = bru_get_compose(bru, fh, fmt->pad, fmt->which); + compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -308,7 +310,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, unsigned int i; for (i = 0; i <= BRU_PAD_SOURCE; ++i) { - format = vsp1_entity_get_pad_format(&bru->entity, fh, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, i, fmt->which); format->code = fmt->format.code; } @@ -318,7 +320,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, } static int bru_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); @@ -335,7 +337,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, return 0; case V4L2_SEL_TGT_COMPOSE: - sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which); + sel->r = *bru_get_compose(bru, cfg, sel->pad, sel->which); return 0; default: @@ -344,7 +346,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, } static int bru_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); @@ -360,7 +362,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, /* The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SOURCE, sel->which); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -368,12 +370,12 @@ static int bru_set_selection(struct v4l2_subdev *subdev, /* Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad, + format = vsp1_entity_get_pad_format(&bru->entity, cfg, sel->pad, sel->which); sel->r.width = format->width; sel->r.height = format->height; - compose = bru_get_compose(bru, fh, sel->pad, sel->which); + compose = bru_get_compose(bru, cfg, sel->pad, sel->which); *compose = sel->r; return 0; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 79af71d5e270..a453bb4ddd37 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -63,12 +63,12 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &entity->formats[pad]; default: @@ -79,14 +79,14 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, /* * vsp1_entity_init_formats - Initialize formats on all pads * @subdev: V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * - * Initialize all pad formats with default values. If fh is not NULL, try + * Initialize all pad formats with default values. If cfg is not NULL, try * formats are initialized on the file handle. Otherwise active formats are * initialized on the device. */ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh) + struct v4l2_subdev_pad_config *cfg) { struct v4l2_subdev_format format; unsigned int pad; @@ -95,17 +95,17 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, memset(&format, 0, sizeof(format)); format.pad = pad; - format.which = fh ? V4L2_SUBDEV_FORMAT_TRY + format.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(subdev, pad, set_fmt, fh, &format); + v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format); } } static int vsp1_entity_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) { - vsp1_entity_init_formats(subdev, fh); + vsp1_entity_init_formats(subdev, fh->pad); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index aa20aaa58208..62c768d1c6aa 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -91,10 +91,10 @@ extern const struct media_entity_operations vsp1_media_ops; struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which); void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh); + struct v4l2_subdev_pad_config *cfg); bool vsp1_entity_is_streaming(struct vsp1_entity *entity); int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 0bc0471746c9..8ffb817ae525 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -55,7 +55,7 @@ static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) */ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct vsp1_hsit *hsit = to_hsit(subdev); @@ -73,12 +73,14 @@ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, } static int hsit_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { + struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fse->pad, + fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -102,25 +104,25 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, } static int hsit_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); - fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, fmt->which); return 0; } static int hsit_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == HSIT_PAD_SOURCE) { @@ -143,7 +145,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE, fmt->which); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 17a6ca7dafe6..39fa5ef20fbb 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -74,13 +74,14 @@ static int lif_s_stream(struct v4l2_subdev *subdev, int enable) */ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; + struct vsp1_lif *lif = to_lif(subdev); if (code->pad == LIF_PAD_SINK) { if (code->index >= ARRAY_SIZE(codes)) @@ -96,7 +97,8 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + format = vsp1_entity_get_pad_format(&lif->entity, cfg, + LIF_PAD_SINK, code->which); code->code = format->code; } @@ -104,12 +106,14 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, } static int lif_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { + struct vsp1_lif *lif = to_lif(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SINK, + fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -129,18 +133,18 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); - fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, fmt->which); return 0; } -static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); @@ -151,7 +155,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == LIF_PAD_SOURCE) { @@ -173,7 +177,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE, fmt->which); *format = fmt->format; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 6f185c3621fe..656ec272a414 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -82,7 +82,7 @@ static int lut_s_stream(struct v4l2_subdev *subdev, int enable) */ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -90,6 +90,7 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_AHSV8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; + struct vsp1_lut *lut = to_lut(subdev); struct v4l2_mbus_framefmt *format; if (code->pad == LUT_PAD_SINK) { @@ -104,7 +105,8 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK); + format = vsp1_entity_get_pad_format(&lut->entity, cfg, + LUT_PAD_SINK, code->which); code->code = format->code; } @@ -112,12 +114,14 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, } static int lut_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { + struct vsp1_lut *lut = to_lut(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = vsp1_entity_get_pad_format(&lut->entity, cfg, + fse->pad, fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -140,18 +144,18 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); - fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, fmt->which); return 0; } -static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); @@ -163,7 +167,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == LUT_PAD_SOURCE) { @@ -182,7 +186,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE, fmt->which); *format = fmt->format; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 1f1ba26a834a..fa71f4695e16 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -25,7 +25,7 @@ */ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { @@ -42,13 +42,14 @@ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, } int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, fse->pad); + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fse->pad, + fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -72,11 +73,11 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_rect * -vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) +vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK); + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK); case V4L2_SUBDEV_FORMAT_ACTIVE: return &rwpf->crop; default: @@ -84,18 +85,18 @@ vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) } } -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, fmt->which); return 0; } -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -107,7 +108,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, fmt->which); if (fmt->pad == RWPF_PAD_SOURCE) { @@ -130,14 +131,14 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, fmt->format = *format; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which); + crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; crop->height = fmt->format.height; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, fmt->which); *format = fmt->format; @@ -145,7 +146,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, } int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -157,11 +158,11 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which); + sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); sel->r.left = 0; sel->r.top = 0; @@ -177,7 +178,7 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, } int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -194,7 +195,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, /* Make sure the crop rectangle is entirely contained in the image. The * WPF top and left offsets are limited to 255. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); @@ -207,11 +208,11 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which); + crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, sel->which); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 2cf1f13d3bf9..f452dce1a931 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -51,20 +51,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse); -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 1129494c7cfc..6310acab60e7 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -166,13 +166,14 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) */ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; + struct vsp1_sru *sru = to_sru(subdev); struct v4l2_mbus_framefmt *format; if (code->pad == SRU_PAD_SINK) { @@ -187,7 +188,8 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + format = vsp1_entity_get_pad_format(&sru->entity, cfg, + SRU_PAD_SINK, code->which); code->code = format->code; } @@ -195,12 +197,14 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, } static int sru_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { + struct vsp1_sru *sru = to_sru(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + format = vsp1_entity_get_pad_format(&sru->entity, cfg, + SRU_PAD_SINK, fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -226,18 +230,18 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); - fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, fmt->which); return 0; } -static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, +static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -258,7 +262,7 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, fh, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, SRU_PAD_SINK, which); fmt->code = format->code; @@ -288,25 +292,25 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); struct v4l2_mbus_framefmt *format; - sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which); + sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, fh, + format = vsp1_entity_get_pad_format(&sru->entity, cfg, SRU_PAD_SOURCE, fmt->which); *format = fmt->format; - sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which); + sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index a4afec133800..ccc8243e3493 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -169,13 +169,14 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) */ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; + struct vsp1_uds *uds = to_uds(subdev); if (code->pad == UDS_PAD_SINK) { if (code->index >= ARRAY_SIZE(codes)) @@ -191,7 +192,8 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + format = vsp1_entity_get_pad_format(&uds->entity, cfg, + UDS_PAD_SINK, code->which); code->code = format->code; } @@ -199,12 +201,14 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, } static int uds_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { + struct vsp1_uds *uds = to_uds(subdev); struct v4l2_mbus_framefmt *format; - format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + format = vsp1_entity_get_pad_format(&uds->entity, cfg, + UDS_PAD_SINK, fse->which); if (fse->index || fse->code != format->code) return -EINVAL; @@ -224,18 +228,18 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); - fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->format = *vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, fmt->which); return 0; } -static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, +static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -256,7 +260,7 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, fh, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, UDS_PAD_SINK, which); fmt->code = format->code; @@ -271,25 +275,25 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); struct v4l2_mbus_framefmt *format; - uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which); + uds_try_format(uds, cfg, fmt->pad, &fmt->format, fmt->which); - format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, fmt->which); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, fh, + format = vsp1_entity_get_pad_format(&uds->entity, cfg, UDS_PAD_SOURCE, fmt->which); *format = fmt->format; - uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); + uds_try_format(uds, cfg, UDS_PAD_SOURCE, format, fmt->which); } return 0; diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig new file mode 100644 index 000000000000..d7324c726fc2 --- /dev/null +++ b/drivers/media/platform/xilinx/Kconfig @@ -0,0 +1,23 @@ +config VIDEO_XILINX + tristate "Xilinx Video IP (EXPERIMENTAL)" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF + select VIDEOBUF2_DMA_CONTIG + ---help--- + Driver for Xilinx Video IP Pipelines + +if VIDEO_XILINX + +config VIDEO_XILINX_TPG + tristate "Xilinx Video Test Pattern Generator" + depends on VIDEO_XILINX + select VIDEO_XILINX_VTC + ---help--- + Driver for the Xilinx Video Test Pattern Generator + +config VIDEO_XILINX_VTC + tristate "Xilinx Video Timing Controller" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video Timing Controller + +endif #VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile new file mode 100644 index 000000000000..e8a0f2a9f733 --- /dev/null +++ b/drivers/media/platform/xilinx/Makefile @@ -0,0 +1,5 @@ +xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o + +obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o +obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c new file mode 100644 index 000000000000..efde88adf624 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -0,0 +1,766 @@ +/* + * Xilinx Video DMA + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/dma/xilinx_dma.h> +#include <linux/lcm.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "xilinx-dma.h" +#include "xilinx-vip.h" +#include "xilinx-vipp.h" + +#define XVIP_DMA_DEF_FORMAT V4L2_PIX_FMT_YUYV +#define XVIP_DMA_DEF_WIDTH 1920 +#define XVIP_DMA_DEF_HEIGHT 1080 + +/* Minimum and maximum widths are expressed in bytes */ +#define XVIP_DMA_MIN_WIDTH 1U +#define XVIP_DMA_MAX_WIDTH 65535U +#define XVIP_DMA_MIN_HEIGHT 1U +#define XVIP_DMA_MAX_HEIGHT 8191U + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static struct v4l2_subdev * +xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xvip_dma_verify_format(struct xvip_dma *dma) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + + subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); + if (subdev == NULL) + return -EPIPE; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (dma->fmtinfo->code != fmt.format.code || + dma->format.height != fmt.format.height || + dma->format.width != fmt.format.width || + dma->format.colorspace != fmt.format.colorspace) + return -EINVAL; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Stream Management + */ + +/** + * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline + * @pipe: The pipeline + * @start: Start (when true) or stop (when false) the pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * or stop all of them. + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) +{ + struct xvip_dma *dma = pipe->output; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + entity = &dma->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, start); + if (start && ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + return 0; +} + +/** + * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: The pipeline + * @on: Turn the stream on when true or off when false + * + * The pipeline is shared between all DMA engines connect at its input and + * output. While the stream state of DMA engines can be controlled + * independently, pipelines have a shared stream state that enable or disable + * all entities in the pipeline. For this reason the pipeline uses a streaming + * counter that tracks the number of DMA engines that have requested the stream + * to be enabled. + * + * When called with the @on argument set to true, this function will increment + * the pipeline streaming count. If the streaming count reaches the number of + * DMA engines in the pipeline it will enable all entities that belong to the + * pipeline. + * + * Similarly, when called with the @on argument set to false, this function will + * decrement the pipeline streaming count and disable all entities in the + * pipeline when the streaming count reaches zero. + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. Stopping the pipeline never fails. The pipeline state is + * not updated when the operation fails. + */ +static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) +{ + int ret = 0; + + mutex_lock(&pipe->lock); + + if (on) { + if (pipe->stream_count == pipe->num_dmas - 1) { + ret = xvip_pipeline_start_stop(pipe, true); + if (ret < 0) + goto done; + } + pipe->stream_count++; + } else { + if (--pipe->stream_count == 0) + xvip_pipeline_start_stop(pipe, false); + } + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +static int xvip_pipeline_validate(struct xvip_pipeline *pipe, + struct xvip_dma *start) +{ + struct media_entity_graph graph; + struct media_entity *entity = &start->video.entity; + struct media_device *mdev = entity->parent; + unsigned int num_inputs = 0; + unsigned int num_outputs = 0; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the video nodes. */ + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + struct xvip_dma *dma; + + if (entity->type != MEDIA_ENT_T_DEVNODE_V4L) + continue; + + dma = to_xvip_dma(media_entity_to_video_device(entity)); + + if (dma->pad.flags & MEDIA_PAD_FL_SINK) { + pipe->output = dma; + num_outputs++; + } else { + num_inputs++; + } + } + + mutex_unlock(&mdev->graph_mutex); + + /* We need exactly one output and zero or one input. */ + if (num_outputs != 1 || num_inputs > 1) + return -EPIPE; + + pipe->num_dmas = num_inputs + num_outputs; + + return 0; +} + +static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + pipe->num_dmas = 0; + pipe->output = NULL; +} + +/** + * xvip_pipeline_cleanup - Cleanup the pipeline after streaming + * @pipe: the pipeline + * + * Decrease the pipeline use count and clean it up if we were the last user. + */ +static void xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + mutex_lock(&pipe->lock); + + /* If we're the last user clean up the pipeline. */ + if (--pipe->use_count == 0) + __xvip_pipeline_cleanup(pipe); + + mutex_unlock(&pipe->lock); +} + +/** + * xvip_pipeline_prepare - Prepare the pipeline for streaming + * @pipe: the pipeline + * @dma: DMA engine at one end of the pipeline + * + * Validate the pipeline if no user exists yet, otherwise just increase the use + * count. + * + * Return: 0 if successful or -EPIPE if the pipeline is not valid. + */ +static int xvip_pipeline_prepare(struct xvip_pipeline *pipe, + struct xvip_dma *dma) +{ + int ret; + + mutex_lock(&pipe->lock); + + /* If we're the first user validate and initialize the pipeline. */ + if (pipe->use_count == 0) { + ret = xvip_pipeline_validate(pipe, dma); + if (ret < 0) { + __xvip_pipeline_cleanup(pipe); + goto done; + } + } + + pipe->use_count++; + ret = 0; + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +/** + * struct xvip_dma_buffer - Video DMA buffer + * @buf: vb2 buffer base object + * @queue: buffer list entry in the DMA engine queued buffers list + * @dma: DMA channel that uses the buffer + */ +struct xvip_dma_buffer { + struct vb2_buffer buf; + struct list_head queue; + struct xvip_dma *dma; +}; + +#define to_xvip_dma_buffer(vb) container_of(vb, struct xvip_dma_buffer, buf) + +static void xvip_dma_complete(void *param) +{ + struct xvip_dma_buffer *buf = param; + struct xvip_dma *dma = buf->dma; + + spin_lock(&dma->queued_lock); + list_del(&buf->queue); + spin_unlock(&dma->queued_lock); + + buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; + buf->buf.v4l2_buf.sequence = dma->sequence++; + v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp); + vb2_set_plane_payload(&buf->buf, 0, dma->format.sizeimage); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); +} + +static int +xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + + /* Make sure the image size is large enough. */ + if (fmt && fmt->fmt.pix.sizeimage < dma->format.sizeimage) + return -EINVAL; + + *nplanes = 1; + + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : dma->format.sizeimage; + alloc_ctxs[0] = dma->alloc_ctx; + + return 0; +} + +static int xvip_dma_buffer_prepare(struct vb2_buffer *vb) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + + buf->dma = dma; + + return 0; +} + +static void xvip_dma_buffer_queue(struct vb2_buffer *vb) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + struct dma_async_tx_descriptor *desc; + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); + u32 flags; + + if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + dma->xt.dir = DMA_DEV_TO_MEM; + dma->xt.src_sgl = false; + dma->xt.dst_sgl = true; + dma->xt.dst_start = addr; + } else { + flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + dma->xt.dir = DMA_MEM_TO_DEV; + dma->xt.src_sgl = true; + dma->xt.dst_sgl = false; + dma->xt.src_start = addr; + } + + dma->xt.frame_size = 1; + dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; + dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; + dma->xt.numf = dma->format.height; + + desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); + if (!desc) { + dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n"); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + return; + } + desc->callback = xvip_dma_complete; + desc->callback_param = buf; + + spin_lock_irq(&dma->queued_lock); + list_add_tail(&buf->queue, &dma->queued_bufs); + spin_unlock_irq(&dma->queued_lock); + + dmaengine_submit(desc); + + if (vb2_is_streaming(&dma->queue)) + dma_async_issue_pending(dma->dma); +} + +static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + struct xvip_dma_buffer *buf, *nbuf; + struct xvip_pipeline *pipe; + int ret; + + dma->sequence = 0; + + /* + * Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + * + * Use the pipeline object embedded in the first DMA object that starts + * streaming. + */ + pipe = dma->video.entity.pipe + ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; + + ret = media_entity_pipeline_start(&dma->video.entity, &pipe->pipe); + if (ret < 0) + goto error; + + /* Verify that the configured format matches the output of the + * connected subdev. + */ + ret = xvip_dma_verify_format(dma); + if (ret < 0) + goto error_stop; + + ret = xvip_pipeline_prepare(pipe, dma); + if (ret < 0) + goto error_stop; + + /* Start the DMA engine. This must be done before starting the blocks + * in the pipeline to avoid DMA synchronization issues. + */ + dma_async_issue_pending(dma->dma); + + /* Start the pipeline. */ + xvip_pipeline_set_stream(pipe, true); + + return 0; + +error_stop: + media_entity_pipeline_stop(&dma->video.entity); + +error: + /* Give back all queued buffers to videobuf2. */ + spin_lock_irq(&dma->queued_lock); + list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED); + list_del(&buf->queue); + } + spin_unlock_irq(&dma->queued_lock); + + return ret; +} + +static void xvip_dma_stop_streaming(struct vb2_queue *vq) +{ + struct xvip_dma *dma = vb2_get_drv_priv(vq); + struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity); + struct xvip_dma_buffer *buf, *nbuf; + + /* Stop the pipeline. */ + xvip_pipeline_set_stream(pipe, false); + + /* Stop and reset the DMA engine. */ + dmaengine_terminate_all(dma->dma); + + /* Cleanup the pipeline and mark it as being stopped. */ + xvip_pipeline_cleanup(pipe); + media_entity_pipeline_stop(&dma->video.entity); + + /* Give back all queued buffers to videobuf2. */ + spin_lock_irq(&dma->queued_lock); + list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + list_del(&buf->queue); + } + spin_unlock_irq(&dma->queued_lock); +} + +static struct vb2_ops xvip_dma_queue_qops = { + .queue_setup = xvip_dma_queue_setup, + .buf_prepare = xvip_dma_buffer_prepare, + .buf_queue = xvip_dma_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = xvip_dma_start_streaming, + .stop_streaming = xvip_dma_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | dma->xdev->v4l2_caps; + + if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver)); + strlcpy(cap->card, dma->video.name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u", + dma->xdev->dev->of_node->name, dma->port); + + return 0; +} + +/* FIXME: without this callback function, some applications are not configured + * with correct formats, and it results in frames in wrong format. Whether this + * callback needs to be required is not clearly defined, so it should be + * clarified through the mailing list. + */ +static int +xvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + if (f->index > 0) + return -EINVAL; + + f->pixelformat = dma->format.pixelformat; + strlcpy(f->description, dma->fmtinfo->description, + sizeof(f->description)); + + return 0; +} + +static int +xvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + format->fmt.pix = dma->format; + + return 0; +} + +static void +__xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, + const struct xvip_video_format **fmtinfo) +{ + const struct xvip_video_format *info; + unsigned int min_width; + unsigned int max_width; + unsigned int min_bpl; + unsigned int max_bpl; + unsigned int width; + unsigned int align; + unsigned int bpl; + + /* Retrieve format information and select the default format if the + * requested format isn't supported. + */ + info = xvip_get_format_by_fourcc(pix->pixelformat); + if (IS_ERR(info)) + info = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); + + pix->pixelformat = info->fourcc; + pix->field = V4L2_FIELD_NONE; + + /* The transfer alignment requirements are expressed in bytes. Compute + * the minimum and maximum values, clamp the requested width and convert + * it back to pixels. + */ + align = lcm(dma->align, info->bpp); + min_width = roundup(XVIP_DMA_MIN_WIDTH, align); + max_width = rounddown(XVIP_DMA_MAX_WIDTH, align); + width = rounddown(pix->width * info->bpp, align); + + pix->width = clamp(width, min_width, max_width) / info->bpp; + pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, + XVIP_DMA_MAX_HEIGHT); + + /* Clamp the requested bytes per line value. If the maximum bytes per + * line value is zero, the module doesn't support user configurable line + * sizes. Override the requested value with the minimum in that case. + */ + min_bpl = pix->width * info->bpp; + max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); + bpl = rounddown(pix->bytesperline, dma->align); + + pix->bytesperline = clamp(bpl, min_bpl, max_bpl); + pix->sizeimage = pix->bytesperline * pix->height; + + if (fmtinfo) + *fmtinfo = info; +} + +static int +xvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + __xvip_dma_try_format(dma, &format->fmt.pix, NULL); + return 0; +} + +static int +xvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + const struct xvip_video_format *info; + + __xvip_dma_try_format(dma, &format->fmt.pix, &info); + + if (vb2_is_busy(&dma->queue)) + return -EBUSY; + + dma->format = format->fmt.pix; + dma->fmtinfo = info; + + return 0; +} + +static const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = { + .vidioc_querycap = xvip_dma_querycap, + .vidioc_enum_fmt_vid_cap = xvip_dma_enum_format, + .vidioc_g_fmt_vid_cap = xvip_dma_get_format, + .vidioc_g_fmt_vid_out = xvip_dma_get_format, + .vidioc_s_fmt_vid_cap = xvip_dma_set_format, + .vidioc_s_fmt_vid_out = xvip_dma_set_format, + .vidioc_try_fmt_vid_cap = xvip_dma_try_format, + .vidioc_try_fmt_vid_out = xvip_dma_try_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static const struct v4l2_file_operations xvip_dma_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Xilinx Video DMA Core + */ + +int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, + enum v4l2_buf_type type, unsigned int port) +{ + char name[14]; + int ret; + + dma->xdev = xdev; + dma->port = port; + mutex_init(&dma->lock); + mutex_init(&dma->pipe.lock); + INIT_LIST_HEAD(&dma->queued_bufs); + spin_lock_init(&dma->queued_lock); + + dma->fmtinfo = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); + dma->format.pixelformat = dma->fmtinfo->fourcc; + dma->format.colorspace = V4L2_COLORSPACE_SRGB; + dma->format.field = V4L2_FIELD_NONE; + dma->format.width = XVIP_DMA_DEF_WIDTH; + dma->format.height = XVIP_DMA_DEF_HEIGHT; + dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp; + dma->format.sizeimage = dma->format.bytesperline * dma->format.height; + + /* Initialize the media entity... */ + dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0); + if (ret < 0) + goto error; + + /* ... and the video node... */ + dma->video.fops = &xvip_dma_fops; + dma->video.v4l2_dev = &xdev->v4l2_dev; + dma->video.queue = &dma->queue; + snprintf(dma->video.name, sizeof(dma->video.name), "%s %s %u", + xdev->dev->of_node->name, + type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", + port); + dma->video.vfl_type = VFL_TYPE_GRABBER; + dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? VFL_DIR_RX : VFL_DIR_TX; + dma->video.release = video_device_release_empty; + dma->video.ioctl_ops = &xvip_dma_ioctl_ops; + dma->video.lock = &dma->lock; + + video_set_drvdata(&dma->video, dma); + + /* ... and the buffers queue... */ + dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev); + if (IS_ERR(dma->alloc_ctx)) + goto error; + + /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() + * V4L2 APIs would be inefficient. Testing on the command line with a + * 'cat /dev/video?' thus won't be possible, but given that the driver + * anyway requires a test tool to setup the pipeline before any video + * stream can be started, requiring a specific V4L2 test tool as well + * instead of 'cat' isn't really a drawback. + */ + dma->queue.type = type; + dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dma->queue.lock = &dma->lock; + dma->queue.drv_priv = dma; + dma->queue.buf_struct_size = sizeof(struct xvip_dma_buffer); + dma->queue.ops = &xvip_dma_queue_qops; + dma->queue.mem_ops = &vb2_dma_contig_memops; + dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC + | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; + ret = vb2_queue_init(&dma->queue); + if (ret < 0) { + dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n"); + goto error; + } + + /* ... and the DMA channel. */ + sprintf(name, "port%u", port); + dma->dma = dma_request_slave_channel(dma->xdev->dev, name); + if (dma->dma == NULL) { + dev_err(dma->xdev->dev, "no VDMA channel found\n"); + ret = -ENODEV; + goto error; + } + + dma->align = 1 << dma->dma->device->copy_align; + + ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(dma->xdev->dev, "failed to register video device\n"); + goto error; + } + + return 0; + +error: + xvip_dma_cleanup(dma); + return ret; +} + +void xvip_dma_cleanup(struct xvip_dma *dma) +{ + if (video_is_registered(&dma->video)) + video_unregister_device(&dma->video); + + if (dma->dma) + dma_release_channel(dma->dma); + + if (!IS_ERR_OR_NULL(dma->alloc_ctx)) + vb2_dma_contig_cleanup_ctx(dma->alloc_ctx); + + media_entity_cleanup(&dma->video.entity); + + mutex_destroy(&dma->lock); + mutex_destroy(&dma->pipe.lock); +} diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h new file mode 100644 index 000000000000..a540111f8d3d --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -0,0 +1,109 @@ +/* + * Xilinx Video DMA + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIP_DMA_H__ +#define __XILINX_VIP_DMA_H__ + +#include <linux/dmaengine.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> + +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/videobuf2-core.h> + +struct dma_chan; +struct xvip_composite_device; +struct xvip_video_format; + +/** + * struct xvip_pipeline - Xilinx Video IP pipeline structure + * @pipe: media pipeline + * @lock: protects the pipeline @stream_count + * @use_count: number of DMA engines using the pipeline + * @stream_count: number of DMA engines currently streaming + * @num_dmas: number of DMA engines in the pipeline + * @output: DMA engine at the output of the pipeline + */ +struct xvip_pipeline { + struct media_pipeline pipe; + + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + + unsigned int num_dmas; + struct xvip_dma *output; +}; + +static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) +{ + return container_of(e->pipe, struct xvip_pipeline, pipe); +} + +/** + * struct xvip_dma - Video DMA channel + * @list: list entry in a composite device dmas list + * @video: V4L2 video device associated with the DMA channel + * @pad: media pad for the video device entity + * @xdev: composite device the DMA channel belongs to + * @pipe: pipeline belonging to the DMA channel + * @port: composite device DT node port number for the DMA channel + * @lock: protects the @format, @fmtinfo and @queue fields + * @format: active V4L2 pixel format + * @fmtinfo: format information corresponding to the active @format + * @queue: vb2 buffers queue + * @alloc_ctx: allocation context for the vb2 @queue + * @sequence: V4L2 buffers sequence number + * @queued_bufs: list of queued buffers + * @queued_lock: protects the buf_queued list + * @dma: DMA engine channel + * @align: transfer alignment required by the DMA channel (in bytes) + * @xt: dma interleaved template for dma configuration + * @sgl: data chunk structure for dma_interleaved_template + */ +struct xvip_dma { + struct list_head list; + struct video_device video; + struct media_pad pad; + + struct xvip_composite_device *xdev; + struct xvip_pipeline pipe; + unsigned int port; + + struct mutex lock; + struct v4l2_pix_format format; + const struct xvip_video_format *fmtinfo; + + struct vb2_queue queue; + void *alloc_ctx; + unsigned int sequence; + + struct list_head queued_bufs; + spinlock_t queued_lock; + + struct dma_chan *dma; + unsigned int align; + struct dma_interleaved_template xt; + struct data_chunk sgl[1]; +}; + +#define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) + +int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, + enum v4l2_buf_type type, unsigned int port); +void xvip_dma_cleanup(struct xvip_dma *dma); + +#endif /* __XILINX_VIP_DMA_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c new file mode 100644 index 000000000000..b5f7d5ecb7f6 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -0,0 +1,931 @@ +/* + * Xilinx Test Pattern Generator + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" +#include "xilinx-vtc.h" + +#define XTPG_CTRL_STATUS_SLAVE_ERROR (1 << 16) +#define XTPG_CTRL_IRQ_SLAVE_ERROR (1 << 16) + +#define XTPG_PATTERN_CONTROL 0x0100 +#define XTPG_PATTERN_MASK (0xf << 0) +#define XTPG_PATTERN_CONTROL_CROSS_HAIRS (1 << 4) +#define XTPG_PATTERN_CONTROL_MOVING_BOX (1 << 5) +#define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT 6 +#define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK (0xf << 6) +#define XTPG_PATTERN_CONTROL_STUCK_PIXEL (1 << 9) +#define XTPG_PATTERN_CONTROL_NOISE (1 << 10) +#define XTPG_PATTERN_CONTROL_MOTION (1 << 12) +#define XTPG_MOTION_SPEED 0x0104 +#define XTPG_CROSS_HAIRS 0x0108 +#define XTPG_CROSS_HAIRS_ROW_SHIFT 0 +#define XTPG_CROSS_HAIRS_ROW_MASK (0xfff << 0) +#define XTPG_CROSS_HAIRS_COLUMN_SHIFT 16 +#define XTPG_CROSS_HAIRS_COLUMN_MASK (0xfff << 16) +#define XTPG_ZPLATE_HOR_CONTROL 0x010c +#define XTPG_ZPLATE_VER_CONTROL 0x0110 +#define XTPG_ZPLATE_START_SHIFT 0 +#define XTPG_ZPLATE_START_MASK (0xffff << 0) +#define XTPG_ZPLATE_SPEED_SHIFT 16 +#define XTPG_ZPLATE_SPEED_MASK (0xffff << 16) +#define XTPG_BOX_SIZE 0x0114 +#define XTPG_BOX_COLOR 0x0118 +#define XTPG_STUCK_PIXEL_THRESH 0x011c +#define XTPG_NOISE_GAIN 0x0120 +#define XTPG_BAYER_PHASE 0x0124 +#define XTPG_BAYER_PHASE_RGGB 0 +#define XTPG_BAYER_PHASE_GRBG 1 +#define XTPG_BAYER_PHASE_GBRG 2 +#define XTPG_BAYER_PHASE_BGGR 3 +#define XTPG_BAYER_PHASE_OFF 4 + +/* + * The minimum blanking value is one clock cycle for the front porch, one clock + * cycle for the sync pulse and one clock cycle for the back porch. + */ +#define XTPG_MIN_HBLANK 3 +#define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) +#define XTPG_MIN_VBLANK 3 +#define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) + +/** + * struct xtpg_device - Xilinx Test Pattern Generator device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @npads: number of pads (1 or 2) + * @has_input: whether an input is connected to the sink pad + * @formats: active V4L2 media bus format for each pad + * @default_format: default V4L2 media bus format + * @vip_format: format information corresponding to the active format + * @bayer: boolean flag if TPG is set to any bayer format + * @ctrl_handler: control handler + * @hblank: horizontal blanking control + * @vblank: vertical blanking control + * @pattern: test pattern control + * @streaming: is the video stream active + * @vtc: video timing controller + * @vtmux_gpio: video timing mux GPIO + */ +struct xtpg_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + unsigned int npads; + bool has_input; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_format; + const struct xvip_video_format *vip_format; + bool bayer; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pattern; + bool streaming; + + struct xvtc_device *vtc; + struct gpio_desc *vtmux_gpio; +}; + +static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xtpg_device, xvip.subdev); +} + +static u32 xtpg_get_bayer_phase(unsigned int code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + return XTPG_BAYER_PHASE_RGGB; + case MEDIA_BUS_FMT_SGRBG8_1X8: + return XTPG_BAYER_PHASE_GRBG; + case MEDIA_BUS_FMT_SGBRG8_1X8: + return XTPG_BAYER_PHASE_GBRG; + case MEDIA_BUS_FMT_SBGGR8_1X8: + return XTPG_BAYER_PHASE_BGGR; + default: + return XTPG_BAYER_PHASE_OFF; + } +} + +static void __xtpg_update_pattern_control(struct xtpg_device *xtpg, + bool passthrough, bool pattern) +{ + u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1; + + /* + * If the TPG has no sink pad or no input connected to its sink pad + * passthrough mode can't be enabled. + */ + if (xtpg->npads == 1 || !xtpg->has_input) + passthrough = false; + + /* If passthrough mode is allowed unmask bit 0. */ + if (passthrough) + pattern_mask &= ~1; + + /* If test pattern mode is allowed unmask all other bits. */ + if (pattern) + pattern_mask &= 1; + + __v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum, + pattern_mask, pattern ? 9 : 0); +} + +static void xtpg_update_pattern_control(struct xtpg_device *xtpg, + bool passthrough, bool pattern) +{ + mutex_lock(xtpg->ctrl_handler.lock); + __xtpg_update_pattern_control(xtpg, passthrough, pattern); + mutex_unlock(xtpg->ctrl_handler.lock); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + unsigned int width = xtpg->formats[0].width; + unsigned int height = xtpg->formats[0].height; + bool passthrough; + u32 bayer_phase; + + if (!enable) { + xvip_stop(&xtpg->xvip); + if (xtpg->vtc) + xvtc_generator_stop(xtpg->vtc); + + xtpg_update_pattern_control(xtpg, true, true); + xtpg->streaming = false; + return 0; + } + + xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); + + if (xtpg->vtc) { + struct xvtc_config config = { + .hblank_start = width, + .hsync_start = width + 1, + .vblank_start = height, + .vsync_start = height + 1, + }; + unsigned int htotal; + unsigned int vtotal; + + htotal = min_t(unsigned int, XVTC_MAX_HSIZE, + v4l2_ctrl_g_ctrl(xtpg->hblank) + width); + vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, + v4l2_ctrl_g_ctrl(xtpg->vblank) + height); + + config.hsync_end = htotal - 1; + config.hsize = htotal; + config.vsync_end = vtotal - 1; + config.vsize = vtotal; + + xvtc_generator_start(xtpg->vtc, &config); + } + + /* + * Configure the bayer phase and video timing mux based on the + * operation mode (passthrough or test pattern generation). The test + * pattern can be modified by the control set handler, we thus need to + * take the control lock here to avoid races. + */ + mutex_lock(xtpg->ctrl_handler.lock); + + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_MASK, xtpg->pattern->cur.val); + + /* + * Switching between passthrough and test pattern generation modes isn't + * allowed during streaming, update the control range accordingly. + */ + passthrough = xtpg->pattern->cur.val == 0; + __xtpg_update_pattern_control(xtpg, passthrough, !passthrough); + + xtpg->streaming = true; + + mutex_unlock(xtpg->ctrl_handler.lock); + + /* + * For TPG v5.0, the bayer phase needs to be off for the pass through + * mode, otherwise the external input would be subsampled. + */ + bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF + : xtpg_get_bayer_phase(xtpg->formats[0].code); + xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); + + if (xtpg->vtmux_gpio) + gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough); + + xvip_start(&xtpg->xvip); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xtpg_get_pad_format(struct xtpg_device *xtpg, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xtpg->formats[pad]; + default: + return NULL; + } +} + +static int xtpg_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + + fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + + return 0; +} + +static int xtpg_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + struct v4l2_mbus_framefmt *__format; + u32 bayer_phase; + + __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); + + /* In two pads mode the source pad format is always identical to the + * sink pad format. + */ + if (xtpg->npads == 2 && fmt->pad == 1) { + fmt->format = *__format; + return 0; + } + + /* Bayer phase is configurable at runtime */ + if (xtpg->bayer) { + bayer_phase = xtpg_get_bayer_phase(fmt->format.code); + if (bayer_phase != XTPG_BAYER_PHASE_OFF) + __format->code = fmt->format.code; + } + + xvip_set_format_size(__format, fmt); + + fmt->format = *__format; + + /* Propagate the format to the source pad. */ + if (xtpg->npads == 2) { + __format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which); + *__format = fmt->format; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + /* Min / max values for pad 0 is always fixed in both one and two pads + * modes. In two pads mode, the source pad(= 1) size is identical to + * the sink pad size */ + if (fse->pad == 0) { + fse->min_width = XVIP_MIN_WIDTH; + fse->max_width = XVIP_MAX_WIDTH; + fse->min_height = XVIP_MIN_HEIGHT; + fse->max_height = XVIP_MAX_HEIGHT; + } else { + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); + *format = xtpg->default_format; + + if (xtpg->npads == 2) { + format = v4l2_subdev_get_try_format(subdev, fh->pad, 1); + *format = xtpg->default_format; + } + + return 0; +} + +static int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct xtpg_device *xtpg = container_of(ctrl->handler, + struct xtpg_device, + ctrl_handler); + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_MASK, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIRS: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOVING_BOX: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_COLOR_MASK: + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, + ctrl->val << + XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_STUCK_PIXEL: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_NOISE: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_NOISE, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOTION: + xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_MOTION, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_MOTION_SPEED: + xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_ROW_MASK, + ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_COLUMN_MASK, + ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + return 0; + case V4L2_CID_XILINX_TPG_BOX_SIZE: + xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_BOX_COLOR: + xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: + xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val); + return 0; + case V4L2_CID_XILINX_TPG_NOISE_GAIN: + xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val); + return 0; + } + + return 0; +} + +static const struct v4l2_ctrl_ops xtpg_ctrl_ops = { + .s_ctrl = xtpg_s_ctrl, +}; + +static struct v4l2_subdev_core_ops xtpg_core_ops = { +}; + +static struct v4l2_subdev_video_ops xtpg_video_ops = { + .s_stream = xtpg_s_stream, +}; + +static struct v4l2_subdev_pad_ops xtpg_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xtpg_enum_frame_size, + .get_fmt = xtpg_get_format, + .set_fmt = xtpg_set_format, +}; + +static struct v4l2_subdev_ops xtpg_ops = { + .core = &xtpg_core_ops, + .video = &xtpg_video_ops, + .pad = &xtpg_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xtpg_internal_ops = { + .open = xtpg_open, + .close = xtpg_close, +}; + +/* + * Control Config + */ + +static const char *const xtpg_pattern_strings[] = { + "Passthrough", + "Horizontal Ramp", + "Vertical Ramp", + "Temporal Ramp", + "Solid Red", + "Solid Green", + "Solid Blue", + "Solid Black", + "Solid White", + "Color Bars", + "Zone Plate", + "Tartan Color Bars", + "Cross Hatch", + "None", + "Vertical/Horizontal Ramps", + "Black/White Checker Board", +}; + +static struct v4l2_ctrl_config xtpg_ctrls[] = { + { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, + .name = "Test Pattern: Cross Hairs", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOVING_BOX, + .name = "Test Pattern: Moving Box", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_COLOR_MASK, + .name = "Test Pattern: Color Mask", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = 0xf, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, + .name = "Test Pattern: Stuck Pixel", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_NOISE, + .name = "Test Pattern: Noise", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOTION, + .name = "Test Pattern: Motion", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOTION_SPEED, + .name = "Test Pattern: Motion Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 8) - 1, + .step = 1, + .def = 4, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW, + .name = "Test Pattern: Cross Hairs Row", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN, + .name = "Test Pattern: Cross Hairs Column", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_START, + .name = "Test Pattern: Zplate Horizontal Start Pos", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0x1e, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED, + .name = "Test Pattern: Zplate Horizontal Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_START, + .name = "Test Pattern: Zplate Vertical Start Pos", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 1, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED, + .name = "Test Pattern: Zplate Vertical Speed", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_BOX_SIZE, + .name = "Test Pattern: Box Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0x32, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_BOX_COLOR, + .name = "Test Pattern: Box Color(RGB)", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 24) - 1, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH, + .name = "Test Pattern: Stuck Pixel threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_NOISE_GAIN, + .name = "Test Pattern: Noise Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 8) - 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xtpg_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int __maybe_unused xtpg_pm_suspend(struct device *dev) +{ + struct xtpg_device *xtpg = dev_get_drvdata(dev); + + xvip_suspend(&xtpg->xvip); + + return 0; +} + +static int __maybe_unused xtpg_pm_resume(struct device *dev) +{ + struct xtpg_device *xtpg = dev_get_drvdata(dev); + + xvip_resume(&xtpg->xvip); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xtpg_parse_of(struct xtpg_device *xtpg) +{ + struct device *dev = xtpg->xvip.dev; + struct device_node *node = xtpg->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + unsigned int nports = 0; + bool has_endpoint = false; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + for_each_child_of_node(ports, port) { + const struct xvip_video_format *format; + struct device_node *endpoint; + + if (!port->name || of_node_cmp(port->name, "port")) + continue; + + format = xvip_of_get_format(port); + if (IS_ERR(format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(format); + } + + /* Get and check the format description */ + if (!xtpg->vip_format) { + xtpg->vip_format = format; + } else if (xtpg->vip_format != format) { + dev_err(dev, "in/out format mismatch in DT"); + return -EINVAL; + } + + if (nports == 0) { + endpoint = of_get_next_child(port, NULL); + if (endpoint) + has_endpoint = true; + of_node_put(endpoint); + } + + /* Count the number of ports. */ + nports++; + } + + if (nports != 1 && nports != 2) { + dev_err(dev, "invalid number of ports %u\n", nports); + return -EINVAL; + } + + xtpg->npads = nports; + if (nports == 2 && has_endpoint) + xtpg->has_input = true; + + return 0; +} + +static int xtpg_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xtpg_device *xtpg; + u32 i, bayer_phase; + int ret; + + xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); + if (!xtpg) + return -ENOMEM; + + xtpg->xvip.dev = &pdev->dev; + + ret = xtpg_parse_of(xtpg); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xtpg->xvip); + if (ret < 0) + return ret; + + xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing", + GPIOD_OUT_HIGH); + if (IS_ERR(xtpg->vtmux_gpio)) { + ret = PTR_ERR(xtpg->vtmux_gpio); + goto error_resource; + } + + xtpg->vtc = xvtc_of_get(pdev->dev.of_node); + if (IS_ERR(xtpg->vtc)) { + ret = PTR_ERR(xtpg->vtc); + goto error_resource; + } + + /* Reset and initialize the core */ + xvip_reset(&xtpg->xvip); + + /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the + * number of pads. + */ + if (xtpg->npads == 2) { + xtpg->pads[0].flags = MEDIA_PAD_FL_SINK; + xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE; + } else { + xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE; + } + + /* Initialize the default format */ + xtpg->default_format.code = xtpg->vip_format->code; + xtpg->default_format.field = V4L2_FIELD_NONE; + xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); + + bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); + if (bayer_phase != XTPG_BAYER_PHASE_OFF) + xtpg->bayer = true; + + xtpg->formats[0] = xtpg->default_format; + if (xtpg->npads == 2) + xtpg->formats[1] = xtpg->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xtpg->xvip.subdev; + v4l2_subdev_init(subdev, &xtpg_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xtpg_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xtpg); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xtpg_media_ops; + + ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0); + if (ret < 0) + goto error; + + v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); + + xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, + V4L2_CID_VBLANK, XTPG_MIN_VBLANK, + XTPG_MAX_VBLANK, 1, 100); + xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, + V4L2_CID_HBLANK, XTPG_MIN_HBLANK, + XTPG_MAX_HBLANK, 1, 100); + xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, + &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(xtpg_pattern_strings) - 1, + 1, 9, xtpg_pattern_strings); + + for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) + v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL); + + if (xtpg->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xtpg->ctrl_handler.error; + goto error; + } + subdev->ctrl_handler = &xtpg->ctrl_handler; + + xtpg_update_pattern_control(xtpg, true, true); + + ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set controls\n"); + goto error; + } + + platform_set_drvdata(pdev, xtpg); + + xvip_print_version(&xtpg->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&xtpg->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvtc_put(xtpg->vtc); +error_resource: + xvip_cleanup_resources(&xtpg->xvip); + return ret; +} + +static int xtpg_remove(struct platform_device *pdev) +{ + struct xtpg_device *xtpg = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xtpg->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xtpg->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xtpg->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); + +static const struct of_device_id xtpg_of_id_table[] = { + { .compatible = "xlnx,v-tpg-5.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, xtpg_of_id_table); + +static struct platform_driver xtpg_driver = { + .driver = { + .name = "xilinx-tpg", + .pm = &xtpg_pm_ops, + .of_match_table = xtpg_of_id_table, + }, + .probe = xtpg_probe, + .remove = xtpg_remove, +}; + +module_platform_driver(xtpg_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c new file mode 100644 index 000000000000..311259129504 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -0,0 +1,323 @@ +/* + * Xilinx Video IP Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <dt-bindings/media/xilinx-vip.h> + +#include "xilinx-vip.h" + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static const struct xvip_video_format xvip_video_formats[] = { + { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, + 2, V4L2_PIX_FMT_YUYV, "4:2:2, packed, YUYV" }, + { XVIP_VF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, + 3, V4L2_PIX_FMT_YUV444, "4:4:4, packed, YUYV" }, + { XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, + 3, 0, NULL }, + { XVIP_VF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8, + 1, V4L2_PIX_FMT_GREY, "Greyscale 8-bit" }, + { XVIP_VF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8, + 1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit RGGB" }, + { XVIP_VF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8, + 1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit GRBG" }, + { XVIP_VF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8, + 1, V4L2_PIX_FMT_SGBRG8, "Bayer 8-bit GBRG" }, + { XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, + 1, V4L2_PIX_FMT_SBGGR8, "Bayer 8-bit BGGR" }, +}; + +/** + * xvip_get_format_by_code - Retrieve format information for a media bus code + * @code: the format media bus code + * + * Return: a pointer to the format information structure corresponding to the + * given V4L2 media bus format @code, or ERR_PTR if no corresponding format can + * be found. + */ +const struct xvip_video_format *xvip_get_format_by_code(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->code == code) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_get_format_by_code); + +/** + * xvip_get_format_by_fourcc - Retrieve format information for a 4CC + * @fourcc: the format 4CC + * + * Return: a pointer to the format information structure corresponding to the + * given V4L2 format @fourcc, or ERR_PTR if no corresponding format can be + * found. + */ +const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->fourcc == fourcc) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_get_format_by_fourcc); + +/** + * xvip_of_get_format - Parse a device tree node and return format information + * @node: the device tree node + * + * Read the xlnx,video-format, xlnx,video-width and xlnx,cfa-pattern properties + * from the device tree @node passed as an argument and return the corresponding + * format information. + * + * Return: a pointer to the format information structure corresponding to the + * format name and width, or ERR_PTR if no corresponding format can be found. + */ +const struct xvip_video_format *xvip_of_get_format(struct device_node *node) +{ + const char *pattern = "mono"; + unsigned int vf_code; + unsigned int i; + u32 width; + int ret; + + ret = of_property_read_u32(node, "xlnx,video-format", &vf_code); + if (ret < 0) + return ERR_PTR(ret); + + ret = of_property_read_u32(node, "xlnx,video-width", &width); + if (ret < 0) + return ERR_PTR(ret); + + if (vf_code == XVIP_VF_MONO_SENSOR) + of_property_read_string(node, "xlnx,cfa-pattern", &pattern); + + for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { + const struct xvip_video_format *format = &xvip_video_formats[i]; + + if (format->vf_code != vf_code || format->width != width) + continue; + + if (vf_code == XVIP_VF_MONO_SENSOR && + strcmp(pattern, format->pattern)) + continue; + + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(xvip_of_get_format); + +/** + * xvip_set_format_size - Set the media bus frame format size + * @format: V4L2 frame format on media bus + * @fmt: media bus format + * + * Set the media bus frame format size. The width / height from the subdevice + * format are set to the given media bus format. The new format size is stored + * in @format. The width and height are clamped using default min / max values. + */ +void xvip_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt) +{ + format->width = clamp_t(unsigned int, fmt->format.width, + XVIP_MIN_WIDTH, XVIP_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT); +} +EXPORT_SYMBOL_GPL(xvip_set_format_size); + +/** + * xvip_clr_or_set - Clear or set the register with a bitmask + * @xvip: Xilinx Video IP device + * @addr: address of register + * @mask: bitmask to be set or cleared + * @set: boolean flag indicating whether to set or clear + * + * Clear or set the register at address @addr with a bitmask @mask depending on + * the boolean flag @set. When the flag @set is true, the bitmask is set in + * the register, otherwise the bitmask is cleared from the register + * when the flag @set is false. + * + * Fox eample, this function can be used to set a control with a boolean value + * requested by users. If the caller knows whether to set or clear in the first + * place, the caller should call xvip_clr() or xvip_set() directly instead of + * using this function. + */ +void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set) +{ + u32 reg; + + reg = xvip_read(xvip, addr); + reg = set ? reg | mask : reg & ~mask; + xvip_write(xvip, addr, reg); +} +EXPORT_SYMBOL_GPL(xvip_clr_or_set); + +/** + * xvip_clr_and_set - Clear and set the register with a bitmask + * @xvip: Xilinx Video IP device + * @addr: address of register + * @clr: bitmask to be cleared + * @set: bitmask to be set + * + * Clear a bit(s) of mask @clr in the register at address @addr, then set + * a bit(s) of mask @set in the register after. + */ +void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set) +{ + u32 reg; + + reg = xvip_read(xvip, addr); + reg &= ~clr; + reg |= set; + xvip_write(xvip, addr, reg); +} +EXPORT_SYMBOL_GPL(xvip_clr_and_set); + +int xvip_init_resources(struct xvip_device *xvip) +{ + struct platform_device *pdev = to_platform_device(xvip->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xvip->iomem = devm_ioremap_resource(xvip->dev, res); + if (IS_ERR(xvip->iomem)) + return PTR_ERR(xvip->iomem); + + xvip->clk = devm_clk_get(xvip->dev, NULL); + if (IS_ERR(xvip->clk)) + return PTR_ERR(xvip->clk); + + clk_prepare_enable(xvip->clk); + return 0; +} +EXPORT_SYMBOL_GPL(xvip_init_resources); + +void xvip_cleanup_resources(struct xvip_device *xvip) +{ + clk_disable_unprepare(xvip->clk); +} +EXPORT_SYMBOL_GPL(xvip_cleanup_resources); + +/* ----------------------------------------------------------------------------- + * Subdev operations handlers + */ + +/** + * xvip_enum_mbus_code - Enumerate the media format code + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: returning media bus code + * + * Enumerate the media bus code of the subdevice. Return the corresponding + * pad format code. This function only works for subdevices with fixed format + * on all pads. Subdevices with multiple format should have their own + * function to enumerate mbus codes. + * + * Return: 0 if the media bus code is found, or -EINVAL if the format index + * is not valid. + */ +int xvip_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct v4l2_mbus_framefmt *format; + + /* Enumerating frame sizes based on the active configuration isn't + * supported yet. + */ + if (code->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, cfg, code->pad); + + code->code = format->code; + + return 0; +} +EXPORT_SYMBOL_GPL(xvip_enum_mbus_code); + +/** + * xvip_enum_frame_size - Enumerate the media bus frame size + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: returning media bus frame size + * + * This function is a drop-in implementation of the subdev enum_frame_size pad + * operation. It assumes that the subdevice has one sink pad and one source + * pad, and that the format on the source pad is always identical to the + * format on the sink pad. Entities with different requirements need to + * implement their own enum_frame_size handlers. + * + * Return: 0 if the media bus frame size is found, or -EINVAL + * if the index or the code is not valid. + */ +int xvip_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + /* Enumerating frame sizes based on the active configuration isn't + * supported yet. + */ + if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == XVIP_PAD_SINK) { + fse->min_width = XVIP_MIN_WIDTH; + fse->max_width = XVIP_MAX_WIDTH; + fse->min_height = XVIP_MIN_HEIGHT; + fse->max_height = XVIP_MAX_HEIGHT; + } else { + /* The size on the source pad is fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} +EXPORT_SYMBOL_GPL(xvip_enum_frame_size); diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h new file mode 100644 index 000000000000..42fee2026815 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vip.h @@ -0,0 +1,238 @@ +/* + * Xilinx Video IP Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIP_H__ +#define __XILINX_VIP_H__ + +#include <linux/io.h> +#include <media/v4l2-subdev.h> + +struct clk; + +/* + * Minimum and maximum width and height common to most video IP cores. IP + * cores with different requirements must define their own values. + */ +#define XVIP_MIN_WIDTH 32 +#define XVIP_MAX_WIDTH 7680 +#define XVIP_MIN_HEIGHT 32 +#define XVIP_MAX_HEIGHT 7680 + +/* + * Pad IDs. IP cores with with multiple inputs or outputs should define + * their own values. + */ +#define XVIP_PAD_SINK 0 +#define XVIP_PAD_SOURCE 1 + +/* Xilinx Video IP Control Registers */ +#define XVIP_CTRL_CONTROL 0x0000 +#define XVIP_CTRL_CONTROL_SW_ENABLE (1 << 0) +#define XVIP_CTRL_CONTROL_REG_UPDATE (1 << 1) +#define XVIP_CTRL_CONTROL_BYPASS (1 << 4) +#define XVIP_CTRL_CONTROL_TEST_PATTERN (1 << 5) +#define XVIP_CTRL_CONTROL_FRAME_SYNC_RESET (1 << 30) +#define XVIP_CTRL_CONTROL_SW_RESET (1 << 31) +#define XVIP_CTRL_STATUS 0x0004 +#define XVIP_CTRL_STATUS_PROC_STARTED (1 << 0) +#define XVIP_CTRL_STATUS_EOF (1 << 1) +#define XVIP_CTRL_ERROR 0x0008 +#define XVIP_CTRL_ERROR_SLAVE_EOL_EARLY (1 << 0) +#define XVIP_CTRL_ERROR_SLAVE_EOL_LATE (1 << 1) +#define XVIP_CTRL_ERROR_SLAVE_SOF_EARLY (1 << 2) +#define XVIP_CTRL_ERROR_SLAVE_SOF_LATE (1 << 3) +#define XVIP_CTRL_IRQ_ENABLE 0x000c +#define XVIP_CTRL_IRQ_ENABLE_PROC_STARTED (1 << 0) +#define XVIP_CTRL_IRQ_EOF (1 << 1) +#define XVIP_CTRL_VERSION 0x0010 +#define XVIP_CTRL_VERSION_MAJOR_MASK (0xff << 24) +#define XVIP_CTRL_VERSION_MAJOR_SHIFT 24 +#define XVIP_CTRL_VERSION_MINOR_MASK (0xff << 16) +#define XVIP_CTRL_VERSION_MINOR_SHIFT 16 +#define XVIP_CTRL_VERSION_REVISION_MASK (0xf << 12) +#define XVIP_CTRL_VERSION_REVISION_SHIFT 12 +#define XVIP_CTRL_VERSION_PATCH_MASK (0xf << 8) +#define XVIP_CTRL_VERSION_PATCH_SHIFT 8 +#define XVIP_CTRL_VERSION_INTERNAL_MASK (0xff << 0) +#define XVIP_CTRL_VERSION_INTERNAL_SHIFT 0 + +/* Xilinx Video IP Timing Registers */ +#define XVIP_ACTIVE_SIZE 0x0020 +#define XVIP_ACTIVE_VSIZE_MASK (0x7ff << 16) +#define XVIP_ACTIVE_VSIZE_SHIFT 16 +#define XVIP_ACTIVE_HSIZE_MASK (0x7ff << 0) +#define XVIP_ACTIVE_HSIZE_SHIFT 0 +#define XVIP_ENCODING 0x0028 +#define XVIP_ENCODING_NBITS_8 (0 << 4) +#define XVIP_ENCODING_NBITS_10 (1 << 4) +#define XVIP_ENCODING_NBITS_12 (2 << 4) +#define XVIP_ENCODING_NBITS_16 (3 << 4) +#define XVIP_ENCODING_NBITS_MASK (3 << 4) +#define XVIP_ENCODING_NBITS_SHIFT 4 +#define XVIP_ENCODING_VIDEO_FORMAT_YUV422 (0 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_YUV444 (1 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_RGB (2 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_YUV420 (3 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_MASK (3 << 0) +#define XVIP_ENCODING_VIDEO_FORMAT_SHIFT 0 + +/** + * struct xvip_device - Xilinx Video IP device structure + * @subdev: V4L2 subdevice + * @dev: (OF) device + * @iomem: device I/O register space remapped to kernel virtual memory + * @clk: video core clock + * @saved_ctrl: saved control register for resume / suspend + */ +struct xvip_device { + struct v4l2_subdev subdev; + struct device *dev; + void __iomem *iomem; + struct clk *clk; + u32 saved_ctrl; +}; + +/** + * struct xvip_video_format - Xilinx Video IP video format description + * @vf_code: AXI4 video format code + * @width: AXI4 format width in bits per component + * @pattern: CFA pattern for Mono/Sensor formats + * @code: media bus format code + * @bpp: bytes per pixel (when stored in memory) + * @fourcc: V4L2 pixel format FCC identifier + * @description: format description, suitable for userspace + */ +struct xvip_video_format { + unsigned int vf_code; + unsigned int width; + const char *pattern; + unsigned int code; + unsigned int bpp; + u32 fourcc; + const char *description; +}; + +const struct xvip_video_format *xvip_get_format_by_code(unsigned int code); +const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc); +const struct xvip_video_format *xvip_of_get_format(struct device_node *node); +void xvip_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt); +int xvip_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code); +int xvip_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse); + +static inline u32 xvip_read(struct xvip_device *xvip, u32 addr) +{ + return ioread32(xvip->iomem + addr); +} + +static inline void xvip_write(struct xvip_device *xvip, u32 addr, u32 value) +{ + iowrite32(value, xvip->iomem + addr); +} + +static inline void xvip_clr(struct xvip_device *xvip, u32 addr, u32 clr) +{ + xvip_write(xvip, addr, xvip_read(xvip, addr) & ~clr); +} + +static inline void xvip_set(struct xvip_device *xvip, u32 addr, u32 set) +{ + xvip_write(xvip, addr, xvip_read(xvip, addr) | set); +} + +void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set); +void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set); + +int xvip_init_resources(struct xvip_device *xvip); +void xvip_cleanup_resources(struct xvip_device *xvip); + +static inline void xvip_reset(struct xvip_device *xvip) +{ + xvip_write(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_RESET); +} + +static inline void xvip_start(struct xvip_device *xvip) +{ + xvip_set(xvip, XVIP_CTRL_CONTROL, + XVIP_CTRL_CONTROL_SW_ENABLE | XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_stop(struct xvip_device *xvip) +{ + xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_resume(struct xvip_device *xvip) +{ + xvip_write(xvip, XVIP_CTRL_CONTROL, + xvip->saved_ctrl | XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_suspend(struct xvip_device *xvip) +{ + xvip->saved_ctrl = xvip_read(xvip, XVIP_CTRL_CONTROL); + xvip_write(xvip, XVIP_CTRL_CONTROL, + xvip->saved_ctrl & ~XVIP_CTRL_CONTROL_SW_ENABLE); +} + +static inline void xvip_set_frame_size(struct xvip_device *xvip, + const struct v4l2_mbus_framefmt *format) +{ + xvip_write(xvip, XVIP_ACTIVE_SIZE, + (format->height << XVIP_ACTIVE_VSIZE_SHIFT) | + (format->width << XVIP_ACTIVE_HSIZE_SHIFT)); +} + +static inline void xvip_get_frame_size(struct xvip_device *xvip, + struct v4l2_mbus_framefmt *format) +{ + u32 reg; + + reg = xvip_read(xvip, XVIP_ACTIVE_SIZE); + format->width = (reg & XVIP_ACTIVE_HSIZE_MASK) >> + XVIP_ACTIVE_HSIZE_SHIFT; + format->height = (reg & XVIP_ACTIVE_VSIZE_MASK) >> + XVIP_ACTIVE_VSIZE_SHIFT; +} + +static inline void xvip_enable_reg_update(struct xvip_device *xvip) +{ + xvip_set(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_disable_reg_update(struct xvip_device *xvip) +{ + xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE); +} + +static inline void xvip_print_version(struct xvip_device *xvip) +{ + u32 version; + + version = xvip_read(xvip, XVIP_CTRL_VERSION); + + dev_info(xvip->dev, "device found, version %u.%02x%x\n", + ((version & XVIP_CTRL_VERSION_MAJOR_MASK) >> + XVIP_CTRL_VERSION_MAJOR_SHIFT), + ((version & XVIP_CTRL_VERSION_MINOR_MASK) >> + XVIP_CTRL_VERSION_MINOR_SHIFT), + ((version & XVIP_CTRL_VERSION_REVISION_MASK) >> + XVIP_CTRL_VERSION_REVISION_SHIFT)); +} + +#endif /* __XILINX_VIP_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c new file mode 100644 index 000000000000..7b7cb9c28d2c --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -0,0 +1,669 @@ +/* + * Xilinx Video IP Composite Device + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-of.h> + +#include "xilinx-dma.h" +#include "xilinx-vipp.h" + +#define XVIPP_DMA_S2MM 0 +#define XVIPP_DMA_MM2S 1 + +/** + * struct xvip_graph_entity - Entity in the video graph + * @list: list entry in a graph entities list + * @node: the entity's DT node + * @entity: media entity, from the corresponding V4L2 subdev + * @asd: subdev asynchronous registration information + * @subdev: V4L2 subdev + */ +struct xvip_graph_entity { + struct list_head list; + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +/* ----------------------------------------------------------------------------- + * Graph Management + */ + +static struct xvip_graph_entity * +xvip_graph_find_entity(struct xvip_composite_device *xdev, + const struct device_node *node) +{ + struct xvip_graph_entity *entity; + + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node == node) + return entity; + } + + return NULL; +} + +static int xvip_graph_build_one(struct xvip_composite_device *xdev, + struct xvip_graph_entity *entity) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct media_entity *local = entity->entity; + struct media_entity *remote; + struct media_pad *local_pad; + struct media_pad *remote_pad; + struct xvip_graph_entity *ent; + struct v4l2_of_link link; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for entity %s\n", local->name); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(entity->node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); + + ret = v4l2_of_parse_link(ep, &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %s\n", + ep->full_name); + continue; + } + + /* Skip sink ports, they will be processed from the other end of + * the link. + */ + if (link.local_port >= local->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.local_port, link.local_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + local_pad = &local->pads[link.local_port]; + + if (local_pad->flags & MEDIA_PAD_FL_SINK) { + dev_dbg(xdev->dev, "skipping sink port %s:%u\n", + link.local_node->full_name, link.local_port); + v4l2_of_put_link(&link); + continue; + } + + /* Skip DMA engines, they will be processed separately. */ + if (link.remote_node == xdev->dev->of_node) { + dev_dbg(xdev->dev, "skipping DMA port %s:%u\n", + link.local_node->full_name, link.local_port); + v4l2_of_put_link(&link); + continue; + } + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, link.remote_node); + if (ent == NULL) { + dev_err(xdev->dev, "no entity found for %s\n", + link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -ENODEV; + break; + } + + remote = ent->entity; + + if (link.remote_port >= remote->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.remote_port, link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + remote_pad = &remote->pads[link.remote_port]; + + v4l2_of_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + + ret = media_entity_create_link(local, local_pad->index, + remote, remote_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + break; + } + } + + of_node_put(ep); + return ret; +} + +static struct xvip_dma * +xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port) +{ + struct xvip_dma *dma; + + list_for_each_entry(dma, &xdev->dmas, list) { + if (dma->port == port) + return dma; + } + + return NULL; +} + +static int xvip_graph_build_dma(struct xvip_composite_device *xdev) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct device_node *node = xdev->dev->of_node; + struct media_entity *source; + struct media_entity *sink; + struct media_pad *source_pad; + struct media_pad *sink_pad; + struct xvip_graph_entity *ent; + struct v4l2_of_link link; + struct device_node *ep = NULL; + struct device_node *next; + struct xvip_dma *dma; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for DMA engines\n"); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name); + + ret = v4l2_of_parse_link(ep, &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %s\n", + ep->full_name); + continue; + } + + /* Find the DMA engine. */ + dma = xvip_graph_find_dma(xdev, link.local_port); + if (dma == NULL) { + dev_err(xdev->dev, "no DMA engine found for port %u\n", + link.local_port); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + dev_dbg(xdev->dev, "creating link for DMA engine %s\n", + dma->video.name); + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, link.remote_node); + if (ent == NULL) { + dev_err(xdev->dev, "no entity found for %s\n", + link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -ENODEV; + break; + } + + if (link.remote_port >= ent->entity->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %s\n", + link.remote_port, link.remote_node->full_name); + v4l2_of_put_link(&link); + ret = -EINVAL; + break; + } + + if (dma->pad.flags & MEDIA_PAD_FL_SOURCE) { + source = &dma->video.entity; + source_pad = &dma->pad; + sink = ent->entity; + sink_pad = &sink->pads[link.remote_port]; + } else { + source = ent->entity; + source_pad = &source->pads[link.remote_port]; + sink = &dma->video.entity; + sink_pad = &dma->pad; + } + + v4l2_of_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + + ret = media_entity_create_link(source, source_pad->index, + sink, sink_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + break; + } + } + + of_node_put(ep); + return ret; +} + +static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct xvip_composite_device *xdev = + container_of(notifier, struct xvip_composite_device, notifier); + struct xvip_graph_entity *entity; + int ret; + + dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); + + /* Create links for every entity. */ + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_build_one(xdev, entity); + if (ret < 0) + return ret; + } + + /* Create links for DMA channels. */ + ret = xvip_graph_build_dma(xdev); + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev); + if (ret < 0) + dev_err(xdev->dev, "failed to register subdev nodes\n"); + + return ret; +} + +static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct xvip_composite_device *xdev = + container_of(notifier, struct xvip_composite_device, notifier); + struct xvip_graph_entity *entity; + + /* Locate the entity corresponding to the bound subdev and store the + * subdev pointer. + */ + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node != subdev->dev->of_node) + continue; + + if (entity->subdev) { + dev_err(xdev->dev, "duplicate subdev for node %s\n", + entity->node->full_name); + return -EINVAL; + } + + dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name); + entity->entity = &subdev->entity; + entity->subdev = subdev; + return 0; + } + + dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name); + return -EINVAL; +} + +static int xvip_graph_parse_one(struct xvip_composite_device *xdev, + struct device_node *node) +{ + struct xvip_graph_entity *entity; + struct device_node *remote; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + dev_dbg(xdev->dev, "parsing node %s\n", node->full_name); + + while (1) { + next = of_graph_get_next_endpoint(node, ep); + if (next == NULL) + break; + + of_node_put(ep); + ep = next; + + dev_dbg(xdev->dev, "handling endpoint %s\n", ep->full_name); + + remote = of_graph_get_remote_port_parent(ep); + if (remote == NULL) { + ret = -EINVAL; + break; + } + + /* Skip entities that we have already processed. */ + if (remote == xdev->dev->of_node || + xvip_graph_find_entity(xdev, remote)) { + of_node_put(remote); + continue; + } + + entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL); + if (entity == NULL) { + of_node_put(remote); + ret = -ENOMEM; + break; + } + + entity->node = remote; + entity->asd.match_type = V4L2_ASYNC_MATCH_OF; + entity->asd.match.of.node = remote; + list_add_tail(&entity->list, &xdev->entities); + xdev->num_subdevs++; + } + + of_node_put(ep); + return ret; +} + +static int xvip_graph_parse(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entity; + int ret; + + /* + * Walk the links to parse the full graph. Start by parsing the + * composite node and then parse entities in turn. The list_for_each + * loop will handle entities added at the end of the list while walking + * the links. + */ + ret = xvip_graph_parse_one(xdev, xdev->dev->of_node); + if (ret < 0) + return 0; + + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_parse_one(xdev, entity->node); + if (ret < 0) + break; + } + + return ret; +} + +static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, + struct device_node *node) +{ + struct xvip_dma *dma; + enum v4l2_buf_type type; + const char *direction; + unsigned int index; + int ret; + + ret = of_property_read_string(node, "direction", &direction); + if (ret < 0) + return ret; + + if (strcmp(direction, "input") == 0) + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else if (strcmp(direction, "output") == 0) + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + return -EINVAL; + + of_property_read_u32(node, "reg", &index); + + dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL); + if (dma == NULL) + return -ENOMEM; + + ret = xvip_dma_init(xdev, dma, type, index); + if (ret < 0) { + dev_err(xdev->dev, "%s initialization failed\n", + node->full_name); + return ret; + } + + list_add_tail(&dma->list, &xdev->dmas); + + xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE + ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int xvip_graph_dma_init(struct xvip_composite_device *xdev) +{ + struct device_node *ports; + struct device_node *port; + int ret; + + ports = of_get_child_by_name(xdev->dev->of_node, "ports"); + if (ports == NULL) { + dev_err(xdev->dev, "ports node not present\n"); + return -EINVAL; + } + + for_each_child_of_node(ports, port) { + ret = xvip_graph_dma_init_one(xdev, port); + if (ret < 0) + return ret; + } + + return 0; +} + +static void xvip_graph_cleanup(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entityp; + struct xvip_graph_entity *entity; + struct xvip_dma *dmap; + struct xvip_dma *dma; + + v4l2_async_notifier_unregister(&xdev->notifier); + + list_for_each_entry_safe(entity, entityp, &xdev->entities, list) { + of_node_put(entity->node); + list_del(&entity->list); + } + + list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) { + xvip_dma_cleanup(dma); + list_del(&dma->list); + } +} + +static int xvip_graph_init(struct xvip_composite_device *xdev) +{ + struct xvip_graph_entity *entity; + struct v4l2_async_subdev **subdevs = NULL; + unsigned int num_subdevs; + unsigned int i; + int ret; + + /* Init the DMA channels. */ + ret = xvip_graph_dma_init(xdev); + if (ret < 0) { + dev_err(xdev->dev, "DMA initialization failed\n"); + goto done; + } + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = xvip_graph_parse(xdev); + if (ret < 0) { + dev_err(xdev->dev, "graph parsing failed\n"); + goto done; + } + + if (!xdev->num_subdevs) { + dev_err(xdev->dev, "no subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + num_subdevs = xdev->num_subdevs; + subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs, + GFP_KERNEL); + if (subdevs == NULL) { + ret = -ENOMEM; + goto done; + } + + i = 0; + list_for_each_entry(entity, &xdev->entities, list) + subdevs[i++] = &entity->asd; + + xdev->notifier.subdevs = subdevs; + xdev->notifier.num_subdevs = num_subdevs; + xdev->notifier.bound = xvip_graph_notify_bound; + xdev->notifier.complete = xvip_graph_notify_complete; + + ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); + if (ret < 0) { + dev_err(xdev->dev, "notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) + xvip_graph_cleanup(xdev); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Controller and V4L2 + */ + +static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) +{ + v4l2_device_unregister(&xdev->v4l2_dev); + media_device_unregister(&xdev->media_dev); +} + +static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) +{ + int ret; + + xdev->media_dev.dev = xdev->dev; + strlcpy(xdev->media_dev.model, "Xilinx Video Composite Device", + sizeof(xdev->media_dev.model)); + xdev->media_dev.hw_revision = 0; + + ret = media_device_register(&xdev->media_dev); + if (ret < 0) { + dev_err(xdev->dev, "media device registration failed (%d)\n", + ret); + return ret; + } + + xdev->v4l2_dev.mdev = &xdev->media_dev; + ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); + if (ret < 0) { + dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", + ret); + media_device_unregister(&xdev->media_dev); + return ret; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xvip_composite_probe(struct platform_device *pdev) +{ + struct xvip_composite_device *xdev; + int ret; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xdev->dev = &pdev->dev; + INIT_LIST_HEAD(&xdev->entities); + INIT_LIST_HEAD(&xdev->dmas); + + ret = xvip_composite_v4l2_init(xdev); + if (ret < 0) + return ret; + + ret = xvip_graph_init(xdev); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xdev); + + dev_info(xdev->dev, "device registered\n"); + + return 0; + +error: + xvip_composite_v4l2_cleanup(xdev); + return ret; +} + +static int xvip_composite_remove(struct platform_device *pdev) +{ + struct xvip_composite_device *xdev = platform_get_drvdata(pdev); + + xvip_graph_cleanup(xdev); + xvip_composite_v4l2_cleanup(xdev); + + return 0; +} + +static const struct of_device_id xvip_composite_of_id_table[] = { + { .compatible = "xlnx,video" }, + { } +}; +MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table); + +static struct platform_driver xvip_composite_driver = { + .driver = { + .name = "xilinx-video", + .of_match_table = xvip_composite_of_id_table, + }, + .probe = xvip_composite_probe, + .remove = xvip_composite_remove, +}; + +module_platform_driver(xvip_composite_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx Video IP Composite Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h new file mode 100644 index 000000000000..faf6b6e80b3b --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vipp.h @@ -0,0 +1,49 @@ +/* + * Xilinx Video IP Composite Device + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VIPP_H__ +#define __XILINX_VIPP_H__ + +#include <linux/list.h> +#include <linux/mutex.h> +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +/** + * struct xvip_composite_device - Xilinx Video IP device structure + * @v4l2_dev: V4L2 device + * @media_dev: media device + * @dev: (OF) device + * @notifier: V4L2 asynchronous subdevs notifier + * @entities: entities in the graph as a list of xvip_graph_entity + * @num_subdevs: number of subdevs in the pipeline + * @dmas: list of DMA channels at the pipeline output and input + * @v4l2_caps: V4L2 capabilities of the whole device (see VIDIOC_QUERYCAP) + */ +struct xvip_composite_device { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + + struct v4l2_async_notifier notifier; + struct list_head entities; + unsigned int num_subdevs; + + struct list_head dmas; + u32 v4l2_caps; +}; + +#endif /* __XILINX_VIPP_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-vtc.c b/drivers/media/platform/xilinx/xilinx-vtc.c new file mode 100644 index 000000000000..01c750edcac5 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vtc.c @@ -0,0 +1,380 @@ +/* + * Xilinx Video Timing Controller + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "xilinx-vip.h" +#include "xilinx-vtc.h" + +#define XVTC_CONTROL_FIELD_ID_POL_SRC (1 << 26) +#define XVTC_CONTROL_ACTIVE_CHROMA_POL_SRC (1 << 25) +#define XVTC_CONTROL_ACTIVE_VIDEO_POL_SRC (1 << 24) +#define XVTC_CONTROL_HSYNC_POL_SRC (1 << 23) +#define XVTC_CONTROL_VSYNC_POL_SRC (1 << 22) +#define XVTC_CONTROL_HBLANK_POL_SRC (1 << 21) +#define XVTC_CONTROL_VBLANK_POL_SRC (1 << 20) +#define XVTC_CONTROL_CHROMA_SRC (1 << 18) +#define XVTC_CONTROL_VBLANK_HOFF_SRC (1 << 17) +#define XVTC_CONTROL_VSYNC_END_SRC (1 << 16) +#define XVTC_CONTROL_VSYNC_START_SRC (1 << 15) +#define XVTC_CONTROL_ACTIVE_VSIZE_SRC (1 << 14) +#define XVTC_CONTROL_FRAME_VSIZE_SRC (1 << 13) +#define XVTC_CONTROL_HSYNC_END_SRC (1 << 11) +#define XVTC_CONTROL_HSYNC_START_SRC (1 << 10) +#define XVTC_CONTROL_ACTIVE_HSIZE_SRC (1 << 9) +#define XVTC_CONTROL_FRAME_HSIZE_SRC (1 << 8) +#define XVTC_CONTROL_SYNC_ENABLE (1 << 5) +#define XVTC_CONTROL_DET_ENABLE (1 << 3) +#define XVTC_CONTROL_GEN_ENABLE (1 << 2) + +#define XVTC_STATUS_FSYNC(n) ((n) << 16) +#define XVTC_STATUS_GEN_ACTIVE_VIDEO (1 << 13) +#define XVTC_STATUS_GEN_VBLANK (1 << 12) +#define XVTC_STATUS_DET_ACTIVE_VIDEO (1 << 11) +#define XVTC_STATUS_DET_VBLANK (1 << 10) +#define XVTC_STATUS_LOCK_LOSS (1 << 9) +#define XVTC_STATUS_LOCK (1 << 8) + +#define XVTC_ERROR_ACTIVE_CHROMA_LOCK (1 << 21) +#define XVTC_ERROR_ACTIVE_VIDEO_LOCK (1 << 20) +#define XVTC_ERROR_HSYNC_LOCK (1 << 19) +#define XVTC_ERROR_VSYNC_LOCK (1 << 18) +#define XVTC_ERROR_HBLANK_LOCK (1 << 17) +#define XVTC_ERROR_VBLANK_LOCK (1 << 16) + +#define XVTC_IRQ_ENABLE_FSYNC(n) ((n) << 16) +#define XVTC_IRQ_ENABLE_GEN_ACTIVE_VIDEO (1 << 13) +#define XVTC_IRQ_ENABLE_GEN_VBLANK (1 << 12) +#define XVTC_IRQ_ENABLE_DET_ACTIVE_VIDEO (1 << 11) +#define XVTC_IRQ_ENABLE_DET_VBLANK (1 << 10) +#define XVTC_IRQ_ENABLE_LOCK_LOSS (1 << 9) +#define XVTC_IRQ_ENABLE_LOCK (1 << 8) + +/* + * The following registers exist in two blocks, one at 0x0020 for the detector + * and one at 0x0060 for the generator. + */ + +#define XVTC_DETECTOR_OFFSET 0x0020 +#define XVTC_GENERATOR_OFFSET 0x0060 + +#define XVTC_ACTIVE_SIZE 0x0000 +#define XVTC_ACTIVE_VSIZE_SHIFT 16 +#define XVTC_ACTIVE_VSIZE_MASK (0x1fff << 16) +#define XVTC_ACTIVE_HSIZE_SHIFT 0 +#define XVTC_ACTIVE_HSIZE_MASK (0x1fff << 0) + +#define XVTC_TIMING_STATUS 0x0004 +#define XVTC_TIMING_STATUS_ACTIVE_VIDEO (1 << 2) +#define XVTC_TIMING_STATUS_VBLANK (1 << 1) +#define XVTC_TIMING_STATUS_LOCKED (1 << 0) + +#define XVTC_ENCODING 0x0008 +#define XVTC_ENCODING_CHROMA_PARITY_SHIFT 8 +#define XVTC_ENCODING_CHROMA_PARITY_MASK (3 << 8) +#define XVTC_ENCODING_CHROMA_PARITY_EVEN_ALL (0 << 8) +#define XVTC_ENCODING_CHROMA_PARITY_ODD_ALL (1 << 8) +#define XVTC_ENCODING_CHROMA_PARITY_EVEN_EVEN (2 << 8) +#define XVTC_ENCODING_CHROMA_PARITY_ODD_EVEN (3 << 8) +#define XVTC_ENCODING_VIDEO_FORMAT_SHIFT 0 +#define XVTC_ENCODING_VIDEO_FORMAT_MASK (0xf << 0) +#define XVTC_ENCODING_VIDEO_FORMAT_YUV422 (0 << 0) +#define XVTC_ENCODING_VIDEO_FORMAT_YUV444 (1 << 0) +#define XVTC_ENCODING_VIDEO_FORMAT_RGB (2 << 0) +#define XVTC_ENCODING_VIDEO_FORMAT_YUV420 (3 << 0) + +#define XVTC_POLARITY 0x000c +#define XVTC_POLARITY_ACTIVE_CHROMA_POL (1 << 5) +#define XVTC_POLARITY_ACTIVE_VIDEO_POL (1 << 4) +#define XVTC_POLARITY_HSYNC_POL (1 << 3) +#define XVTC_POLARITY_VSYNC_POL (1 << 2) +#define XVTC_POLARITY_HBLANK_POL (1 << 1) +#define XVTC_POLARITY_VBLANK_POL (1 << 0) + +#define XVTC_HSIZE 0x0010 +#define XVTC_HSIZE_MASK (0x1fff << 0) + +#define XVTC_VSIZE 0x0014 +#define XVTC_VSIZE_MASK (0x1fff << 0) + +#define XVTC_HSYNC 0x0018 +#define XVTC_HSYNC_END_SHIFT 16 +#define XVTC_HSYNC_END_MASK (0x1fff << 16) +#define XVTC_HSYNC_START_SHIFT 0 +#define XVTC_HSYNC_START_MASK (0x1fff << 0) + +#define XVTC_F0_VBLANK_H 0x001c +#define XVTC_F0_VBLANK_HEND_SHIFT 16 +#define XVTC_F0_VBLANK_HEND_MASK (0x1fff << 16) +#define XVTC_F0_VBLANK_HSTART_SHIFT 0 +#define XVTC_F0_VBLANK_HSTART_MASK (0x1fff << 0) + +#define XVTC_F0_VSYNC_V 0x0020 +#define XVTC_F0_VSYNC_VEND_SHIFT 16 +#define XVTC_F0_VSYNC_VEND_MASK (0x1fff << 16) +#define XVTC_F0_VSYNC_VSTART_SHIFT 0 +#define XVTC_F0_VSYNC_VSTART_MASK (0x1fff << 0) + +#define XVTC_F0_VSYNC_H 0x0024 +#define XVTC_F0_VSYNC_HEND_SHIFT 16 +#define XVTC_F0_VSYNC_HEND_MASK (0x1fff << 16) +#define XVTC_F0_VSYNC_HSTART_SHIFT 0 +#define XVTC_F0_VSYNC_HSTART_MASK (0x1fff << 0) + +#define XVTC_FRAME_SYNC_CONFIG(n) (0x0100 + 4 * (n)) +#define XVTC_FRAME_SYNC_V_START_SHIFT 16 +#define XVTC_FRAME_SYNC_V_START_MASK (0x1fff << 16) +#define XVTC_FRAME_SYNC_H_START_SHIFT 0 +#define XVTC_FRAME_SYNC_H_START_MASK (0x1fff << 0) + +#define XVTC_GENERATOR_GLOBAL_DELAY 0x0104 + +/** + * struct xvtc_device - Xilinx Video Timing Controller device structure + * @xvip: Xilinx Video IP device + * @list: entry in the global VTC list + * @has_detector: the VTC has a timing detector + * @has_generator: the VTC has a timing generator + * @config: generator timings configuration + */ +struct xvtc_device { + struct xvip_device xvip; + struct list_head list; + + bool has_detector; + bool has_generator; + + struct xvtc_config config; +}; + +static LIST_HEAD(xvtc_list); +static DEFINE_MUTEX(xvtc_lock); + +static inline void xvtc_gen_write(struct xvtc_device *xvtc, u32 addr, u32 value) +{ + xvip_write(&xvtc->xvip, XVTC_GENERATOR_OFFSET + addr, value); +} + +/* ----------------------------------------------------------------------------- + * Generator Operations + */ + +int xvtc_generator_start(struct xvtc_device *xvtc, + const struct xvtc_config *config) +{ + int ret; + + if (!xvtc->has_generator) + return -ENXIO; + + ret = clk_prepare_enable(xvtc->xvip.clk); + if (ret < 0) + return ret; + + /* We don't care about the chroma active signal, encoding parameters are + * not important for now. + */ + xvtc_gen_write(xvtc, XVTC_POLARITY, + XVTC_POLARITY_ACTIVE_CHROMA_POL | + XVTC_POLARITY_ACTIVE_VIDEO_POL | + XVTC_POLARITY_HSYNC_POL | XVTC_POLARITY_VSYNC_POL | + XVTC_POLARITY_HBLANK_POL | XVTC_POLARITY_VBLANK_POL); + + /* Hardcode the polarity to active high, as required by the video in to + * AXI4-stream core. + */ + xvtc_gen_write(xvtc, XVTC_ENCODING, 0); + + /* Configure the timings. The VBLANK and VSYNC signals assertion and + * deassertion are hardcoded to the first pixel of the line. + */ + xvtc_gen_write(xvtc, XVTC_ACTIVE_SIZE, + (config->vblank_start << XVTC_ACTIVE_VSIZE_SHIFT) | + (config->hblank_start << XVTC_ACTIVE_HSIZE_SHIFT)); + xvtc_gen_write(xvtc, XVTC_HSIZE, config->hsize); + xvtc_gen_write(xvtc, XVTC_VSIZE, config->vsize); + xvtc_gen_write(xvtc, XVTC_HSYNC, + (config->hsync_end << XVTC_HSYNC_END_SHIFT) | + (config->hsync_start << XVTC_HSYNC_START_SHIFT)); + xvtc_gen_write(xvtc, XVTC_F0_VBLANK_H, 0); + xvtc_gen_write(xvtc, XVTC_F0_VSYNC_V, + (config->vsync_end << XVTC_F0_VSYNC_VEND_SHIFT) | + (config->vsync_start << XVTC_F0_VSYNC_VSTART_SHIFT)); + xvtc_gen_write(xvtc, XVTC_F0_VSYNC_H, 0); + + /* Enable the generator. Set the source of all generator parameters to + * generator registers. + */ + xvip_write(&xvtc->xvip, XVIP_CTRL_CONTROL, + XVTC_CONTROL_ACTIVE_CHROMA_POL_SRC | + XVTC_CONTROL_ACTIVE_VIDEO_POL_SRC | + XVTC_CONTROL_HSYNC_POL_SRC | XVTC_CONTROL_VSYNC_POL_SRC | + XVTC_CONTROL_HBLANK_POL_SRC | XVTC_CONTROL_VBLANK_POL_SRC | + XVTC_CONTROL_CHROMA_SRC | XVTC_CONTROL_VBLANK_HOFF_SRC | + XVTC_CONTROL_VSYNC_END_SRC | XVTC_CONTROL_VSYNC_START_SRC | + XVTC_CONTROL_ACTIVE_VSIZE_SRC | + XVTC_CONTROL_FRAME_VSIZE_SRC | XVTC_CONTROL_HSYNC_END_SRC | + XVTC_CONTROL_HSYNC_START_SRC | + XVTC_CONTROL_ACTIVE_HSIZE_SRC | + XVTC_CONTROL_FRAME_HSIZE_SRC | XVTC_CONTROL_GEN_ENABLE | + XVIP_CTRL_CONTROL_REG_UPDATE); + + return 0; +} +EXPORT_SYMBOL_GPL(xvtc_generator_start); + +int xvtc_generator_stop(struct xvtc_device *xvtc) +{ + if (!xvtc->has_generator) + return -ENXIO; + + xvip_write(&xvtc->xvip, XVIP_CTRL_CONTROL, 0); + + clk_disable_unprepare(xvtc->xvip.clk); + + return 0; +} +EXPORT_SYMBOL_GPL(xvtc_generator_stop); + +struct xvtc_device *xvtc_of_get(struct device_node *np) +{ + struct device_node *xvtc_node; + struct xvtc_device *found = NULL; + struct xvtc_device *xvtc; + + if (!of_find_property(np, "xlnx,vtc", NULL)) + return NULL; + + xvtc_node = of_parse_phandle(np, "xlnx,vtc", 0); + if (xvtc_node == NULL) + return ERR_PTR(-EINVAL); + + mutex_lock(&xvtc_lock); + list_for_each_entry(xvtc, &xvtc_list, list) { + if (xvtc->xvip.dev->of_node == xvtc_node) { + found = xvtc; + break; + } + } + mutex_unlock(&xvtc_lock); + + of_node_put(xvtc_node); + + if (!found) + return ERR_PTR(-EPROBE_DEFER); + + return found; +} +EXPORT_SYMBOL_GPL(xvtc_of_get); + +void xvtc_put(struct xvtc_device *xvtc) +{ +} +EXPORT_SYMBOL_GPL(xvtc_put); + +/* ----------------------------------------------------------------------------- + * Registration and Unregistration + */ + +static void xvtc_register_device(struct xvtc_device *xvtc) +{ + mutex_lock(&xvtc_lock); + list_add_tail(&xvtc->list, &xvtc_list); + mutex_unlock(&xvtc_lock); +} + +static void xvtc_unregister_device(struct xvtc_device *xvtc) +{ + mutex_lock(&xvtc_lock); + list_del(&xvtc->list); + mutex_unlock(&xvtc_lock); +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xvtc_parse_of(struct xvtc_device *xvtc) +{ + struct device_node *node = xvtc->xvip.dev->of_node; + + xvtc->has_detector = of_property_read_bool(node, "xlnx,detector"); + xvtc->has_generator = of_property_read_bool(node, "xlnx,generator"); + + return 0; +} + +static int xvtc_probe(struct platform_device *pdev) +{ + struct xvtc_device *xvtc; + int ret; + + xvtc = devm_kzalloc(&pdev->dev, sizeof(*xvtc), GFP_KERNEL); + if (!xvtc) + return -ENOMEM; + + xvtc->xvip.dev = &pdev->dev; + + ret = xvtc_parse_of(xvtc); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xvtc->xvip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, xvtc); + + xvip_print_version(&xvtc->xvip); + + xvtc_register_device(xvtc); + + return 0; +} + +static int xvtc_remove(struct platform_device *pdev) +{ + struct xvtc_device *xvtc = platform_get_drvdata(pdev); + + xvtc_unregister_device(xvtc); + + xvip_cleanup_resources(&xvtc->xvip); + + return 0; +} + +static const struct of_device_id xvtc_of_id_table[] = { + { .compatible = "xlnx,v-tc-6.1" }, + { } +}; +MODULE_DEVICE_TABLE(of, xvtc_of_id_table); + +static struct platform_driver xvtc_driver = { + .driver = { + .name = "xilinx-vtc", + .of_match_table = xvtc_of_id_table, + }, + .probe = xvtc_probe, + .remove = xvtc_remove, +}; + +module_platform_driver(xvtc_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx Video Timing Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vtc.h b/drivers/media/platform/xilinx/xilinx-vtc.h new file mode 100644 index 000000000000..e1bb2cfcf428 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vtc.h @@ -0,0 +1,42 @@ +/* + * Xilinx Video Timing Controller + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_VTC_H__ +#define __XILINX_VTC_H__ + +struct device_node; +struct xvtc_device; + +#define XVTC_MAX_HSIZE 8191 +#define XVTC_MAX_VSIZE 8191 + +struct xvtc_config { + unsigned int hblank_start; + unsigned int hsync_start; + unsigned int hsync_end; + unsigned int hsize; + unsigned int vblank_start; + unsigned int vsync_start; + unsigned int vsync_end; + unsigned int vsize; +}; + +struct xvtc_device *xvtc_of_get(struct device_node *np); +void xvtc_put(struct xvtc_device *xvtc); + +int xvtc_generator_start(struct xvtc_device *xvtc, + const struct xvtc_config *config); +int xvtc_generator_stop(struct xvtc_device *xvtc); + +#endif /* __XILINX_VTC_H__ */ |