--- zzzz-none-000/linux-3.10.107/drivers/media/platform/omap3isp/ispvideo.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/media/platform/omap3isp/ispvideo.c 2021-02-04 17:41:59.000000000 +0000 @@ -11,23 +11,12 @@ * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include #include #include #include -#include #include #include #include @@ -35,6 +24,7 @@ #include #include #include +#include #include "ispvideo.h" #include "isp.h" @@ -49,74 +39,74 @@ * corresponding in-memory formats to the table below!!! */ static struct isp_format_info formats[] = { - { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, - V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, + { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, 1, }, - { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10, - V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8, + { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_Y10, 10, 2, }, - { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10, - V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8, + { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_Y12, 12, 2, }, - { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8, + { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 8, 1, }, - { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, - V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8, + { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 8, 1, }, - { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8, + { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 8, 1, }, - { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, - V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, + { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8, 1, }, - { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, - V4L2_MBUS_FMT_SBGGR10_1X10, 0, + { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, 0, V4L2_PIX_FMT_SBGGR10DPCM8, 8, 1, }, - { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, - V4L2_MBUS_FMT_SGBRG10_1X10, 0, + { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, + MEDIA_BUS_FMT_SGBRG10_1X10, 0, V4L2_PIX_FMT_SGBRG10DPCM8, 8, 1, }, - { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, - V4L2_MBUS_FMT_SGRBG10_1X10, 0, + { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + MEDIA_BUS_FMT_SGRBG10_1X10, 0, V4L2_PIX_FMT_SGRBG10DPCM8, 8, 1, }, - { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, - V4L2_MBUS_FMT_SRGGB10_1X10, 0, + { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, + MEDIA_BUS_FMT_SRGGB10_1X10, 0, V4L2_PIX_FMT_SRGGB10DPCM8, 8, 1, }, - { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, + { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR10, 10, 2, }, - { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, - V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8, + { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG10, 10, 2, }, - { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, - V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8, + { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG10, 10, 2, }, - { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, - V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8, + { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB10, 10, 2, }, - { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8, + { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR12, 12, 2, }, - { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, - V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8, + { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG12, 12, 2, }, - { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, - V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8, + { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG12, 12, 2, }, - { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, - V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8, + { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB12, 12, 2, }, - { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, - V4L2_MBUS_FMT_UYVY8_1X16, 0, + { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_UYVY8_1X16, 0, V4L2_PIX_FMT_UYVY, 16, 2, }, - { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, - V4L2_MBUS_FMT_YUYV8_1X16, 0, + { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, 0, V4L2_PIX_FMT_YUYV, 16, 2, }, - { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_MBUS_FMT_UYVY8_2X8, - V4L2_MBUS_FMT_UYVY8_2X8, 0, + { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, 0, V4L2_PIX_FMT_UYVY, 8, 2, }, - { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_MBUS_FMT_YUYV8_2X8, - V4L2_MBUS_FMT_YUYV8_2X8, 0, + { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, 0, V4L2_PIX_FMT_YUYV, 8, 2, }, /* Empty entry to catch the unsupported pixel code (0) used by the CCDC * module and avoid NULL pointer dereferences. @@ -124,8 +114,7 @@ { 0, } }; -const struct isp_format_info * -omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) +const struct isp_format_info *omap3isp_video_format_info(u32 code) { unsigned int i; @@ -219,7 +208,7 @@ { struct media_pad *remote; - remote = media_entity_remote_source(&video->pad); + remote = media_entity_remote_pad(&video->pad); if (remote == NULL || media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -278,55 +267,6 @@ return 0; } -/* - * Validate a pipeline by checking both ends of all links for format - * discrepancies. - * - * Compute the minimum time per frame value as the maximum of time per frame - * limits reported by every block in the pipeline. - * - * Return 0 if all formats match, or -EPIPE if at least one link is found with - * different formats on its two ends or if the pipeline doesn't start with a - * video source (either a subdev with no input pad, or a non-subdev entity). - */ -static int isp_video_validate_pipeline(struct isp_pipeline *pipe) -{ - struct isp_device *isp = pipe->output->isp; - struct media_pad *pad; - struct v4l2_subdev *subdev; - - subdev = isp_video_remote_subdev(pipe->output, NULL); - if (subdev == NULL) - return -EPIPE; - - while (1) { - /* Retrieve the sink format */ - pad = &subdev->entity.pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - /* Update the maximum frame rate */ - if (subdev == &isp->isp_res.subdev) - omap3isp_resizer_max_rate(&isp->isp_res, - &pipe->max_rate); - - /* Retrieve the source format. Return an error if no source - * entity can be found, and stop checking the pipeline if the - * source entity isn't a subdev. - */ - pad = media_entity_remote_source(pad); - if (pad == NULL) - return -EPIPE; - - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - subdev = media_entity_to_v4l2_subdev(pad->entity); - } - - return 0; -} - static int __isp_video_get_format(struct isp_video *video, struct v4l2_format *format) { @@ -339,14 +279,11 @@ if (subdev == NULL) return -EINVAL; - mutex_lock(&video->mutex); - fmt.pad = pad; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); - if (ret == -ENOIOCTLCMD) - ret = -EINVAL; + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); mutex_unlock(&video->mutex); if (ret) @@ -371,111 +308,66 @@ vfh->format.fmt.pix.height != format.fmt.pix.height || vfh->format.fmt.pix.width != format.fmt.pix.width || vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || - vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) + vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage || + vfh->format.fmt.pix.field != format.fmt.pix.field) return -EINVAL; - return ret; -} - -/* ----------------------------------------------------------------------------- - * IOMMU management - */ - -#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) - -/* - * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list - * @dev: Device pointer specific to the OMAP3 ISP. - * @sglist: Pointer to source Scatter gather list to allocate. - * @sglen: Number of elements of the scatter-gatter list. - * - * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if - * we ran out of memory. - */ -static dma_addr_t -ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) -{ - struct sg_table *sgt; - u32 da; - - sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); - if (sgt == NULL) - return -ENOMEM; - - sgt->sgl = (struct scatterlist *)sglist; - sgt->nents = sglen; - sgt->orig_nents = sglen; - - da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG); - if (IS_ERR_VALUE(da)) - kfree(sgt); - - return da; -} - -/* - * ispmmu_vunmap - Unmap a device address from the ISP MMU - * @dev: Device pointer specific to the OMAP3 ISP. - * @da: Device address generated from a ispmmu_vmap call. - */ -static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) -{ - struct sg_table *sgt; - - sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da); - kfree(sgt); + return 0; } /* ----------------------------------------------------------------------------- * Video queue operations */ -static void isp_video_queue_prepare(struct isp_video_queue *queue, - unsigned int *nbuffers, unsigned int *size) +static int isp_video_queue_setup(struct vb2_queue *queue, + const void *parg, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct isp_video_fh *vfh = - container_of(queue, struct isp_video_fh, queue); + struct isp_video_fh *vfh = vb2_get_drv_priv(queue); struct isp_video *video = vfh->video; - *size = vfh->format.fmt.pix.sizeimage; - if (*size == 0) - return; + *num_planes = 1; - *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); -} + sizes[0] = vfh->format.fmt.pix.sizeimage; + if (sizes[0] == 0) + return -EINVAL; -static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) -{ - struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); - struct isp_buffer *buffer = to_isp_buffer(buf); - struct isp_video *video = vfh->video; + alloc_ctxs[0] = video->alloc_ctx; - if (buffer->isp_addr) { - ispmmu_vunmap(video->isp, buffer->isp_addr); - buffer->isp_addr = 0; - } + *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); + + return 0; } -static int isp_video_buffer_prepare(struct isp_video_buffer *buf) +static int isp_video_buffer_prepare(struct vb2_buffer *buf) { - struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); + struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; - unsigned long addr; + dma_addr_t addr; - addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); - if (IS_ERR_VALUE(addr)) + /* Refuse to prepare the buffer is the video node has registered an + * error. We don't need to take any lock here as the operation is + * inherently racy. The authoritative check will be performed in the + * queue handler, which can't return an error, this check is just a best + * effort to notify userspace as early as possible. + */ + if (unlikely(video->error)) return -EIO; + addr = vb2_dma_contig_plane_dma_addr(buf, 0); if (!IS_ALIGNED(addr, 32)) { - dev_dbg(video->isp->dev, "Buffer address must be " - "aligned to 32 bytes boundary.\n"); - ispmmu_vunmap(video->isp, buffer->isp_addr); + dev_dbg(video->isp->dev, + "Buffer address must be aligned to 32 bytes boundary.\n"); return -EINVAL; } - buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; - buffer->isp_addr = addr; + vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, + vfh->format.fmt.pix.sizeimage); + buffer->dma = addr; + return 0; } @@ -488,10 +380,11 @@ * If the pipeline is busy, it will be restarted in the output module interrupt * handler. */ -static void isp_video_buffer_queue(struct isp_video_buffer *buf) +static void isp_video_buffer_queue(struct vb2_buffer *buf) { - struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); + struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); enum isp_pipeline_state state; @@ -499,8 +392,18 @@ unsigned int empty; unsigned int start; + spin_lock_irqsave(&video->irqlock, flags); + + if (unlikely(video->error)) { + vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&video->irqlock, flags); + return; + } + empty = list_empty(&video->dmaqueue); - list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); + list_add_tail(&buffer->irqlist, &video->dmaqueue); + + spin_unlock_irqrestore(&video->irqlock, flags); if (empty) { if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -524,23 +427,22 @@ } } -static const struct isp_video_queue_operations isp_video_queue_ops = { - .queue_prepare = &isp_video_queue_prepare, - .buffer_prepare = &isp_video_buffer_prepare, - .buffer_queue = &isp_video_buffer_queue, - .buffer_cleanup = &isp_video_buffer_cleanup, +static const struct vb2_ops isp_video_queue_ops = { + .queue_setup = isp_video_queue_setup, + .buf_prepare = isp_video_buffer_prepare, + .buf_queue = isp_video_buffer_queue, }; /* * omap3isp_video_buffer_next - Complete the current buffer and return the next * @video: ISP video object * - * Remove the current video buffer from the DMA queue and fill its timestamp, - * field count and state fields before waking up its completion handler. + * Remove the current video buffer from the DMA queue and fill its timestamp and + * field count before handing it back to videobuf2. * - * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no - * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise. - * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE. + * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no + * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. + * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE. * * The DMA queue is expected to contain at least one buffer. * @@ -550,26 +452,22 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) { struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); - struct isp_video_queue *queue = video->queue; enum isp_pipeline_state state; - struct isp_video_buffer *buf; + struct isp_buffer *buf; unsigned long flags; - struct timespec ts; - spin_lock_irqsave(&queue->irqlock, flags); + spin_lock_irqsave(&video->irqlock, flags); if (WARN_ON(list_empty(&video->dmaqueue))) { - spin_unlock_irqrestore(&queue->irqlock, flags); + spin_unlock_irqrestore(&video->irqlock, flags); return NULL; } - buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, + buf = list_first_entry(&video->dmaqueue, struct isp_buffer, irqlist); list_del(&buf->irqlist); - spin_unlock_irqrestore(&queue->irqlock, flags); + spin_unlock_irqrestore(&video->irqlock, flags); - ktime_get_ts(&ts); - buf->vbuf.timestamp.tv_sec = ts.tv_sec; - buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + v4l2_get_timestamp(&buf->vb.timestamp); /* Do frame number propagation only if this is the output video node. * Frame number either comes from the CSI receivers or it gets @@ -578,22 +476,32 @@ * first, so the input number might lag behind by 1 in some cases. */ if (video == pipe->output && !pipe->do_propagation) - buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); + buf->vb.sequence = + atomic_inc_return(&pipe->frame_number); else - buf->vbuf.sequence = atomic_read(&pipe->frame_number); + buf->vb.sequence = atomic_read(&pipe->frame_number); + + if (pipe->field != V4L2_FIELD_NONE) + buf->vb.sequence /= 2; + + buf->vb.field = pipe->field; /* Report pipeline errors to userspace on the capture device side. */ - if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { - buf->state = ISP_BUF_STATE_ERROR; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { + state = VB2_BUF_STATE_ERROR; pipe->error = false; } else { - buf->state = ISP_BUF_STATE_DONE; + state = VB2_BUF_STATE_DONE; } - wake_up(&buf->wait); + vb2_buffer_done(&buf->vb.vb2_buf, state); + + spin_lock_irqsave(&video->irqlock, flags); if (list_empty(&video->dmaqueue)) { - if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + spin_unlock_irqrestore(&video->irqlock, flags); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISP_PIPELINE_QUEUE_OUTPUT | ISP_PIPELINE_STREAM; else @@ -608,16 +516,45 @@ return NULL; } - if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { - spin_lock_irqsave(&pipe->lock, flags); + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { + spin_lock(&pipe->lock); pipe->state &= ~ISP_PIPELINE_STREAM; - spin_unlock_irqrestore(&pipe->lock, flags); + spin_unlock(&pipe->lock); } - buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, + buf = list_first_entry(&video->dmaqueue, struct isp_buffer, irqlist); - buf->state = ISP_BUF_STATE_ACTIVE; - return to_isp_buffer(buf); + + spin_unlock_irqrestore(&video->irqlock, flags); + + return buf; +} + +/* + * omap3isp_video_cancel_stream - Cancel stream on a video node + * @video: ISP video object + * + * Cancelling a stream mark all buffers on the video node as erroneous and makes + * sure no new buffer can be queued. + */ +void omap3isp_video_cancel_stream(struct isp_video *video) +{ + unsigned long flags; + + spin_lock_irqsave(&video->irqlock, flags); + + while (!list_empty(&video->dmaqueue)) { + struct isp_buffer *buf; + + buf = list_first_entry(&video->dmaqueue, + struct isp_buffer, irqlist); + list_del(&buf->irqlist); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + video->error = true; + + spin_unlock_irqrestore(&video->irqlock, flags); } /* @@ -634,12 +571,15 @@ { struct isp_buffer *buf = NULL; - if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - omap3isp_video_queue_discard_done(video->queue); + if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&video->queue_lock); + vb2_discard_done(video->queue); + mutex_unlock(&video->queue_lock); + } if (!list_empty(&video->dmaqueue)) { buf = list_first_entry(&video->dmaqueue, - struct isp_buffer, buffer.irqlist); + struct isp_buffer, irqlist); video->ops->queue(video, buf); video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; } else { @@ -661,10 +601,13 @@ strlcpy(cap->card, video->video.name, sizeof(cap->card)); strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; return 0; } @@ -695,7 +638,40 @@ if (format->type != video->type) return -EINVAL; - mutex_lock(&video->mutex); + /* Replace unsupported field orders with sane defaults. */ + switch (format->fmt.pix.field) { + case V4L2_FIELD_NONE: + /* Progressive is supported everywhere. */ + break; + case V4L2_FIELD_ALTERNATE: + /* ALTERNATE is not supported on output nodes. */ + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_INTERLACED: + /* The ISP has no concept of video standard, select the + * top-bottom order when the unqualified interlaced order is + * requested. + */ + format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; + /* Fall-through */ + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + /* Interlaced orders are only supported at the CCDC output. */ + if (video != &video->isp->isp_ccdc.video_out) + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + default: + /* All other field orders are currently unsupported, default to + * progressive. + */ + format->fmt.pix.field = V4L2_FIELD_NONE; + break; + } /* Fill the bytesperline and sizeimage fields by converting to media bus * format and back to pixel format. @@ -703,9 +679,10 @@ isp_video_pix_to_mbus(&format->fmt.pix, &fmt); isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); + mutex_lock(&video->mutex); vfh->format = *format; - mutex_unlock(&video->mutex); + return 0; } @@ -847,33 +824,56 @@ isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + int ret; - return omap3isp_video_queue_reqbufs(&vfh->queue, rb); + mutex_lock(&video->queue_lock); + ret = vb2_reqbufs(&vfh->queue, rb); + mutex_unlock(&video->queue_lock); + + return ret; } static int isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + int ret; - return omap3isp_video_queue_querybuf(&vfh->queue, b); + mutex_lock(&video->queue_lock); + ret = vb2_querybuf(&vfh->queue, b); + mutex_unlock(&video->queue_lock); + + return ret; } static int isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + int ret; + + mutex_lock(&video->queue_lock); + ret = vb2_qbuf(&vfh->queue, b); + mutex_unlock(&video->queue_lock); - return omap3isp_video_queue_qbuf(&vfh->queue, b); + return ret; } static int isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct isp_video_fh *vfh = to_isp_video_fh(fh); + struct isp_video *video = video_drvdata(file); + int ret; + + mutex_lock(&video->queue_lock); + ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); + mutex_unlock(&video->queue_lock); - return omap3isp_video_queue_dqbuf(&vfh->queue, b, - file->f_flags & O_NONBLOCK); + return ret; } static int isp_video_check_external_subdevs(struct isp_video *video, @@ -893,7 +893,11 @@ struct v4l2_ext_controls ctrls; struct v4l2_ext_control ctrl; unsigned int i; - int ret = 0; + int ret; + + /* Memory-to-memory pipelines have no external subdev. */ + if (pipe->input != NULL) + return 0; for (i = 0; i < ARRAY_SIZE(ents); i++) { /* Is the entity part of the pipeline? */ @@ -901,7 +905,7 @@ continue; /* ISP entities have always sink pad == 0. Find source. */ - source_pad = media_entity_remote_source(&ents[i]->pads[0]); + source_pad = media_entity_remote_pad(&ents[i]->pads[0]); if (source_pad == NULL) continue; @@ -912,7 +916,7 @@ if (!source) { dev_warn(isp->dev, "can't find source, failing now\n"); - return ret; + return -EINVAL; } if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -1009,11 +1013,6 @@ mutex_lock(&video->stream_lock); - if (video->streaming) { - mutex_unlock(&video->stream_lock); - return -EBUSY; - } - /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ @@ -1022,8 +1021,7 @@ pipe->entities = 0; - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); + /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; @@ -1054,11 +1052,6 @@ if (ret < 0) goto err_check_format; - /* Validate the pipeline and update its state. */ - ret = isp_video_validate_pipeline(pipe); - if (ret < 0) - goto err_check_format; - pipe->error = false; spin_lock_irqsave(&pipe->lock, flags); @@ -1076,8 +1069,11 @@ video->queue = &vfh->queue; INIT_LIST_HEAD(&video->dmaqueue); atomic_set(&pipe->frame_number, -1); + pipe->field = vfh->format.fmt.pix.field; - ret = omap3isp_video_queue_streamon(&vfh->queue); + mutex_lock(&video->queue_lock); + ret = vb2_streamon(&vfh->queue, type); + mutex_unlock(&video->queue_lock); if (ret < 0) goto err_check_format; @@ -1090,24 +1086,23 @@ ISP_PIPELINE_STREAM_CONTINUOUS); if (ret < 0) goto err_set_stream; - spin_lock_irqsave(&video->queue->irqlock, flags); + spin_lock_irqsave(&video->irqlock, flags); if (list_empty(&video->dmaqueue)) video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; - spin_unlock_irqrestore(&video->queue->irqlock, flags); + spin_unlock_irqrestore(&video->irqlock, flags); } - video->streaming = 1; - mutex_unlock(&video->stream_lock); return 0; err_set_stream: - omap3isp_video_queue_streamoff(&vfh->queue); + mutex_lock(&video->queue_lock); + vb2_streamoff(&vfh->queue, type); + mutex_unlock(&video->queue_lock); err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ /* 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 * access buffers that might have been freed but still present in the @@ -1138,9 +1133,9 @@ mutex_lock(&video->stream_lock); /* Make sure we're not streaming yet. */ - mutex_lock(&vfh->queue.lock); - streaming = vfh->queue.streaming; - mutex_unlock(&vfh->queue.lock); + mutex_lock(&video->queue_lock); + streaming = vb2_is_streaming(&vfh->queue); + mutex_unlock(&video->queue_lock); if (!streaming) goto done; @@ -1159,12 +1154,15 @@ /* Stop the stream. */ omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); - omap3isp_video_queue_streamoff(&vfh->queue); + omap3isp_video_cancel_stream(video); + + mutex_lock(&video->queue_lock); + vb2_streamoff(&vfh->queue, type); + mutex_unlock(&video->queue_lock); video->queue = NULL; - video->streaming = 0; + video->error = false; - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); done: @@ -1230,6 +1228,7 @@ { struct isp_video *video = video_drvdata(file); struct isp_video_fh *handle; + struct vb2_queue *queue; int ret = 0; handle = kzalloc(sizeof(*handle), GFP_KERNEL); @@ -1251,9 +1250,20 @@ goto done; } - omap3isp_video_queue_init(&handle->queue, video->type, - &isp_video_queue_ops, video->isp->dev, - sizeof(struct isp_buffer)); + queue = &handle->queue; + queue->type = video->type; + queue->io_modes = VB2_MMAP | VB2_USERPTR; + queue->drv_priv = handle; + queue->ops = &isp_video_queue_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->buf_struct_size = sizeof(struct isp_buffer); + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + ret = vb2_queue_init(&handle->queue); + if (ret < 0) { + omap3isp_put(video->isp); + goto done; + } memset(&handle->format, 0, sizeof(handle->format)); handle->format.type = video->type; @@ -1280,9 +1290,9 @@ /* Disable streaming and free the buffers queue resources. */ isp_video_streamoff(file, vfh, video->type); - mutex_lock(&handle->queue.lock); - omap3isp_video_queue_cleanup(&handle->queue); - mutex_unlock(&handle->queue.lock); + mutex_lock(&video->queue_lock); + vb2_queue_release(&handle->queue); + mutex_unlock(&video->queue_lock); omap3isp_pipeline_pm_use(&video->video.entity, 0); @@ -1299,16 +1309,21 @@ static unsigned int isp_video_poll(struct file *file, poll_table *wait) { struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); - struct isp_video_queue *queue = &vfh->queue; + struct isp_video *video = video_drvdata(file); + int ret; - return omap3isp_video_queue_poll(queue, file, wait); + mutex_lock(&video->queue_lock); + ret = vb2_poll(&vfh->queue, file, wait); + mutex_unlock(&video->queue_lock); + + return ret; } 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); - return omap3isp_video_queue_mmap(&vfh->queue, vma); + return vb2_mmap(&vfh->queue, vma); } static struct v4l2_file_operations isp_video_fops = { @@ -1335,11 +1350,13 @@ switch (video->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: direction = "output"; - video->pad.flags = MEDIA_PAD_FL_SINK; + video->pad.flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: direction = "input"; - video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->pad.flags = MEDIA_PAD_FL_SOURCE + | MEDIA_PAD_FL_MUST_CONNECT; video->video.vfl_dir = VFL_DIR_TX; break; @@ -1347,15 +1364,23 @@ return -EINVAL; } + video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev); + if (IS_ERR(video->alloc_ctx)) + return PTR_ERR(video->alloc_ctx); + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); - if (ret < 0) + if (ret < 0) { + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); return ret; + } mutex_init(&video->mutex); atomic_set(&video->active, 0); spin_lock_init(&video->pipe.lock); mutex_init(&video->stream_lock); + mutex_init(&video->queue_lock); + spin_lock_init(&video->irqlock); /* Initialize the video device. */ if (video->ops == NULL) @@ -1376,7 +1401,9 @@ void omap3isp_video_cleanup(struct isp_video *video) { + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); media_entity_cleanup(&video->video.entity); + mutex_destroy(&video->queue_lock); mutex_destroy(&video->stream_lock); mutex_destroy(&video->mutex); }