diff options
Diffstat (limited to 'drivers/media/platform')
99 files changed, 8977 insertions, 964 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 84e041c0a70e..2c2670c44333 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -110,6 +110,7 @@ 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" +source "drivers/media/platform/rcar-vin/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" @@ -152,6 +153,36 @@ config VIDEO_CODA Coda is a range of video codec IPs that supports H.264, MPEG-4, and other video formats. +config VIDEO_MEDIATEK_VPU + tristate "Mediatek Video Processor Unit" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MEDIATEK || COMPILE_TEST + ---help--- + This driver provides downloading VPU firmware and + communicating with VPU. This driver for hw video + codec embedded in Mediatek's MT8173 SOCs. It is able + to handle video decoding/encoding in a range of formats. + + To compile this driver as a module, choose M here: the + module will be called mtk-vpu. + +config VIDEO_MEDIATEK_VCODEC + tristate "Mediatek Video Codec driver" + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MEDIATEK || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_MEDIATEK_VPU + default n + ---help--- + Mediatek video codec driver provides HW capability to + encode and decode in a range of video formats + This driver rely on VPU driver to communicate with VPU. + + To compile this driver as a module, choose M here: the + module will be called mtk-vcodec + config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index bbb7bd1eb268..c6b3b9259a03 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -55,4 +55,10 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ + ccflags-y += -I$(srctree)/drivers/media/i2c + +obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/ diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index e749eb7c3be9..b33b9e35e60e 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1901,21 +1901,20 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); unsigned size = vpfe->fmt.fmt.pix.sizeimage; if (vq->num_buffers + *nbuffers < 3) *nbuffers = 3 - vq->num_buffers; - alloc_ctxs[0] = vpfe->alloc_ctx; if (*nplanes) { if (sizes[0] < size) @@ -2364,13 +2363,6 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) goto probe_out; /* Initialize videobuf2 queue as per the buffer type */ - vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev); - if (IS_ERR(vpfe->alloc_ctx)) { - vpfe_err(vpfe, "Failed to get the context\n"); - err = PTR_ERR(vpfe->alloc_ctx); - goto probe_out; - } - q = &vpfe->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; @@ -2381,11 +2373,11 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &vpfe->lock; q->min_buffers_needed = 1; + q->dev = vpfe->pdev; err = vb2_queue_init(q); if (err) { vpfe_err(vpfe, "vb2_queue_init() failed\n"); - vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx); goto probe_out; } diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 777bf97fea57..17d7aa426788 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -264,8 +264,6 @@ struct vpfe_device { struct v4l2_rect crop; /* Buffer queue used in video-buf */ struct vb2_queue buffer_queue; - /* Allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* Queue of filled frames */ struct list_head dma_queue; /* IRQ lock for DMA queue */ diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index d0092dae7a57..8eb03397d736 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -91,8 +91,6 @@ struct bcap_device { struct bcap_buffer *cur_frm; /* buffer queue used in videobuf2 */ struct vb2_queue buffer_queue; - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* queue of filled frames */ struct list_head dma_queue; /* used in videobuf2 callback */ @@ -203,13 +201,12 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) static int bcap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); if (vq->num_buffers + *nbuffers < 2) *nbuffers = 2; - alloc_ctxs[0] = bcap_dev->alloc_ctx; if (*nplanes) return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0; @@ -820,12 +817,6 @@ static int bcap_probe(struct platform_device *pdev) } bcap_dev->ppi->priv = bcap_dev; - bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(bcap_dev->alloc_ctx)) { - ret = PTR_ERR(bcap_dev->alloc_ctx); - goto err_free_ppi; - } - vfd = &bcap_dev->video_dev; /* initialize field of video device */ vfd->release = video_device_release_empty; @@ -839,7 +830,7 @@ static int bcap_probe(struct platform_device *pdev) if (ret) { v4l2_err(pdev->dev.driver, "Unable to register v4l2 device\n"); - goto err_cleanup_ctx; + goto err_free_ppi; } v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); @@ -863,6 +854,7 @@ static int bcap_probe(struct platform_device *pdev) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &bcap_dev->mutex; q->min_buffers_needed = 1; + q->dev = &pdev->dev; ret = vb2_queue_init(q); if (ret) @@ -967,8 +959,6 @@ err_free_handler: v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); err_unreg_v4l2: v4l2_device_unregister(&bcap_dev->v4l2_dev); -err_cleanup_ctx: - vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); err_free_ppi: ppi_delete_instance(bcap_dev->ppi); err_free_dev: @@ -986,7 +976,6 @@ static int bcap_remove(struct platform_device *pdev) 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); ppi_delete_instance(bcap_dev->ppi); kfree(bcap_dev); return 0; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 133ab9f70f85..c39718a63e5e 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1139,7 +1139,7 @@ static void set_default_params(struct coda_ctx *ctx) */ static int coda_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct coda_ctx *ctx = vb2_get_drv_priv(vq); struct coda_q_data *q_data; @@ -1151,9 +1151,6 @@ static int coda_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - /* Set to vb2-dma-contig allocator context, ignored by vb2-vmalloc */ - alloc_ctxs[0] = ctx->dev->alloc_ctx; - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "get %d buffer(s) of size %d each.\n", *nbuffers, size); @@ -1599,6 +1596,7 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) * that videobuf2 will keep the value of bytesused intact. */ vq->allow_zero_bytesused = 1; + vq->dev = &ctx->dev->plat_dev->dev; return vb2_queue_init(vq); } @@ -2040,16 +2038,10 @@ static void coda_fw_callback(const struct firmware *fw, void *context) if (ret < 0) goto put_pm; - dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(dev->alloc_ctx)) { - v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n"); - goto put_pm; - } - dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); - goto rel_ctx; + goto put_pm; } for (i = 0; i < dev->devtype->num_vdevs; i++) { @@ -2072,8 +2064,6 @@ rel_vfd: while (--i >= 0) video_unregister_device(&dev->vfd[i]); v4l2_m2m_release(dev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); put_pm: pm_runtime_put_sync(&pdev->dev); } @@ -2226,7 +2216,7 @@ static int coda_probe(struct platform_device *pdev) dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); if (IS_ERR(dev->rstc)) { ret = PTR_ERR(dev->rstc); - if (ret == -ENOENT || ret == -ENOSYS) { + if (ret == -ENOENT || ret == -ENOTSUPP) { dev->rstc = NULL; } else { dev_err(&pdev->dev, "failed get reset control: %d\n", @@ -2324,8 +2314,6 @@ static int coda_remove(struct platform_device *pdev) if (dev->m2m_dev) v4l2_m2m_release(dev->m2m_dev); pm_runtime_disable(&pdev->dev); - if (dev->alloc_ctx) - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); destroy_workqueue(dev->workqueue); if (dev->iram.vaddr) diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 8f2c71e06966..53f96661683c 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -92,7 +92,6 @@ struct coda_dev { struct mutex coda_mutex; struct workqueue_struct *workqueue; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; struct list_head instances; unsigned long instance_mask; struct dentry *debugfs_root; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0abcdfe97a6c..0b1709e96673 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -230,7 +230,7 @@ static int vpbe_buffer_prepare(struct vb2_buffer *vb) static int vpbe_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { /* Get the file handle object and layer object */ @@ -242,7 +242,6 @@ vpbe_buffer_queue_setup(struct vb2_queue *vq, /* Store number of buffers allocated in numbuffer member */ if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS) *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers; - alloc_ctxs[0] = layer->alloc_ctx; if (*nplanes) return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0; @@ -1451,20 +1450,13 @@ static int vpbe_display_probe(struct platform_device *pdev) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &disp_dev->dev[i]->opslock; + q->dev = disp_dev->vpbe_dev->pdev; err = vb2_queue_init(q); if (err) { v4l2_err(v4l2_dev, "vb2_queue_init() failed\n"); goto probe_out; } - disp_dev->dev[i]->alloc_ctx = - vb2_dma_contig_init_ctx(disp_dev->vpbe_dev->pdev); - if (IS_ERR(disp_dev->dev[i]->alloc_ctx)) { - v4l2_err(v4l2_dev, "Failed to get the context\n"); - err = PTR_ERR(disp_dev->dev[i]->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue); if (register_device(disp_dev->dev[i], disp_dev, pdev)) { @@ -1482,7 +1474,6 @@ probe_out: for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { /* Unregister video device */ if (disp_dev->dev[k] != NULL) { - vb2_dma_contig_cleanup_ctx(disp_dev->dev[k]->alloc_ctx); video_unregister_device(&disp_dev->dev[k]->video_dev); kfree(disp_dev->dev[k]); } @@ -1510,7 +1501,6 @@ static int vpbe_display_remove(struct platform_device *pdev) for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the layer object */ vpbe_display_layer = disp_dev->dev[i]; - vb2_dma_contig_cleanup_ctx(vpbe_display_layer->alloc_ctx); /* Unregister video device */ video_unregister_device(&vpbe_display_layer->video_dev); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 08f7028c7560..5104cc0ee40e 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -107,14 +107,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -133,7 +133,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = common->alloc_ctx; /* Calculate the offset for Y and C data in the buffer */ vpif_calculate_offsets(ch); @@ -1371,6 +1370,7 @@ static int vpif_probe_complete(void) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &common->lock; + q->dev = vpif_dev; err = vb2_queue_init(q); if (err) { @@ -1378,13 +1378,6 @@ static int vpif_probe_complete(void) goto probe_out; } - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - err = PTR_ERR(common->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&common->dma_queue); /* Initialize the video_device structure */ @@ -1412,7 +1405,6 @@ probe_out: /* Get the pointer to the channel object */ ch = vpif_obj.dev[k]; common = &ch->common[k]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(&ch->video_dev); } @@ -1546,7 +1538,6 @@ static int vpif_remove(struct platform_device *device) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 4a7600929b61..9e35b6771d22 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -65,8 +65,6 @@ struct common_obj { struct v4l2_format fmt; /* Buffer queue used in video-buf */ struct vb2_queue buffer_queue; - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; /* Queue of filled frames */ struct list_head dma_queue; /* Used in video-buf */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index f40755cf1bf2..75b27233ec2f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -102,14 +102,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * @nbuffers: ptr to number of buffers requested by application * @nplanes:: contains number of distinct video planes needed to hold a frame * @sizes[]: contains the size (in bytes) of each plane. - * @alloc_ctxs: ptr to allocation context + * @alloc_devs: ptr to allocation context * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -126,7 +126,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = common->alloc_ctx; /* Calculate the offset for Y and C data in the buffer */ vpif_calculate_offsets(ch); @@ -1191,19 +1190,13 @@ static int vpif_probe_complete(void) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 1; q->lock = &common->lock; + q->dev = vpif_dev; err = vb2_queue_init(q); if (err) { vpif_err("vpif_display: vb2_queue_init() failed\n"); goto probe_out; } - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - err = PTR_ERR(common->alloc_ctx); - goto probe_out; - } - INIT_LIST_HEAD(&common->dma_queue); /* register video device */ @@ -1233,7 +1226,6 @@ probe_out: for (k = 0; k < j; k++) { ch = vpif_obj.dev[k]; common = &ch->common[k]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); video_unregister_device(&ch->video_dev); } return err; @@ -1355,7 +1347,6 @@ static int vpif_remove(struct platform_device *device) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ 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 e7a1723a1b7a..af2765fdcea8 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -74,8 +74,6 @@ struct common_obj { struct v4l2_format fmt; /* Used to store the format */ struct vb2_queue buffer_queue; /* Buffer queue used in * video-buf */ - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; struct list_head dma_queue; /* Queue of filled frames */ spinlock_t irqlock; /* Used in video-buf */ diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c04973669a47..787bd16c19e5 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1123,19 +1123,13 @@ static int gsc_probe(struct platform_device *pdev) if (ret < 0) goto err_m2m; - /* Initialize continious memory allocator */ - gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(gsc->alloc_ctx)) { - ret = PTR_ERR(gsc->alloc_ctx); - goto err_pm; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); pm_runtime_put(dev); return 0; -err_pm: - pm_runtime_put(dev); + err_m2m: gsc_unregister_m2m_device(gsc); err_v4l2: @@ -1152,7 +1146,7 @@ static int gsc_remove(struct platform_device *pdev) gsc_unregister_m2m_device(gsc); v4l2_device_unregister(&gsc->v4l2_dev); - vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); gsc_clk_put(gsc); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index ec4000c72172..7ad7b9dc2243 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -327,7 +327,6 @@ struct gsc_driverdata { * @irq_queue: interrupt handler waitqueue * @m2m: memory-to-memory V4L2 device information * @state: flags used to synchronize m2m and capture mode operation - * @alloc_ctx: videobuf2 memory allocator context * @vdev: video device for G-Scaler instance */ struct gsc_dev { @@ -341,7 +340,6 @@ struct gsc_dev { wait_queue_head_t irq_queue; struct gsc_m2m_device m2m; unsigned long state; - struct vb2_alloc_ctx *alloc_ctx; struct video_device vdev; struct v4l2_device v4l2_dev; }; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index a600e32e2543..ec6494cbdd45 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -213,7 +213,7 @@ put_device: static int gsc_m2m_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct gsc_ctx *ctx = vb2_get_drv_priv(vq); struct gsc_frame *frame; @@ -227,10 +227,8 @@ static int gsc_m2m_queue_setup(struct vb2_queue *vq, return -EINVAL; *num_planes = frame->fmt->num_planes; - for (i = 0; i < frame->fmt->num_planes; i++) { + for (i = 0; i < frame->fmt->num_planes; i++) sizes[i] = frame->payload[i]; - allocators[i] = ctx->gsc_dev->alloc_ctx; - } return 0; } @@ -591,6 +589,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->gsc_dev->lock; + src_vq->dev = &ctx->gsc_dev->pdev->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -605,6 +604,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->gsc_dev->lock; + dst_vq->dev = &ctx->gsc_dev->pdev->dev; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index bf47d3b9cbe7..fdec499fbbda 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -340,7 +340,7 @@ int fimc_capture_resume(struct fimc_dev *fimc) static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_ctx *ctx = vq->drv_priv; struct fimc_frame *frame = &ctx->d_frame; @@ -354,11 +354,9 @@ static int queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = ctx->fimc_dev->alloc_ctx; - } return 0; } @@ -371,8 +369,6 @@ static int queue_setup(struct vb2_queue *vq, sizes[i] = frame->payload[i]; else sizes[i] = max_t(u32, size, frame->payload[i]); - - allocators[i] = ctx->fimc_dev->alloc_ctx; } return 0; @@ -1779,6 +1775,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q->buf_struct_size = sizeof(struct fimc_vid_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &fimc->lock; + q->dev = &fimc->pdev->dev; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index b1c1cea82a27..8f89ca21b631 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1018,19 +1018,11 @@ static int fimc_probe(struct platform_device *pdev) goto err_sd; } - /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(fimc->alloc_ctx)) { - ret = PTR_ERR(fimc->alloc_ctx); - goto err_gclk; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); return 0; -err_gclk: - if (!pm_runtime_enabled(dev)) - clk_disable(fimc->clock[CLK_GATE]); err_sd: fimc_unregister_capture_subdev(fimc); err_sclk: @@ -1123,7 +1115,7 @@ static int fimc_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); fimc_unregister_capture_subdev(fimc); - vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 6b7435453d2a..5615fefbf7af 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -307,7 +307,6 @@ struct fimc_m2m_device { */ struct fimc_vid_cap { struct fimc_ctx *ctx; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct exynos_video_entity ve; struct media_pad vd_pad; @@ -417,7 +416,6 @@ struct fimc_ctx; * @m2m: memory-to-memory V4L2 device information * @vid_cap: camera capture device information * @state: flags used to synchronize m2m and capture mode operation - * @alloc_ctx: videobuf2 memory allocator context * @pipeline: fimc video capture pipeline data structure */ struct fimc_dev { @@ -436,7 +434,6 @@ struct fimc_dev { struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; - struct vb2_alloc_ctx *alloc_ctx; }; /** diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 979c388ebf60..32ca55f16677 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -204,9 +204,6 @@ static int fimc_is_register_subdevs(struct fimc_is *is) if (ret < 0) return ret; - /* Initialize memory allocator context for the ISP DMA. */ - is->isp.alloc_ctx = is->alloc_ctx; - for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { for_each_available_child_of_node(i2c_bus, child) { ret = fimc_is_parse_sensor_config(is, index, child); @@ -847,18 +844,14 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; - is->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(is->alloc_ctx)) { - ret = PTR_ERR(is->alloc_ctx); - goto err_pm; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes * will be created within the subdev's registered() callback. */ ret = fimc_is_register_subdevs(is); if (ret < 0) - goto err_vb; + goto err_pm; ret = fimc_is_debugfs_create(is); if (ret < 0) @@ -877,8 +870,6 @@ err_dfs: fimc_is_debugfs_remove(is); err_sd: fimc_is_unregister_subdevs(is); -err_vb: - vb2_dma_contig_cleanup_ctx(is->alloc_ctx); err_pm: if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); @@ -939,7 +930,7 @@ static int fimc_is_remove(struct platform_device *pdev) fimc_is_runtime_suspend(dev); free_irq(is->irq, is); fimc_is_unregister_subdevs(is); - vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); fimc_is_debugfs_remove(is); release_firmware(is->fw.f_w); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 386eb49ece7e..3a82c6a214c7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -233,7 +233,6 @@ struct chain_config { * @pdev: pointer to FIMC-IS platform device * @pctrl: pointer to pinctrl structure for this device * @v4l2_dev: pointer to top the level v4l2_device - * @alloc_ctx: videobuf2 memory allocator context * @lock: mutex serializing video device and the subdev operations * @slock: spinlock protecting this data structure and the hw registers * @clocks: FIMC-LITE gate clock @@ -256,7 +255,6 @@ struct fimc_is { struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM]; struct fimc_is_setfile setfile; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_ctrl_handler ctrl_handler; struct mutex lock; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index c0816728cbfe..400ce0cb0c0d 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -40,7 +40,7 @@ static int isp_video_capture_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_isp *isp = vb2_get_drv_priv(vq); struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; @@ -57,20 +57,16 @@ static int isp_video_capture_queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = isp->alloc_ctx; - } return 0; } *num_planes = fmt->memplanes; - for (i = 0; i < fmt->memplanes; i++) { + for (i = 0; i < fmt->memplanes; i++) sizes[i] = (wh * fmt->depth[i]) / 8; - allocators[i] = isp->alloc_ctx; - } return 0; } @@ -597,6 +593,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, q->drv_priv = isp; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &isp->video_lock; + q->dev = &isp->pdev->dev; ret = vb2_queue_init(q); if (ret < 0) diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index e0686b5f1bf8..3cdd52491294 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -148,7 +148,6 @@ struct fimc_is_video { /** * struct fimc_isp - FIMC-IS ISP data structure * @pdev: pointer to FIMC-IS platform device - * @alloc_ctx: videobuf2 memory allocator context * @subdev: ISP v4l2_subdev * @subdev_pads: the ISP subdev media pads * @test_pattern: test pattern controls @@ -161,7 +160,6 @@ struct fimc_is_video { */ struct fimc_isp { struct platform_device *pdev; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; struct v4l2_mbus_framefmt src_fmt; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index dc1b929f7a33..a0f149fb88e1 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -357,7 +357,7 @@ static void stop_streaming(struct vb2_queue *q) static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_lite *fimc = vq->drv_priv; struct flite_frame *frame = &fimc->out_frame; @@ -371,20 +371,16 @@ static int queue_setup(struct vb2_queue *vq, if (*num_planes) { if (*num_planes != fmt->memplanes) return -EINVAL; - for (i = 0; i < *num_planes; i++) { + for (i = 0; i < *num_planes; i++) if (sizes[i] < (wh * fmt->depth[i]) / 8) return -EINVAL; - allocators[i] = fimc->alloc_ctx; - } return 0; } *num_planes = fmt->memplanes; - for (i = 0; i < fmt->memplanes; i++) { + for (i = 0; i < fmt->memplanes; i++) sizes[i] = (wh * fmt->depth[i]) / 8; - allocators[i] = fimc->alloc_ctx; - } return 0; } @@ -1300,6 +1296,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->drv_priv = fimc; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &fimc->lock; + q->dev = &fimc->pdev->dev; ret = vb2_queue_init(q); if (ret < 0) @@ -1551,11 +1548,7 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_sd; } - fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(fimc->alloc_ctx)) { - ret = PTR_ERR(fimc->alloc_ctx); - goto err_clk_dis; - } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc_lite_set_default_config(fimc); @@ -1563,9 +1556,6 @@ static int fimc_lite_probe(struct platform_device *pdev) fimc->index); return 0; -err_clk_dis: - if (!pm_runtime_enabled(dev)) - clk_disable(fimc->clock); err_sd: fimc_lite_unregister_capture_subdev(fimc); err_clk_put: @@ -1651,7 +1641,7 @@ static int fimc_lite_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); - vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_lite_clk_put(fimc); dev_info(dev, "Driver unloaded\n"); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 11690d563e06..9ae1e96a1bc7 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -113,7 +113,6 @@ struct flite_buffer { * @ve: exynos video device entity structure * @v4l2_dev: pointer to top the level v4l2_device * @fh: v4l2 file handle - * @alloc_ctx: videobuf2 memory allocator context * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads @@ -148,7 +147,6 @@ struct fimc_lite { struct exynos_video_entity ve; struct v4l2_device *v4l2_dev; struct v4l2_fh fh; - struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 55ec4c99d484..b1309e114edb 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -50,30 +50,28 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (src_vb && dst_vb) { + if (src_vb) v4l2_m2m_buf_done(src_vb, vb_state); + if (dst_vb) v4l2_m2m_buf_done(dst_vb, vb_state); + if (src_vb && dst_vb) v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, ctx->fh.m2m_ctx); - } } /* Complete the transaction which has been scheduled for execution. */ -static int fimc_m2m_shutdown(struct fimc_ctx *ctx) +static void fimc_m2m_shutdown(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; - int ret; if (!fimc_m2m_pending(fimc)) - return 0; + return; fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); - ret = wait_event_timeout(fimc->irq_queue, - !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), - FIMC_SHUTDOWN_TIMEOUT); - - return ret == 0 ? -ETIMEDOUT : ret; + wait_event_timeout(fimc->irq_queue, + !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), + FIMC_SHUTDOWN_TIMEOUT); } static int start_streaming(struct vb2_queue *q, unsigned int count) @@ -88,12 +86,10 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; - int ret; - ret = fimc_m2m_shutdown(ctx); - if (ret == -ETIMEDOUT) - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + fimc_m2m_shutdown(ctx); + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); pm_runtime_put(&ctx->fimc_dev->pdev->dev); } @@ -178,7 +174,7 @@ static void fimc_job_abort(void *priv) static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct fimc_ctx *ctx = vb2_get_drv_priv(vq); struct fimc_frame *f; @@ -195,10 +191,8 @@ static int fimc_queue_setup(struct vb2_queue *vq, return -EINVAL; *num_planes = f->fmt->memplanes; - for (i = 0; i < f->fmt->memplanes; i++) { + for (i = 0; i < f->fmt->memplanes; i++) sizes[i] = f->payload[i]; - allocators[i] = ctx->fimc_dev->alloc_ctx; - } return 0; } @@ -562,6 +556,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->fimc_dev->lock; + src_vq->dev = &ctx->fimc_dev->pdev->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -575,6 +570,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->fimc_dev->lock; + dst_vq->dev = &ctx->fimc_dev->pdev->dev; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index bf954424e7be..86e681daa89d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -649,23 +649,6 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) return 0; } -static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - 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; - format->width = S5PCSIS_DEF_PIX_WIDTH; - format->height = S5PCSIS_DEF_PIX_HEIGHT; - format->field = V4L2_FIELD_NONE; - - return 0; -} - -static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = { - .open = s5pcsis_open, -}; - static struct v4l2_subdev_core_ops s5pcsis_core_ops = { .s_power = s5pcsis_s_power, .log_status = s5pcsis_log_status, diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 7383818c2be6..0fcb5c78031d 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -136,7 +136,6 @@ struct deinterlace_dev { struct dma_chan *dma_chan; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; }; struct deinterlace_ctx { @@ -799,7 +798,7 @@ struct vb2_dc_conf { static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); struct deinterlace_q_data *q_data; @@ -820,8 +819,6 @@ static int deinterlace_queue_setup(struct vb2_queue *vq, *nbuffers = count; sizes[0] = size; - alloc_ctxs[0] = ctx->dev->alloc_ctx; - dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; @@ -874,6 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = ctx->dev->v4l2_dev.dev; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -891,6 +889,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = ctx->dev->v4l2_dev.dev; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; @@ -1046,13 +1045,6 @@ static int deinterlace_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(pcdev->alloc_ctx); - goto err_ctx; - } - pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(pcdev->m2m_dev)) { v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); @@ -1064,8 +1056,6 @@ static int deinterlace_probe(struct platform_device *pdev) err_m2m: video_unregister_device(&pcdev->vfd); -err_ctx: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); unreg_dev: v4l2_device_unregister(&pcdev->v4l2_dev); rel_dma: @@ -1082,7 +1072,6 @@ static int deinterlace_remove(struct platform_device *pdev) v4l2_m2m_release(pcdev->m2m_dev); 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); return 0; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 9b878deb1437..9171174521b2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1051,7 +1051,7 @@ static int mcam_read_setup(struct mcam_camera *cam) static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, unsigned int *num_planes, unsigned int sizes[], - void *alloc_ctxs[]) + struct device *alloc_devs[]) { struct mcam_camera *cam = vb2_get_drv_priv(vq); int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2; @@ -1059,10 +1059,6 @@ static int mcam_vb_queue_setup(struct vb2_queue *vq, if (*nbufs < minbufs) *nbufs = minbufs; - if (cam->buffer_mode == B_DMA_contig) - alloc_ctxs[0] = cam->vb_alloc_ctx; - else if (cam->buffer_mode == B_DMA_sg) - alloc_ctxs[0] = cam->vb_alloc_ctx_sg; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; @@ -1271,6 +1267,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; vq->buf_struct_size = sizeof(struct mcam_vb_buffer); + vq->dev = cam->dev; INIT_LIST_HEAD(&cam->buffers); switch (cam->buffer_mode) { case B_DMA_contig: @@ -1279,9 +1276,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->mem_ops = &vb2_dma_contig_memops; cam->dma_setup = mcam_ctlr_dma_contig; cam->frame_complete = mcam_dma_contig_done; - cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev); - if (IS_ERR(cam->vb_alloc_ctx)) - return PTR_ERR(cam->vb_alloc_ctx); #endif break; case B_DMA_sg: @@ -1290,9 +1284,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->mem_ops = &vb2_dma_sg_memops; cam->dma_setup = mcam_ctlr_dma_sg; cam->frame_complete = mcam_dma_sg_done; - cam->vb_alloc_ctx_sg = vb2_dma_sg_init_ctx(cam->dev); - if (IS_ERR(cam->vb_alloc_ctx_sg)) - return PTR_ERR(cam->vb_alloc_ctx_sg); #endif break; case B_vmalloc: @@ -1309,18 +1300,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) return vb2_queue_init(vq); } -static void mcam_cleanup_vb2(struct mcam_camera *cam) -{ -#ifdef MCAM_MODE_DMA_CONTIG - if (cam->buffer_mode == B_DMA_contig) - vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx); -#endif -#ifdef MCAM_MODE_DMA_SG - if (cam->buffer_mode == B_DMA_sg) - vb2_dma_sg_cleanup_ctx(cam->vb_alloc_ctx_sg); -#endif -} - /* ---------------------------------------------------------------------- */ /* @@ -1875,7 +1854,6 @@ void mccic_shutdown(struct mcam_camera *cam) cam_warn(cam, "Removing a device with users!\n"); mcam_ctlr_power_down(cam); } - mcam_cleanup_vb2(cam); if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); video_unregister_device(&cam->vdev); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 35cd9e5aedf8..beb339f5561f 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -176,8 +176,6 @@ struct mcam_camera { /* DMA buffers - DMA modes */ struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS]; - struct vb2_alloc_ctx *vb_alloc_ctx; - struct vb2_alloc_ctx *vb_alloc_ctx_sg; /* Mode-specific ops, set at open time */ void (*dma_setup)(struct mcam_camera *cam); diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile new file mode 100644 index 000000000000..dc5cb006d600 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/Makefile @@ -0,0 +1,19 @@ + + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o mtk-vcodec-common.o + + + +mtk-vcodec-enc-y := venc/venc_vp8_if.o \ + venc/venc_h264_if.o \ + mtk_vcodec_enc.o \ + mtk_vcodec_enc_drv.o \ + mtk_vcodec_enc_pm.o \ + venc_drv_if.o \ + venc_vpu_if.o \ + + +mtk-vcodec-common-y := mtk_vcodec_intr.o \ + mtk_vcodec_util.o\ + +ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h new file mode 100644 index 000000000000..94f0a425be42 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -0,0 +1,335 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VCODEC_DRV_H_ +#define _MTK_VCODEC_DRV_H_ + +#include <linux/platform_device.h> +#include <linux/videodev2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> + +#include "mtk_vcodec_util.h" + +#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" +#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" +#define MTK_PLATFORM_STR "platform:mt8173" + + +#define MTK_VCODEC_MAX_PLANES 3 +#define MTK_V4L2_BENCHMARK 0 +#define WAIT_INTR_TIMEOUT_MS 1000 + +/** + * enum mtk_hw_reg_idx - MTK hw register base index + */ +enum mtk_hw_reg_idx { + VDEC_SYS, + VDEC_MISC, + VDEC_LD, + VDEC_TOP, + VDEC_CM, + VDEC_AD, + VDEC_AV, + VDEC_PP, + VDEC_HWD, + VDEC_HWQ, + VDEC_HWB, + VDEC_HWG, + NUM_MAX_VDEC_REG_BASE, + /* h264 encoder */ + VENC_SYS = NUM_MAX_VDEC_REG_BASE, + /* vp8 encoder */ + VENC_LT_SYS, + NUM_MAX_VCODEC_REG_BASE +}; + +/** + * enum mtk_instance_type - The type of an MTK Vcodec instance. + */ +enum mtk_instance_type { + MTK_INST_DECODER = 0, + MTK_INST_ENCODER = 1, +}; + +/** + * enum mtk_instance_state - The state of an MTK Vcodec instance. + * @MTK_STATE_FREE - default state when instance is created + * @MTK_STATE_INIT - vcodec instance is initialized + * @MTK_STATE_HEADER - vdec had sps/pps header parsed or venc + * had sps/pps header encoded + * @MTK_STATE_FLUSH - vdec is flushing. Only used by decoder + * @MTK_STATE_ABORT - vcodec should be aborted + */ +enum mtk_instance_state { + MTK_STATE_FREE = 0, + MTK_STATE_INIT = 1, + MTK_STATE_HEADER = 2, + MTK_STATE_FLUSH = 3, + MTK_STATE_ABORT = 4, +}; + +/** + * struct mtk_encode_param - General encoding parameters type + */ +enum mtk_encode_param { + MTK_ENCODE_PARAM_NONE = 0, + MTK_ENCODE_PARAM_BITRATE = (1 << 0), + MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), + MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), + MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), + MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), +}; + +enum mtk_fmt_type { + MTK_FMT_DEC = 0, + MTK_FMT_ENC = 1, + MTK_FMT_FRAME = 2, +}; + +/** + * struct mtk_video_fmt - Structure used to store information about pixelformats + */ +struct mtk_video_fmt { + u32 fourcc; + enum mtk_fmt_type type; + u32 num_planes; +}; + +/** + * struct mtk_codec_framesizes - Structure used to store information about + * framesizes + */ +struct mtk_codec_framesizes { + u32 fourcc; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * struct mtk_q_type - Type of queue + */ +enum mtk_q_type { + MTK_Q_DATA_SRC = 0, + MTK_Q_DATA_DST = 1, +}; + +/** + * struct mtk_q_data - Structure used to store information about queue + */ +struct mtk_q_data { + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + enum v4l2_field field; + unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; + unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; + struct mtk_video_fmt *fmt; +}; + +/** + * struct mtk_enc_params - General encoding parameters + * @bitrate: target bitrate in bits per second + * @num_b_frame: number of b frames between p-frame + * @rc_frame: frame based rate control + * @rc_mb: macroblock based rate control + * @seq_hdr_mode: H.264 sequence header is encoded separately or joined + * with the first frame + * @intra_period: I frame period + * @gop_size: group of picture size, it's used as the intra frame period + * @framerate_num: frame rate numerator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @framerate_denom: frame rate denominator. ex: framerate_num=30 and + * framerate_denom=1 menas FPS is 30 + * @h264_max_qp: Max value for H.264 quantization parameter + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @force_intra: force/insert intra frame + */ +struct mtk_enc_params { + unsigned int bitrate; + unsigned int num_b_frame; + unsigned int rc_frame; + unsigned int rc_mb; + unsigned int seq_hdr_mode; + unsigned int intra_period; + unsigned int gop_size; + unsigned int framerate_num; + unsigned int framerate_denom; + unsigned int h264_max_qp; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int force_intra; +}; + +/** + * struct mtk_vcodec_pm - Power management data structure + */ +struct mtk_vcodec_pm { + struct clk *vcodecpll; + struct clk *univpll_d2; + struct clk *clk_cci400_sel; + struct clk *vdecpll; + struct clk *vdec_sel; + struct clk *vencpll_d2; + struct clk *venc_sel; + struct clk *univpll1_d2; + struct clk *venc_lt_sel; + struct device *larbvdec; + struct device *larbvenc; + struct device *larbvenclt; + struct device *dev; + struct mtk_vcodec_dev *mtkdev; +}; + +/** + * struct mtk_vcodec_ctx - Context (instance) private data. + * + * @type: type of the instance - decoder or encoder + * @dev: pointer to the mtk_vcodec_dev of the device + * @list: link to ctx_list of mtk_vcodec_dev + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue + * of the context + * @id: index of the context that this structure describes + * @state: state of the context + * @param_change: indicate encode parameter type + * @enc_params: encoding parameters + * @enc_if: hoooked encoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to + * finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @encode_work: worker for the encoding + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + */ +struct mtk_vcodec_ctx { + enum mtk_instance_type type; + struct mtk_vcodec_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct mtk_q_data q_data[2]; + int id; + enum mtk_instance_state state; + enum mtk_encode_param param_change; + struct mtk_enc_params enc_params; + + struct venc_common_if *enc_if; + unsigned long drv_handle; + + int int_cond; + int int_type; + wait_queue_head_t queue; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct encode_work; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; +}; + +/** + * struct mtk_vcodec_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_enc: Video device for encoder. + * + * @m2m_dev_enc: m2m device for encoder. + * @plat_dev: platform device + * @vpu_plat_dev: mtk vpu platform device + * @ctx_list: list of struct mtk_vcodec_ctx + * @irqlock: protect data access by irq handler and work thread + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of MTK Vcodec registers. + * + * @id_counter: used to identify current opened instance + * @num_instances: counter of active MTK Vcodec instances + * + * @encode_workqueue: encode work queue + * + * @int_cond: used to identify interrupt condition happen + * @int_type: used to identify what kind of interrupt condition happen + * @dev_mutex: video_device lock + * @queue: waitqueue for waiting for completion of device commands + * + * @enc_irq: h264 encoder irq resource + * @enc_lt_irq: vp8 encoder irq resource + * + * @enc_mutex: encoder hardware lock. + * + * @pm: power management control + * @dec_capability: used to identify decode capability, ex: 4k + * @enc_capability: used to identify encode capability + */ +struct mtk_vcodec_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_enc; + + struct v4l2_m2m_dev *m2m_dev_enc; + struct platform_device *plat_dev; + struct platform_device *vpu_plat_dev; + struct list_head ctx_list; + spinlock_t irqlock; + struct mtk_vcodec_ctx *curr_ctx; + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + + unsigned long id_counter; + int num_instances; + + struct workqueue_struct *encode_workqueue; + + int int_cond; + int int_type; + struct mutex dev_mutex; + wait_queue_head_t queue; + + int enc_irq; + int enc_lt_irq; + + struct mutex enc_mutex; + + struct mtk_vcodec_pm pm; + unsigned int dec_capability; + unsigned int enc_capability; +}; + +static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mtk_vcodec_ctx, fh); +} + +static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); +} + +#endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c new file mode 100644 index 000000000000..6dcae0a0a1f2 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -0,0 +1,1287 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <soc/mediatek/smi.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "venc_drv_if.h" + +#define MTK_VENC_MIN_W 160U +#define MTK_VENC_MIN_H 128U +#define MTK_VENC_MAX_W 1920U +#define MTK_VENC_MAX_H 1088U +#define DFT_CFG_WIDTH MTK_VENC_MIN_W +#define DFT_CFG_HEIGHT MTK_VENC_MIN_H +#define MTK_MAX_CTRLS_HINT 20 +#define OUT_FMT_IDX 0 +#define CAP_FMT_IDX 4 + + +static void mtk_venc_worker(struct work_struct *work); + +static struct mtk_video_fmt mtk_video_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) + +static const struct mtk_codec_framesizes mtk_venc_framesizes[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, + }, +}; + +#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_venc_framesizes) + +static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + struct mtk_enc_params *p = &ctx->enc_params; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", + ctrl->val); + p->bitrate = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_BITRATE; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", + ctrl->val); + p->num_b_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", + ctrl->val); + p->rc_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", + ctrl->val); + p->h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", + ctrl->val); + p->seq_hdr_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", + ctrl->val); + p->rc_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", + ctrl->val); + p->h264_profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", + ctrl->val); + p->h264_level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", + ctrl->val); + p->intra_period = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", + ctrl->val); + p->gop_size = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); + p->force_intra = 1; + ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { + .s_ctrl = vidioc_venc_s_ctrl, +}; + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) +{ + struct mtk_video_fmt *fmt; + int i, j = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (output_queue && mtk_video_formats[i].type != MTK_FMT_FRAME) + continue; + if (!output_queue && mtk_video_formats[i].type != MTK_FMT_ENC) + continue; + + if (j == f->index) { + fmt = &mtk_video_formats[i]; + f->pixelformat = fmt->fourcc; + memset(f->reserved, 0, sizeof(f->reserved)); + return 0; + } + ++j; + } + + return -EINVAL; +} + +static int vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + int i = 0; + + if (fsize->index != 0) + return -EINVAL; + + for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) { + if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc) + continue; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = mtk_venc_framesizes[i].stepwise; + return 0; + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, false); +} + +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, true); +} + +static int vidioc_venc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); + strlcpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + + return 0; +} + +static int vidioc_venc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + ctx->enc_params.framerate_num = + a->parm.output.timeperframe.denominator; + ctx->enc_params.framerate_denom = + a->parm.output.timeperframe.numerator; + ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; + + return 0; +} + +static int vidioc_venc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.timeperframe.denominator = + ctx->enc_params.framerate_num; + a->parm.output.timeperframe.numerator = + ctx->enc_params.framerate_denom; + + return 0; +} + +static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[MTK_Q_DATA_SRC]; + + return &ctx->q_data[MTK_Q_DATA_DST]; +} + +static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &mtk_video_formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + return fmt; + } + + return NULL; +} + +/* V4L2 specification suggests the driver corrects the format struct if any of + * the dimensions is unsupported + */ +static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + int i; + + pix_fmt_mp->field = V4L2_FIELD_NONE; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pix_fmt_mp->num_planes = 1; + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + int tmp_w, tmp_h; + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W); + + /* find next closer width align 16, heign align 32, size align + * 64 rectangle + */ + tmp_w = pix_fmt_mp->width; + tmp_h = pix_fmt_mp->height; + v4l_bound_align_image(&pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W, 4, + &pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H, 5, 6); + + if (pix_fmt_mp->width < tmp_w && + (pix_fmt_mp->width + 16) <= MTK_VENC_MAX_W) + pix_fmt_mp->width += 16; + if (pix_fmt_mp->height < tmp_h && + (pix_fmt_mp->height + 32) <= MTK_VENC_MAX_H) + pix_fmt_mp->height += 32; + + mtk_v4l2_debug(0, + "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->width * pix_fmt_mp->height); + + pix_fmt_mp->num_planes = fmt->num_planes; + pix_fmt_mp->plane_fmt[0].sizeimage = + pix_fmt_mp->width * pix_fmt_mp->height + + ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); + pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; + + if (pix_fmt_mp->num_planes == 2) { + pix_fmt_mp->plane_fmt[1].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + + (ALIGN(pix_fmt_mp->width, 16) * 16); + pix_fmt_mp->plane_fmt[2].sizeimage = 0; + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->width; + pix_fmt_mp->plane_fmt[2].bytesperline = 0; + } else if (pix_fmt_mp->num_planes == 3) { + pix_fmt_mp->plane_fmt[1].sizeimage = + pix_fmt_mp->plane_fmt[2].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + + ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->plane_fmt[2].bytesperline = + pix_fmt_mp->width / 2; + } + } + + for (i = 0; i < pix_fmt_mp->num_planes; i++) + memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, + sizeof(pix_fmt_mp->plane_fmt[0].reserved)); + + pix_fmt_mp->flags = 0; + memset(&pix_fmt_mp->reserved, 0x0, + sizeof(pix_fmt_mp->reserved)); + + return 0; +} + +static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, + struct venc_enc_param *param) +{ + struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC]; + struct mtk_enc_params *enc_params = &ctx->enc_params; + + switch (q_data_src->fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_I420; + break; + case V4L2_PIX_FMT_YVU420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_YV12; + break; + case V4L2_PIX_FMT_NV12M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV21M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV21; + break; + default: + mtk_v4l2_err("Unsupport fourcc =%d", q_data_src->fmt->fourcc); + break; + } + param->h264_profile = enc_params->h264_profile; + param->h264_level = enc_params->h264_level; + + /* Config visible resolution */ + param->width = q_data_src->visible_width; + param->height = q_data_src->visible_height; + /* Config coded resolution */ + param->buf_width = q_data_src->coded_width; + param->buf_height = q_data_src->coded_height; + param->frm_rate = enc_params->framerate_num / + enc_params->framerate_denom; + param->intra_period = enc_params->intra_period; + param->gop_size = enc_params->gop_size; + param->bitrate = enc_params->bitrate; + + mtk_v4l2_debug(0, + "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d", + param->input_yuv_fmt, param->h264_profile, + param->h264_level, param->width, param->height, + param->buf_width, param->buf_height, + param->frm_rate, param->bitrate, + param->gop_size, param->intra_period); +} + +static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int i, ret; + struct mtk_video_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + q_data = mtk_venc_get_q_data(ctx, f->type); + if (!q_data) { + mtk_v4l2_err("fail to get q data"); + return -EINVAL; + } + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + + q_data->fmt = fmt; + ret = vidioc_try_fmt(f, q_data->fmt); + if (ret) + return ret; + + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + q_data->field = f->fmt.pix_mp.field; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + if (ctx->state == MTK_STATE_FREE) { + ret = venc_if_init(ctx, q_data->fmt->fourcc); + if (ret) { + mtk_v4l2_err("venc_if_init failed=%d, codec type=%x", + ret, q_data->fmt->fourcc); + return -EBUSY; + } + ctx->state = MTK_STATE_INIT; + } + + return 0; +} + +static int vidioc_venc_s_fmt_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int ret, i; + struct mtk_video_fmt *fmt; + unsigned int pitch_w_div16; + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + q_data = mtk_venc_get_q_data(ctx, f->type); + if (!q_data) { + mtk_v4l2_err("fail to get q data"); + return -EINVAL; + } + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W); + + q_data->visible_width = f->fmt.pix_mp.width; + q_data->visible_height = f->fmt.pix_mp.height; + q_data->fmt = fmt; + ret = vidioc_try_fmt(f, q_data->fmt); + if (ret) + return ret; + + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + + pitch_w_div16 = DIV_ROUND_UP(q_data->visible_width, 16); + if (pitch_w_div16 % 8 != 0) { + /* Adjust returned width/height, so application could correctly + * allocate hw required memory + */ + q_data->visible_height += 32; + vidioc_try_fmt(f, q_data->fmt); + } + + q_data->field = f->fmt.pix_mp.field; + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quantization = f->fmt.pix_mp.quantization; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + return 0; +} + +static int vidioc_venc_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data; + int i; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, f->type); + + pix->width = q_data->coded_width; + pix->height = q_data->coded_height; + pix->pixelformat = q_data->fmt->fourcc; + pix->field = q_data->field; + pix->num_planes = q_data->fmt->num_planes; + for (i = 0; i < pix->num_planes; i++) { + pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; + pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + memset(&(pix->plane_fmt[i].reserved[0]), 0x0, + sizeof(pix->plane_fmt[i].reserved)); + } + + pix->flags = 0; + pix->colorspace = ctx->colorspace; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + pix->xfer_func = ctx->xfer_func; + + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quantization; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_video_fmt *fmt; + + fmt = mtk_venc_find_format(f); + if (!fmt) { + f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; + fmt = mtk_venc_find_format(f); + } + if (!f->fmt.pix_mp.colorspace) { + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_venc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_venc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = vidioc_venc_qbuf, + .vidioc_dqbuf = vidioc_venc_dqbuf, + + .vidioc_querycap = vidioc_venc_querycap, + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_s_parm = vidioc_venc_s_parm, + .vidioc_g_parm = vidioc_venc_g_parm, + .vidioc_s_fmt_vid_cap_mplane = vidioc_venc_s_fmt_cap, + .vidioc_s_fmt_vid_out_mplane = vidioc_venc_s_fmt_out, + + .vidioc_g_fmt_vid_cap_mplane = vidioc_venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_venc_g_fmt, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, +}; + +static int vb2ops_venc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_q_data *q_data; + unsigned int i; + + q_data = mtk_venc_get_q_data(ctx, vq->type); + + if (q_data == NULL) + return -EINVAL; + + if (*nplanes) { + for (i = 0; i < *nplanes; i++) + if (sizes[i] < q_data->sizeimage[i]) + return -EINVAL; + } else { + *nplanes = q_data->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->sizeimage[i]; + } + + return 0; +} + +static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_q_data *q_data; + int i; + + q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type); + + for (i = 0; i < q_data->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), + q_data->sizeimage[i]); + return -EINVAL; + } + } + + return 0; +} + +static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = + container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + + struct mtk_video_enc_buf *mtk_buf = + container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); + + if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + (ctx->param_change != MTK_ENCODE_PARAM_NONE)) { + mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x", + ctx->id, + mtk_buf->vb.vb2_buf.index, + ctx->param_change); + mtk_buf->param_change = ctx->param_change; + mtk_buf->enc_params = ctx->enc_params; + ctx->param_change = MTK_ENCODE_PARAM_NONE; + } + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); +} + +static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct venc_enc_param param; + int ret; + int i; + + /* Once state turn into MTK_STATE_ABORT, we need stop_streaming + * to clear it + */ + if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) { + ret = -EIO; + goto err_set_param; + } + + /* Do the initialization when both start_streaming have been called */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q)) + return 0; + } else { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q)) + return 0; + } + + mtk_venc_set_param(ctx, ¶m); + ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->param_change = MTK_ENCODE_PARAM_NONE; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->enc_params.seq_hdr_mode != + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) { + ret = venc_if_set_param(ctx, + VENC_SET_PARAM_PREPEND_HEADER, + NULL); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->state = MTK_STATE_HEADER; + } + + return 0; + +err_set_param: + for (i = 0; i < q->num_buffers; ++i) { + if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { + mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", + ctx->id, i, q->type, + (int)q->bufs[i]->state); + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]), + VB2_BUF_STATE_QUEUED); + } + } + + return ret; +} + +static void vb2ops_venc_stop_streaming(struct vb2_queue *q) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_buffer *src_buf, *dst_buf; + int ret; + + mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { + dst_buf->planes[0].bytesused = 0; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + } + } else { + while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_ERROR); + } + + if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) || + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) { + mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d", + ctx->id, q->type, + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); + return; + } + + /* Release the encoder if both streams are stopped. */ + ret = venc_if_deinit(ctx); + if (ret) + mtk_v4l2_err("venc_if_deinit failed=%d", ret); + + ctx->state = MTK_STATE_FREE; +} + +static struct vb2_ops mtk_venc_vb2_ops = { + .queue_setup = vb2ops_venc_queue_setup, + .buf_prepare = vb2ops_venc_buf_prepare, + .buf_queue = vb2ops_venc_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vb2ops_venc_start_streaming, + .stop_streaming = vb2ops_venc_stop_streaming, +}; + +static int mtk_venc_encode_header(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + struct vb2_buffer *dst_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + mtk_v4l2_debug(1, "No dst buffer"); + return -EINVAL; + } + + bs_buf.va = vb2_plane_vaddr(dst_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.size = (size_t)dst_buf->planes[0].length; + + mtk_v4l2_debug(1, + "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", + ctx->id, + dst_buf->index, bs_buf.va, + (u64)bs_buf.dma_addr, + bs_buf.size); + + ret = venc_if_encode(ctx, + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + NULL, &bs_buf, &enc_result); + + if (ret) { + dst_buf->planes[0].bytesused = 0; + ctx->state = MTK_STATE_ABORT; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + return -EINVAL; + } + + ctx->state = MTK_STATE_HEADER; + dst_buf->planes[0].bytesused = enc_result.bs_size; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE); + + return 0; +} + +static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) +{ + struct venc_enc_param enc_prm; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + struct vb2_v4l2_buffer *vb2_v4l2 = + container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + struct mtk_video_enc_buf *mtk_buf = + container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); + + int ret = 0; + + memset(&enc_prm, 0, sizeof(enc_prm)); + if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE) + return 0; + + if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) { + enc_prm.bitrate = mtk_buf->enc_params.bitrate; + mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + enc_prm.bitrate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_BITRATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) { + enc_prm.frm_rate = mtk_buf->enc_params.framerate_num / + mtk_buf->enc_params.framerate_denom; + mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + enc_prm.frm_rate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_FRAMERATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) { + enc_prm.gop_size = mtk_buf->enc_params.gop_size; + mtk_v4l2_debug(1, "change param intra period=%d", + enc_prm.gop_size); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_GOP_SIZE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) { + mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d", + ctx->id, + mtk_buf->vb.vb2_buf.index, + mtk_buf->enc_params.force_intra); + if (mtk_buf->enc_params.force_intra) + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_FORCE_INTRA, + NULL); + } + + mtk_buf->param_change = MTK_ENCODE_PARAM_NONE; + + if (ret) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_err("venc_if_set_param %d failed=%d", + mtk_buf->param_change, ret); + return -1; + } + + return 0; +} + +/* + * v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker() + * to call v4l2_m2m_job_finish(). + * If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock. + * So this function must not try to acquire dev->dev_mutex. + * This means v4l2 ioctls and mtk_venc_worker() can run at the same time. + * mtk_venc_worker() should be carefully implemented to avoid bugs. + */ +static void mtk_venc_worker(struct work_struct *work) +{ + struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, + encode_work); + struct vb2_buffer *src_buf, *dst_buf; + struct venc_frm_buf frm_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + int ret, i; + struct vb2_v4l2_buffer *vb2_v4l2; + + /* check dst_buf, dst_buf may be removed in device_run + * to stored encdoe header so we need check dst_buf and + * call job_finish here to prevent recursion + */ + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + return; + } + + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + memset(&frm_buf, 0, sizeof(frm_buf)); + for (i = 0; i < src_buf->num_planes ; i++) { + frm_buf.fb_addr[i].va = vb2_plane_vaddr(src_buf, i); + frm_buf.fb_addr[i].dma_addr = + vb2_dma_contig_plane_dma_addr(src_buf, i); + frm_buf.fb_addr[i].size = + (size_t)src_buf->planes[i].length; + } + bs_buf.va = vb2_plane_vaddr(dst_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.size = (size_t)dst_buf->planes[0].length; + + mtk_v4l2_debug(2, + "Framebuf VA=%p PA=%llx Size=0x%lx;VA=%p PA=0x%llx Size=0x%lx;VA=%p PA=0x%llx Size=%zu", + frm_buf.fb_addr[0].va, + (u64)frm_buf.fb_addr[0].dma_addr, + frm_buf.fb_addr[0].size, + frm_buf.fb_addr[1].va, + (u64)frm_buf.fb_addr[1].dma_addr, + frm_buf.fb_addr[1].size, + frm_buf.fb_addr[2].va, + (u64)frm_buf.fb_addr[2].dma_addr, + frm_buf.fb_addr[2].size); + + ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, + &frm_buf, &bs_buf, &enc_result); + + vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf); + if (enc_result.is_key_frm) + vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (ret) { + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_ERROR); + dst_buf->planes[0].bytesused = 0; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + } else { + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), + VB2_BUF_STATE_DONE); + dst_buf->planes[0].bytesused = enc_result.bs_size; + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), + VB2_BUF_STATE_DONE); + mtk_v4l2_debug(2, "venc_if_encode bs size=%d", + enc_result.bs_size); + } + + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + + mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", + src_buf->index, dst_buf->index, ret, + enc_result.bs_size); +} + +static void m2mops_venc_device_run(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->state != MTK_STATE_HEADER)) { + /* encode h264 sps/pps header */ + mtk_venc_encode_header(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); + return; + } + + mtk_venc_param_change(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); +} + +static int m2mops_venc_job_ready(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) { + mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.", + ctx->id, ctx->state); + return 0; + } + + return 1; +} + +static void m2mops_venc_job_abort(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + ctx->state = MTK_STATE_ABORT; +} + +static void m2mops_venc_lock(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + mutex_lock(&ctx->dev->dev_mutex); +} + +static void m2mops_venc_unlock(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + mutex_unlock(&ctx->dev->dev_mutex); +} + +const struct v4l2_m2m_ops mtk_venc_m2m_ops = { + .device_run = m2mops_venc_device_run, + .job_ready = m2mops_venc_job_ready, + .job_abort = m2mops_venc_job_abort, + .lock = m2mops_venc_lock, + .unlock = m2mops_venc_unlock, +}; + +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_q_data *q_data; + + ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + INIT_WORK(&ctx->encode_work, mtk_venc_worker); + + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->field = V4L2_FIELD_NONE; + + q_data->fmt = &mtk_video_formats[OUT_FMT_IDX]; + + v4l_bound_align_image(&q_data->coded_width, + MTK_VENC_MIN_W, + MTK_VENC_MAX_W, 4, + &q_data->coded_height, + MTK_VENC_MIN_H, + MTK_VENC_MAX_H, 5, 6); + + if (q_data->coded_width < DFT_CFG_WIDTH && + (q_data->coded_width + 16) <= MTK_VENC_MAX_W) + q_data->coded_width += 16; + if (q_data->coded_height < DFT_CFG_HEIGHT && + (q_data->coded_height + 32) <= MTK_VENC_MAX_H) + q_data->coded_height += 32; + + q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height; + q_data->bytesperline[0] = q_data->coded_width; + q_data->sizeimage[1] = q_data->sizeimage[0] / 2; + q_data->bytesperline[1] = q_data->coded_width; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->fmt = &mtk_video_formats[CAP_FMT_IDX]; + q_data->field = V4L2_FIELD_NONE; + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = + DFT_CFG_WIDTH * DFT_CFG_HEIGHT; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0; + +} + +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) +{ + const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops; + struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; + + v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT); + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE, + 1, 4000000, 1, 4000000); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, + 0, 2, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 1); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + 0, 0, 0, 0); + v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + 0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + 0, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + if (handler->error) { + mtk_v4l2_err("Init control handler fail %d", + handler->error); + return handler->error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + + return 0; +} + +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + + /* Note: VB2_USERPTR works with dma-contig because mt8173 + * support iommu + * https://patchwork.kernel.org/patch/8335461/ + * https://patchwork.kernel.org/patch/7596181/ + */ + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf); + src_vq->ops = &mtk_venc_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = &ctx->dev->plat_dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &mtk_venc_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = &ctx->dev->plat_dev->dev; + + return vb2_queue_init(dst_vq); +} + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_unlock(&dev->enc_mutex); + return 0; +} + +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_lock(&dev->enc_mutex); + return 0; +} + +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx) +{ + venc_if_deinit(ctx); +} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h new file mode 100644 index 000000000000..d7a154a97510 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VCODEC_ENC_H_ +#define _MTK_VCODEC_ENC_H_ + +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + +#define MTK_VENC_IRQ_STATUS_SPS 0x1 +#define MTK_VENC_IRQ_STATUS_PPS 0x2 +#define MTK_VENC_IRQ_STATUS_FRM 0x4 +#define MTK_VENC_IRQ_STATUS_DRAM 0x8 +#define MTK_VENC_IRQ_STATUS_PAUSE 0x10 +#define MTK_VENC_IRQ_STATUS_SWITCH 0x20 + +#define MTK_VENC_IRQ_STATUS_OFFSET 0x05C +#define MTK_VENC_IRQ_ACK_OFFSET 0x060 + +/** + * struct mtk_video_enc_buf - Private data related to each VB2 buffer. + * @vb: Pointer to related VB2 buffer. + * @list: list that buffer link to + * @param_change: Types of encode parameter change before encoding this + * buffer + * @enc_params: Encode parameters changed before encode this buffer + */ +struct mtk_video_enc_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + u32 param_change; + struct mtk_enc_params enc_params; +}; + +extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops; +extern const struct v4l2_m2m_ops mtk_venc_m2m_ops; + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx); +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx); + +#endif /* _MTK_VCODEC_ENC_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c new file mode 100644 index 000000000000..9c10cc24a5b1 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -0,0 +1,442 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <linux/pm_runtime.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + +module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR); +module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR); + +/* Wake up context wait_queue */ +static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason) +{ + ctx->int_cond = 1; + ctx->int_type = reason; + wake_up_interruptible(&ctx->queue); +} + +static void clean_irq_status(unsigned int irq_status, void __iomem *addr) +{ + if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) + writel(MTK_VENC_IRQ_STATUS_PAUSE, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH) + writel(MTK_VENC_IRQ_STATUS_SWITCH, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_DRAM) + writel(MTK_VENC_IRQ_STATUS_DRAM, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SPS) + writel(MTK_VENC_IRQ_STATUS_SPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_PPS) + writel(MTK_VENC_IRQ_STATUS_PPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_FRM) + writel(MTK_VENC_IRQ_STATUS_FRM, addr); + +} +static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + unsigned long flags; + void __iomem *addr; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + + mtk_v4l2_debug(1, "id=%d", ctx->id); + addr = dev->reg_base[VENC_SYS] + MTK_VENC_IRQ_ACK_OFFSET; + + ctx->irq_status = readl(dev->reg_base[VENC_SYS] + + (MTK_VENC_IRQ_STATUS_OFFSET)); + + clean_irq_status(ctx->irq_status, addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); + return IRQ_HANDLED; +} + +static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + unsigned long flags; + void __iomem *addr; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + + mtk_v4l2_debug(1, "id=%d", ctx->id); + ctx->irq_status = readl(dev->reg_base[VENC_LT_SYS] + + (MTK_VENC_IRQ_STATUS_OFFSET)); + + addr = dev->reg_base[VENC_LT_SYS] + MTK_VENC_IRQ_ACK_OFFSET; + + clean_irq_status(ctx->irq_status, addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); + return IRQ_HANDLED; +} + +static void mtk_vcodec_enc_reset_handler(void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + + mtk_v4l2_debug(0, "Watchdog timeout!!"); + + mutex_lock(&dev->dev_mutex); + list_for_each_entry(ctx, &dev->ctx_list, list) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", + ctx->id); + } + mutex_unlock(&dev->dev_mutex); +} + +static int fops_vcodec_open(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = NULL; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_lock(&dev->dev_mutex); + /* + * Use simple counter to uniquely identify this context. Only + * used for logging. + */ + ctx->id = dev->id_counter++; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + INIT_LIST_HEAD(&ctx->list); + ctx->dev = dev; + init_waitqueue_head(&ctx->queue); + + ctx->type = MTK_INST_ENCODER; + ret = mtk_vcodec_enc_ctrls_setup(ctx); + if (ret) { + mtk_v4l2_err("Failed to setup controls() (%d)", + ret); + goto err_ctrls_setup; + } + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, + &mtk_vcodec_enc_queue_init); + if (IS_ERR((__force void *)ctx->m2m_ctx)) { + ret = PTR_ERR((__force void *)ctx->m2m_ctx); + mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", + ret); + goto err_m2m_ctx_init; + } + mtk_vcodec_enc_set_default_params(ctx); + + if (v4l2_fh_is_singular(&ctx->fh)) { + /* + * vpu_load_firmware checks if it was loaded already and + * does nothing in that case + */ + ret = vpu_load_firmware(dev->vpu_plat_dev); + if (ret < 0) { + /* + * Return 0 if downloading firmware successfully, + * otherwise it is failed + */ + mtk_v4l2_err("vpu_load_firmware failed!"); + goto err_load_fw; + } + + dev->enc_capability = + vpu_get_venc_hw_capa(dev->vpu_plat_dev); + mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); + } + + mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", + ctx->id, ctx, ctx->m2m_ctx); + + dev->num_instances++; + list_add(&ctx->list, &dev->ctx_list); + + mutex_unlock(&dev->dev_mutex); + mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), + ctx->id); + return ret; + + /* Deinit when failure occurred */ +err_load_fw: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_ctrls_setup: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + + mtk_v4l2_debug(1, "[%d] encoder", ctx->id); + mutex_lock(&dev->dev_mutex); + + mtk_vcodec_enc_release(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + + list_del_init(&ctx->list); + dev->num_instances--; + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + return 0; +} + +static const struct v4l2_file_operations mtk_vcodec_fops = { + .owner = THIS_MODULE, + .open = fops_vcodec_open, + .release = fops_vcodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int mtk_vcodec_probe(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev; + struct video_device *vfd_enc; + struct resource *res; + int i, j, ret; + DEFINE_DMA_ATTRS(attrs); + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->ctx_list); + dev->plat_dev = pdev; + + dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev); + if (dev->vpu_plat_dev == NULL) { + mtk_v4l2_err("[VPU] vpu device in not ready"); + return -EPROBE_DEFER; + } + + vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler, + dev, VPU_RST_ENC); + + ret = mtk_vcodec_init_enc_pm(dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get mt vcodec clock source!"); + return ret; + } + + for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, j); + if (res == NULL) { + dev_err(&pdev->dev, "get memory resource failed."); + ret = -ENXIO; + goto err_res; + } + dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((__force void *)dev->reg_base[i])) { + dev_err(&pdev->dev, + "devm_ioremap_resource %d failed.", i); + ret = PTR_ERR((__force void *)dev->reg_base[i]); + goto err_res; + } + mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get irq resource"); + ret = -ENOENT; + goto err_res; + } + + dev->enc_irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, dev->enc_irq, + mtk_vcodec_enc_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "Failed to install dev->enc_irq %d (%d)", + dev->enc_irq, + ret); + ret = -EINVAL; + goto err_res; + } + + dev->enc_lt_irq = platform_get_irq(pdev, 1); + ret = devm_request_irq(&pdev->dev, + dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to install dev->enc_lt_irq %d (%d)", + dev->enc_lt_irq, ret); + ret = -EINVAL; + goto err_res; + } + + disable_irq(dev->enc_irq); + disable_irq(dev->enc_lt_irq); /* VENC_LT */ + mutex_init(&dev->enc_mutex); + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irqlock); + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + "[MTK_V4L2_VENC]"); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + mtk_v4l2_err("v4l2_device_register err=%d", ret); + goto err_res; + } + + init_waitqueue_head(&dev->queue); + + /* allocate video device for encoder and register it */ + vfd_enc = video_device_alloc(); + if (!vfd_enc) { + mtk_v4l2_err("Failed to allocate video device"); + ret = -ENOMEM; + goto err_enc_alloc; + } + vfd_enc->fops = &mtk_vcodec_fops; + vfd_enc->ioctl_ops = &mtk_venc_ioctl_ops; + vfd_enc->release = video_device_release; + vfd_enc->lock = &dev->dev_mutex; + vfd_enc->v4l2_dev = &dev->v4l2_dev; + vfd_enc->vfl_dir = VFL_DIR_M2M; + vfd_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + + snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s", + MTK_VCODEC_ENC_NAME); + video_set_drvdata(vfd_enc, dev); + dev->vfd_enc = vfd_enc; + platform_set_drvdata(pdev, dev); + + dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); + if (IS_ERR((__force void *)dev->m2m_dev_enc)) { + mtk_v4l2_err("Failed to init mem2mem enc device"); + ret = PTR_ERR((__force void *)dev->m2m_dev_enc); + goto err_enc_mem_init; + } + + dev->encode_workqueue = + alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME, + WQ_MEM_RECLAIM | + WQ_FREEZABLE); + if (!dev->encode_workqueue) { + mtk_v4l2_err("Failed to create encode workqueue"); + ret = -EINVAL; + goto err_event_workq; + } + + ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1); + if (ret) { + mtk_v4l2_err("Failed to register video device"); + goto err_enc_reg; + } + + /* Avoid the iommu eat big hunks */ + dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs); + + mtk_v4l2_debug(0, "encoder registered as /dev/video%d", + vfd_enc->num); + + return 0; + +err_enc_reg: + destroy_workqueue(dev->encode_workqueue); +err_event_workq: + v4l2_m2m_release(dev->m2m_dev_enc); +err_enc_mem_init: + video_unregister_device(vfd_enc); +err_enc_alloc: + v4l2_device_unregister(&dev->v4l2_dev); +err_res: + mtk_vcodec_release_enc_pm(dev); + return ret; +} + +static const struct of_device_id mtk_vcodec_enc_match[] = { + {.compatible = "mediatek,mt8173-vcodec-enc",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); + +static int mtk_vcodec_enc_remove(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + + mtk_v4l2_debug_enter(); + flush_workqueue(dev->encode_workqueue); + destroy_workqueue(dev->encode_workqueue); + if (dev->m2m_dev_enc) + v4l2_m2m_release(dev->m2m_dev_enc); + + if (dev->vfd_enc) + video_unregister_device(dev->vfd_enc); + + v4l2_device_unregister(&dev->v4l2_dev); + mtk_vcodec_release_enc_pm(dev); + return 0; +} + +static struct platform_driver mtk_vcodec_enc_driver = { + .probe = mtk_vcodec_probe, + .remove = mtk_vcodec_enc_remove, + .driver = { + .name = MTK_VCODEC_ENC_NAME, + .owner = THIS_MODULE, + .of_match_table = mtk_vcodec_enc_match, + }, +}; + +module_platform_driver(mtk_vcodec_enc_driver); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c new file mode 100644 index 000000000000..2379e9766c4d --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -0,0 +1,137 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#include <linux/clk.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <soc/mediatek/smi.h> + +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + + +int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) +{ + struct device_node *node; + struct platform_device *pdev; + struct device *dev; + struct mtk_vcodec_pm *pm; + int ret = 0; + + pdev = mtkdev->plat_dev; + pm = &mtkdev->pm; + memset(pm, 0, sizeof(struct mtk_vcodec_pm)); + pm->mtkdev = mtkdev; + pm->dev = &pdev->dev; + dev = &pdev->dev; + + node = of_parse_phandle(dev->of_node, "mediatek,larb", 0); + if (!node) { + mtk_v4l2_err("no mediatek,larb found"); + return -1; + } + pdev = of_find_device_by_node(node); + if (!pdev) { + mtk_v4l2_err("no mediatek,larb device found"); + return -1; + } + pm->larbvenc = &pdev->dev; + + node = of_parse_phandle(dev->of_node, "mediatek,larb", 1); + if (!node) { + mtk_v4l2_err("no mediatek,larb found"); + return -1; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + mtk_v4l2_err("no mediatek,larb device found"); + return -1; + } + + pm->larbvenclt = &pdev->dev; + pdev = mtkdev->plat_dev; + pm->dev = &pdev->dev; + + pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src"); + if (pm->vencpll_d2 == NULL) { + mtk_v4l2_err("devm_clk_get vencpll_d2 fail"); + ret = -1; + } + + pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel"); + if (pm->venc_sel == NULL) { + mtk_v4l2_err("devm_clk_get venc_sel fail"); + ret = -1; + } + + pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src"); + if (pm->univpll1_d2 == NULL) { + mtk_v4l2_err("devm_clk_get univpll1_d2 fail"); + ret = -1; + } + + pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); + if (pm->venc_lt_sel == NULL) { + mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); + ret = -1; + } + + return ret; +} + +void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) +{ +} + + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) +{ + int ret; + + ret = clk_prepare_enable(pm->venc_sel); + if (ret) + mtk_v4l2_err("clk_prepare_enable fail %d", ret); + + ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2); + if (ret) + mtk_v4l2_err("clk_set_parent fail %d", ret); + + ret = clk_prepare_enable(pm->venc_lt_sel); + if (ret) + mtk_v4l2_err("clk_prepare_enable fail %d", ret); + + ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2); + if (ret) + mtk_v4l2_err("clk_set_parent fail %d", ret); + + ret = mtk_smi_larb_get(pm->larbvenc); + if (ret) + mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret); + + ret = mtk_smi_larb_get(pm->larbvenclt); + if (ret) + mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret); + +} + +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) +{ + mtk_smi_larb_put(pm->larbvenc); + mtk_smi_larb_put(pm->larbvenclt); + clk_disable_unprepare(pm->venc_lt_sel); + clk_disable_unprepare(pm->venc_sel); +} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h new file mode 100644 index 000000000000..f32167138976 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VCODEC_ENC_PM_H_ +#define _MTK_VCODEC_ENC_PM_H_ + +#include "mtk_vcodec_drv.h" + +int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *dev); +void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *dev); + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); + +#endif /* _MTK_VCODEC_ENC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c new file mode 100644 index 000000000000..52e7e5c9afa0 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#include <linux/errno.h> +#include <linux/wait.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command, + unsigned int timeout_ms) +{ + wait_queue_head_t *waitqueue; + long timeout_jiff, ret; + int status = 0; + + waitqueue = (wait_queue_head_t *)&ctx->queue; + timeout_jiff = msecs_to_jiffies(timeout_ms); + + ret = wait_event_interruptible_timeout(*waitqueue, + (ctx->int_cond && + (ctx->int_type == command)), + timeout_jiff); + + if (!ret) { + status = -1; /* timeout */ + mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!", + ctx->id, ctx->type, command, timeout_ms, + ctx->int_cond, ctx->int_type); + } else if (-ERESTARTSYS == ret) { + mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", + ctx->id, ctx->type, command, ctx->int_cond, + ctx->int_type); + status = -1; + } + + ctx->int_cond = 0; + ctx->int_type = 0; + + return status; +} +EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h new file mode 100644 index 000000000000..33e890f5aa9c --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VCODEC_INTR_H_ +#define _MTK_VCODEC_INTR_H_ + +#define MTK_INST_IRQ_RECEIVED 0x1 +#define MTK_INST_WORK_THREAD_ABORT_DONE 0x2 + +struct mtk_vcodec_ctx; + +/* timeout is ms */ +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *data, int command, + unsigned int timeout_ms); + +#endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c new file mode 100644 index 000000000000..5e3651372a3c --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#include <linux/module.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" +#include "mtk_vpu.h" + +/* For encoder, this will enable logs in venc/*/ +bool mtk_vcodec_dbg; +EXPORT_SYMBOL(mtk_vcodec_dbg); + +/* The log level of v4l2 encoder or decoder driver. + * That is, files under mtk-vcodec/. + */ +int mtk_v4l2_dbg_level; +EXPORT_SYMBOL(mtk_v4l2_dbg_level); + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx) +{ + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + + if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { + mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); + return NULL; + } + return ctx->dev->reg_base[reg_idx]; +} +EXPORT_SYMBOL(mtk_vcodec_get_reg_addr); + +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); + + if (!mem->va) { + mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), + size); + return -ENOMEM; + } + + memset(mem->va, 0, size); + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + return 0; +} +EXPORT_SYMBOL(mtk_vcodec_mem_alloc); + +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + if (!mem->va) { + mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), + size); + return; + } + + dma_free_coherent(dev, size, mem->va, mem->dma_addr); + mem->va = NULL; + mem->dma_addr = 0; + mem->size = 0; + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); +} +EXPORT_SYMBOL(mtk_vcodec_mem_free); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h new file mode 100644 index 000000000000..d6345fc04840 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen <pc.chen@mediatek.com> +* Tiffany Lin <tiffany.lin@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VCODEC_UTIL_H_ +#define _MTK_VCODEC_UTIL_H_ + +#include <linux/types.h> +#include <linux/dma-direction.h> + +struct mtk_vcodec_mem { + size_t size; + void *va; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_ctx; + +extern int mtk_v4l2_dbg_level; +extern bool mtk_vcodec_dbg; + +#define DEBUG 1 + +#if defined(DEBUG) + +#define mtk_v4l2_debug(level, fmt, args...) \ + do { \ + if (mtk_v4l2_dbg_level >= level) \ + pr_info("[MTK_V4L2] level=%d %s(),%d: " fmt "\n",\ + level, __func__, __LINE__, ##args); \ + } while (0) + +#define mtk_v4l2_err(fmt, args...) \ + pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \ + ##args) + + +#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") +#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") + +#define mtk_vcodec_debug(h, fmt, args...) \ + do { \ + if (mtk_vcodec_dbg) \ + pr_info("[MTK_VCODEC][%d]: %s() " fmt "\n", \ + ((struct mtk_vcodec_ctx *)h->ctx)->id, \ + __func__, ##args); \ + } while (0) + +#define mtk_vcodec_err(h, fmt, args...) \ + pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \ + ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args) + +#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") +#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") + +#else + +#define mtk_v4l2_debug(level, fmt, args...) +#define mtk_v4l2_err(fmt, args...) +#define mtk_v4l2_debug_enter() +#define mtk_v4l2_debug_leave() + +#define mtk_vcodec_debug(h, fmt, args...) +#define mtk_vcodec_err(h, fmt, args...) +#define mtk_vcodec_debug_enter(h) +#define mtk_vcodec_debug_leave(h) + +#endif + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx); +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c new file mode 100644 index 000000000000..f4e18bb44cb9 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> + * Daniel Hsiao <daniel.hsiao@mediatek.com> + * PoChun Lin <pochun.lin@mediatek.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. + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" +#include "mtk_vpu.h" + +static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; + +#define H264_FILLER_MARKER_SIZE ARRAY_SIZE(h264_filler_marker) +#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098 + +/** + * enum venc_h264_vpu_work_buf - h264 encoder buffer index + */ +enum venc_h264_vpu_work_buf { + VENC_H264_VPU_WORK_BUF_RC_INFO, + VENC_H264_VPU_WORK_BUF_RC_CODE, + VENC_H264_VPU_WORK_BUF_REC_LUMA, + VENC_H264_VPU_WORK_BUF_REC_CHROMA, + VENC_H264_VPU_WORK_BUF_REF_LUMA, + VENC_H264_VPU_WORK_BUF_REF_CHROMA, + VENC_H264_VPU_WORK_BUF_MV_INFO_1, + VENC_H264_VPU_WORK_BUF_MV_INFO_2, + VENC_H264_VPU_WORK_BUF_SKIP_FRAME, + VENC_H264_VPU_WORK_BUF_MAX, +}; + +/** + * enum venc_h264_bs_mode - for bs_mode argument in h264_enc_vpu_encode + */ +enum venc_h264_bs_mode { + H264_BS_MODE_SPS, + H264_BS_MODE_PPS, + H264_BS_MODE_FRAME, +}; + +/* + * struct venc_h264_vpu_config - Structure for h264 encoder configuration + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to + * hardware requirements. + * @buf_h: buffer height + * @gop_size: group of picture size (idr frame) + * @intra_period: intra frame period + * @framerate: frame rate in fps + * @profile: as specified in standard + * @level: as specified in standard + * @wfd: WFD mode 1:on, 0:off + */ +struct venc_h264_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 intra_period; + u32 framerate; + u32 profile; + u32 level; + u32 wfd; +}; + +/* + * struct venc_h264_vpu_buf - Structure for buffer information + * @align: buffer alignment (in bytes) + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_h264_vpu_buf { + u32 align; + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_h264_vsi - Structure for VPU driver control and info share + * This structure is allocated in VPU side and shared to AP side. + * @config: h264 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_h264_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_h264_vsi { + struct venc_h264_vpu_config config; + struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_h264_inst - h264 encoder AP driver instance + * @hw_base: h264 encoder hardware register base + * @work_bufs: working buffer + * @pps_buf: buffer to store the pps bitstream + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count + * @prepend_hdr: when the v4l2 layer send VENC_SET_PARAM_PREPEND_HEADER cmd + * through h264_enc_set_param interface, it will set this flag and prepend the + * sps/pps in h264_enc_encode function. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_h264_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; + struct mtk_vcodec_mem pps_buf; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int prepend_hdr; + struct venc_vpu_inst vpu_inst; + struct venc_h264_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline void h264_write_reg(struct venc_h264_inst *inst, u32 addr, + u32 val) +{ + writel(val, inst->hw_base + addr); +} + +static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +static unsigned int h264_get_profile(struct venc_h264_inst *inst, + unsigned int profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + mtk_vcodec_err(inst, "unsupported CONSTRAINED_BASELINE"); + return 0; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + mtk_vcodec_err(inst, "unsupported EXTENDED"); + return 0; + default: + mtk_vcodec_debug(inst, "unsupported profile %d", profile); + return 100; + } +} + +static unsigned int h264_get_level(struct venc_h264_inst *inst, + unsigned int level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + mtk_vcodec_err(inst, "unsupported 1B"); + return 0; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + default: + mtk_vcodec_debug(inst, "unsupported level %d", level); + return 31; + } +} + +static void h264_enc_free_work_buf(struct venc_h264_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Except the SKIP_FRAME buffers, + * other buffers need to be freed by AP. + */ + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { + if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME) + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); + + mtk_vcodec_debug_leave(inst); +} + +static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) +{ + int i; + int ret = 0; + struct venc_h264_vpu_buf *wb = inst->vsi->work_bufs; + + mtk_vcodec_debug_enter(inst); + + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { + /* + * This 'wb' structure is set by VPU side and shared to AP for + * buffer allocation and IO virtual addr mapping. For most of + * the buffers, AP will allocate the buffer according to 'size' + * field and store the IO virtual addr in 'iova' field. There + * are two exceptions: + * (1) RC_CODE buffer, it's pre-allocated in the VPU side, and + * save the VPU addr in the 'vpua' field. The AP will translate + * the VPU addr to the corresponding IO virtual addr and store + * in 'iova' field for reg setting in VPU side. + * (2) SKIP_FRAME buffer, it's pre-allocated in the VPU side, + * and save the VPU addr in the 'vpua' field. The AP will + * translate the VPU addr to the corresponding AP side virtual + * address and do some memcpy access to move to bitstream buffer + * assigned by v4l2 layer. + */ + inst->work_bufs[i].size = wb[i].size; + if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) { + inst->work_bufs[i].va = vpu_mapping_dm_addr( + inst->vpu_inst.dev, wb[i].vpua); + inst->work_bufs[i].dma_addr = 0; + } else { + ret = mtk_vcodec_mem_alloc(inst->ctx, + &inst->work_bufs[i]); + if (ret) { + mtk_vcodec_err(inst, + "cannot allocate buf %d", i); + goto err_alloc; + } + /* + * This RC_CODE is pre-allocated by VPU and saved in VPU + * addr. So we need use memcpy to copy RC_CODE from VPU + * addr into IO virtual addr in 'iova' field for reg + * setting in VPU side. + */ + if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) { + void *tmp_va; + + tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, + wb[i].vpua); + memcpy(inst->work_bufs[i].va, tmp_va, + wb[i].size); + } + } + wb[i].iova = inst->work_bufs[i].dma_addr; + + mtk_vcodec_debug(inst, + "work_buf[%d] va=0x%p iova=0x%p size=%zu", + i, inst->work_bufs[i].va, + (void *)inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); + } + + /* the pps_buf is used by AP side only */ + inst->pps_buf.size = 128; + ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf); + if (ret) { + mtk_vcodec_err(inst, "cannot allocate pps_buf"); + goto err_alloc; + } + + mtk_vcodec_debug_leave(inst); + + return ret; + +err_alloc: + h264_enc_free_work_buf(inst); + + return ret; +} + +static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) +{ + unsigned int irq_status = 0; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + + if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); + } + return irq_status; +} + +static int h264_encode_sps(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL, + bs_buf, bs_size); + if (ret) + return ret; + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_SPS) { + mtk_vcodec_err(inst, "expect irq status %d", + MTK_VENC_IRQ_STATUS_SPS); + return -EINVAL; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + + return ret; +} + +static int h264_encode_pps(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL, + bs_buf, bs_size); + if (ret) + return ret; + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_PPS) { + mtk_vcodec_err(inst, "expect irq status %d", + MTK_VENC_IRQ_STATUS_PPS); + return -EINVAL; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + + return ret; +} + +static int h264_encode_header(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int bs_size_sps; + unsigned int bs_size_pps; + + ret = h264_encode_sps(inst, bs_buf, &bs_size_sps); + if (ret) + return ret; + + ret = h264_encode_pps(inst, &inst->pps_buf, &bs_size_pps); + if (ret) + return ret; + + memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps); + *bs_size = bs_size_sps + bs_size_pps; + + return ret; +} + +static int h264_encode_frame(struct venc_h264_inst *inst, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, + bs_buf, bs_size); + if (ret) + return ret; + + /* + * skip frame case: The skip frame buffer is composed by vpu side only, + * it does not trigger the hw, so skip the wait interrupt operation. + */ + if (inst->vpu_inst.state == VEN_IPI_MSG_ENC_STATE_SKIP) { + *bs_size = inst->vpu_inst.bs_size; + memcpy(bs_buf->va, + inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va, + *bs_size); + ++inst->frm_cnt; + return ret; + } + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { + mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + return -EIO; + } + + *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); + + ++inst->frm_cnt; + mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-", + inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm); + + return ret; +} + +static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, + int size) +{ + unsigned char *p = buf; + + if (size < H264_FILLER_MARKER_SIZE) { + mtk_vcodec_err(inst, "filler size too small %d", size); + return; + } + + memcpy(p, h264_filler_marker, ARRAY_SIZE(h264_filler_marker)); + size -= H264_FILLER_MARKER_SIZE; + p += H264_FILLER_MARKER_SIZE; + memset(p, 0xff, size); +} + +static int h264_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +{ + int ret = 0; + struct venc_h264_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; + inst->vpu_inst.id = IPI_VENC_H264; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + (*handle) = (unsigned long)inst; + + return ret; +} + +static int h264_enc_encode(unsigned long handle, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_debug(inst, "opt %d ->", opt); + + enable_irq(ctx->dev->enc_irq); + + switch (opt) { + case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: { + unsigned int bs_size_hdr; + + ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); + if (ret) + goto encode_err; + + result->bs_size = bs_size_hdr; + result->is_key_frm = false; + break; + } + + case VENC_START_OPT_ENCODE_FRAME: { + int hdr_sz; + int hdr_sz_ext; + int filler_sz = 0; + const int bs_alignment = 128; + struct mtk_vcodec_mem tmp_bs_buf; + unsigned int bs_size_hdr; + unsigned int bs_size_frm; + + if (!inst->prepend_hdr) { + ret = h264_encode_frame(inst, frm_buf, bs_buf, + &result->bs_size); + if (ret) + goto encode_err; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + } + + mtk_vcodec_debug(inst, "h264_encode_frame prepend SPS/PPS"); + + ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); + if (ret) + goto encode_err; + + hdr_sz = bs_size_hdr; + hdr_sz_ext = (hdr_sz & (bs_alignment - 1)); + if (hdr_sz_ext) { + filler_sz = bs_alignment - hdr_sz_ext; + if (hdr_sz_ext + H264_FILLER_MARKER_SIZE > bs_alignment) + filler_sz += bs_alignment; + h264_encode_filler(inst, bs_buf->va + hdr_sz, + filler_sz); + } + + tmp_bs_buf.va = bs_buf->va + hdr_sz + filler_sz; + tmp_bs_buf.dma_addr = bs_buf->dma_addr + hdr_sz + filler_sz; + tmp_bs_buf.size = bs_buf->size - (hdr_sz + filler_sz); + + ret = h264_encode_frame(inst, frm_buf, &tmp_bs_buf, + &bs_size_frm); + if (ret) + goto encode_err; + + result->bs_size = hdr_sz + filler_sz + bs_size_frm; + + mtk_vcodec_debug(inst, "hdr %d filler %d frame %d bs %d", + hdr_sz, filler_sz, bs_size_frm, + result->bs_size); + + inst->prepend_hdr = 0; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + } + + default: + mtk_vcodec_err(inst, "venc_start_opt %d not supported", opt); + ret = -EINVAL; + break; + } + +encode_err: + + disable_irq(ctx->dev->enc_irq); + mtk_vcodec_debug(inst, "opt %d <-", opt); + + return ret; +} + +static int h264_enc_set_param(unsigned long handle, + enum venc_set_param_type type, + struct venc_enc_param *enc_prm) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + + mtk_vcodec_debug(inst, "->type=%d", type); + + switch (type) { + case VENC_SET_PARAM_ENC: + inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi->config.bitrate = enc_prm->bitrate; + inst->vsi->config.pic_w = enc_prm->width; + inst->vsi->config.pic_h = enc_prm->height; + inst->vsi->config.buf_w = enc_prm->buf_width; + inst->vsi->config.buf_h = enc_prm->buf_height; + inst->vsi->config.gop_size = enc_prm->gop_size; + inst->vsi->config.framerate = enc_prm->frm_rate; + inst->vsi->config.intra_period = enc_prm->intra_period; + inst->vsi->config.profile = + h264_get_profile(inst, enc_prm->h264_profile); + inst->vsi->config.level = + h264_get_level(inst, enc_prm->h264_level); + inst->vsi->config.wfd = 0; + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + if (ret) + break; + if (inst->work_buf_allocated) { + h264_enc_free_work_buf(inst); + inst->work_buf_allocated = false; + } + ret = h264_enc_alloc_work_buf(inst); + if (ret) + break; + inst->work_buf_allocated = true; + break; + + case VENC_SET_PARAM_PREPEND_HEADER: + inst->prepend_hdr = 1; + mtk_vcodec_debug(inst, "set prepend header mode"); + break; + + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int h264_enc_deinit(unsigned long handle) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + h264_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +static struct venc_common_if venc_h264_if = { + h264_enc_init, + h264_enc_encode, + h264_enc_set_param, + h264_enc_deinit, +}; + +struct venc_common_if *get_h264_enc_comm_if(void); + +struct venc_common_if *get_h264_enc_comm_if(void) +{ + return &venc_h264_if; +} diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c new file mode 100644 index 000000000000..431ae706a427 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> + * PoChun Lin <pochun.lin@mediatek.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. + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" +#include "mtk_vpu.h" + +#define VENC_BITSTREAM_FRAME_SIZE 0x0098 +#define VENC_BITSTREAM_HEADER_LEN 0x00e8 + +/* This ac_tag is vp8 frame tag. */ +#define MAX_AC_TAG_SIZE 10 + +/** + * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index + */ +enum venc_vp8_vpu_work_buf { + VENC_VP8_VPU_WORK_BUF_LUMA, + VENC_VP8_VPU_WORK_BUF_LUMA2, + VENC_VP8_VPU_WORK_BUF_LUMA3, + VENC_VP8_VPU_WORK_BUF_CHROMA, + VENC_VP8_VPU_WORK_BUF_CHROMA2, + VENC_VP8_VPU_WORK_BUF_CHROMA3, + VENC_VP8_VPU_WORK_BUF_MV_INFO, + VENC_VP8_VPU_WORK_BUF_BS_HEADER, + VENC_VP8_VPU_WORK_BUF_PROB_BUF, + VENC_VP8_VPU_WORK_BUF_RC_INFO, + VENC_VP8_VPU_WORK_BUF_RC_CODE, + VENC_VP8_VPU_WORK_BUF_RC_CODE2, + VENC_VP8_VPU_WORK_BUF_RC_CODE3, + VENC_VP8_VPU_WORK_BUF_MAX, +}; + +/* + * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width (with 16 alignment). Buffer size is stream resolution + * in pixels aligned to hardware requirements. + * @buf_h: buffer height (with 16 alignment) + * @gop_size: group of picture size (key frame) + * @framerate: frame rate in fps + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + */ +struct venc_vp8_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 framerate; + u32 ts_mode; +}; + +/* + * struct venc_vp8_vpu_buf -Structure for buffer information + * @align: buffer alignment (in bytes) + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_vp8_vpu_buf { + u32 align; + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_vp8_vsi - Structure for VPU driver control and info share + * This structure is allocated in VPU side and shared to AP side. + * @config: vp8 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_vp8_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_vp8_vsi { + struct venc_vp8_vpu_config config; + struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_vp8_inst - vp8 encoder AP driver instance + * @hw_base: vp8 encoder hardware register base + * @work_bufs: working buffer + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count, it's used for I-frame judgement and + * reset when force intra cmd received. + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_vp8_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int ts_mode; + struct venc_vpu_inst vpu_inst; + struct venc_vp8_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline void vp8_enc_write_reg(struct venc_vp8_inst *inst, u32 addr, + u32 val) +{ + writel(val, inst->hw_base + addr); +} + +static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Buffers need to be freed by AP. */ + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if ((inst->work_bufs[i].size == 0)) + continue; + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_debug_leave(inst); +} + +static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) +{ + int i; + int ret = 0; + struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs; + + mtk_vcodec_debug_enter(inst); + + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if ((wb[i].size == 0)) + continue; + /* + * This 'wb' structure is set by VPU side and shared to AP for + * buffer allocation and IO virtual addr mapping. For most of + * the buffers, AP will allocate the buffer according to 'size' + * field and store the IO virtual addr in 'iova' field. For the + * RC_CODEx buffers, they are pre-allocated in the VPU side + * because they are inside VPU SRAM, and save the VPU addr in + * the 'vpua' field. The AP will translate the VPU addr to the + * corresponding IO virtual addr and store in 'iova' field. + */ + inst->work_bufs[i].size = wb[i].size; + ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); + if (ret) { + mtk_vcodec_err(inst, + "cannot alloc work_bufs[%d]", i); + goto err_alloc; + } + /* + * This RC_CODEx is pre-allocated by VPU and saved in VPU addr. + * So we need use memcpy to copy RC_CODEx from VPU addr into IO + * virtual addr in 'iova' field for reg setting in VPU side. + */ + if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) { + void *tmp_va; + + tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, + wb[i].vpua); + memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size); + } + wb[i].iova = inst->work_bufs[i].dma_addr; + + mtk_vcodec_debug(inst, + "work_bufs[%d] va=0x%p,iova=0x%p,size=%zu", + i, inst->work_bufs[i].va, + (void *)inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); + } + + mtk_vcodec_debug_leave(inst); + + return ret; + +err_alloc: + vp8_enc_free_work_buf(inst); + + return ret; +} + +static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst) +{ + unsigned int irq_status = 0; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + + if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "isr return %x", irq_status); + } + return irq_status; +} + +/* + * Compose ac_tag, bitstream header and bitstream payload into + * one bitstream buffer. + */ +static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + unsigned int not_key; + u32 bs_frm_size; + u32 bs_hdr_len; + unsigned int ac_tag_size; + u8 ac_tag[MAX_AC_TAG_SIZE]; + + bs_frm_size = vp8_enc_read_reg(inst, VENC_BITSTREAM_FRAME_SIZE); + bs_hdr_len = vp8_enc_read_reg(inst, VENC_BITSTREAM_HEADER_LEN); + + /* if a frame is key frame, not_key is 0 */ + not_key = !inst->vpu_inst.is_key_frm; + *(u32 *)ac_tag = __cpu_to_le32((bs_hdr_len << 5) | 0x10 | not_key); + /* key frame */ + if (not_key == 0) { + ac_tag_size = MAX_AC_TAG_SIZE; + ac_tag[3] = 0x9d; + ac_tag[4] = 0x01; + ac_tag[5] = 0x2a; + ac_tag[6] = inst->vsi->config.pic_w; + ac_tag[7] = inst->vsi->config.pic_w >> 8; + ac_tag[8] = inst->vsi->config.pic_h; + ac_tag[9] = inst->vsi->config.pic_h >> 8; + } else { + ac_tag_size = 3; + } + + if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) { + mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)", + bs_buf->size); + return -EINVAL; + } + + /* + * (1) The vp8 bitstream header and body are generated by the HW vp8 + * encoder separately at the same time. We cannot know the bitstream + * header length in advance. + * (2) From the vp8 spec, there is no stuffing byte allowed between the + * ac tag, bitstream header and bitstream body. + */ + memmove(bs_buf->va + bs_hdr_len + ac_tag_size, + bs_buf->va, bs_frm_size); + memcpy(bs_buf->va + ac_tag_size, + inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HEADER].va, + bs_hdr_len); + memcpy(bs_buf->va, ac_tag, ac_tag_size); + *bs_size = bs_frm_size + bs_hdr_len + ac_tag_size; + + return 0; +} + +static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int irq_status; + + mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt); + + ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size); + if (ret) + return ret; + + irq_status = vp8_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { + mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + return -EIO; + } + + if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) { + mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed"); + return -EINVAL; + } + + inst->frm_cnt++; + mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size, + inst->vpu_inst.is_key_frm); + + return ret; +} + +static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +{ + int ret = 0; + struct venc_vp8_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; + inst->vpu_inst.id = IPI_VENC_VP8; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + (*handle) = (unsigned long)inst; + + return ret; +} + +static int vp8_enc_encode(unsigned long handle, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_debug_enter(inst); + + enable_irq(ctx->dev->enc_lt_irq); + + switch (opt) { + case VENC_START_OPT_ENCODE_FRAME: + ret = vp8_enc_encode_frame(inst, frm_buf, bs_buf, + &result->bs_size); + if (ret) + goto encode_err; + result->is_key_frm = inst->vpu_inst.is_key_frm; + break; + + default: + mtk_vcodec_err(inst, "opt not support:%d", opt); + ret = -EINVAL; + break; + } + +encode_err: + + disable_irq(ctx->dev->enc_lt_irq); + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_set_param(unsigned long handle, + enum venc_set_param_type type, + struct venc_enc_param *enc_prm) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + + mtk_vcodec_debug(inst, "->type=%d", type); + + switch (type) { + case VENC_SET_PARAM_ENC: + inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi->config.bitrate = enc_prm->bitrate; + inst->vsi->config.pic_w = enc_prm->width; + inst->vsi->config.pic_h = enc_prm->height; + inst->vsi->config.buf_w = enc_prm->buf_width; + inst->vsi->config.buf_h = enc_prm->buf_height; + inst->vsi->config.gop_size = enc_prm->gop_size; + inst->vsi->config.framerate = enc_prm->frm_rate; + inst->vsi->config.ts_mode = inst->ts_mode; + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + if (ret) + break; + if (inst->work_buf_allocated) { + vp8_enc_free_work_buf(inst); + inst->work_buf_allocated = false; + } + ret = vp8_enc_alloc_work_buf(inst); + if (ret) + break; + inst->work_buf_allocated = true; + break; + + /* + * VENC_SET_PARAM_TS_MODE must be called before VENC_SET_PARAM_ENC + */ + case VENC_SET_PARAM_TS_MODE: + inst->ts_mode = 1; + mtk_vcodec_debug(inst, "set ts_mode"); + break; + + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_deinit(unsigned long handle) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + vp8_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +static struct venc_common_if venc_vp8_if = { + vp8_enc_init, + vp8_enc_encode, + vp8_enc_set_param, + vp8_enc_deinit, +}; + +struct venc_common_if *get_vp8_enc_comm_if(void); + +struct venc_common_if *get_vp8_enc_comm_if(void) +{ + return &venc_vp8_if; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h new file mode 100644 index 000000000000..6308d44dedf6 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_base.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> + * Jungchang Tsao <jungchang.tsao@mediatek.com> + * Tiffany Lin <tiffany.lin@mediatek.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. + * + * 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. + */ + +#ifndef _VENC_DRV_BASE_ +#define _VENC_DRV_BASE_ + +#include "mtk_vcodec_drv.h" + +#include "venc_drv_if.h" + +struct venc_common_if { + /** + * (*init)() - initialize driver + * @ctx: [in] mtk v4l2 context + * @handle: [out] driver handle + */ + int (*init)(struct mtk_vcodec_ctx *ctx, unsigned long *handle); + + /** + * (*encode)() - trigger encode + * @handle: [in] driver handle + * @opt: [in] encode option + * @frm_buf: [in] frame buffer to store input frame + * @bs_buf: [in] bitstream buffer to store output bitstream + * @result: [out] encode result + */ + int (*encode)(unsigned long handle, enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + + /** + * (*set_param)() - set driver's parameter + * @handle: [in] driver handle + * @type: [in] parameter type + * @in: [in] buffer to store the parameter + */ + int (*set_param)(unsigned long handle, enum venc_set_param_type type, + struct venc_enc_param *in); + + /** + * (*deinit)() - deinitialize driver. + * @handle: [in] driver handle + */ + int (*deinit)(unsigned long handle); +}; + +#endif diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c new file mode 100644 index 000000000000..c4c83e7189c3 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> + * Jungchang Tsao <jungchang.tsao@mediatek.com> + * Tiffany Lin <tiffany.lin@mediatek.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. + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "venc_drv_base.h" +#include "venc_drv_if.h" + +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vpu.h" + +struct venc_common_if *get_h264_enc_comm_if(void); +struct venc_common_if *get_vp8_enc_comm_if(void); + +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +{ + int ret = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_VP8: + ctx->enc_if = get_vp8_enc_comm_if(); + break; + case V4L2_PIX_FMT_H264: + ctx->enc_if = get_h264_enc_comm_if(); + break; + default: + return -EINVAL; + } + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->init(ctx, (unsigned long *)&ctx->drv_handle); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, struct venc_enc_param *in) +{ + int ret = 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->set_param(ctx->drv_handle, type, in); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + enum venc_start_opt opt, struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result) +{ + int ret = 0; + unsigned long flags; + + mtk_venc_lock(ctx); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = ctx; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf, + bs_buf, result); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = NULL; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_venc_unlock(ctx); + return ret; +} + +int venc_if_deinit(struct mtk_vcodec_ctx *ctx) +{ + int ret = 0; + + if (ctx->drv_handle == 0) + return 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->deinit(ctx->drv_handle); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + ctx->drv_handle = 0; + + return ret; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h new file mode 100644 index 000000000000..a6e7d32e55cb --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> + * Jungchang Tsao <jungchang.tsao@mediatek.com> + * Tiffany Lin <tiffany.lin@mediatek.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. + * + * 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. + */ + +#ifndef _VENC_DRV_IF_H_ +#define _VENC_DRV_IF_H_ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" + +/* + * enum venc_yuv_fmt - The type of input yuv format + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_YUV_FORMAT_I420: I420 YUV format + * @VENC_YUV_FORMAT_YV12: YV12 YUV format + * @VENC_YUV_FORMAT_NV12: NV12 YUV format + * @VENC_YUV_FORMAT_NV21: NV21 YUV format + */ +enum venc_yuv_fmt { + VENC_YUV_FORMAT_I420 = 3, + VENC_YUV_FORMAT_YV12 = 5, + VENC_YUV_FORMAT_NV12 = 6, + VENC_YUV_FORMAT_NV21 = 7, +}; + +/* + * enum venc_start_opt - encode frame option used in venc_if_encode() + * @VENC_START_OPT_ENCODE_SEQUENCE_HEADER: encode SPS/PPS for H264 + * @VENC_START_OPT_ENCODE_FRAME: encode normal frame + */ +enum venc_start_opt { + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + VENC_START_OPT_ENCODE_FRAME, +}; + +/* + * enum venc_set_param_type - The type of set parameter used in + * venc_if_set_param() + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_SET_PARAM_ENC: set encoder parameters + * @VENC_SET_PARAM_FORCE_INTRA: force an intra frame + * @VENC_SET_PARAM_ADJUST_BITRATE: adjust bitrate (in bps) + * @VENC_SET_PARAM_ADJUST_FRAMERATE: set frame rate + * @VENC_SET_PARAM_GOP_SIZE: set IDR interval + * @VENC_SET_PARAM_INTRA_PERIOD: set I frame interval + * @VENC_SET_PARAM_SKIP_FRAME: set H264 skip one frame + * @VENC_SET_PARAM_PREPEND_HEADER: set H264 prepend SPS/PPS before IDR + * @VENC_SET_PARAM_TS_MODE: set VP8 temporal scalability mode + */ +enum venc_set_param_type { + VENC_SET_PARAM_ENC, + VENC_SET_PARAM_FORCE_INTRA, + VENC_SET_PARAM_ADJUST_BITRATE, + VENC_SET_PARAM_ADJUST_FRAMERATE, + VENC_SET_PARAM_GOP_SIZE, + VENC_SET_PARAM_INTRA_PERIOD, + VENC_SET_PARAM_SKIP_FRAME, + VENC_SET_PARAM_PREPEND_HEADER, + VENC_SET_PARAM_TS_MODE, +}; + +/* + * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in + * venc_if_set_param() + * @input_fourcc: input yuv format + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @width: image width + * @height: image height + * @buf_width: buffer width + * @buf_height: buffer height + * @frm_rate: frame rate in fps + * @intra_period: intra frame period + * @bitrate: target bitrate in bps + * @gop_size: group of picture size + */ +struct venc_enc_param { + enum venc_yuv_fmt input_yuv_fmt; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int width; + unsigned int height; + unsigned int buf_width; + unsigned int buf_height; + unsigned int frm_rate; + unsigned int intra_period; + unsigned int bitrate; + unsigned int gop_size; +}; + +/* + * struct venc_frm_buf - frame buffer information used in venc_if_encode() + * @fb_addr: plane frame buffer addresses + */ +struct venc_frm_buf { + struct mtk_vcodec_mem fb_addr[MTK_VCODEC_MAX_PLANES]; +}; + +/* + * struct venc_done_result - This is return information used in venc_if_encode() + * @bs_size: output bitstream size + * @is_key_frm: output is key frame or not + */ +struct venc_done_result { + unsigned int bs_size; + bool is_key_frm; +}; + +/* + * venc_if_init - Create the driver handle + * @ctx: device context + * @fourcc: encoder input format + * Return: 0 if creating handle successfully, otherwise it is failed. + */ +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); + +/* + * venc_if_deinit - Release the driver handle + * @ctx: device context + * Return: 0 if releasing handle successfully, otherwise it is failed. + */ +int venc_if_deinit(struct mtk_vcodec_ctx *ctx); + +/* + * venc_if_set_param - Set parameter to driver + * @ctx: device context + * @type: parameter type + * @in: input parameter + * Return: 0 if setting param successfully, otherwise it is failed. + */ +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, + struct venc_enc_param *in); + +/* + * venc_if_encode - Encode one frame + * @ctx: device context + * @opt: encode frame option + * @frm_buf: input frame buffer information + * @bs_buf: output bitstream buffer infomraiton + * @result: encode result + * Return: 0 if encoding frame successfully, otherwise it is failed. + */ +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + +#endif /* _VENC_DRV_IF_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h new file mode 100644 index 000000000000..4c869cb6fbf7 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> + * Daniel Hsiao <daniel.hsiao@mediatek.com> + * Tiffany Lin <tiffany.lin@mediatek.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. + * + * 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. + */ + +#ifndef _VENC_IPI_MSG_H_ +#define _VENC_IPI_MSG_H_ + +#define AP_IPIMSG_VENC_BASE 0xC000 +#define VPU_IPIMSG_VENC_BASE 0xD000 + +/** + * enum venc_ipi_msg_id - message id between AP and VPU + * (ipi stands for inter-processor interrupt) + * @AP_IPIMSG_ENC_XXX: AP to VPU cmd message id + * @VPU_IPIMSG_ENC_XXX_DONE: VPU ack AP cmd message id + */ +enum venc_ipi_msg_id { + AP_IPIMSG_ENC_INIT = AP_IPIMSG_VENC_BASE, + AP_IPIMSG_ENC_SET_PARAM, + AP_IPIMSG_ENC_ENCODE, + AP_IPIMSG_ENC_DEINIT, + + VPU_IPIMSG_ENC_INIT_DONE = VPU_IPIMSG_VENC_BASE, + VPU_IPIMSG_ENC_SET_PARAM_DONE, + VPU_IPIMSG_ENC_ENCODE_DONE, + VPU_IPIMSG_ENC_DEINIT_DONE, +}; + +/** + * struct venc_ap_ipi_msg_init - AP to VPU init cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_INIT) + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + * @venc_inst: AP encoder instance + * (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_ap_ipi_msg_init { + uint32_t msg_id; + uint32_t reserved; + uint64_t venc_inst; +}; + +/** + * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_SET_PARAM) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data[8]: data array to store the set parameters + */ +struct venc_ap_ipi_msg_set_param { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t param_id; + uint32_t data_item; + uint32_t data[8]; +}; + +/** + * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @bs_mode: bitstream mode for h264 + * (H264_BS_MODE_SPS/H264_BS_MODE_PPS/H264_BS_MODE_FRAME) + * @input_addr: pointer to input image buffer plane + * @bs_addr: pointer to output bit stream buffer + * @bs_size: bit stream buffer size + */ +struct venc_ap_ipi_msg_enc { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t bs_mode; + uint32_t input_addr[3]; + uint32_t bs_addr; + uint32_t bs_size; +}; + +/** + * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + */ +struct venc_ap_ipi_msg_deinit { + uint32_t msg_id; + uint32_t vpu_inst_addr; +}; + +/** + * enum venc_ipi_msg_status - VPU ack AP cmd status + */ +enum venc_ipi_msg_status { + VENC_IPI_MSG_STATUS_OK, + VENC_IPI_MSG_STATUS_FAIL, +}; + +/** + * struct venc_vpu_ipi_msg_common - VPU ack AP cmd common structure + * @msg_id: message id (VPU_IPIMSG_XXX_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_common { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +/** + * struct venc_vpu_ipi_msg_init - VPU ack AP init cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + */ +struct venc_vpu_ipi_msg_init { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t vpu_inst_addr; + uint32_t reserved; +}; + +/** + * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data[6]: data array to store the return result + */ +struct venc_vpu_ipi_msg_set_param { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t param_id; + uint32_t data_item; + uint32_t data[6]; +}; + +/** + * enum venc_ipi_msg_enc_state - Type of encode state + * VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded + * VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full + * VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame + * VEN_IPI_MSG_ENC_STATE_ERROR: encounter error + */ +enum venc_ipi_msg_enc_state { + VEN_IPI_MSG_ENC_STATE_FRAME, + VEN_IPI_MSG_ENC_STATE_PART, + VEN_IPI_MSG_ENC_STATE_SKIP, + VEN_IPI_MSG_ENC_STATE_ERROR, +}; + +/** + * struct venc_vpu_ipi_msg_enc - VPU ack AP enc cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_ENCODE_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @state: encode state (venc_ipi_msg_enc_state) + * @is_key_frm: whether the encoded frame is key frame + * @bs_size: encoded bitstream size + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + */ +struct venc_vpu_ipi_msg_enc { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t state; + uint32_t is_key_frm; + uint32_t bs_size; + uint32_t reserved; +}; + +/** + * struct venc_vpu_ipi_msg_deinit - VPU ack AP deinit cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_DEINIT_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_deinit { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +#endif /* _VENC_IPI_MSG_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c new file mode 100644 index 000000000000..b92c6d2a892d --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin <pochun.lin@mediatek.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. + * + * 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. + */ + +#include "mtk_vpu.h" +#include "venc_ipi_msg.h" +#include "venc_vpu_if.h" + +static void handle_enc_init_msg(struct venc_vpu_inst *vpu, void *data) +{ + struct venc_vpu_ipi_msg_init *msg = data; + + vpu->inst_addr = msg->vpu_inst_addr; + vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr); +} + +static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, void *data) +{ + struct venc_vpu_ipi_msg_enc *msg = data; + + vpu->state = msg->state; + vpu->bs_size = msg->bs_size; + vpu->is_key_frm = msg->is_key_frm; +} + +static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct venc_vpu_ipi_msg_common *msg = data; + struct venc_vpu_inst *vpu = (struct venc_vpu_inst *)msg->venc_inst; + + mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d", + msg->msg_id, vpu, msg->status); + + switch (msg->msg_id) { + case VPU_IPIMSG_ENC_INIT_DONE: + handle_enc_init_msg(vpu, data); + break; + case VPU_IPIMSG_ENC_SET_PARAM_DONE: + break; + case VPU_IPIMSG_ENC_ENCODE_DONE: + handle_enc_encode_msg(vpu, data); + break; + case VPU_IPIMSG_ENC_DEINIT_DONE: + break; + default: + mtk_vcodec_err(vpu, "unknown msg id %x", msg->msg_id); + break; + } + + vpu->signaled = 1; + vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); + + mtk_vcodec_debug_leave(vpu); +} + +static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, + int len) +{ + int status; + + mtk_vcodec_debug_enter(vpu); + + if (!vpu->dev) { + mtk_vcodec_err(vpu, "inst dev is NULL"); + return -EINVAL; + } + + status = vpu_ipi_send(vpu->dev, vpu->id, msg, len); + if (status) { + uint32_t msg_id = *(uint32_t *)msg; + + mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", + msg_id, len, status); + return -EINVAL; + } + if (vpu->failure) + return -EINVAL; + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +int vpu_enc_init(struct venc_vpu_inst *vpu) +{ + int status; + struct venc_ap_ipi_msg_init out; + + mtk_vcodec_debug_enter(vpu); + + init_waitqueue_head(&vpu->wq_hd); + vpu->signaled = 0; + vpu->failure = 0; + + status = vpu_ipi_register(vpu->dev, vpu->id, vpu_enc_ipi_handler, + NULL, NULL); + if (status) { + mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); + return -EINVAL; + } + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_INIT; + out.venc_inst = (unsigned long)vpu; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *enc_param) +{ + struct venc_ap_ipi_msg_set_param out; + + mtk_vcodec_debug(vpu, "id %d ->", id); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_SET_PARAM; + out.vpu_inst_addr = vpu->inst_addr; + out.param_id = id; + switch (id) { + case VENC_SET_PARAM_ENC: + out.data_item = 0; + break; + case VENC_SET_PARAM_FORCE_INTRA: + out.data_item = 0; + break; + case VENC_SET_PARAM_ADJUST_BITRATE: + out.data_item = 1; + out.data[0] = enc_param->bitrate; + break; + case VENC_SET_PARAM_ADJUST_FRAMERATE: + out.data_item = 1; + out.data[0] = enc_param->frm_rate; + break; + case VENC_SET_PARAM_GOP_SIZE: + out.data_item = 1; + out.data[0] = enc_param->gop_size; + break; + case VENC_SET_PARAM_INTRA_PERIOD: + out.data_item = 1; + out.data[0] = enc_param->intra_period; + break; + case VENC_SET_PARAM_SKIP_FRAME: + out.data_item = 0; + break; + default: + mtk_vcodec_err(vpu, "id %d not supported", id); + return -EINVAL; + } + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, + "AP_IPIMSG_ENC_SET_PARAM %d fail", id); + return -EINVAL; + } + + mtk_vcodec_debug(vpu, "id %d <-", id); + + return 0; +} + +int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + struct venc_ap_ipi_msg_enc out; + + mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_ENCODE; + out.vpu_inst_addr = vpu->inst_addr; + out.bs_mode = bs_mode; + if (frm_buf) { + if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) && + (frm_buf->fb_addr[1].dma_addr % 16 == 0) && + (frm_buf->fb_addr[2].dma_addr % 16 == 0)) { + out.input_addr[0] = frm_buf->fb_addr[0].dma_addr; + out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; + out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; + } else { + mtk_vcodec_err(vpu, "dma_addr not align to 16"); + return -EINVAL; + } + } + if (bs_buf) { + out.bs_addr = bs_buf->dma_addr; + out.bs_size = bs_buf->size; + } + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", + bs_mode); + return -EINVAL; + } + + mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-", + bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); + + return 0; +} + +int vpu_enc_deinit(struct venc_vpu_inst *vpu) +{ + struct venc_ap_ipi_msg_deinit out; + + mtk_vcodec_debug_enter(vpu); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_DEINIT; + out.vpu_inst_addr = vpu->inst_addr; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h new file mode 100644 index 000000000000..215d1e01362e --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin <pochun.lin@mediatek.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. + * + * 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. + */ + +#ifndef _VENC_VPU_IF_H_ +#define _VENC_VPU_IF_H_ + +#include "mtk_vpu.h" +#include "venc_drv_if.h" + +/* + * struct venc_vpu_inst - encoder VPU driver instance + * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done + * @signaled: flag used for checking vpu interrupt done + * @failure: flag to show vpu cmd succeeds or not + * @state: enum venc_ipi_msg_enc_state + * @bs_size: bitstream size for skip frame case usage + * @is_key_frm: key frame flag + * @inst_addr: VPU instance addr + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @id: the id of inter-processor interrupt + * @ctx: context for v4l2 layer integration + * @dev: device for v4l2 layer integration + */ +struct venc_vpu_inst { + wait_queue_head_t wq_hd; + int signaled; + int failure; + int state; + int bs_size; + int is_key_frm; + unsigned int inst_addr; + void *vsi; + enum ipi_id id; + struct mtk_vcodec_ctx *ctx; + struct platform_device *dev; +}; + +int vpu_enc_init(struct venc_vpu_inst *vpu); +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *param); +int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size); +int vpu_enc_deinit(struct venc_vpu_inst *vpu); + +#endif diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mtk-vpu/Makefile new file mode 100644 index 000000000000..58cc1b4bc9f2 --- /dev/null +++ b/drivers/media/platform/mtk-vpu/Makefile @@ -0,0 +1,3 @@ +mtk-vpu-y += mtk_vpu.o + +obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu.o diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c new file mode 100644 index 000000000000..b60d02c0f653 --- /dev/null +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -0,0 +1,950 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.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. +* +* 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. +*/ +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/iommu.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> +#include <linux/sched.h> +#include <linux/sizes.h> + +#include "mtk_vpu.h" + +/** + * VPU (video processor unit) is a tiny processor controlling video hardware + * related to video codec, scaling and color format converting. + * VPU interfaces with other blocks by share memory and interrupt. + **/ + +#define INIT_TIMEOUT_MS 2000U +#define IPI_TIMEOUT_MS 2000U +#define VPU_FW_VER_LEN 16 + +/* maximum program/data TCM (Tightly-Coupled Memory) size */ +#define VPU_PTCM_SIZE (96 * SZ_1K) +#define VPU_DTCM_SIZE (32 * SZ_1K) +/* the offset to get data tcm address */ +#define VPU_DTCM_OFFSET 0x18000UL +/* daynamic allocated maximum extended memory size */ +#define VPU_EXT_P_SIZE SZ_1M +#define VPU_EXT_D_SIZE SZ_4M +/* maximum binary firmware size */ +#define VPU_P_FW_SIZE (VPU_PTCM_SIZE + VPU_EXT_P_SIZE) +#define VPU_D_FW_SIZE (VPU_DTCM_SIZE + VPU_EXT_D_SIZE) +/* the size of share buffer between Host and VPU */ +#define SHARE_BUF_SIZE 48 + +/* binary firmware name */ +#define VPU_P_FW "vpu_p.bin" +#define VPU_D_FW "vpu_d.bin" + +#define VPU_RESET 0x0 +#define VPU_TCM_CFG 0x0008 +#define VPU_PMEM_EXT0_ADDR 0x000C +#define VPU_PMEM_EXT1_ADDR 0x0010 +#define VPU_TO_HOST 0x001C +#define VPU_DMEM_EXT0_ADDR 0x0014 +#define VPU_DMEM_EXT1_ADDR 0x0018 +#define HOST_TO_VPU 0x0024 +#define VPU_PC_REG 0x0060 +#define VPU_WDT_REG 0x0084 + +/* vpu inter-processor communication interrupt */ +#define VPU_IPC_INT BIT(8) + +/** + * enum vpu_fw_type - VPU firmware type + * + * @P_FW: program firmware + * @D_FW: data firmware + * + */ +enum vpu_fw_type { + P_FW, + D_FW, +}; + +/** + * struct vpu_mem - VPU extended program/data memory information + * + * @va: the kernel virtual memory address of VPU extended memory + * @pa: the physical memory address of VPU extended memory + * + */ +struct vpu_mem { + void *va; + dma_addr_t pa; +}; + +/** + * struct vpu_regs - VPU TCM and configuration registers + * + * @tcm: the register for VPU Tightly-Coupled Memory + * @cfg: the register for VPU configuration + * @irq: the irq number for VPU interrupt + */ +struct vpu_regs { + void __iomem *tcm; + void __iomem *cfg; + int irq; +}; + +/** + * struct vpu_wdt_handler - VPU watchdog reset handler + * + * @reset_func: reset handler + * @priv: private data + */ +struct vpu_wdt_handler { + void (*reset_func)(void *); + void *priv; +}; + +/** + * struct vpu_wdt - VPU watchdog workqueue + * + * @handler: VPU watchdog reset handler + * @ws: workstruct for VPU watchdog + * @wq: workqueue for VPU watchdog + */ +struct vpu_wdt { + struct vpu_wdt_handler handler[VPU_RST_MAX]; + struct work_struct ws; + struct workqueue_struct *wq; +}; + +/** + * struct vpu_run - VPU initialization status + * + * @signaled: the signal of vpu initialization completed + * @fw_ver: VPU firmware version + * @enc_capability: encoder capability which is not used for now and + * the value is reserved for future use + * @wq: wait queue for VPU initialization status + */ +struct vpu_run { + u32 signaled; + char fw_ver[VPU_FW_VER_LEN]; + unsigned int enc_capability; + wait_queue_head_t wq; +}; + +/** + * struct vpu_ipi_desc - VPU IPI descriptor + * + * @handler: IPI handler + * @name: the name of IPI handler + * @priv: the private data of IPI handler + */ +struct vpu_ipi_desc { + ipi_handler_t handler; + const char *name; + void *priv; +}; + +/** + * struct share_obj - DTCM (Data Tightly-Coupled Memory) buffer shared with + * AP and VPU + * + * @id: IPI id + * @len: share buffer length + * @share_buf: share buffer data + */ +struct share_obj { + s32 id; + u32 len; + unsigned char share_buf[SHARE_BUF_SIZE]; +}; + +/** + * struct mtk_vpu - vpu driver data + * @extmem: VPU extended memory information + * @reg: VPU TCM and configuration registers + * @run: VPU initialization status + * @ipi_desc: VPU IPI descriptor + * @recv_buf: VPU DTCM share buffer for receiving. The + * receive buffer is only accessed in interrupt context. + * @send_buf: VPU DTCM share buffer for sending + * @dev: VPU struct device + * @clk: VPU clock on/off + * @fw_loaded: indicate VPU firmware loaded + * @enable_4GB: VPU 4GB mode on/off + * @vpu_mutex: protect mtk_vpu (except recv_buf) and ensure only + * one client to use VPU service at a time. For example, + * suppose a client is using VPU to decode VP8. + * If the other client wants to encode VP8, + * it has to wait until VP8 decode completes. + * @wdt_refcnt WDT reference count to make sure the watchdog can be + * disabled if no other client is using VPU service + * @ack_wq: The wait queue for each codec and mdp. When sleeping + * processes wake up, they will check the condition + * "ipi_id_ack" to run the corresponding action or + * go back to sleep. + * @ipi_id_ack: The ACKs for registered IPI function sending + * interrupt to VPU + * + */ +struct mtk_vpu { + struct vpu_mem extmem[2]; + struct vpu_regs reg; + struct vpu_run run; + struct vpu_wdt wdt; + struct vpu_ipi_desc ipi_desc[IPI_MAX]; + struct share_obj *recv_buf; + struct share_obj *send_buf; + struct device *dev; + struct clk *clk; + bool fw_loaded; + bool enable_4GB; + struct mutex vpu_mutex; /* for protecting vpu data data structure */ + u32 wdt_refcnt; + wait_queue_head_t ack_wq; + bool ipi_id_ack[IPI_MAX]; +}; + +static inline void vpu_cfg_writel(struct mtk_vpu *vpu, u32 val, u32 offset) +{ + writel(val, vpu->reg.cfg + offset); +} + +static inline u32 vpu_cfg_readl(struct mtk_vpu *vpu, u32 offset) +{ + return readl(vpu->reg.cfg + offset); +} + +static inline bool vpu_running(struct mtk_vpu *vpu) +{ + return vpu_cfg_readl(vpu, VPU_RESET) & BIT(0); +} + +static void vpu_clock_disable(struct mtk_vpu *vpu) +{ + /* Disable VPU watchdog */ + mutex_lock(&vpu->vpu_mutex); + if (!--vpu->wdt_refcnt) + vpu_cfg_writel(vpu, + vpu_cfg_readl(vpu, VPU_WDT_REG) & ~(1L << 31), + VPU_WDT_REG); + mutex_unlock(&vpu->vpu_mutex); + + clk_disable(vpu->clk); +} + +static int vpu_clock_enable(struct mtk_vpu *vpu) +{ + int ret; + + ret = clk_enable(vpu->clk); + if (ret) + return ret; + /* Enable VPU watchdog */ + mutex_lock(&vpu->vpu_mutex); + if (!vpu->wdt_refcnt++) + vpu_cfg_writel(vpu, + vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31), + VPU_WDT_REG); + mutex_unlock(&vpu->vpu_mutex); + + return ret; +} + +int vpu_ipi_register(struct platform_device *pdev, + enum ipi_id id, ipi_handler_t handler, + const char *name, void *priv) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct vpu_ipi_desc *ipi_desc; + + if (!vpu) { + dev_err(&pdev->dev, "vpu device in not ready\n"); + return -EPROBE_DEFER; + } + + if (id >= 0 && id < IPI_MAX && handler) { + ipi_desc = vpu->ipi_desc; + ipi_desc[id].name = name; + ipi_desc[id].handler = handler; + ipi_desc[id].priv = priv; + return 0; + } + + dev_err(&pdev->dev, "register vpu ipi id %d with invalid arguments\n", + id); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vpu_ipi_register); + +int vpu_ipi_send(struct platform_device *pdev, + enum ipi_id id, void *buf, + unsigned int len) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct share_obj *send_obj = vpu->send_buf; + unsigned long timeout; + int ret = 0; + + if (id <= IPI_VPU_INIT || id >= IPI_MAX || + len > sizeof(send_obj->share_buf) || !buf) { + dev_err(vpu->dev, "failed to send ipi message\n"); + return -EINVAL; + } + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "failed to enable vpu clock\n"); + return ret; + } + if (!vpu_running(vpu)) { + dev_err(vpu->dev, "vpu_ipi_send: VPU is not running\n"); + ret = -EINVAL; + goto clock_disable; + } + + mutex_lock(&vpu->vpu_mutex); + + /* Wait until VPU receives the last command */ + timeout = jiffies + msecs_to_jiffies(IPI_TIMEOUT_MS); + do { + if (time_after(jiffies, timeout)) { + dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n"); + ret = -EIO; + goto mut_unlock; + } + } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); + + memcpy((void *)send_obj->share_buf, buf, len); + send_obj->len = len; + send_obj->id = id; + + vpu->ipi_id_ack[id] = false; + /* send the command to VPU */ + vpu_cfg_writel(vpu, 0x1, HOST_TO_VPU); + + mutex_unlock(&vpu->vpu_mutex); + + /* wait for VPU's ACK */ + timeout = msecs_to_jiffies(IPI_TIMEOUT_MS); + ret = wait_event_timeout(vpu->ack_wq, vpu->ipi_id_ack[id], timeout); + vpu->ipi_id_ack[id] = false; + if (ret == 0) { + dev_err(vpu->dev, "vpu ipi %d ack time out !", id); + ret = -EIO; + goto clock_disable; + } + vpu_clock_disable(vpu); + + return 0; + +mut_unlock: + mutex_unlock(&vpu->vpu_mutex); +clock_disable: + vpu_clock_disable(vpu); + + return ret; +} +EXPORT_SYMBOL_GPL(vpu_ipi_send); + +static void vpu_wdt_reset_func(struct work_struct *ws) +{ + struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws); + struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt); + struct vpu_wdt_handler *handler = wdt->handler; + int index, ret; + + dev_info(vpu->dev, "vpu reset\n"); + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret); + return; + } + mutex_lock(&vpu->vpu_mutex); + vpu_cfg_writel(vpu, 0x0, VPU_RESET); + vpu->fw_loaded = false; + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + + for (index = 0; index < VPU_RST_MAX; index++) { + if (handler[index].reset_func) { + handler[index].reset_func(handler[index].priv); + dev_dbg(vpu->dev, "wdt handler func %d\n", index); + } + } +} + +int vpu_wdt_reg_handler(struct platform_device *pdev, + void wdt_reset(void *), + void *priv, enum rst_id id) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct vpu_wdt_handler *handler; + + if (!vpu) { + dev_err(&pdev->dev, "vpu device in not ready\n"); + return -EPROBE_DEFER; + } + + handler = vpu->wdt.handler; + + if (id >= 0 && id < VPU_RST_MAX && wdt_reset) { + dev_dbg(vpu->dev, "wdt register id %d\n", id); + mutex_lock(&vpu->vpu_mutex); + handler[id].reset_func = wdt_reset; + handler[id].priv = priv; + mutex_unlock(&vpu->vpu_mutex); + return 0; + } + + dev_err(vpu->dev, "register vpu wdt handler failed\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vpu_wdt_reg_handler); + +unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + + return vpu->run.enc_capability; +} +EXPORT_SYMBOL_GPL(vpu_get_venc_hw_capa); + +void *vpu_mapping_dm_addr(struct platform_device *pdev, + u32 dtcm_dmem_addr) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + + if (!dtcm_dmem_addr || + (dtcm_dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) { + dev_err(vpu->dev, "invalid virtual data memory address\n"); + return ERR_PTR(-EINVAL); + } + + if (dtcm_dmem_addr < VPU_DTCM_SIZE) + return (__force void *)(dtcm_dmem_addr + vpu->reg.tcm + + VPU_DTCM_OFFSET); + + return vpu->extmem[D_FW].va + (dtcm_dmem_addr - VPU_DTCM_SIZE); +} +EXPORT_SYMBOL_GPL(vpu_mapping_dm_addr); + +struct platform_device *vpu_get_plat_device(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *vpu_node; + struct platform_device *vpu_pdev; + + vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0); + if (!vpu_node) { + dev_err(dev, "can't get vpu node\n"); + return NULL; + } + + vpu_pdev = of_find_device_by_node(vpu_node); + if (WARN_ON(!vpu_pdev)) { + dev_err(dev, "vpu pdev failed\n"); + of_node_put(vpu_node); + return NULL; + } + + return vpu_pdev; +} +EXPORT_SYMBOL_GPL(vpu_get_plat_device); + +/* load vpu program/data memory */ +static int load_requested_vpu(struct mtk_vpu *vpu, + const struct firmware *vpu_fw, + u8 fw_type) +{ + size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE; + size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE; + char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW; + size_t dl_size = 0; + size_t extra_fw_size = 0; + void *dest; + int ret; + + ret = request_firmware(&vpu_fw, fw_name, vpu->dev); + if (ret < 0) { + dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret); + return ret; + } + dl_size = vpu_fw->size; + if (dl_size > fw_size) { + dev_err(vpu->dev, "fw %s size %zu is abnormal\n", fw_name, + dl_size); + release_firmware(vpu_fw); + return -EFBIG; + } + dev_dbg(vpu->dev, "Downloaded fw %s size: %zu.\n", + fw_name, + dl_size); + /* reset VPU */ + vpu_cfg_writel(vpu, 0x0, VPU_RESET); + + /* handle extended firmware size */ + if (dl_size > tcm_size) { + dev_dbg(vpu->dev, "fw size %zu > limited fw size %zu\n", + dl_size, tcm_size); + extra_fw_size = dl_size - tcm_size; + dev_dbg(vpu->dev, "extra_fw_size %zu\n", extra_fw_size); + dl_size = tcm_size; + } + dest = (__force void *)vpu->reg.tcm; + if (fw_type == D_FW) + dest += VPU_DTCM_OFFSET; + memcpy(dest, vpu_fw->data, dl_size); + /* download to extended memory if need */ + if (extra_fw_size > 0) { + dest = vpu->extmem[fw_type].va; + dev_dbg(vpu->dev, "download extended memory type %x\n", + fw_type); + memcpy(dest, vpu_fw->data + tcm_size, extra_fw_size); + } + + release_firmware(vpu_fw); + + return 0; +} + +int vpu_load_firmware(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct vpu_run *run = &vpu->run; + const struct firmware *vpu_fw = NULL; + int ret; + + if (!pdev) { + dev_err(dev, "VPU platform device is invalid\n"); + return -EINVAL; + } + + mutex_lock(&vpu->vpu_mutex); + if (vpu->fw_loaded) { + mutex_unlock(&vpu->vpu_mutex); + return 0; + } + mutex_unlock(&vpu->vpu_mutex); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "enable clock failed %d\n", ret); + return ret; + } + + mutex_lock(&vpu->vpu_mutex); + + run->signaled = false; + dev_dbg(vpu->dev, "firmware request\n"); + /* Downloading program firmware to device*/ + ret = load_requested_vpu(vpu, vpu_fw, P_FW); + if (ret < 0) { + dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret); + goto OUT_LOAD_FW; + } + + /* Downloading data firmware to device */ + ret = load_requested_vpu(vpu, vpu_fw, D_FW); + if (ret < 0) { + dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret); + goto OUT_LOAD_FW; + } + + vpu->fw_loaded = true; + /* boot up vpu */ + vpu_cfg_writel(vpu, 0x1, VPU_RESET); + + ret = wait_event_interruptible_timeout(run->wq, + run->signaled, + msecs_to_jiffies(INIT_TIMEOUT_MS) + ); + if (ret == 0) { + ret = -ETIME; + dev_err(dev, "wait vpu initialization timout!\n"); + goto OUT_LOAD_FW; + } else if (-ERESTARTSYS == ret) { + dev_err(dev, "wait vpu interrupted by a signal!\n"); + goto OUT_LOAD_FW; + } + + ret = 0; + dev_info(dev, "vpu is ready. Fw version %s\n", run->fw_ver); + +OUT_LOAD_FW: + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + + return ret; +} +EXPORT_SYMBOL_GPL(vpu_load_firmware); + +static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct mtk_vpu *vpu = (struct mtk_vpu *)priv; + struct vpu_run *run = (struct vpu_run *)data; + + vpu->run.signaled = run->signaled; + strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN); + vpu->run.enc_capability = run->enc_capability; + wake_up_interruptible(&vpu->run.wq); +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[256]; + unsigned int len; + unsigned int running, pc, vpu_to_host, host_to_vpu, wdt; + int ret; + struct device *dev = file->private_data; + struct mtk_vpu *vpu = dev_get_drvdata(dev); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); + return 0; + } + + /* vpu register status */ + running = vpu_running(vpu); + pc = vpu_cfg_readl(vpu, VPU_PC_REG); + wdt = vpu_cfg_readl(vpu, VPU_WDT_REG); + host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU); + vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); + vpu_clock_disable(vpu); + + if (running) { + len = snprintf(buf, sizeof(buf), "VPU is running\n\n" + "FW Version: %s\n" + "PC: 0x%x\n" + "WDT: 0x%x\n" + "Host to VPU: 0x%x\n" + "VPU to Host: 0x%x\n", + vpu->run.fw_ver, pc, wdt, + host_to_vpu, vpu_to_host); + } else { + len = snprintf(buf, sizeof(buf), "VPU not running\n"); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations vpu_debug_fops = { + .open = simple_open, + .read = vpu_debug_read, +}; +#endif /* CONFIG_DEBUG_FS */ + +static void vpu_free_ext_mem(struct mtk_vpu *vpu, u8 fw_type) +{ + struct device *dev = vpu->dev; + size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; + + dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va, + vpu->extmem[fw_type].pa); +} + +static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type) +{ + struct device *dev = vpu->dev; + size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; + u32 vpu_ext_mem0 = fw_type ? VPU_DMEM_EXT0_ADDR : VPU_PMEM_EXT0_ADDR; + u32 vpu_ext_mem1 = fw_type ? VPU_DMEM_EXT1_ADDR : VPU_PMEM_EXT1_ADDR; + u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0; + + vpu->extmem[fw_type].va = dma_alloc_coherent(dev, + fw_ext_size, + &vpu->extmem[fw_type].pa, + GFP_KERNEL); + if (!vpu->extmem[fw_type].va) { + dev_err(dev, "Failed to allocate the extended program memory\n"); + return PTR_ERR(vpu->extmem[fw_type].va); + } + + /* Disable extend0. Enable extend1 */ + vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0); + vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb, + vpu_ext_mem1); + + dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n", + fw_type ? "Data" : "Program", + (unsigned long long)vpu->extmem[fw_type].pa, + vpu->extmem[fw_type].va); + + return 0; +} + +static void vpu_ipi_handler(struct mtk_vpu *vpu) +{ + struct share_obj *rcv_obj = vpu->recv_buf; + struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc; + + if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) { + ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf, + rcv_obj->len, + ipi_desc[rcv_obj->id].priv); + if (rcv_obj->id > IPI_VPU_INIT) { + vpu->ipi_id_ack[rcv_obj->id] = true; + wake_up(&vpu->ack_wq); + } + } else { + dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id); + } +} + +static int vpu_ipi_init(struct mtk_vpu *vpu) +{ + /* Disable VPU to host interrupt */ + vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); + + /* shared buffer initialization */ + vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm + + VPU_DTCM_OFFSET); + vpu->send_buf = vpu->recv_buf + 1; + memset(vpu->recv_buf, 0, sizeof(struct share_obj)); + memset(vpu->send_buf, 0, sizeof(struct share_obj)); + + return 0; +} + +static irqreturn_t vpu_irq_handler(int irq, void *priv) +{ + struct mtk_vpu *vpu = priv; + u32 vpu_to_host; + int ret; + + /* + * Clock should have been enabled already. + * Enable again in case vpu_ipi_send times out + * and has disabled the clock. + */ + ret = clk_enable(vpu->clk); + if (ret) { + dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); + return IRQ_NONE; + } + vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); + if (vpu_to_host & VPU_IPC_INT) { + vpu_ipi_handler(vpu); + } else { + dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host); + queue_work(vpu->wdt.wq, &vpu->wdt.ws); + } + + /* VPU won't send another interrupt until we set VPU_TO_HOST to 0. */ + vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); + clk_disable(vpu->clk); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *vpu_debugfs; +#endif +static int mtk_vpu_probe(struct platform_device *pdev) +{ + struct mtk_vpu *vpu; + struct device *dev; + struct resource *res; + int ret = 0; + + dev_dbg(&pdev->dev, "initialization\n"); + + dev = &pdev->dev; + vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL); + if (!vpu) + return -ENOMEM; + + vpu->dev = &pdev->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcm"); + vpu->reg.tcm = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)vpu->reg.tcm)) { + dev_err(dev, "devm_ioremap_resource vpu tcm failed.\n"); + return PTR_ERR((__force void *)vpu->reg.tcm); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_reg"); + vpu->reg.cfg = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)vpu->reg.cfg)) { + dev_err(dev, "devm_ioremap_resource vpu cfg failed.\n"); + return PTR_ERR((__force void *)vpu->reg.cfg); + } + + /* Get VPU clock */ + vpu->clk = devm_clk_get(dev, "main"); + if (!vpu->clk) { + dev_err(dev, "get vpu clock failed\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, vpu); + + ret = clk_prepare(vpu->clk); + if (ret) { + dev_err(dev, "prepare vpu clock failed\n"); + return ret; + } + + /* VPU watchdog */ + vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt"); + if (!vpu->wdt.wq) { + dev_err(dev, "initialize wdt workqueue failed\n"); + return -ENOMEM; + } + INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func); + mutex_init(&vpu->vpu_mutex); + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "enable vpu clock failed\n"); + goto workqueue_destroy; + } + + dev_dbg(dev, "vpu ipi init\n"); + ret = vpu_ipi_init(vpu); + if (ret) { + dev_err(dev, "Failed to init ipi\n"); + goto disable_vpu_clk; + } + + /* register vpu initialization IPI */ + ret = vpu_ipi_register(pdev, IPI_VPU_INIT, vpu_init_ipi_handler, + "vpu_init", vpu); + if (ret) { + dev_err(dev, "Failed to register IPI_VPU_INIT\n"); + goto vpu_mutex_destroy; + } + +#ifdef CONFIG_DEBUG_FS + vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev, + &vpu_debug_fops); + if (!vpu_debugfs) { + ret = -ENOMEM; + goto cleanup_ipi; + } +#endif + + /* Set PTCM to 96K and DTCM to 32K */ + vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG); + + vpu->enable_4GB = !!(totalram_pages > (SZ_2G >> PAGE_SHIFT)); + dev_info(dev, "4GB mode %u\n", vpu->enable_4GB); + + if (vpu->enable_4GB) { + ret = of_reserved_mem_device_init(dev); + if (ret) + dev_info(dev, "init reserved memory failed\n"); + /* continue to use dynamic allocation if failed */ + } + + ret = vpu_alloc_ext_mem(vpu, D_FW); + if (ret) { + dev_err(dev, "Allocate DM failed\n"); + goto remove_debugfs; + } + + ret = vpu_alloc_ext_mem(vpu, P_FW); + if (ret) { + dev_err(dev, "Allocate PM failed\n"); + goto free_d_mem; + } + + init_waitqueue_head(&vpu->run.wq); + init_waitqueue_head(&vpu->ack_wq); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "get IRQ resource failed.\n"); + ret = -ENXIO; + goto free_p_mem; + } + vpu->reg.irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0, + pdev->name, vpu); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto free_p_mem; + } + + vpu_clock_disable(vpu); + dev_dbg(dev, "initialization completed\n"); + + return 0; + +free_p_mem: + vpu_free_ext_mem(vpu, P_FW); +free_d_mem: + vpu_free_ext_mem(vpu, D_FW); +remove_debugfs: + of_reserved_mem_device_release(dev); +#ifdef CONFIG_DEBUG_FS + debugfs_remove(vpu_debugfs); +cleanup_ipi: +#endif + memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX); +vpu_mutex_destroy: + mutex_destroy(&vpu->vpu_mutex); +disable_vpu_clk: + vpu_clock_disable(vpu); +workqueue_destroy: + destroy_workqueue(vpu->wdt.wq); + + return ret; +} + +static const struct of_device_id mtk_vpu_match[] = { + { + .compatible = "mediatek,mt8173-vpu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vpu_match); + +static int mtk_vpu_remove(struct platform_device *pdev) +{ + struct mtk_vpu *vpu = platform_get_drvdata(pdev); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove(vpu_debugfs); +#endif + if (vpu->wdt.wq) { + flush_workqueue(vpu->wdt.wq); + destroy_workqueue(vpu->wdt.wq); + } + vpu_free_ext_mem(vpu, P_FW); + vpu_free_ext_mem(vpu, D_FW); + mutex_destroy(&vpu->vpu_mutex); + clk_unprepare(vpu->clk); + + return 0; +} + +static struct platform_driver mtk_vpu_driver = { + .probe = mtk_vpu_probe, + .remove = mtk_vpu_remove, + .driver = { + .name = "mtk_vpu", + .of_match_table = mtk_vpu_match, + }, +}; + +module_platform_driver(mtk_vpu_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver"); diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h new file mode 100644 index 000000000000..5ab37f04bdfd --- /dev/null +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.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. +* +* 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. +*/ + +#ifndef _MTK_VPU_H +#define _MTK_VPU_H + +#include <linux/platform_device.h> + +/** + * VPU (video processor unit) is a tiny processor controlling video hardware + * related to video codec, scaling and color format converting. + * VPU interfaces with other blocks by share memory and interrupt. + **/ + +typedef void (*ipi_handler_t) (void *data, + unsigned int len, + void *priv); + +/** + * enum ipi_id - the id of inter-processor interrupt + * + * @IPI_VPU_INIT: The interrupt from vpu is to notfiy kernel + VPU initialization completed. + IPI_VPU_INIT is sent from VPU when firmware is + loaded. AP doesn't need to send IPI_VPU_INIT + command to VPU. + For other IPI below, AP should send the request + to VPU to trigger the interrupt. + * @IPI_VENC_H264: The interrupt from vpu is to notify kernel to + handle H264 video encoder job, and vice versa. + * @IPI_VENC_VP8: The interrupt fro vpu is to notify kernel to + handle VP8 video encoder job,, and vice versa. + * @IPI_MAX: The maximum IPI number + */ + +enum ipi_id { + IPI_VPU_INIT = 0, + IPI_VENC_H264, + IPI_VENC_VP8, + IPI_MAX, +}; + +/** + * enum rst_id - reset id to register reset function for VPU watchdog timeout + * + * @VPU_RST_ENC: encoder reset id + * @VPU_RST_MAX: maximum reset id + */ +enum rst_id { + VPU_RST_ENC, + VPU_RST_MAX, +}; + +/** + * vpu_ipi_register - register an ipi function + * + * @pdev: VPU platform device + * @id: IPI ID + * @handler: IPI handler + * @name: IPI name + * @priv: private data for IPI handler + * + * Register an ipi function to receive ipi interrupt from VPU. + * + * Return: Return 0 if ipi registers successfully, otherwise it is failed. + */ +int vpu_ipi_register(struct platform_device *pdev, enum ipi_id id, + ipi_handler_t handler, const char *name, void *priv); + +/** + * vpu_ipi_send - send data from AP to vpu. + * + * @pdev: VPU platform device + * @id: IPI ID + * @buf: the data buffer + * @len: the data buffer length + * + * This function is thread-safe. When this function returns, + * VPU has received the data and starts the processing. + * When the processing completes, IPI handler registered + * by vpu_ipi_register will be called in interrupt context. + * + * Return: Return 0 if sending data successfully, otherwise it is failed. + **/ +int vpu_ipi_send(struct platform_device *pdev, + enum ipi_id id, void *buf, + unsigned int len); + +/** + * vpu_get_plat_device - get VPU's platform device + * + * @pdev: the platform device of the module requesting VPU platform + * device for using VPU API. + * + * Return: Return NULL if it is failed. + * otherwise it is VPU's platform device + **/ +struct platform_device *vpu_get_plat_device(struct platform_device *pdev); + +/** + * vpu_wdt_reg_handler - register a VPU watchdog handler + * + * @pdev: VPU platform device + * @vpu_wdt_reset_func: the callback reset function + * @private_data: the private data for reset function + * @rst_id: reset id + * + * Register a handler performing own tasks when vpu reset by watchdog + * + * Return: Return 0 if the handler is added successfully, + * otherwise it is failed. + * + **/ +int vpu_wdt_reg_handler(struct platform_device *pdev, + void vpu_wdt_reset_func(void *), + void *priv, enum rst_id id); +/** + * vpu_get_venc_hw_capa - get video encoder hardware capability + * + * @pdev: VPU platform device + * + * Return: video encoder hardware capability + **/ +unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev); + +/** + * vpu_load_firmware - download VPU firmware and boot it + * + * @pdev: VPU platform device + * + * Return: Return 0 if downloading firmware successfully, + * otherwise it is failed + **/ +int vpu_load_firmware(struct platform_device *pdev); + +/** + * vpu_mapping_dm_addr - Mapping DTCM/DMEM to kernel virtual address + * + * @pdev: VPU platform device + * @dmem_addr: VPU's data memory address + * + * Mapping the VPU's DTCM (Data Tightly-Coupled Memory) / + * DMEM (Data Extended Memory) memory address to + * kernel virtual address. + * + * Return: Return ERR_PTR(-EINVAL) if mapping failed, + * otherwise the mapped kernel virtual address + **/ +void *vpu_mapping_dm_addr(struct platform_device *pdev, + u32 dtcm_dmem_addr); +#endif /* _MTK_VPU_H */ diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 3c4012d42d69..c639406fe72e 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -211,7 +211,6 @@ struct emmaprp_dev { struct clk *clk_emma_ahb, *clk_emma_ipg; struct v4l2_m2m_dev *m2m_dev; - struct vb2_alloc_ctx *alloc_ctx; }; struct emmaprp_ctx { @@ -690,7 +689,7 @@ static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { */ static int emmaprp_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq); struct emmaprp_q_data *q_data; @@ -710,8 +709,6 @@ static int emmaprp_queue_setup(struct vb2_queue *vq, *nbuffers = count; sizes[0] = size; - alloc_ctxs[0] = ctx->dev->alloc_ctx; - dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; @@ -765,6 +762,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &emmaprp_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = ctx->dev->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -777,6 +775,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &emmaprp_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = ctx->dev->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -948,18 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev) if (ret) goto rel_vdev; - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(pcdev->alloc_ctx); - goto rel_vdev; - } - pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(pcdev->m2m_dev)) { v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(pcdev->m2m_dev); - goto rel_ctx; + goto rel_vdev; } ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); @@ -973,8 +965,6 @@ static int emmaprp_probe(struct platform_device *pdev) rel_m2m: v4l2_m2m_release(pcdev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); rel_vdev: video_device_release(vfd); unreg_dev: @@ -993,7 +983,6 @@ static int emmaprp_remove(struct platform_device *pdev) video_unregister_device(pcdev->vfd); v4l2_m2m_release(pcdev->m2m_dev); - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); v4l2_device_unregister(&pcdev->v4l2_dev); mutex_destroy(&pcdev->dev_mutex); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 1b1a95d546f6..7d9f35976d18 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -331,7 +331,7 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) static int isp_video_queue_setup(struct vb2_queue *queue, unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct isp_video_fh *vfh = vb2_get_drv_priv(queue); struct isp_video *video = vfh->video; @@ -342,8 +342,6 @@ static int isp_video_queue_setup(struct vb2_queue *queue, if (sizes[0] == 0) return -EINVAL; - alloc_ctxs[0] = video->alloc_ctx; - *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); return 0; @@ -1308,6 +1306,7 @@ static int isp_video_open(struct file *file) queue->mem_ops = &vb2_dma_contig_memops; queue->buf_struct_size = sizeof(struct isp_buffer); queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->dev = video->isp->dev; ret = vb2_queue_init(&handle->queue); if (ret < 0) { @@ -1414,15 +1413,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name) 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_pads_init(&video->video.entity, 1, &video->pad); - if (ret < 0) { - vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + if (ret < 0) return ret; - } mutex_init(&video->mutex); atomic_set(&video->active, 0); @@ -1451,7 +1444,6 @@ int omap3isp_video_init(struct isp_video *video, const char *name) 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); diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 6a48d5879c56..f6a2082b4a0a 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -171,7 +171,6 @@ struct isp_video { bool error; /* Video buffers queue */ - void *alloc_ctx; struct vb2_queue *queue; struct mutex queue_lock; /* protects the queue */ spinlock_t irqlock; /* protects dmaqueue */ diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig new file mode 100644 index 000000000000..b2ff2d4e8bb1 --- /dev/null +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_RCAR_VIN + tristate "R-Car Video Input (VIN) Driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + ---help--- + Support for Renesas R-Car Video Input (VIN) driver. + Supports R-Car Gen2 SoCs. + + To compile this driver as a module, choose M here: the + module will be called rcar-vin. diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile new file mode 100644 index 000000000000..48c5632c21dc --- /dev/null +++ b/drivers/media/platform/rcar-vin/Makefile @@ -0,0 +1,3 @@ +rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o + +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c new file mode 100644 index 000000000000..4b2007b73463 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -0,0 +1,334 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-of.h> + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * Async notifier + */ + +#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) + +static int rvin_mbus_supported(struct rvin_dev *vin) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + sd = vin_to_source(vin); + + code.index = 0; + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + code.index++; + switch (code.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + vin->source.code = code.code; + vin_dbg(vin, "Found supported media bus format: %d\n", + vin->source.code); + return true; + default: + break; + } + } + + return false; +} + +static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + int ret; + + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); + if (ret < 0) { + vin_err(vin, "Failed to register subdev nodes\n"); + return ret; + } + + if (!rvin_mbus_supported(vin)) { + vin_err(vin, "No supported mediabus format found\n"); + return -EINVAL; + } + + return rvin_v4l2_probe(vin); +} + +static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + rvin_v4l2_remove(vin); +} + +static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + vin_dbg(vin, "subdev %s bound\n", subdev->name); + + vin->entity.entity = &subdev->entity; + vin->entity.subdev = subdev; + + return 0; +} + +static int rvin_graph_parse(struct rvin_dev *vin, + struct device_node *node) +{ + struct device_node *remote; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + while (1) { + next = of_graph_get_next_endpoint(node, ep); + if (!next) + break; + + of_node_put(ep); + ep = next; + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + ret = -EINVAL; + break; + } + + /* Skip entities that we have already processed. */ + if (remote == vin->dev->of_node) { + of_node_put(remote); + continue; + } + + /* Remote node to connect */ + if (!vin->entity.node) { + vin->entity.node = remote; + vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; + vin->entity.asd.match.of.node = remote; + ret++; + } + } + + of_node_put(ep); + + return ret; +} + +static int rvin_graph_init(struct rvin_dev *vin) +{ + struct v4l2_async_subdev **subdevs = NULL; + int ret; + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = rvin_graph_parse(vin, vin->dev->of_node); + if (ret < 0) { + vin_err(vin, "Graph parsing failed\n"); + goto done; + } + + if (!ret) { + vin_err(vin, "No subdev found in graph\n"); + goto done; + } + + if (ret != 1) { + vin_err(vin, "More then one subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); + if (subdevs == NULL) { + ret = -ENOMEM; + goto done; + } + + subdevs[0] = &vin->entity.asd; + + vin->notifier.subdevs = subdevs; + vin->notifier.num_subdevs = 1; + vin->notifier.bound = rvin_graph_notify_bound; + vin->notifier.unbind = rvin_graph_notify_unbind; + vin->notifier.complete = rvin_graph_notify_complete; + + ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + if (ret < 0) { + vin_err(vin, "Notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) { + v4l2_async_notifier_unregister(&vin->notifier); + of_node_put(vin->entity.node); + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static const struct of_device_id rvin_of_id_table[] = { + { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, + { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rvin_of_id_table); + +static int rvin_parse_dt(struct rvin_dev *vin) +{ + const struct of_device_id *match; + struct v4l2_of_endpoint ep; + struct device_node *np; + int ret; + + match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev); + if (!match) + return -ENODEV; + + vin->chip = (enum chip_id)match->data; + + np = of_graph_get_next_endpoint(vin->dev->of_node, NULL); + if (!np) { + vin_err(vin, "Could not find endpoint\n"); + return -EINVAL; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + vin_err(vin, "Could not parse endpoint\n"); + return ret; + } + + of_node_put(np); + + vin->mbus_cfg.type = ep.bus_type; + + switch (vin->mbus_cfg.type) { + case V4L2_MBUS_PARALLEL: + vin->mbus_cfg.flags = ep.bus.parallel.flags; + break; + case V4L2_MBUS_BT656: + vin->mbus_cfg.flags = 0; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_vin_probe(struct platform_device *pdev) +{ + struct rvin_dev *vin; + struct resource *mem; + int irq, ret; + + vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL); + if (!vin) + return -ENOMEM; + + vin->dev = &pdev->dev; + + ret = rvin_parse_dt(vin); + if (ret) + return ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + vin->base = devm_ioremap_resource(vin->dev, mem); + if (IS_ERR(vin->base)) + return PTR_ERR(vin->base); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return ret; + + ret = rvin_dma_probe(vin, irq); + if (ret) + return ret; + + ret = rvin_graph_init(vin); + if (ret < 0) + goto error; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, vin); + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct rvin_dev *vin = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&vin->notifier); + + rvin_dma_remove(vin); + + return 0; +} + +static struct platform_driver rcar_vin_driver = { + .driver = { + .name = "rcar-vin", + .of_match_table = rvin_of_id_table, + }, + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c new file mode 100644 index 000000000000..496aa97b6400 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -0,0 +1,1187 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include <media/videobuf2-dma-contig.h> + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * HW Functions + */ + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ +#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ +#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ +#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ +#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ +#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ +#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ +#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ +#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ +#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ +#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ +#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ +#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ +#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ +#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ +#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ +#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ +#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ +#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ +#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ +#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ +#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ +#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ +#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ +#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ + + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV10_BT656 (2 << 16) +#define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB1555 (1 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) +#define VNDMR2_VLV(n) ((n & 0xf) << 12) + +static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset) +{ + iowrite32(value, vin->base + offset); +} + +static u32 rvin_read(struct rvin_dev *vin, u32 offset) +{ + return ioread32(vin->base + offset); +} + +static int rvin_setup(struct rvin_dev *vin) +{ + u32 vnmc, dmr, dmr2, interrupts; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (vin->continuous) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* + * Input interface + */ + switch (vin->source.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; + break; + case MEDIA_BUS_FMT_YUYV10_2X10: + /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; + break; + default: + break; + } + + /* Enable VSYNC Field Toogle mode after one VSYNC input */ + dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); + + /* Hsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; + + /* + * Output format + */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV16: + rvin_write(vin, + ALIGN(vin->format.width * vin->format.height, 0x80), + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_XRGB555: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_XBGR32: + if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) { + dmr = VNDMR_EXRGB; + break; + } + /* fall through */ + default: + vin_err(vin, "Invalid pixelformat (0x%x)\n", + vin->format.pixelformat); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (input_is_yuv == output_is_yuv) + vnmc |= VNMC_BPS; + + /* Progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE : VNIE_EFE; + + /* Ack interrupts */ + rvin_write(vin, interrupts, VNINTS_REG); + /* Enable interrupts */ + rvin_write(vin, interrupts, VNIE_REG); + /* Start capturing */ + rvin_write(vin, dmr, VNDMR_REG); + rvin_write(vin, dmr2, VNDMR2_REG); + + /* Enable module */ + rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); + + return 0; +} + +static void rvin_capture_on(struct rvin_dev *vin) +{ + vin_dbg(vin, "Capture on in %s mode\n", + vin->continuous ? "continuous" : "single"); + + if (vin->continuous) + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); + else + /* Single Frame Capture Mode */ + rvin_write(vin, VNFC_S_FRAME, VNFC_REG); +} + +static void rvin_capture_off(struct rvin_dev *vin) +{ + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); +} + +static int rvin_capture_start(struct rvin_dev *vin) +{ + int ret; + + rvin_crop_scale_comp(vin); + + ret = rvin_setup(vin); + if (ret) + return ret; + + rvin_capture_on(vin); + + return 0; +} + +static void rvin_capture_stop(struct rvin_dev *vin) +{ + rvin_capture_off(vin); + + /* Disable module */ + rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); +} + +static void rvin_disable_interrupts(struct rvin_dev *vin) +{ + rvin_write(vin, 0, VNIE_REG); +} + +static u32 rvin_get_interrupt_status(struct rvin_dev *vin) +{ + return rvin_read(vin, VNINTS_REG); +} + +static void rvin_ack_interrupt(struct rvin_dev *vin) +{ + rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); +} + +static bool rvin_capture_active(struct rvin_dev *vin) +{ + return rvin_read(vin, VNMS_REG) & VNMS_CA; +} + +static int rvin_get_active_slot(struct rvin_dev *vin) +{ + if (vin->continuous) + return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK) + >> VNMS_FBS_SHIFT; + + return 0; +} + +static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) +{ + const struct rvin_video_format *fmt; + int offsetx, offsety; + dma_addr_t offset; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + + /* + * There is no HW support for composition do the beast we can + * by modifying the buffer offset + */ + offsetx = vin->compose.left * fmt->bpp; + offsety = vin->compose.top * vin->format.bytesperline; + offset = addr + offsetx + offsety; + + /* + * The address needs to be 128 bytes aligned. Driver should never accept + * settings that do not satisfy this in the first place... + */ + if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) + return; + + rvin_write(vin, offset, VNMB_REG(slot)); +} + +/* ----------------------------------------------------------------------------- + * Crop and Scaling Gen2 + */ + +struct vin_coeff { + unsigned short xs_value; + u32 coeff_set[24]; +}; + +static const struct vin_coeff vin_coeff_set[] = { + { 0x0000, { + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 }, + }, + { 0x1000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x000003f8, 0x00000403, 0x3de0d9f0, + 0x001fffed, 0x00000804, 0x3cc1f9c3, + 0x001003de, 0x00000c01, 0x3cb34d7f, + 0x002003d2, 0x00000c00, 0x3d24a92d, + 0x00200bca, 0x00000bff, 0x3df600d2, + 0x002013cc, 0x000007ff, 0x3ed70c7e, + 0x00100fde, 0x00000000, 0x3f87c036 }, + }, + { 0x1200, { + 0x002ffff1, 0x002ffff1, 0x02a0a9c8, + 0x002003e7, 0x001ffffa, 0x000185bc, + 0x002007dc, 0x000003ff, 0x3e52859c, + 0x00200bd4, 0x00000002, 0x3d53996b, + 0x00100fd0, 0x00000403, 0x3d04ad2d, + 0x00000bd5, 0x00000403, 0x3d35ace7, + 0x3ff003e4, 0x00000801, 0x3dc674a1, + 0x3fffe800, 0x00000800, 0x3e76f461 }, + }, + { 0x1400, { + 0x00100be3, 0x00100be3, 0x04d1359a, + 0x00000fdb, 0x002003ed, 0x0211fd93, + 0x00000fd6, 0x002003f4, 0x0002d97b, + 0x000007d6, 0x002ffffb, 0x3e93b956, + 0x3ff003da, 0x001003ff, 0x3db49926, + 0x3fffefe9, 0x00100001, 0x3d655cee, + 0x3fffd400, 0x00000003, 0x3d65f4b6, + 0x000fb421, 0x00000402, 0x3dc6547e }, + }, + { 0x1600, { + 0x00000bdd, 0x00000bdd, 0x06519578, + 0x3ff007da, 0x00000be3, 0x03c24973, + 0x3ff003d9, 0x00000be9, 0x01b30d5f, + 0x3ffff7df, 0x001003f1, 0x0003c542, + 0x000fdfec, 0x001003f7, 0x3ec4711d, + 0x000fc400, 0x002ffffd, 0x3df504f1, + 0x001fa81a, 0x002ffc00, 0x3d957cc2, + 0x002f8c3c, 0x00100000, 0x3db5c891 }, + }, + { 0x1800, { + 0x3ff003dc, 0x3ff003dc, 0x0791e558, + 0x000ff7dd, 0x3ff007de, 0x05328554, + 0x000fe7e3, 0x3ff00be2, 0x03232546, + 0x000fd7ee, 0x000007e9, 0x0143bd30, + 0x001fb800, 0x000007ee, 0x00044511, + 0x002fa015, 0x000007f4, 0x3ef4bcee, + 0x002f8832, 0x001003f9, 0x3e4514c7, + 0x001f7853, 0x001003fd, 0x3de54c9f }, + }, + { 0x1a00, { + 0x000fefe0, 0x000fefe0, 0x08721d3c, + 0x001fdbe7, 0x000ffbde, 0x0652a139, + 0x001fcbf0, 0x000003df, 0x0463292e, + 0x002fb3ff, 0x3ff007e3, 0x0293a91d, + 0x002f9c12, 0x3ff00be7, 0x01241905, + 0x001f8c29, 0x000007ed, 0x3fe470eb, + 0x000f7c46, 0x000007f2, 0x3f04b8ca, + 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, + }, + { 0x1c00, { + 0x001fd3e9, 0x001fd3e9, 0x08f23d26, + 0x002fbff3, 0x001fe3e4, 0x0712ad23, + 0x002fa800, 0x000ff3e0, 0x05631d1b, + 0x001f9810, 0x000ffbe1, 0x03b3890d, + 0x000f8c23, 0x000003e3, 0x0233e8fa, + 0x3fef843b, 0x000003e7, 0x00f430e4, + 0x3fbf8456, 0x3ff00bea, 0x00046cc8, + 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, + }, + { 0x1e00, { + 0x001fbbf4, 0x001fbbf4, 0x09425112, + 0x001fa800, 0x002fc7ed, 0x0792b110, + 0x000f980e, 0x001fdbe6, 0x0613110a, + 0x3fff8c20, 0x001fe7e3, 0x04a368fd, + 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, + 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, + 0x3f5f9c61, 0x000003e6, 0x00e428c5, + 0x3f1fb07b, 0x000003eb, 0x3fe440af }, + }, + { 0x2000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x3fff980c, 0x001fb7f5, 0x0812b0ff, + 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, + 0x3faf902d, 0x001fd3e8, 0x055348f1, + 0x3f7f983f, 0x001fe3e5, 0x04038ce3, + 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, + 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, + 0x3ecfd880, 0x000fffe6, 0x00c404ac }, + }, + { 0x2200, { + 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, + 0x3fbf9818, 0x3fffa400, 0x0842a8f1, + 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, + 0x3f5fa037, 0x000fc3ef, 0x05d330e4, + 0x3f2fac49, 0x001fcfea, 0x04a364d9, + 0x3effc05c, 0x001fdbe7, 0x038394ca, + 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, + 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, + }, + { 0x2400, { + 0x3f9fa014, 0x3f9fa014, 0x098260e6, + 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, + 0x3f4fa431, 0x3fefa400, 0x0742d8e1, + 0x3f1fb440, 0x3fffb3f8, 0x062310d9, + 0x3eefc850, 0x000fbbf2, 0x050340d0, + 0x3ecfe062, 0x000fcbec, 0x041364c2, + 0x3ea00073, 0x001fd3ea, 0x03037cb5, + 0x3e902086, 0x001fdfe8, 0x022388a5 }, + }, + { 0x2600, { + 0x3f5fa81e, 0x3f5fa81e, 0x096258da, + 0x3f3fac2b, 0x3f8fa412, 0x088290d8, + 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, + 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, + 0x3ecfe456, 0x3fefaffa, 0x05531cc6, + 0x3eb00066, 0x3fffbbf3, 0x047334bb, + 0x3ea01c77, 0x000fc7ee, 0x039348ae, + 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, + }, + { 0x2800, { + 0x3f2fb426, 0x3f2fb426, 0x094250ce, + 0x3f0fc032, 0x3f4fac1b, 0x086284cd, + 0x3eefd040, 0x3f7fa811, 0x0782acc9, + 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, + 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, + 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, + 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, + 0x3ec06884, 0x000fbff2, 0x03031c9e }, + }, + { 0x2a00, { + 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, + 0x3eefd439, 0x3f2fb822, 0x08526cc2, + 0x3edfe845, 0x3f4fb018, 0x078294bf, + 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, + 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, + 0x3ec0386b, 0x3fafac00, 0x0502e8ac, + 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, + 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, + }, + { 0x2c00, { + 0x3eefdc31, 0x3eefdc31, 0x08e238b8, + 0x3edfec3d, 0x3f0fc828, 0x082258b9, + 0x3ed00049, 0x3f1fc01e, 0x077278b6, + 0x3ed01455, 0x3f3fb815, 0x06c294b2, + 0x3ed03460, 0x3f5fb40d, 0x0602acac, + 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, + 0x3f107476, 0x3f9fb400, 0x0472c89d, + 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, + }, + { 0x2e00, { + 0x3eefec37, 0x3eefec37, 0x088220b0, + 0x3ee00041, 0x3effdc2d, 0x07f244ae, + 0x3ee0144c, 0x3f0fd023, 0x07625cad, + 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, + 0x3f004861, 0x3f3fbc13, 0x060288a6, + 0x3f20686b, 0x3f5fb80c, 0x05529c9e, + 0x3f408c74, 0x3f6fb805, 0x04b2ac96, + 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, + }, + { 0x3000, { + 0x3ef0003a, 0x3ef0003a, 0x084210a6, + 0x3ef01045, 0x3effec32, 0x07b228a7, + 0x3f00284e, 0x3f0fdc29, 0x073244a4, + 0x3f104058, 0x3f0fd420, 0x06a258a2, + 0x3f305c62, 0x3f2fc818, 0x0612689d, + 0x3f508069, 0x3f3fc011, 0x05728496, + 0x3f80a072, 0x3f4fc00a, 0x04d28c90, + 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, + }, + { 0x3200, { + 0x3f00103e, 0x3f00103e, 0x07f1fc9e, + 0x3f102447, 0x3f000035, 0x0782149d, + 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, + 0x3f405458, 0x3f0fe424, 0x06924099, + 0x3f607061, 0x3f1fd41d, 0x06024c97, + 0x3f909068, 0x3f2fcc16, 0x05726490, + 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, + 0x0000d077, 0x3f4fc409, 0x04627484 }, + }, + { 0x3400, { + 0x3f202040, 0x3f202040, 0x07a1e898, + 0x3f303449, 0x3f100c38, 0x0741fc98, + 0x3f504c50, 0x3f10002f, 0x06e21495, + 0x3f706459, 0x3f1ff028, 0x06722492, + 0x3fa08060, 0x3f1fe421, 0x05f2348f, + 0x3fd09c67, 0x3f1fdc19, 0x05824c89, + 0x0000bc6e, 0x3f2fd014, 0x04f25086, + 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, + }, + { 0x3600, { + 0x3f403042, 0x3f403042, 0x0761d890, + 0x3f504848, 0x3f301c3b, 0x0701f090, + 0x3f805c50, 0x3f200c33, 0x06a2008f, + 0x3fa07458, 0x3f10002b, 0x06520c8d, + 0x3fd0905e, 0x3f1ff424, 0x05e22089, + 0x0000ac65, 0x3f1fe81d, 0x05823483, + 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, + 0x0080e871, 0x3f2fd412, 0x0482407c }, + }, + { 0x3800, { + 0x3f604043, 0x3f604043, 0x0721c88a, + 0x3f80544a, 0x3f502c3c, 0x06d1d88a, + 0x3fb06851, 0x3f301c35, 0x0681e889, + 0x3fd08456, 0x3f30082f, 0x0611fc88, + 0x00009c5d, 0x3f200027, 0x05d20884, + 0x0030b863, 0x3f2ff421, 0x05621880, + 0x0070d468, 0x3f2fe81b, 0x0502247c, + 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, + }, + { 0x3a00, { + 0x3f904c44, 0x3f904c44, 0x06e1b884, + 0x3fb0604a, 0x3f70383e, 0x0691c885, + 0x3fe07451, 0x3f502c36, 0x0661d483, + 0x00009055, 0x3f401831, 0x0601ec81, + 0x0030a85b, 0x3f300c2a, 0x05b1f480, + 0x0070c061, 0x3f300024, 0x0562047a, + 0x00b0d867, 0x3f3ff41e, 0x05020c77, + 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, + }, + { 0x3c00, { + 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, + 0x3fe06c4b, 0x3f902c3f, 0x0681c081, + 0x0000844f, 0x3f703838, 0x0631cc7d, + 0x00309855, 0x3f602433, 0x05d1d47e, + 0x0060b459, 0x3f50142e, 0x0581e47b, + 0x00a0c85f, 0x3f400828, 0x0531f078, + 0x00e0e064, 0x3f300021, 0x0501fc73, + 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, + }, + { 0x3e00, { + 0x3fe06444, 0x3fe06444, 0x0681a07a, + 0x00007849, 0x3fc0503f, 0x0641b07a, + 0x0020904d, 0x3fa0403a, 0x05f1c07a, + 0x0060a453, 0x3f803034, 0x05c1c878, + 0x0090b858, 0x3f70202f, 0x0571d477, + 0x00d0d05d, 0x3f501829, 0x0531e073, + 0x0110e462, 0x3f500825, 0x04e1e471, + 0x01510065, 0x3f40001f, 0x04a1f06d }, + }, + { 0x4000, { + 0x00007044, 0x00007044, 0x06519476, + 0x00208448, 0x3fe05c3f, 0x0621a476, + 0x0050984d, 0x3fc04c3a, 0x05e1b075, + 0x0080ac52, 0x3fa03c35, 0x05a1b875, + 0x00c0c056, 0x3f803030, 0x0561c473, + 0x0100d45b, 0x3f70202b, 0x0521d46f, + 0x0140e860, 0x3f601427, 0x04d1d46e, + 0x01810064, 0x3f500822, 0x0491dc6b }, + }, + { 0x5000, { + 0x0110a442, 0x0110a442, 0x0551545e, + 0x0140b045, 0x00e0983f, 0x0531585f, + 0x0160c047, 0x00c08c3c, 0x0511645e, + 0x0190cc4a, 0x00908039, 0x04f1685f, + 0x01c0dc4c, 0x00707436, 0x04d1705e, + 0x0200e850, 0x00506833, 0x04b1785b, + 0x0230f453, 0x00305c30, 0x0491805a, + 0x02710056, 0x0010542d, 0x04718059 }, + }, + { 0x6000, { + 0x01c0bc40, 0x01c0bc40, 0x04c13052, + 0x01e0c841, 0x01a0b43d, 0x04c13851, + 0x0210cc44, 0x0180a83c, 0x04a13453, + 0x0230d845, 0x0160a03a, 0x04913c52, + 0x0260e047, 0x01409838, 0x04714052, + 0x0280ec49, 0x01208c37, 0x04514c50, + 0x02b0f44b, 0x01008435, 0x04414c50, + 0x02d1004c, 0x00e07c33, 0x0431544f }, + }, + { 0x7000, { + 0x0230c83e, 0x0230c83e, 0x04711c4c, + 0x0250d03f, 0x0210c43c, 0x0471204b, + 0x0270d840, 0x0200b83c, 0x0451244b, + 0x0290dc42, 0x01e0b43a, 0x0441244c, + 0x02b0e443, 0x01c0b038, 0x0441284b, + 0x02d0ec44, 0x01b0a438, 0x0421304a, + 0x02f0f445, 0x0190a036, 0x04213449, + 0x0310f847, 0x01709c34, 0x04213848 }, + }, + { 0x8000, { + 0x0280d03d, 0x0280d03d, 0x04310c48, + 0x02a0d43e, 0x0270c83c, 0x04311047, + 0x02b0dc3e, 0x0250c83a, 0x04311447, + 0x02d0e040, 0x0240c03a, 0x04211446, + 0x02e0e840, 0x0220bc39, 0x04111847, + 0x0300e842, 0x0210b438, 0x04012445, + 0x0310f043, 0x0200b037, 0x04012045, + 0x0330f444, 0x01e0ac36, 0x03f12445 }, + }, + { 0xefff, { + 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, + 0x0340e03a, 0x0330e039, 0x03c0f03e, + 0x0350e03b, 0x0330dc39, 0x03c0ec3e, + 0x0350e43a, 0x0320dc38, 0x03c0f43e, + 0x0360e43b, 0x0320d839, 0x03b0f03e, + 0x0360e83b, 0x0310d838, 0x03c0fc3b, + 0x0370e83b, 0x0310d439, 0x03a0f83d, + 0x0370e83c, 0x0300d438, 0x03b0fc3c }, + } +}; + +static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) +{ + int i; + const struct vin_coeff *p_prev_set = NULL; + const struct vin_coeff *p_set = NULL; + + /* Look for suitable coefficient values */ + for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { + p_prev_set = p_set; + p_set = &vin_coeff_set[i]; + + if (xs < p_set->xs_value) + break; + } + + /* Use previous value if its XS value is closer */ + if (p_prev_set && p_set && + xs - p_prev_set->xs_value < p_set->xs_value - xs) + p_set = p_prev_set; + + /* Set coefficient registers */ + rvin_write(vin, p_set->coeff_set[0], VNC1A_REG); + rvin_write(vin, p_set->coeff_set[1], VNC1B_REG); + rvin_write(vin, p_set->coeff_set[2], VNC1C_REG); + + rvin_write(vin, p_set->coeff_set[3], VNC2A_REG); + rvin_write(vin, p_set->coeff_set[4], VNC2B_REG); + rvin_write(vin, p_set->coeff_set[5], VNC2C_REG); + + rvin_write(vin, p_set->coeff_set[6], VNC3A_REG); + rvin_write(vin, p_set->coeff_set[7], VNC3B_REG); + rvin_write(vin, p_set->coeff_set[8], VNC3C_REG); + + rvin_write(vin, p_set->coeff_set[9], VNC4A_REG); + rvin_write(vin, p_set->coeff_set[10], VNC4B_REG); + rvin_write(vin, p_set->coeff_set[11], VNC4C_REG); + + rvin_write(vin, p_set->coeff_set[12], VNC5A_REG); + rvin_write(vin, p_set->coeff_set[13], VNC5B_REG); + rvin_write(vin, p_set->coeff_set[14], VNC5C_REG); + + rvin_write(vin, p_set->coeff_set[15], VNC6A_REG); + rvin_write(vin, p_set->coeff_set[16], VNC6B_REG); + rvin_write(vin, p_set->coeff_set[17], VNC6C_REG); + + rvin_write(vin, p_set->coeff_set[18], VNC7A_REG); + rvin_write(vin, p_set->coeff_set[19], VNC7B_REG); + rvin_write(vin, p_set->coeff_set[20], VNC7C_REG); + + rvin_write(vin, p_set->coeff_set[21], VNC8A_REG); + rvin_write(vin, p_set->coeff_set[22], VNC8B_REG); + rvin_write(vin, p_set->coeff_set[23], VNC8C_REG); +} + +void rvin_crop_scale_comp(struct rvin_dev *vin) +{ + u32 xs, ys; + + /* Set Start/End Pixel/Line Pre-Clip */ + rvin_write(vin, vin->crop.left, VNSPPRC_REG); + rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); + rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, + VNELPRC_REG); + break; + default: + rvin_write(vin, vin->crop.top, VNSLPRC_REG); + rvin_write(vin, vin->crop.top + vin->crop.height - 1, + VNELPRC_REG); + break; + } + + /* Set scaling coefficient */ + ys = 0; + if (vin->crop.height != vin->compose.height) + ys = (4096 * vin->crop.height) / vin->compose.height; + rvin_write(vin, ys, VNYS_REG); + + xs = 0; + if (vin->crop.width != vin->compose.width) + xs = (4096 * vin->crop.width) / vin->compose.width; + + /* Horizontal upscaling is up to double size */ + if (xs > 0 && xs < 2048) + xs = 2048; + + rvin_write(vin, xs, VNXS_REG); + + /* Horizontal upscaling is done out by scaling down from double size */ + if (xs < 4096) + xs *= 2; + + rvin_set_coeff(vin, xs); + + /* Set Start/End Pixel/Line Post-Clip */ + rvin_write(vin, 0, VNSPPOC_REG); + rvin_write(vin, 0, VNSLPOC_REG); + rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); + break; + default: + rvin_write(vin, vin->format.height - 1, VNELPOC_REG); + break; + } + + if (vin->format.pixelformat == V4L2_PIX_FMT_NV16) + rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG); + else + rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG); + + vin_dbg(vin, + "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", + vin->crop.width, vin->crop.height, vin->crop.left, + vin->crop.top, ys, xs, vin->format.width, vin->format.height, + 0, 0); +} + +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height) +{ + /* All VIN channels on Gen2 have scalers */ + pix->width = width; + pix->height = height; +} + +/* ----------------------------------------------------------------------------- + * DMA Functions + */ + +#define RVIN_TIMEOUT_MS 100 +#define RVIN_RETRIES 10 + +struct rvin_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rvin_buffer, \ + vb)->list) + +/* Moves a buffer from the queue to the HW slots */ +static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +{ + struct rvin_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr_top; + + if (vin->queue_buf[slot] != NULL) + return true; + + if (list_empty(&vin->buf_list)) + return false; + + vin_dbg(vin, "Filling HW slot: %d\n", slot); + + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rvin_set_slot_addr(vin, slot, phys_addr_top); + + return true; +} + +static bool rvin_fill_hw(struct rvin_dev *vin) +{ + int slot, limit; + + limit = vin->continuous ? HW_BUFFER_NUM : 1; + + for (slot = 0; slot < limit; slot++) + if (!rvin_fill_hw_slot(vin, slot)) + return false; + return true; +} + +static irqreturn_t rvin_irq(int irq, void *data) +{ + struct rvin_dev *vin = data; + u32 int_status; + int slot; + unsigned int sequence, handled = 0; + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + int_status = rvin_get_interrupt_status(vin); + if (!int_status) + goto done; + + rvin_ack_interrupt(vin); + handled = 1; + + /* Nothing to do if capture status is 'STOPPED' */ + if (vin->state == STOPPED) { + vin_dbg(vin, "IRQ while state stopped\n"); + goto done; + } + + /* Nothing to do if capture status is 'STOPPING' */ + if (vin->state == STOPPING) { + vin_dbg(vin, "IRQ while state stopping\n"); + goto done; + } + + /* Prepare for capture and update state */ + slot = rvin_get_active_slot(vin); + sequence = vin->sequence++; + + vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", + sequence, slot, + slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0', + slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0', + slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0', + !list_empty(&vin->buf_list)); + + /* HW have written to a slot that is not prepared we are in trouble */ + if (WARN_ON((vin->queue_buf[slot] == NULL))) + goto done; + + /* Capture frame */ + vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->sequence = sequence; + vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); + vin->queue_buf[slot] = NULL; + + /* Prepare for next frame */ + if (!rvin_fill_hw(vin)) { + + /* + * Can't supply HW with new buffers fast enough. Halt + * capture until more buffers are available. + */ + vin->state = STALLED; + + /* + * The continuous capturing requires an explicit stop + * operation when there is no buffer to be set into + * the VnMBm registers. + */ + if (vin->continuous) { + rvin_capture_off(vin); + vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence); + } + } else { + /* + * The single capturing requires an explicit capture + * operation to fetch the next frame. + */ + if (!vin->continuous) + rvin_capture_on(vin); + } +done: + spin_unlock_irqrestore(&vin->qlock, flags); + + return IRQ_RETVAL(handled); +} + +/* Need to hold qlock before calling */ +static void return_all_buffers(struct rvin_dev *vin, + enum vb2_buffer_state state) +{ + struct rvin_buffer *buf, *node; + int i; + + for (i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->queue_buf[i]) { + vb2_buffer_done(&vin->queue_buf[i]->vb2_buf, + state); + vin->queue_buf[i] = NULL; + } + } + + list_for_each_entry_safe(buf, node, &vin->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } +} + +static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) + +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + + /* Make sure the image size is large enough. */ + if (*nplanes) + return sizes[0] < vin->format.sizeimage ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = vin->format.sizeimage; + + return 0; +}; + +static int rvin_buffer_prepare(struct vb2_buffer *vb) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = vin->format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + vin_err(vin, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void rvin_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + list_add_tail(to_buf_list(vbuf), &vin->buf_list); + + /* + * If capture is stalled add buffer to HW and restart + * capturing if HW is ready to continue. + */ + if (vin->state == STALLED) + if (rvin_fill_hw(vin)) + rvin_capture_on(vin); + + spin_unlock_irqrestore(&vin->qlock, flags); +} + +static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int ret; + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 1); + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = RUNNING; + vin->sequence = 0; + + /* Continuous capture requires more buffers then there are HW slots */ + vin->continuous = count > HW_BUFFER_NUM; + + /* + * This should never happen but if we don't have enough + * buffers for HW bail out + */ + if (!rvin_fill_hw(vin)) { + vin_err(vin, "HW not ready to start, not enough buffers available\n"); + ret = -EINVAL; + goto out; + } + + ret = rvin_capture_start(vin); +out: + /* Return all buffers if something went wrong */ + if (ret) { + return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + v4l2_subdev_call(sd, video, s_stream, 0); + } + + spin_unlock_irqrestore(&vin->qlock, flags); + + return ret; +} + +static void rvin_stop_streaming(struct vb2_queue *vq) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int retries = 0; + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = STOPPING; + + /* Wait for streaming to stop */ + while (retries++ < RVIN_RETRIES) { + + rvin_capture_stop(vin); + + /* Check if HW is stopped */ + if (!rvin_capture_active(vin)) { + vin->state = STOPPED; + break; + } + + spin_unlock_irqrestore(&vin->qlock, flags); + msleep(RVIN_TIMEOUT_MS); + spin_lock_irqsave(&vin->qlock, flags); + } + + if (vin->state != STOPPED) { + /* + * If this happens something have gone horribly wrong. + * Set state to stopped to prevent the interrupt handler + * to make things worse... + */ + vin_err(vin, "Failed stop HW, something is seriously broken\n"); + vin->state = STOPPED; + } + + /* Release all active buffers */ + return_all_buffers(vin, VB2_BUF_STATE_ERROR); + + spin_unlock_irqrestore(&vin->qlock, flags); + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 0); + + /* disable interrupts */ + rvin_disable_interrupts(vin); +} + +static struct vb2_ops rvin_qops = { + .queue_setup = rvin_queue_setup, + .buf_prepare = rvin_buffer_prepare, + .buf_queue = rvin_buffer_queue, + .start_streaming = rvin_start_streaming, + .stop_streaming = rvin_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +void rvin_dma_remove(struct rvin_dev *vin) +{ + mutex_destroy(&vin->lock); + + v4l2_device_unregister(&vin->v4l2_dev); +} + +int rvin_dma_probe(struct rvin_dev *vin, int irq) +{ + struct vb2_queue *q = &vin->queue; + int i, ret; + + /* Initialize the top-level structure */ + ret = v4l2_device_register(vin->dev, &vin->v4l2_dev); + if (ret) + return ret; + + mutex_init(&vin->lock); + INIT_LIST_HEAD(&vin->buf_list); + + spin_lock_init(&vin->qlock); + + vin->state = STOPPED; + + for (i = 0; i < HW_BUFFER_NUM; i++) + vin->queue_buf[i] = NULL; + + /* buffer queue */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + q->lock = &vin->lock; + q->drv_priv = vin; + q->buf_struct_size = sizeof(struct rvin_buffer); + q->ops = &rvin_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->dev = vin->dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + vin_err(vin, "failed to initialize VB2 queue\n"); + goto error; + } + + /* irq */ + ret = devm_request_irq(vin->dev, irq, rvin_irq, IRQF_SHARED, + KBUILD_MODNAME, vin); + if (ret) { + vin_err(vin, "failed to request irq\n"); + goto error; + } + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c new file mode 100644 index 000000000000..10a5c107e8b9 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -0,0 +1,874 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/pm_runtime.h> + +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-rect.h> + +#include "rcar-vin.h" + +#define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV +#define RVIN_MAX_WIDTH 2048 +#define RVIN_MAX_HEIGHT 2048 + +/* ----------------------------------------------------------------------------- + * Format Conversions + */ + +static const struct rvin_video_format rvin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB555, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .bpp = 4, + }, +}; + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) + if (rvin_formats[i].fourcc == pixelformat) + return rvin_formats + i; + + return NULL; +} + +static u32 rvin_format_bytesperline(struct v4l2_pix_format *pix) +{ + const struct rvin_video_format *fmt; + + fmt = rvin_format_from_pixel(pix->pixelformat); + + if (WARN_ON(!fmt)) + return -EINVAL; + + return pix->width * fmt->bpp; +} + +static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) +{ + if (pix->pixelformat == V4L2_PIX_FMT_NV16) + return pix->bytesperline * pix->height * 2; + + return pix->bytesperline * pix->height; +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +static int __rvin_try_format_source(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_pad_config *pad_cfg; + struct v4l2_subdev_format format = { + .which = which, + }; + int ret; + + sd = vin_to_source(vin); + + v4l2_fill_mbus_format(&format.format, pix, vin->source.code); + + pad_cfg = v4l2_subdev_alloc_pad_config(sd); + if (pad_cfg == NULL) + return -ENOMEM; + + format.pad = vin->src_pad_idx; + + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt, + pad_cfg, &format); + if (ret < 0) + goto cleanup; + + v4l2_fill_pix_format(pix, &format.format); + + source->width = pix->width; + source->height = pix->height; + + vin_dbg(vin, "Source resolution: %ux%u\n", source->width, + source->height); + +cleanup: + v4l2_subdev_free_pad_config(pad_cfg); + return 0; +} + +static int __rvin_try_format(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + const struct rvin_video_format *info; + u32 rwidth, rheight, walign; + + /* Requested */ + rwidth = pix->width; + rheight = pix->height; + + /* + * Retrieve format information and select the current format if the + * requested format isn't supported. + */ + info = rvin_format_from_pixel(pix->pixelformat); + if (!info) { + vin_dbg(vin, "Format %x not found, keeping %x\n", + pix->pixelformat, vin->format.pixelformat); + *pix = vin->format; + pix->width = rwidth; + pix->height = rheight; + } + + /* Always recalculate */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* Limit to source capabilities */ + __rvin_try_format_source(vin, which, pix, source); + + /* If source can't match format try if VIN can scale */ + if (source->width != rwidth || source->height != rheight) + rvin_scale_try(vin, pix, rwidth, rheight); + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, + &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + default: + pix->field = V4L2_FIELD_NONE; + break; + } + + pix->bytesperline = max_t(u32, pix->bytesperline, + rvin_format_bytesperline(pix)); + pix->sizeimage = max_t(u32, pix->sizeimage, + rvin_format_sizeimage(pix)); + + vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", + rwidth, rheight, pix->width, pix->height, + pix->bytesperline, pix->sizeimage); + + return 0; +} + +static int rvin_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rvin_dev *vin = video_drvdata(file); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(vin->dev)); + return 0; +} + +static int rvin_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + + return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, + &source); +} + +static int rvin_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + int ret; + + if (vb2_is_busy(&vin->queue)) + return -EBUSY; + + ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, + &source); + if (ret) + return ret; + + vin->source.width = source.width; + vin->source.height = source.height; + + vin->format = f->fmt.pix; + + return 0; +} + +static int rvin_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + + f->fmt.pix = vin->format; + + return 0; +} + +static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(rvin_formats)) + return -EINVAL; + + f->pixelformat = rvin_formats[f->index].fourcc; + + return 0; +} + +static int rvin_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->source.width; + s->r.height = vin->source.height; + break; + case V4L2_SEL_TGT_CROP: + s->r = vin->crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->format.width; + s->r.height = vin->format.height; + break; + case V4L2_SEL_TGT_COMPOSE: + s->r = vin->compose; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rvin_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + const struct rvin_video_format *fmt; + struct v4l2_rect r = s->r; + struct v4l2_rect max_rect; + struct v4l2_rect min_rect = { + .width = 6, + .height = 2, + }; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_rect_set_min_size(&r, &min_rect); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Can't crop outside of source input */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->source.width; + max_rect.height = vin->source.height; + v4l2_rect_map_inside(&r, &max_rect); + + v4l_bound_align_image(&r.width, 2, vin->source.width, 1, + &r.height, 4, vin->source.height, 2, 0); + + r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); + r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); + + vin->crop = s->r = r; + + vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", + r.width, r.height, r.left, r.top, + vin->source.width, vin->source.height); + break; + case V4L2_SEL_TGT_COMPOSE: + /* Make sure compose rect fits inside output format */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->format.width; + max_rect.height = vin->format.height; + v4l2_rect_map_inside(&r, &max_rect); + + /* + * Composing is done by adding a offset to the buffer address, + * the HW wants this address to be aligned to HW_BUFFER_MASK. + * Make sure the top and left values meets this requirement. + */ + while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK) + r.top--; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + while ((r.left * fmt->bpp) & HW_BUFFER_MASK) + r.left--; + + vin->compose = s->r = r; + + vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", + r.width, r.height, r.left, r.top, + vin->format.width, vin->format.height); + break; + default: + return -EINVAL; + } + + /* HW supports modifying configuration while running */ + rvin_crop_scale_comp(vin); + + return 0; +} + +static int rvin_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(sd, video, cropcap, crop); +} + +static int rvin_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int ret; + + if (i->index != 0) + return -EINVAL; + + ret = v4l2_subdev_call(sd, video, g_input_status, &i->status); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = vin->vdev.tvnorms; + + if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) + i->capabilities = V4L2_IN_CAP_DV_TIMINGS; + + strlcpy(i->name, "Camera", sizeof(i->name)); + + return 0; +} + +static int rvin_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int rvin_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, querystd, a); +} + +static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret = v4l2_subdev_call(sd, video, s_std, a); + + if (ret < 0) + return ret; + + /* Changing the standard will change the width/height */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + vin->format.width = mf->width; + vin->format.height = mf->height; + + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + return 0; +} + +static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, g_std, a); +} + +static int rvin_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int rvin_enum_dv_timings(struct file *file, void *priv_fh, + struct v4l2_enum_dv_timings *timings) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int pad, ret; + + pad = timings->pad; + timings->pad = vin->src_pad_idx; + + ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); + + timings->pad = pad; + + return ret; +} + +static int rvin_s_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int err; + + err = v4l2_subdev_call(sd, + video, s_dv_timings, timings); + if (!err) { + vin->source.width = timings->bt.width; + vin->source.height = timings->bt.height; + vin->format.width = timings->bt.width; + vin->format.height = timings->bt.height; + } + return err; +} + +static int rvin_g_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, + video, g_dv_timings, timings); +} + +static int rvin_query_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, + video, query_dv_timings, timings); +} + +static int rvin_dv_timings_cap(struct file *file, void *priv_fh, + struct v4l2_dv_timings_cap *cap) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int pad, ret; + + pad = cap->pad; + cap->pad = vin->src_pad_idx; + + ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); + + cap->pad = pad; + + return ret; +} + +static const struct v4l2_ioctl_ops rvin_ioctl_ops = { + .vidioc_querycap = rvin_querycap, + .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, + + .vidioc_g_selection = rvin_g_selection, + .vidioc_s_selection = rvin_s_selection, + + .vidioc_cropcap = rvin_cropcap, + + .vidioc_enum_input = rvin_enum_input, + .vidioc_g_input = rvin_g_input, + .vidioc_s_input = rvin_s_input, + + .vidioc_dv_timings_cap = rvin_dv_timings_cap, + .vidioc_enum_dv_timings = rvin_enum_dv_timings, + .vidioc_g_dv_timings = rvin_g_dv_timings, + .vidioc_s_dv_timings = rvin_s_dv_timings, + .vidioc_query_dv_timings = rvin_query_dv_timings, + + .vidioc_querystd = rvin_querystd, + .vidioc_g_std = rvin_g_std, + .vidioc_s_std = rvin_s_std, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = rvin_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * File Operations + */ + +static int rvin_power_on(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + pm_runtime_get_sync(vin->v4l2_dev.dev); + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + return 0; +} + +static int rvin_power_off(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + ret = v4l2_subdev_call(sd, core, s_power, 0); + + pm_runtime_put(vin->v4l2_dev.dev); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int rvin_initialize_device(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = vin->format.width, + .height = vin->format.height, + .field = vin->format.field, + .colorspace = vin->format.colorspace, + .pixelformat = vin->format.pixelformat, + }, + }; + + ret = rvin_power_on(vin); + if (ret < 0) + return ret; + + pm_runtime_enable(&vin->vdev.dev); + ret = pm_runtime_resume(&vin->vdev.dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + + /* + * Try to configure with default parameters. Notice: this is the + * very first open, so, we cannot race against other calls, + * apart from someone else calling open() simultaneously, but + * .host_lock is protecting us against it. + */ + ret = rvin_s_fmt_vid_cap(file, NULL, &f); + if (ret < 0) + goto esfmt; + + v4l2_ctrl_handler_setup(&vin->ctrl_handler); + + return 0; +esfmt: + pm_runtime_disable(&vin->vdev.dev); +eresume: + rvin_power_off(vin); + + return ret; +} + +static int rvin_open(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + mutex_lock(&vin->lock); + + file->private_data = vin; + + ret = v4l2_fh_open(file); + if (ret) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (rvin_initialize_device(file)) { + v4l2_fh_release(file); + ret = -ENODEV; + } + +unlock: + mutex_unlock(&vin->lock); + return ret; +} + +static int rvin_release(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + bool fh_singular; + int ret; + + mutex_lock(&vin->lock); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + + /* + * If this was the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) { + pm_runtime_suspend(&vin->vdev.dev); + pm_runtime_disable(&vin->vdev.dev); + rvin_power_off(vin); + } + + mutex_unlock(&vin->lock); + + return ret; +} + +static const struct v4l2_file_operations rvin_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = rvin_open, + .release = rvin_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +void rvin_v4l2_remove(struct rvin_dev *vin) +{ + v4l2_info(&vin->v4l2_dev, "Removing %s\n", + video_device_node_name(&vin->vdev)); + + /* Checks internaly if handlers have been init or not */ + v4l2_ctrl_handler_free(&vin->ctrl_handler); + + /* Checks internaly if vdev have been init or not */ + video_unregister_device(&vin->vdev); +} + +static void rvin_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct rvin_dev *vin = + container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + v4l2_event_queue(&vin->vdev, arg); + break; + default: + break; + } +} + +int rvin_v4l2_probe(struct rvin_dev *vin) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct video_device *vdev = &vin->vdev; + struct v4l2_subdev *sd = vin_to_source(vin); +#if defined(CONFIG_MEDIA_CONTROLLER) + int pad_idx; +#endif + int ret; + + v4l2_set_subdev_hostdata(sd, vin); + + vin->v4l2_dev.notify = rvin_notify; + + ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (vin->vdev.tvnorms == 0) { + /* Disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); + } + + /* Add the controls */ + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); + if (ret < 0) + return ret; + + ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); + if (ret < 0) + return ret; + + /* video node */ + vdev->fops = &rvin_fops; + vdev->v4l2_dev = &vin->v4l2_dev; + vdev->queue = &vin->queue; + strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &rvin_ioctl_ops; + vdev->lock = &vin->lock; + vdev->ctrl_handler = &vin->ctrl_handler; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + + vin->src_pad_idx = 0; +#if defined(CONFIG_MEDIA_CONTROLLER) + for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) + if (sd->entity.pads[pad_idx].flags + == MEDIA_PAD_FL_SOURCE) + break; + if (pad_idx >= sd->entity.num_pads) + return -EINVAL; + + vin->src_pad_idx = pad_idx; +#endif + fmt.pad = vin->src_pad_idx; + + /* Try to improve our guess of a reasonable window format */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + /* Set default format */ + vin->format.width = mf->width; + vin->format.height = mf->height; + vin->format.colorspace = mf->colorspace; + vin->format.field = mf->field; + vin->format.pixelformat = RVIN_DEFAULT_FORMAT; + + + /* Set initial crop and compose */ + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + vin_err(vin, "Failed to register video device\n"); + return ret; + } + + video_set_drvdata(&vin->vdev, vin); + + v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", + video_device_node_name(&vin->vdev)); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h new file mode 100644 index 000000000000..31ad39a39937 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -0,0 +1,163 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __RCAR_VIN__ +#define __RCAR_VIN__ + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> + +/* Number of HW buffers */ +#define HW_BUFFER_NUM 3 + +/* Address alignment mask for HW buffers */ +#define HW_BUFFER_MASK 0x7f + +enum chip_id { + RCAR_GEN2, + RCAR_H1, + RCAR_M1, +}; + +/** + * STOPPED - No operation in progress + * RUNNING - Operation in progress have buffers + * STALLED - No operation in progress have no buffers + * STOPPING - Stopping operation + */ +enum rvin_dma_state { + STOPPED = 0, + RUNNING, + STALLED, + STOPPING, +}; + +/** + * struct rvin_source_fmt - Source information + * @code: Media bus format from source + * @width: Width from source + * @height: Height from source + */ +struct rvin_source_fmt { + u32 code; + u32 width; + u32 height; +}; + +/** + * struct rvin_video_format - Data format stored in memory + * @fourcc: Pixelformat + * @bpp: Bytes per pixel + */ +struct rvin_video_format { + u32 fourcc; + u8 bpp; +}; + +struct rvin_graph_entity { + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +/** + * struct rvin_dev - Renesas VIN device structure + * @dev: (OF) device + * @base: device I/O register space remapped to virtual memory + * @chip: type of VIN chip + * @mbus_cfg media bus configuration + * + * @vdev: V4L2 video device associated with VIN + * @v4l2_dev: V4L2 device + * @src_pad_idx: source pad index for media controller drivers + * @ctrl_handler: V4L2 control handler + * @notifier: V4L2 asynchronous subdevs notifier + * @entity: entity in the DT for subdevice + * + * @lock: protects @queue + * @queue: vb2 buffers queue + * + * @qlock: protects @queue_buf, @buf_list, @continuous, @sequence + * @state + * @queue_buf: Keeps track of buffers given to HW slot + * @buf_list: list of queued buffers + * @continuous: tracks if active operation is continuous or single mode + * @sequence: V4L2 buffers sequence number + * @state: keeps track of operation state + * + * @source: active format from the video source + * @format: active V4L2 pixel format + * + * @crop: active cropping + * @compose: active composing + */ +struct rvin_dev { + struct device *dev; + void __iomem *base; + enum chip_id chip; + struct v4l2_mbus_config mbus_cfg; + + struct video_device vdev; + struct v4l2_device v4l2_dev; + int src_pad_idx; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + struct rvin_graph_entity entity; + + struct mutex lock; + struct vb2_queue queue; + + spinlock_t qlock; + struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; + struct list_head buf_list; + bool continuous; + unsigned int sequence; + enum rvin_dma_state state; + + struct rvin_source_fmt source; + struct v4l2_pix_format format; + + struct v4l2_rect crop; + struct v4l2_rect compose; +}; + +#define vin_to_source(vin) vin->entity.subdev + +/* Debug */ +#define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) +#define vin_info(d, fmt, arg...) dev_info(d->dev, fmt, ##arg) +#define vin_warn(d, fmt, arg...) dev_warn(d->dev, fmt, ##arg) +#define vin_err(d, fmt, arg...) dev_err(d->dev, fmt, ##arg) + +int rvin_dma_probe(struct rvin_dev *vin, int irq); +void rvin_dma_remove(struct rvin_dev *vin); + +int rvin_v4l2_probe(struct rvin_dev *vin); +void rvin_v4l2_remove(struct rvin_dev *vin); + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat); + +/* Cropping, composing and scaling */ +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height); +void rvin_crop_scale_comp(struct rvin_dev *vin); + +#endif diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 552789a69c86..16782ceb29c3 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -203,7 +203,6 @@ * @irq: JPEG IP irq * @clk: JPEG IP clock * @dev: JPEG IP struct device - * @alloc_ctx: videobuf2 memory allocator's context * @ref_count: reference counter */ struct jpu { @@ -220,7 +219,6 @@ struct jpu { unsigned int irq; struct clk *clk; struct device *dev; - void *alloc_ctx; int ref_count; }; @@ -1016,7 +1014,7 @@ error_free: */ static int jpu_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct jpu_ctx *ctx = vb2_get_drv_priv(vq); struct jpu_q_data *q_data; @@ -1033,17 +1031,14 @@ static int jpu_queue_setup(struct vb2_queue *vq, if (sizes[i] < q_size) return -EINVAL; - alloc_ctxs[i] = ctx->jpu->alloc_ctx; } return 0; } *nplanes = q_data->format.num_planes; - for (i = 0; i < *nplanes; i++) { + for (i = 0; i < *nplanes; i++) sizes[i] = q_data->format.plane_fmt[i].sizeimage; - alloc_ctxs[i] = ctx->jpu->alloc_ctx; - } return 0; } @@ -1214,6 +1209,7 @@ static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->jpu->mutex; + src_vq->dev = ctx->jpu->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -1228,6 +1224,7 @@ static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->jpu->mutex; + dst_vq->dev = ctx->jpu->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -1676,13 +1673,6 @@ static int jpu_probe(struct platform_device *pdev) goto device_register_rollback; } - jpu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(jpu->alloc_ctx)) { - v4l2_err(&jpu->v4l2_dev, "Failed to init memory allocator\n"); - ret = PTR_ERR(jpu->alloc_ctx); - goto m2m_init_rollback; - } - /* fill in qantization and Huffman tables for encoder */ for (i = 0; i < JPU_MAX_QUALITY; i++) jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]); @@ -1699,7 +1689,7 @@ static int jpu_probe(struct platform_device *pdev) ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); - goto vb2_allocator_rollback; + goto m2m_init_rollback; } video_set_drvdata(&jpu->vfd_encoder, jpu); @@ -1732,9 +1722,6 @@ static int jpu_probe(struct platform_device *pdev) enc_vdev_register_rollback: video_unregister_device(&jpu->vfd_encoder); -vb2_allocator_rollback: - vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); - m2m_init_rollback: v4l2_m2m_release(jpu->m2m_dev); @@ -1750,7 +1737,6 @@ static int jpu_remove(struct platform_device *pdev) video_unregister_device(&jpu->vfd_decoder); video_unregister_device(&jpu->vfd_encoder); - vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); v4l2_m2m_release(jpu->m2m_dev); v4l2_device_unregister(&jpu->v4l2_dev); diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index bd060ef5d1e1..0413a861a59a 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -437,10 +437,9 @@ static void stop_streaming(struct vb2_queue *vq) static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct camif_vp *vp = vb2_get_drv_priv(vq); - struct camif_dev *camif = vp->camif; struct camif_frame *frame = &vp->out_frame; const struct camif_fmt *fmt = vp->out_fmt; unsigned int size; @@ -449,7 +448,6 @@ static int queue_setup(struct vb2_queue *vq, return -EINVAL; size = (frame->f_width * frame->f_height * fmt->depth) / 8; - allocators[0] = camif->alloc_ctx; if (*num_planes) return sizes[0] < size ? -EINVAL : 0; @@ -1138,6 +1136,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->drv_priv = vp; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &vp->camif->lock; + q->dev = camif->v4l2_dev.dev; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index af237af204e2..ec4001970313 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -474,16 +474,9 @@ static int s3c_camif_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; - /* Initialize contiguous memory allocator */ - camif->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(camif->alloc_ctx)) { - ret = PTR_ERR(camif->alloc_ctx); - goto err_alloc; - } - ret = camif_media_dev_init(camif); if (ret < 0) - goto err_mdev; + goto err_alloc; ret = camif_register_sensor(camif); if (ret < 0) @@ -517,8 +510,6 @@ err_sens: media_device_unregister(&camif->media_dev); media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); -err_mdev: - vb2_dma_contig_cleanup_ctx(camif->alloc_ctx); err_alloc: pm_runtime_put(dev); pm_runtime_disable(dev); diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h index 57cbc3d9725d..1f5c8c94ce89 100644 --- a/drivers/media/platform/s3c-camif/camif-core.h +++ b/drivers/media/platform/s3c-camif/camif-core.h @@ -254,7 +254,6 @@ struct camif_vp { * @ctrl_handler: v4l2 control handler (owned by @subdev) * @test_pattern: test pattern controls * @vp: video path (DMA) description (codec/preview) - * @alloc_ctx: memory buffer allocator context * @variant: variant information for this device * @dev: pointer to the CAMIF device struct * @pdata: a copy of the driver's platform data @@ -291,7 +290,6 @@ struct camif_dev { u8 colorfx_cr; struct camif_vp vp[CAMIF_VP_NUM]; - struct vb2_alloc_ctx *alloc_ctx; const struct s3c_camif_variant *variant; struct device *dev; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 612d1ea514f1..391dd7a7b362 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -103,7 +103,7 @@ static struct g2d_frame *get_frame(struct g2d_ctx *ctx, static int g2d_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct g2d_ctx *ctx = vb2_get_drv_priv(vq); struct g2d_frame *f = get_frame(ctx, vq->type); @@ -113,7 +113,6 @@ static int g2d_queue_setup(struct vb2_queue *vq, sizes[0] = f->size; *nplanes = 1; - alloc_ctxs[0] = ctx->dev->alloc_ctx; if (*nbuffers == 0) *nbuffers = 1; @@ -159,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->dev->mutex; + src_vq->dev = ctx->dev->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -172,6 +172,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->dev->mutex; + dst_vq->dev = ctx->dev->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -681,15 +682,11 @@ static int g2d_probe(struct platform_device *pdev) goto put_clk_gate; } - dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(dev->alloc_ctx)) { - ret = PTR_ERR(dev->alloc_ctx); - goto unprep_clk_gate; - } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) - goto alloc_ctx_cleanup; + goto unprep_clk_gate; vfd = video_device_alloc(); if (!vfd) { v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); @@ -734,8 +731,6 @@ rel_vdev: video_device_release(vfd); unreg_v4l2_dev: v4l2_device_unregister(&dev->v4l2_dev); -alloc_ctx_cleanup: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); unprep_clk_gate: clk_unprepare(dev->gate); put_clk_gate: @@ -756,7 +751,7 @@ static int g2d_remove(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_unprepare(dev->gate); clk_put(dev->gate); clk_unprepare(dev->clk); diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index e31df541aa62..dd812b557e87 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -25,7 +25,6 @@ struct g2d_dev { struct mutex mutex; spinlock_t ctrl_lock; atomic_t num_inst; - struct vb2_alloc_ctx *alloc_ctx; void __iomem *regs; struct clk *clk; struct clk *gate; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index caa19b408551..785e6936c881 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2436,7 +2436,7 @@ static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { static int s5p_jpeg_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); struct s5p_jpeg_q_data *q_data = NULL; @@ -2457,7 +2457,6 @@ static int s5p_jpeg_queue_setup(struct vb2_queue *vq, *nbuffers = count; *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = ctx->jpeg->alloc_ctx; return 0; } @@ -2563,6 +2562,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->jpeg->lock; + src_vq->dev = ctx->jpeg->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -2576,6 +2576,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->jpeg->lock; + dst_vq->dev = ctx->jpeg->dev; return vb2_queue_init(dst_vq); } @@ -2843,19 +2844,14 @@ static int s5p_jpeg_probe(struct platform_device *pdev) goto device_register_rollback; } - jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(jpeg->alloc_ctx)) { - v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n"); - ret = PTR_ERR(jpeg->alloc_ctx); - goto m2m_init_rollback; - } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); /* JPEG encoder /dev/videoX node */ jpeg->vfd_encoder = video_device_alloc(); if (!jpeg->vfd_encoder) { v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n"); ret = -ENOMEM; - goto vb2_allocator_rollback; + goto m2m_init_rollback; } snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name), "%s-enc", S5P_JPEG_M2M_NAME); @@ -2871,7 +2867,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); video_device_release(jpeg->vfd_encoder); - goto vb2_allocator_rollback; + goto m2m_init_rollback; } video_set_drvdata(jpeg->vfd_encoder, jpeg); @@ -2920,9 +2916,6 @@ static int s5p_jpeg_probe(struct platform_device *pdev) enc_vdev_register_rollback: video_unregister_device(jpeg->vfd_encoder); -vb2_allocator_rollback: - vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); - m2m_init_rollback: v4l2_m2m_release(jpeg->m2m_dev); @@ -2941,7 +2934,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev) video_unregister_device(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); - vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 9b1db0934909..4492a3535df5 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -110,7 +110,6 @@ enum exynos4_jpeg_img_quality_level { * @irq: JPEG IP irq * @clocks: JPEG IP clock(s) * @dev: JPEG IP struct device - * @alloc_ctx: videobuf2 memory allocator's context * @variant: driver variant to be used * @irq_status interrupt flags set during single encode/decode operation @@ -130,7 +129,6 @@ struct s5p_jpeg { enum exynos4_jpeg_result irq_ret; struct clk *clocks[JPEG_MAX_CLOCKS]; struct device *dev; - void *alloc_ctx; struct s5p_jpeg_variant *variant; u32 irq_status; }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b16466fe35ee..e3f104fafd0a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,6 +22,7 @@ #include <media/v4l2-event.h> #include <linux/workqueue.h> #include <linux/of.h> +#include <linux/of_reserved_mem.h> #include <media/videobuf2-v4l2.h> #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -29,11 +30,11 @@ #include "s5p_mfc_dec.h" #include "s5p_mfc_enc.h" #include "s5p_mfc_intr.h" +#include "s5p_mfc_iommu.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_cmd.h" #include "s5p_mfc_pm.h" -#define S5P_MFC_NAME "s5p-mfc" #define S5P_MFC_DEC_NAME "s5p-mfc-dec" #define S5P_MFC_ENC_NAME "s5p-mfc-enc" @@ -1043,55 +1044,94 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; -static int match_child(struct device *dev, void *data) +/* DMA memory related helper functions */ +static void s5p_mfc_memdev_release(struct device *dev) { - if (!dev_name(dev)) - return 0; - return !strcmp(dev_name(dev), (char *)data); + of_reserved_mem_device_release(dev); } -static void *mfc_get_drv_data(struct platform_device *pdev); - -static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) +static struct device *s5p_mfc_alloc_memdev(struct device *dev, + const char *name, unsigned int idx) { - unsigned int mem_info[2] = { }; + struct device *child; + int ret; - dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - return -ENOMEM; - } - device_initialize(dev->mem_dev_l); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-l", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, "%s:%s", dev_name(dev), name); + child->parent = dev; + child->bus = dev->bus; + child->coherent_dma_mask = dev->coherent_dma_mask; + child->dma_mask = dev->dma_mask; + child->release = s5p_mfc_memdev_release; + + if (device_add(child) == 0) { + ret = of_reserved_mem_device_init_by_idx(child, dev->of_node, + idx); + if (ret == 0) + return child; } - dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - return -ENOMEM; + put_device(child); + return NULL; +} + +static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + struct device *dev = &mfc_dev->plat_dev->dev; + + /* + * When IOMMU is available, we cannot use the default configuration, + * because of MFC firmware requirements: address space limited to + * 256M and non-zero default start address. + * This is still simplified, not optimal configuration, but for now + * IOMMU core doesn't allow to configure device's IOMMUs channel + * separately. + */ + if (exynos_is_iommu_available(dev)) { + int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE, + S5P_MFC_IOMMU_DMA_SIZE); + if (ret == 0) + mfc_dev->mem_dev_l = mfc_dev->mem_dev_r = dev; + return ret; } - device_initialize(dev->mem_dev_r); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-r", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + + /* + * Create and initialize virtual devices for accessing + * reserved memory regions. + */ + mfc_dev->mem_dev_l = s5p_mfc_alloc_memdev(dev, "left", + MFC_BANK1_ALLOC_CTX); + if (!mfc_dev->mem_dev_l) + return -ENODEV; + mfc_dev->mem_dev_r = s5p_mfc_alloc_memdev(dev, "right", + MFC_BANK2_ALLOC_CTX); + if (!mfc_dev->mem_dev_r) { + device_unregister(mfc_dev->mem_dev_l); + return -ENODEV; } + return 0; } +static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + struct device *dev = &mfc_dev->plat_dev->dev; + + if (exynos_is_iommu_available(dev)) { + exynos_unconfigure_iommu(dev); + return; + } + + device_unregister(mfc_dev->mem_dev_l); + device_unregister(mfc_dev->mem_dev_r); +} + +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1117,14 +1157,11 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->variant = mfc_get_drv_data(pdev); - ret = s5p_mfc_init_pm(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get mfc clock source\n"); - return ret; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - + if (res == NULL) { + dev_err(&pdev->dev, "failed to get io resource\n"); + return -ENOENT; + } dev->regs_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); @@ -1132,54 +1169,36 @@ static int s5p_mfc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get irq resource\n"); - ret = -ENOENT; - goto err_res; + return -ENOENT; } dev->irq = res->start; ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, 0, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); - goto err_res; + return ret; } - if (pdev->dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret < 0) - goto err_res; - } else { - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-l", match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-r", match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + ret = s5p_mfc_configure_dma_memory(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to configure DMA memory\n"); + return ret; } - dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); - if (IS_ERR(dev->alloc_ctx[0])) { - ret = PTR_ERR(dev->alloc_ctx[0]); - goto err_res; - } - dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); - if (IS_ERR(dev->alloc_ctx[1])) { - ret = PTR_ERR(dev->alloc_ctx[1]); - goto err_mem_init_ctx_1; + ret = s5p_mfc_init_pm(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get mfc clock source\n"); + goto err_dma; } + vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32)); + vb2_dma_contig_set_max_seg_size(dev->mem_dev_r, DMA_BIT_MASK(32)); + mutex_init(&dev->mfc_mutex); ret = s5p_mfc_alloc_firmware(dev); if (ret) - goto err_alloc_fw; + goto err_res; ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) @@ -1201,14 +1220,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_dec_reg; - } - v4l2_info(&dev->v4l2_dev, - "decoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); /* encoder */ @@ -1226,14 +1237,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_enc_reg; - } - v4l2_info(&dev->v4l2_dev, - "encoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); @@ -1250,26 +1253,41 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_hw_cmds(dev); s5p_mfc_init_regs(dev); + /* Register decoder and encoder */ + ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto err_dec_reg; + } + v4l2_info(&dev->v4l2_dev, + "decoder registered as /dev/video%d\n", dev->vfd_dec->num); + + ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto err_enc_reg; + } + v4l2_info(&dev->v4l2_dev, + "encoder registered as /dev/video%d\n", dev->vfd_enc->num); + pr_debug("%s--\n", __func__); return 0; /* Deinit MFC if probe had failed */ err_enc_reg: - video_device_release(dev->vfd_enc); -err_enc_alloc: video_unregister_device(dev->vfd_dec); err_dec_reg: + video_device_release(dev->vfd_enc); +err_enc_alloc: video_device_release(dev->vfd_dec); err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev_reg: s5p_mfc_release_firmware(dev); -err_alloc_fw: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); -err_mem_init_ctx_1: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); err_res: s5p_mfc_final_pm(dev); +err_dma: + s5p_mfc_unconfigure_dma_memory(dev); pr_debug("%s-- with error\n", __func__); return ret; @@ -1289,14 +1307,13 @@ static int s5p_mfc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); + video_device_release(dev->vfd_enc); + video_device_release(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); s5p_mfc_release_firmware(dev); - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); - if (pdev->dev.of_node) { - put_device(dev->mem_dev_l); - put_device(dev->mem_dev_r); - } + s5p_mfc_unconfigure_dma_memory(dev); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r); s5p_mfc_final_pm(dev); return 0; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 9eb2481ec292..373e346fce3e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -25,6 +25,8 @@ #include "regs-mfc.h" #include "regs-mfc-v8.h" +#define S5P_MFC_NAME "s5p-mfc" + /* Definitions related to MFC memory */ /* Offset base used to differentiate between CAPTURE and OUTPUT @@ -285,7 +287,6 @@ struct s5p_mfc_priv_buf { * @watchdog_cnt: counter for the watchdog * @watchdog_workqueue: workqueue for the watchdog * @watchdog_work: worker for the watchdog - * @alloc_ctx: videobuf2 allocator contexts for two memory banks * @enter_suspend: flag set when entering suspend * @ctx_buf: common context memory (MFCv6) * @warn_start: hardware error code from which warnings start @@ -328,7 +329,6 @@ struct s5p_mfc_dev { struct timer_list watchdog_timer; struct workqueue_struct *watchdog_workqueue; struct work_struct watchdog_work; - void *alloc_ctx[2]; unsigned long enter_suspend; struct s5p_mfc_priv_buf ctx_buf; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index f2d6376ce618..47c997d9e8cb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -265,9 +265,10 @@ static int vidioc_querycap(struct file *file, void *priv, { struct s5p_mfc_dev *dev = video_drvdata(file); - strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; + strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, dev->vfd_dec->name, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&dev->plat_dev->dev)); /* * This is only a mem-to-mem video device. The capture and output * device capability flags are left only for backward compatibility @@ -423,7 +424,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_mp = &f->fmt.pix_mp; if (ret) return ret; - if (ctx->vq_src.streaming || ctx->vq_dst.streaming) { + if (vb2_is_streaming(&ctx->vq_src) || vb2_is_streaming(&ctx->vq_dst)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); ret = -EBUSY; goto out; @@ -474,7 +475,6 @@ static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, ret = vb2_reqbufs(&ctx->vq_src, reqbufs); if (ret) goto out; - s5p_mfc_close_mfc_inst(dev, ctx); ctx->src_bufs_cnt = 0; ctx->output_state = QUEUE_FREE; } else if (ctx->output_state == QUEUE_FREE) { @@ -565,7 +565,7 @@ out: return ret; } -/* Reqeust buffers */ +/* Request buffers */ static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { @@ -573,7 +573,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); if (reqbufs->memory != V4L2_MEMORY_MMAP) { - mfc_err("Only V4L2_MEMORY_MAP is supported\n"); + mfc_debug(2, "Only V4L2_MEMORY_MMAP is supported\n"); return -EINVAL; } @@ -821,7 +821,7 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, if (cmd->flags != 0) return -EINVAL; - if (!ctx->vq_src.streaming) + if (!vb2_is_streaming(&ctx->vq_src)) return -EINVAL; spin_lock_irqsave(&dev->irqlock, flags); @@ -890,7 +890,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count, unsigned int *plane_count, unsigned int psize[], - void *allocators[]) + struct device *alloc_devs[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -931,16 +931,14 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[1] = ctx->chroma_size; if (IS_MFCV6_PLUS(dev)) - allocators[0] = - ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_l; else - allocators[0] = - ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX]; - allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_r; + alloc_devs[1] = ctx->dev->mem_dev_l; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && ctx->state == MFCINST_INIT) { psize[0] = ctx->dec_src_buf_size; - allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_l; } else { mfc_err("This video node is dedicated to decoding. Decoding not initialized\n"); return -EINVAL; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 034b5c1d35a1..fcc2e054c61f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -943,9 +943,10 @@ static int vidioc_querycap(struct file *file, void *priv, { struct s5p_mfc_dev *dev = video_drvdata(file); - strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; + strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, dev->vfd_enc->name, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&dev->plat_dev->dev)); /* * This is only a mem-to-mem video device. The capture and output * device capability flags are left only for backward compatibility @@ -1043,10 +1044,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - mfc_err("must be set encoding output size\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; @@ -1060,11 +1057,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - mfc_err("failed to try output format\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; @@ -1144,7 +1136,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (reqbufs->count == 0) { + mfc_debug(2, "Freeing buffers\n"); ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, + ctx); ctx->capture_state = QUEUE_FREE; return ret; } @@ -1817,7 +1812,7 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count, unsigned int *plane_count, - unsigned int psize[], void *allocators[]) + unsigned int psize[], struct device *alloc_devs[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -1837,7 +1832,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, if (*buf_count > MFC_MAX_BUFFERS) *buf_count = MFC_MAX_BUFFERS; psize[0] = ctx->enc_dst_buf_size; - allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_l; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (ctx->src_fmt) *plane_count = ctx->src_fmt->num_planes; @@ -1853,15 +1848,11 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[1] = ctx->chroma_size; if (IS_MFCV6_PLUS(dev)) { - allocators[0] = - ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; - allocators[1] = - ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_l; + alloc_devs[1] = ctx->dev->mem_dev_l; } else { - allocators[0] = - ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX]; - allocators[1] = - ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX]; + alloc_devs[0] = ctx->dev->mem_dev_r; + alloc_devs[1] = ctx->dev->mem_dev_r; } } else { mfc_err("invalid queue type: %d\n", vq->type); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h new file mode 100644 index 000000000000..6962132ae8fa --- /dev/null +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: Marek Szyprowski <m.szyprowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef S5P_MFC_IOMMU_H_ +#define S5P_MFC_IOMMU_H_ + +#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu +#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M + +#if defined(CONFIG_EXYNOS_IOMMU) && defined(CONFIG_ARM_DMA_USE_IOMMU) + +#include <asm/dma-iommu.h> + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return dev->archdata.iommu != NULL; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + struct dma_iommu_mapping *mapping = NULL; + int ret; + + /* Disable the default mapping created by device core */ + if (to_dma_iommu_mapping(dev)) + exynos_unconfigure_iommu(dev); + + mapping = arm_iommu_create_mapping(dev->bus, base, size); + if (IS_ERR(mapping)) { + pr_warn("Failed to create IOMMU mapping for device %s\n", + dev_name(dev)); + return PTR_ERR(mapping); + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + pr_warn("Failed to attached device %s to IOMMU_mapping\n", + dev_name(dev)); + arm_iommu_release_mapping(mapping); + return ret; + } + + return 0; +} + +#else + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return false; +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + return -ENOSYS; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) { } + +#endif + +#endif /* S5P_MFC_IOMMU_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 5f97a3398c11..930dc2dddae6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -54,6 +54,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME); if (IS_ERR(pm->clock)) { mfc_info("Failed to get MFC special clock control\n"); + pm->clock = NULL; } else { clk_set_rate(pm->clock, MFC_SCLK_RATE); ret = clk_prepare_enable(pm->clock); @@ -76,8 +77,10 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) err_s_clk: clk_put(pm->clock); + pm->clock = NULL; err_p_ip_clk: clk_put(pm->clock_gate); + pm->clock_gate = NULL; err_g_ip_clk: return ret; } @@ -88,9 +91,11 @@ void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) pm->clock) { clk_disable_unprepare(pm->clock); clk_put(pm->clock); + pm->clock = NULL; } clk_unprepare(pm->clock_gate); clk_put(pm->clock_gate); + pm->clock_gate = NULL; #ifdef CONFIG_PM pm_runtime_disable(pm->device); #endif @@ -98,12 +103,13 @@ void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) int s5p_mfc_clock_on(void) { - int ret; + int ret = 0; #ifdef CLK_DEBUG atomic_inc(&clk_ref); mfc_debug(3, "+ %d\n", atomic_read(&clk_ref)); #endif - ret = clk_enable(pm->clock_gate); + if (!IS_ERR_OR_NULL(pm->clock_gate)) + ret = clk_enable(pm->clock_gate); return ret; } @@ -113,7 +119,8 @@ void s5p_mfc_clock_off(void) atomic_dec(&clk_ref); mfc_debug(3, "- %d\n", atomic_read(&clk_ref)); #endif - clk_disable(pm->clock_gate); + if (!IS_ERR_OR_NULL(pm->clock_gate)) + clk_disable(pm->clock_gate); } int s5p_mfc_power_on(void) diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index 4dd62a918fcf..869f0ce86f6e 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -245,8 +245,6 @@ struct mxr_device { /** V4L2 device */ struct v4l2_device v4l2_dev; - /** context of allocator */ - void *alloc_ctx; /** event wait queue */ wait_queue_head_t event_queue; /** state flags */ diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 7ab5578a0405..ee74e2b44d69 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -80,12 +80,7 @@ int mxr_acquire_video(struct mxr_device *mdev, goto fail; } - mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); - if (IS_ERR(mdev->alloc_ctx)) { - mxr_err(mdev, "could not acquire vb2 allocator\n"); - ret = PTR_ERR(mdev->alloc_ctx); - goto fail_v4l2_dev; - } + vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32)); /* registering outputs */ mdev->output_cnt = 0; @@ -120,7 +115,7 @@ int mxr_acquire_video(struct mxr_device *mdev, mxr_err(mdev, "failed to register any output\n"); ret = -ENODEV; /* skipping fail_output because there is nothing to free */ - goto fail_vb2_allocator; + goto fail_v4l2_dev; } return 0; @@ -131,10 +126,6 @@ fail_output: kfree(mdev->output[i]); memset(mdev->output, 0, sizeof(mdev->output)); -fail_vb2_allocator: - /* freeing allocator context */ - vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); - fail_v4l2_dev: /* NOTE: automatically unregister all subdevs */ v4l2_device_unregister(v4l2_dev); @@ -151,7 +142,7 @@ void mxr_release_video(struct mxr_device *mdev) for (i = 0; i < mdev->output_cnt; ++i) kfree(mdev->output[i]); - vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(mdev->dev); v4l2_device_unregister(&mdev->v4l2_dev); } @@ -883,7 +874,7 @@ static const struct v4l2_file_operations mxr_fops = { static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) + struct device *alloc_devs[]) { struct mxr_layer *layer = vb2_get_drv_priv(vq); const struct mxr_format *fmt = layer->fmt; @@ -901,7 +892,6 @@ static int queue_setup(struct vb2_queue *vq, *nplanes = fmt->num_subframes; for (i = 0; i < fmt->num_subframes; ++i) { - alloc_ctxs[i] = layer->mdev->alloc_ctx; sizes[i] = planes[i].sizeimage; mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); } @@ -1110,6 +1100,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, .min_buffers_needed = 1, .mem_ops = &vb2_dma_contig_memops, .lock = &layer->mutex, + .dev = mdev->dev, }; return layer; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 82b5d69b87fa..15a562af13c7 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -118,7 +118,6 @@ struct sh_veu_dev { struct sh_veu_file *output; struct mutex fop_lock; void __iomem *base; - struct vb2_alloc_ctx *alloc_ctx; spinlock_t lock; bool is_2h; unsigned int xaction; @@ -866,7 +865,7 @@ static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { static int sh_veu_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct sh_veu_dev *veu = vb2_get_drv_priv(vq); struct sh_veu_vfmt *vfmt = sh_veu_get_vfmt(veu, vq->type); @@ -882,14 +881,11 @@ static int sh_veu_queue_setup(struct vb2_queue *vq, *nbuffers = count; } - if (*nplanes) { - alloc_ctxs[0] = veu->alloc_ctx; + if (*nplanes) return sizes[0] < size ? -EINVAL : 0; - } *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = veu->alloc_ctx; dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); @@ -948,6 +944,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->lock = &veu->fop_lock; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = veu->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret < 0) @@ -962,6 +959,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->lock = &veu->fop_lock; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = veu->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -1148,12 +1146,6 @@ static int sh_veu_probe(struct platform_device *pdev) vdev = &veu->vdev; - veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(veu->alloc_ctx)) { - ret = PTR_ERR(veu->alloc_ctx); - goto einitctx; - } - *vdev = sh_veu_videodev; vdev->v4l2_dev = &veu->v4l2_dev; spin_lock_init(&veu->lock); @@ -1187,8 +1179,6 @@ evidreg: pm_runtime_disable(&pdev->dev); v4l2_m2m_release(veu->m2m_dev); em2minit: - vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); -einitctx: v4l2_device_unregister(&veu->v4l2_dev); return ret; } @@ -1202,7 +1192,6 @@ static int sh_veu_remove(struct platform_device *pdev) video_unregister_device(&veu->vdev); pm_runtime_disable(&pdev->dev); v4l2_m2m_release(veu->m2m_dev); - vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); v4l2_device_unregister(&veu->v4l2_dev); return 0; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 115740498274..e1f39b4cf1cd 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -86,7 +86,6 @@ struct sh_vou_device { v4l2_std_id std; int pix_idx; struct vb2_queue queue; - struct vb2_alloc_ctx *alloc_ctx; struct sh_vou_buffer *active; enum sh_vou_status status; unsigned sequence; @@ -245,7 +244,7 @@ static void sh_vou_stream_config(struct sh_vou_device *vou_dev) /* Locking: caller holds fop_lock mutex */ static int sh_vou_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); struct v4l2_pix_format *pix = &vou_dev->pix; @@ -253,7 +252,6 @@ static int sh_vou_queue_setup(struct vb2_queue *vq, dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - alloc_ctxs[0] = vou_dev->alloc_ctx; if (*nplanes) return sizes[0] < pix->height * bytes_per_line ? -EINVAL : 0; *nplanes = 1; @@ -1304,16 +1302,11 @@ static int sh_vou_probe(struct platform_device *pdev) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 2; q->lock = &vou_dev->fop_lock; + q->dev = &pdev->dev; ret = vb2_queue_init(q); if (ret) - goto einitctx; + goto ei2cgadap; - vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(vou_dev->alloc_ctx)) { - dev_err(&pdev->dev, "Can't allocate buffer context"); - ret = PTR_ERR(vou_dev->alloc_ctx); - goto einitctx; - } vdev->queue = q; INIT_LIST_HEAD(&vou_dev->buf_list); @@ -1348,8 +1341,6 @@ ei2cnd: ereset: i2c_put_adapter(i2c_adap); ei2cgadap: - vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); -einitctx: pm_runtime_disable(&pdev->dev); v4l2_device_unregister(&vou_dev->v4l2_dev); return ret; @@ -1367,7 +1358,6 @@ static int sh_vou_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); - vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); v4l2_device_unregister(&vou_dev->v4l2_dev); return 0; } diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 83029a4854ae..39f66414f621 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -25,8 +25,8 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface -config VIDEO_RCAR_VIN - tristate "R-Car Video Input (VIN) support" +config VIDEO_RCAR_VIN_OLD + tristate "R-Car Video Input (VIN) support (DEPRECATED)" depends on VIDEO_DEV && SOC_CAMERA depends on ARCH_RENESAS || COMPILE_TEST depends on HAS_DMA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7ee71ae231c7..7703cb7ce456 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o -obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o +obj-$(CONFIG_VIDEO_RCAR_VIN_OLD) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index ab2d9b9b1f5d..30211f6b4483 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -72,8 +72,6 @@ struct atmel_isi { int sequence; - struct vb2_alloc_ctx *alloc_ctx; - /* Allocate descriptors for dma buffer use */ struct fbd *p_fb_descriptors; dma_addr_t fb_descriptors_phys; @@ -305,7 +303,7 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) ------------------------------------------------------------------*/ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -322,7 +320,6 @@ static int queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - alloc_ctxs[0] = isi->alloc_ctx; isi->sequence = 0; isi->active = NULL; @@ -567,6 +564,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ici->host_lock; + q->dev = ici->v4l2_dev.dev; return vb2_queue_init(q); } @@ -963,7 +961,6 @@ static int atmel_isi_remove(struct platform_device *pdev) struct atmel_isi, soc_host); soc_camera_host_unregister(soc_host); - vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); dma_free_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, isi->p_fb_descriptors, @@ -1067,12 +1064,6 @@ static int atmel_isi_probe(struct platform_device *pdev) list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); } - isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(isi->alloc_ctx)) { - ret = PTR_ERR(isi->alloc_ctx); - goto err_alloc_ctx; - } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); isi->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(isi->regs)) { @@ -1119,8 +1110,6 @@ err_register_soc_camera_host: pm_runtime_disable(&pdev->dev); err_req_irq: err_ioremap: - vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); -err_alloc_ctx: dma_free_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, isi->p_fb_descriptors, diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 3f9c1b8456c3..9c137522c660 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -484,7 +484,6 @@ struct rcar_vin_priv { struct list_head capture; #define MAX_BUFFER_NUM 3 struct vb2_v4l2_buffer *queue_buf[MAX_BUFFER_NUM]; - struct vb2_alloc_ctx *alloc_ctx; enum v4l2_field field; unsigned int pdata_flags; unsigned int vb_count; @@ -534,14 +533,12 @@ struct rcar_vin_cam { static int rcar_vin_videobuf_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct rcar_vin_priv *priv = ici->priv; - alloc_ctxs[0] = priv->alloc_ctx; - if (!vq->num_buffers) priv->sequence = 0; @@ -1816,6 +1813,7 @@ static int rcar_vin_init_videobuf2(struct vb2_queue *vq, vq->buf_struct_size = sizeof(struct rcar_vin_buffer); vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->lock = &ici->host_lock; + vq->dev = ici->v4l2_dev.dev; return vb2_queue_init(vq); } @@ -1912,10 +1910,6 @@ static int rcar_vin_probe(struct platform_device *pdev) if (ret) return ret; - priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(priv->alloc_ctx)) - return PTR_ERR(priv->alloc_ctx); - priv->ici.priv = priv; priv->ici.v4l2_dev.dev = &pdev->dev; priv->ici.drv_name = dev_name(&pdev->dev); @@ -1946,7 +1940,6 @@ static int rcar_vin_probe(struct platform_device *pdev) cleanup: pm_runtime_disable(&pdev->dev); - vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); return ret; } @@ -1954,12 +1947,9 @@ cleanup: static int rcar_vin_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct rcar_vin_priv *priv = container_of(soc_host, - struct rcar_vin_priv, ici); soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); - vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); return 0; } diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index b9f369c0fb94..02b519dde42a 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -113,7 +113,6 @@ struct sh_mobile_ceu_dev { spinlock_t lock; /* Protects video buffer lists */ struct list_head capture; struct vb2_v4l2_buffer *active; - struct vb2_alloc_ctx *alloc_ctx; struct sh_mobile_ceu_info *pdata; struct completion complete; @@ -211,14 +210,12 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) */ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - alloc_ctxs[0] = pcdev->alloc_ctx; - if (!vq->num_buffers) pcdev->sequence = 0; @@ -1670,6 +1667,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ici->host_lock; + q->dev = ici->v4l2_dev.dev; return vb2_queue_init(q); } @@ -1822,12 +1820,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.ops = &sh_mobile_ceu_host_ops; pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - err = PTR_ERR(pcdev->alloc_ctx); - goto exit_free_clk; - } - if (pcdev->pdata && pcdev->pdata->asd_sizes) { struct v4l2_async_subdev **asd; char name[] = "sh-mobile-csi2"; @@ -1872,7 +1864,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) if (!csi2_pdev) { err = -ENOMEM; - goto exit_free_ctx; + goto exit_free_clk; } pcdev->csi2_pdev = csi2_pdev; @@ -1955,8 +1947,6 @@ exit_pdev_put: pcdev->csi2_pdev->resource = NULL; platform_device_put(pcdev->csi2_pdev); } -exit_free_ctx: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: pm_runtime_disable(&pdev->dev); exit_release_mem: @@ -1976,7 +1966,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2_pdev && csi2_pdev->dev.driver) { struct module *csi2_drv = csi2_pdev->dev.driver->owner; platform_device_del(csi2_pdev); diff --git a/drivers/media/platform/sti/bdisp/bdisp-filter.h b/drivers/media/platform/sti/bdisp/bdisp-filter.h index fc8c54f725ad..53e52fb4127f 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-filter.h +++ b/drivers/media/platform/sti/bdisp/bdisp-filter.h @@ -19,178 +19,6 @@ struct bdisp_filter_h_spec { const u16 max; const u8 coef[BDISP_HF_NB]; }; - -static const struct bdisp_filter_h_spec bdisp_h_spec[] = { - { - .min = 0, - .max = 921, - .coef = { - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00, - 0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00, - 0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00, - 0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00, - 0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00, - 0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00, - 0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00 - } - }, - { - .min = 921, - .max = 1024, - .coef = { - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, - 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, - 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, - 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, - 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff - } - }, - { - .min = 1024, - .max = 1126, - .coef = { - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, - 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, - 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, - 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, - 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff - } - }, - { - .min = 1126, - .max = 1228, - .coef = { - 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, - 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, - 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, - 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, - 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, - 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff - } - }, - { - .min = 1228, - .max = 1331, - .coef = { - 0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04, - 0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02, - 0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00, - 0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff, - 0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd, - 0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc, - 0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb, - 0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc - } - }, - { - .min = 1331, - .max = 1433, - .coef = { - 0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06, - 0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05, - 0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04, - 0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02, - 0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00, - 0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff, - 0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe, - 0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd - } - }, - { - .min = 1433, - .max = 1536, - .coef = { - 0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06, - 0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06, - 0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06, - 0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04, - 0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03, - 0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01, - 0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00, - 0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff - } - }, - { - .min = 1536, - .max = 2048, - .coef = { - 0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd, - 0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff, - 0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00, - 0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01, - 0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02, - 0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03, - 0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04, - 0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05 - } - }, - { - .min = 2048, - .max = 3072, - .coef = { - 0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd, - 0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc, - 0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc, - 0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb, - 0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb, - 0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb, - 0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb, - 0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc - } - }, - { - .min = 3072, - .max = 4096, - .coef = { - 0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02, - 0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01, - 0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00, - 0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00, - 0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00, - 0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00, - 0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff, - 0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff - } - }, - { - .min = 4096, - .max = 5120, - .coef = { - 0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04, - 0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04, - 0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03, - 0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03, - 0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02, - 0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02, - 0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01, - 0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01 - } - }, - { - .min = 5120, - .max = 65535, - .coef = { - 0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06, - 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, - 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, - 0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04, - 0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04, - 0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04, - 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03, - 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03 - } - } -}; - /** * struct bdisp_filter_v_spec - Vertical filter specification * @@ -204,138 +32,6 @@ struct bdisp_filter_v_spec { const u8 coef[BDISP_VF_NB]; }; -static const struct bdisp_filter_v_spec bdisp_v_spec[] = { - { - .min = 0, - .max = 1024, - .coef = { - 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x06, 0x3d, 0xfd, 0x00, - 0xfe, 0x0f, 0x38, 0xfb, 0x00, - 0xfd, 0x19, 0x2f, 0xfb, 0x00, - 0xfc, 0x24, 0x24, 0xfc, 0x00, - 0xfb, 0x2f, 0x19, 0xfd, 0x00, - 0xfb, 0x38, 0x0f, 0xfe, 0x00, - 0xfd, 0x3d, 0x06, 0x00, 0x00 - } - }, - { - .min = 1024, - .max = 1331, - .coef = { - 0xfc, 0x05, 0x3e, 0x05, 0xfc, - 0xf8, 0x0e, 0x3b, 0xff, 0x00, - 0xf5, 0x18, 0x38, 0xf9, 0x02, - 0xf4, 0x21, 0x31, 0xf5, 0x05, - 0xf4, 0x2a, 0x27, 0xf4, 0x07, - 0xf6, 0x30, 0x1e, 0xf4, 0x08, - 0xf9, 0x35, 0x15, 0xf6, 0x07, - 0xff, 0x37, 0x0b, 0xf9, 0x06 - } - }, - { - .min = 1331, - .max = 1433, - .coef = { - 0xf8, 0x0a, 0x3c, 0x0a, 0xf8, - 0xf6, 0x12, 0x3b, 0x02, 0xfb, - 0xf4, 0x1b, 0x35, 0xfd, 0xff, - 0xf4, 0x23, 0x30, 0xf8, 0x01, - 0xf6, 0x29, 0x27, 0xf6, 0x04, - 0xf9, 0x2e, 0x1e, 0xf5, 0x06, - 0xfd, 0x31, 0x16, 0xf6, 0x06, - 0x02, 0x32, 0x0d, 0xf8, 0x07 - } - }, - { - .min = 1433, - .max = 1536, - .coef = { - 0xf6, 0x0e, 0x38, 0x0e, 0xf6, - 0xf5, 0x15, 0x38, 0x06, 0xf8, - 0xf5, 0x1d, 0x33, 0x00, 0xfb, - 0xf6, 0x23, 0x2d, 0xfc, 0xfe, - 0xf9, 0x28, 0x26, 0xf9, 0x00, - 0xfc, 0x2c, 0x1e, 0xf7, 0x03, - 0x00, 0x2e, 0x18, 0xf6, 0x04, - 0x05, 0x2e, 0x11, 0xf7, 0x05 - } - }, - { - .min = 1536, - .max = 2048, - .coef = { - 0xfb, 0x13, 0x24, 0x13, 0xfb, - 0xfd, 0x17, 0x23, 0x0f, 0xfa, - 0xff, 0x1a, 0x23, 0x0b, 0xf9, - 0x01, 0x1d, 0x22, 0x07, 0xf9, - 0x04, 0x20, 0x1f, 0x04, 0xf9, - 0x07, 0x22, 0x1c, 0x01, 0xfa, - 0x0b, 0x24, 0x17, 0xff, 0xfb, - 0x0f, 0x24, 0x14, 0xfd, 0xfc - } - }, - { - .min = 2048, - .max = 3072, - .coef = { - 0x05, 0x10, 0x16, 0x10, 0x05, - 0x06, 0x11, 0x16, 0x0f, 0x04, - 0x08, 0x13, 0x15, 0x0e, 0x02, - 0x09, 0x14, 0x16, 0x0c, 0x01, - 0x0b, 0x15, 0x15, 0x0b, 0x00, - 0x0d, 0x16, 0x13, 0x0a, 0x00, - 0x0f, 0x17, 0x13, 0x08, 0xff, - 0x11, 0x18, 0x12, 0x07, 0xfe - } - }, - { - .min = 3072, - .max = 4096, - .coef = { - 0x09, 0x0f, 0x10, 0x0f, 0x09, - 0x09, 0x0f, 0x12, 0x0e, 0x08, - 0x0a, 0x10, 0x11, 0x0e, 0x07, - 0x0b, 0x11, 0x11, 0x0d, 0x06, - 0x0c, 0x11, 0x12, 0x0c, 0x05, - 0x0d, 0x12, 0x11, 0x0c, 0x04, - 0x0e, 0x12, 0x11, 0x0b, 0x04, - 0x0f, 0x13, 0x11, 0x0a, 0x03 - } - }, - { - .min = 4096, - .max = 5120, - .coef = { - 0x0a, 0x0e, 0x10, 0x0e, 0x0a, - 0x0b, 0x0e, 0x0f, 0x0e, 0x0a, - 0x0b, 0x0f, 0x10, 0x0d, 0x09, - 0x0c, 0x0f, 0x10, 0x0d, 0x08, - 0x0d, 0x0f, 0x0f, 0x0d, 0x08, - 0x0d, 0x10, 0x10, 0x0c, 0x07, - 0x0e, 0x10, 0x0f, 0x0c, 0x07, - 0x0f, 0x10, 0x10, 0x0b, 0x06 - } - }, - { - .min = 5120, - .max = 65535, - .coef = { - 0x0b, 0x0e, 0x0e, 0x0e, 0x0b, - 0x0b, 0x0e, 0x0f, 0x0d, 0x0b, - 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, - 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, - 0x0d, 0x0f, 0x0e, 0x0d, 0x09, - 0x0d, 0x0f, 0x0f, 0x0c, 0x09, - 0x0e, 0x0f, 0x0e, 0x0c, 0x09, - 0x0e, 0x0f, 0x0f, 0x0c, 0x08 - } - } -}; - -#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec) -#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec) - /* RGB YUV 601 standard conversion */ static const u32 bdisp_rgb_to_yuv[] = { 0x0e1e8bee, 0x08420419, 0xfb5ed471, 0x08004080, diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c index 052c932ac942..3df66d11c795 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -47,6 +47,311 @@ struct bdisp_filter_addr { dma_addr_t paddr; /* Physical address for filter table */ }; +static const struct bdisp_filter_h_spec bdisp_h_spec[] = { + { + .min = 0, + .max = 921, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00, + 0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00, + 0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00, + 0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00, + 0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00, + 0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00, + 0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00 + } + }, + { + .min = 921, + .max = 1024, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1024, + .max = 1126, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1126, + .max = 1228, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1228, + .max = 1331, + .coef = { + 0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04, + 0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02, + 0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00, + 0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff, + 0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd, + 0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc, + 0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb, + 0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc + } + }, + { + .min = 1331, + .max = 1433, + .coef = { + 0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06, + 0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05, + 0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04, + 0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02, + 0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00, + 0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff, + 0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe, + 0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd + } + }, + { + .min = 1433, + .max = 1536, + .coef = { + 0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06, + 0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06, + 0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06, + 0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04, + 0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03, + 0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01, + 0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00, + 0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff + } + }, + { + .min = 1536, + .max = 2048, + .coef = { + 0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd, + 0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff, + 0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00, + 0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01, + 0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02, + 0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03, + 0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04, + 0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05 + } + }, + { + .min = 2048, + .max = 3072, + .coef = { + 0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd, + 0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc, + 0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc, + 0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb, + 0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb, + 0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb, + 0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb, + 0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc + } + }, + { + .min = 3072, + .max = 4096, + .coef = { + 0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02, + 0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01, + 0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00, + 0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00, + 0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00, + 0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00, + 0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff, + 0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff + } + }, + { + .min = 4096, + .max = 5120, + .coef = { + 0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04, + 0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04, + 0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03, + 0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03, + 0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02, + 0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02, + 0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01, + 0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01 + } + }, + { + .min = 5120, + .max = 65535, + .coef = { + 0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06, + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, + 0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04, + 0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04, + 0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04, + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03, + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03 + } + } +}; + +#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec) + + +static const struct bdisp_filter_v_spec bdisp_v_spec[] = { + { + .min = 0, + .max = 1024, + .coef = { + 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x06, 0x3d, 0xfd, 0x00, + 0xfe, 0x0f, 0x38, 0xfb, 0x00, + 0xfd, 0x19, 0x2f, 0xfb, 0x00, + 0xfc, 0x24, 0x24, 0xfc, 0x00, + 0xfb, 0x2f, 0x19, 0xfd, 0x00, + 0xfb, 0x38, 0x0f, 0xfe, 0x00, + 0xfd, 0x3d, 0x06, 0x00, 0x00 + } + }, + { + .min = 1024, + .max = 1331, + .coef = { + 0xfc, 0x05, 0x3e, 0x05, 0xfc, + 0xf8, 0x0e, 0x3b, 0xff, 0x00, + 0xf5, 0x18, 0x38, 0xf9, 0x02, + 0xf4, 0x21, 0x31, 0xf5, 0x05, + 0xf4, 0x2a, 0x27, 0xf4, 0x07, + 0xf6, 0x30, 0x1e, 0xf4, 0x08, + 0xf9, 0x35, 0x15, 0xf6, 0x07, + 0xff, 0x37, 0x0b, 0xf9, 0x06 + } + }, + { + .min = 1331, + .max = 1433, + .coef = { + 0xf8, 0x0a, 0x3c, 0x0a, 0xf8, + 0xf6, 0x12, 0x3b, 0x02, 0xfb, + 0xf4, 0x1b, 0x35, 0xfd, 0xff, + 0xf4, 0x23, 0x30, 0xf8, 0x01, + 0xf6, 0x29, 0x27, 0xf6, 0x04, + 0xf9, 0x2e, 0x1e, 0xf5, 0x06, + 0xfd, 0x31, 0x16, 0xf6, 0x06, + 0x02, 0x32, 0x0d, 0xf8, 0x07 + } + }, + { + .min = 1433, + .max = 1536, + .coef = { + 0xf6, 0x0e, 0x38, 0x0e, 0xf6, + 0xf5, 0x15, 0x38, 0x06, 0xf8, + 0xf5, 0x1d, 0x33, 0x00, 0xfb, + 0xf6, 0x23, 0x2d, 0xfc, 0xfe, + 0xf9, 0x28, 0x26, 0xf9, 0x00, + 0xfc, 0x2c, 0x1e, 0xf7, 0x03, + 0x00, 0x2e, 0x18, 0xf6, 0x04, + 0x05, 0x2e, 0x11, 0xf7, 0x05 + } + }, + { + .min = 1536, + .max = 2048, + .coef = { + 0xfb, 0x13, 0x24, 0x13, 0xfb, + 0xfd, 0x17, 0x23, 0x0f, 0xfa, + 0xff, 0x1a, 0x23, 0x0b, 0xf9, + 0x01, 0x1d, 0x22, 0x07, 0xf9, + 0x04, 0x20, 0x1f, 0x04, 0xf9, + 0x07, 0x22, 0x1c, 0x01, 0xfa, + 0x0b, 0x24, 0x17, 0xff, 0xfb, + 0x0f, 0x24, 0x14, 0xfd, 0xfc + } + }, + { + .min = 2048, + .max = 3072, + .coef = { + 0x05, 0x10, 0x16, 0x10, 0x05, + 0x06, 0x11, 0x16, 0x0f, 0x04, + 0x08, 0x13, 0x15, 0x0e, 0x02, + 0x09, 0x14, 0x16, 0x0c, 0x01, + 0x0b, 0x15, 0x15, 0x0b, 0x00, + 0x0d, 0x16, 0x13, 0x0a, 0x00, + 0x0f, 0x17, 0x13, 0x08, 0xff, + 0x11, 0x18, 0x12, 0x07, 0xfe + } + }, + { + .min = 3072, + .max = 4096, + .coef = { + 0x09, 0x0f, 0x10, 0x0f, 0x09, + 0x09, 0x0f, 0x12, 0x0e, 0x08, + 0x0a, 0x10, 0x11, 0x0e, 0x07, + 0x0b, 0x11, 0x11, 0x0d, 0x06, + 0x0c, 0x11, 0x12, 0x0c, 0x05, + 0x0d, 0x12, 0x11, 0x0c, 0x04, + 0x0e, 0x12, 0x11, 0x0b, 0x04, + 0x0f, 0x13, 0x11, 0x0a, 0x03 + } + }, + { + .min = 4096, + .max = 5120, + .coef = { + 0x0a, 0x0e, 0x10, 0x0e, 0x0a, + 0x0b, 0x0e, 0x0f, 0x0e, 0x0a, + 0x0b, 0x0f, 0x10, 0x0d, 0x09, + 0x0c, 0x0f, 0x10, 0x0d, 0x08, + 0x0d, 0x0f, 0x0f, 0x0d, 0x08, + 0x0d, 0x10, 0x10, 0x0c, 0x07, + 0x0e, 0x10, 0x0f, 0x0c, 0x07, + 0x0f, 0x10, 0x10, 0x0b, 0x06 + } + }, + { + .min = 5120, + .max = 65535, + .coef = { + 0x0b, 0x0e, 0x0e, 0x0e, 0x0b, + 0x0b, 0x0e, 0x0f, 0x0d, 0x0b, + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, + 0x0d, 0x0f, 0x0e, 0x0d, 0x09, + 0x0d, 0x0f, 0x0f, 0x0c, 0x09, + 0x0e, 0x0f, 0x0e, 0x0c, 0x09, + 0x0e, 0x0f, 0x0f, 0x0c, 0x08 + } + } +}; + +#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec) + static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER]; static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER]; diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index d12a419c044a..3b1ac687d0df 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -439,7 +439,7 @@ static void bdisp_ctrls_delete(struct bdisp_ctx *ctx) static int bdisp_queue_setup(struct vb2_queue *vq, unsigned int *nb_buf, unsigned int *nb_planes, - unsigned int sizes[], void *allocators[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct bdisp_ctx *ctx = vb2_get_drv_priv(vq); struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type); @@ -453,7 +453,6 @@ static int bdisp_queue_setup(struct vb2_queue *vq, dev_err(ctx->bdisp_dev->dev, "Invalid format\n"); return -EINVAL; } - allocators[0] = ctx->bdisp_dev->alloc_ctx; if (*nb_planes) return sizes[0] < frame->sizeimage ? -EINVAL : 0; @@ -553,6 +552,7 @@ static int queue_init(void *priv, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->bdisp_dev->lock; + src_vq->dev = ctx->bdisp_dev->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -567,6 +567,7 @@ static int queue_init(void *priv, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->bdisp_dev->lock; + dst_vq->dev = ctx->bdisp_dev->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -1269,8 +1270,6 @@ static int bdisp_remove(struct platform_device *pdev) bdisp_hw_free_filters(bdisp->dev); - vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); - pm_runtime_disable(&pdev->dev); bdisp_debugfs_remove(bdisp); @@ -1371,18 +1370,11 @@ static int bdisp_probe(struct platform_device *pdev) goto err_dbg; } - /* Continuous memory allocator */ - bdisp->alloc_ctx = vb2_dma_contig_init_ctx(dev); - if (IS_ERR(bdisp->alloc_ctx)) { - ret = PTR_ERR(bdisp->alloc_ctx); - goto err_pm; - } - /* Filters */ if (bdisp_hw_alloc_filters(bdisp->dev)) { dev_err(bdisp->dev, "no memory for filters\n"); ret = -ENOMEM; - goto err_vb2_dma; + goto err_pm; } /* Register */ @@ -1401,8 +1393,6 @@ static int bdisp_probe(struct platform_device *pdev) err_filter: bdisp_hw_free_filters(bdisp->dev); -err_vb2_dma: - vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); err_pm: pm_runtime_put(dev); err_dbg: diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/sti/bdisp/bdisp.h index 0cf98577222c..b3fbf9902595 100644 --- a/drivers/media/platform/sti/bdisp/bdisp.h +++ b/drivers/media/platform/sti/bdisp/bdisp.h @@ -175,7 +175,6 @@ struct bdisp_dbg { * @id: device index * @m2m: memory-to-memory V4L2 device information * @state: flags used to synchronize m2m and capture mode operation - * @alloc_ctx: videobuf2 memory allocator context * @clock: IP clock * @regs: registers * @irq_queue: interrupt handler waitqueue @@ -193,7 +192,6 @@ struct bdisp_dev { u16 id; struct bdisp_m2m_device m2m; unsigned long state; - struct vb2_alloc_ctx *alloc_ctx; struct clk *clock; void __iomem *regs; wait_queue_head_t irq_queue; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 82001e6b5553..e967fcfdc1d8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -287,7 +287,6 @@ struct cal_ctx { /* Several counters */ unsigned long jiffies; - struct vb2_alloc_ctx *alloc_ctx; struct cal_dmaqueue vidq; /* Input Number */ @@ -1226,14 +1225,13 @@ static int cal_enum_frameintervals(struct file *file, void *priv, */ static int cal_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); unsigned size = ctx->v_fmt.fmt.pix.sizeimage; if (vq->num_buffers + *nbuffers < 3) *nbuffers = 3 - vq->num_buffers; - alloc_ctxs[0] = ctx->alloc_ctx; if (*nplanes) { if (sizes[0] < size) @@ -1551,6 +1549,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; q->min_buffers_needed = 3; + q->dev = ctx->v4l2_dev.dev; ret = vb2_queue_init(q); if (ret) @@ -1578,18 +1577,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", video_device_node_name(vfd)); - ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev); - if (IS_ERR(ctx->alloc_ctx)) { - ctx_err(ctx, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(ctx->alloc_ctx); - goto vdev_unreg; - } - return 0; - -vdev_unreg: - video_unregister_device(vfd); - return ret; } static struct device_node * @@ -1914,7 +1902,6 @@ static int cal_remove(struct platform_device *pdev) video_device_node_name(&ctx->vdev)); camerarx_phy_disable(ctx); v4l2_async_notifier_unregister(&ctx->notifier); - vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_device_unregister(&ctx->v4l2_dev); video_unregister_device(&ctx->vdev); diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 1fa00c2cf3d7..55a1458ac783 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -362,7 +362,6 @@ struct vpe_dev { void __iomem *base; struct resource *res; - struct vb2_alloc_ctx *alloc_ctx; struct vpdma_data *vpdma; /* vpdma data handle */ struct sc_data *sc; /* scaler data handle */ struct csc_data *csc; /* csc data handle */ @@ -1797,7 +1796,7 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { */ static int vpe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { int i; struct vpe_ctx *ctx = vb2_get_drv_priv(vq); @@ -1807,10 +1806,8 @@ static int vpe_queue_setup(struct vb2_queue *vq, *nplanes = q_data->fmt->coplanar ? 2 : 1; - for (i = 0; i < *nplanes; i++) { + for (i = 0; i < *nplanes; i++) sizes[i] = q_data->sizeimage[i]; - alloc_ctxs[i] = ctx->dev->alloc_ctx; - } vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, sizes[VPE_LUMA]); @@ -1907,6 +1904,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &dev->dev_mutex; + src_vq->dev = dev->v4l2_dev.dev; ret = vb2_queue_init(src_vq); if (ret) @@ -1921,6 +1919,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &dev->dev_mutex; + dst_vq->dev = dev->v4l2_dev.dev; return vb2_queue_init(dst_vq); } @@ -2161,7 +2160,6 @@ static void vpe_fw_cb(struct platform_device *pdev) vpe_runtime_put(pdev); pm_runtime_disable(&pdev->dev); v4l2_m2m_release(dev->m2m_dev); - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); return; @@ -2213,18 +2211,11 @@ static int vpe_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); - dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(dev->alloc_ctx)) { - vpe_err(dev, "Failed to alloc vb2 context\n"); - ret = PTR_ERR(dev->alloc_ctx); - goto v4l2_dev_unreg; - } - dev->m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(dev->m2m_dev)) { vpe_err(dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto rel_ctx; + goto v4l2_dev_unreg; } pm_runtime_enable(&pdev->dev); @@ -2269,8 +2260,6 @@ runtime_put: rel_m2m: pm_runtime_disable(&pdev->dev); v4l2_m2m_release(dev->m2m_dev); -rel_ctx: - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_dev_unreg: v4l2_device_unregister(&dev->v4l2_dev); @@ -2286,7 +2275,6 @@ static int vpe_remove(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); video_unregister_device(&dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); - vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); vpe_set_clock_enable(dev, 0); vpe_runtime_put(pdev); diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index c4b5fab83666..6b17015048ae 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -711,7 +711,7 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { static int vim2m_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); struct vim2m_q_data *q_data; @@ -731,11 +731,6 @@ static int vim2m_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - /* - * videobuf2-vmalloc allocator is context-less so no need to set - * alloc_ctxs array. - */ - dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); return 0; diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c index 3d1604cb982f..ebd7b9c4dd83 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -51,8 +51,6 @@ static const struct vivid_format formats[] = { }, }; -static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); - static const struct v4l2_frequency_band bands_adc[] = { { .tuner = 0, @@ -215,7 +213,7 @@ static int vivid_thread_sdr_cap(void *data) static int sdr_cap_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, - unsigned sizes[], void *alloc_ctxs[]) + unsigned sizes[], struct device *alloc_devs[]) { /* 2 = max 16-bit sample returned */ sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c index cda45a582bfe..d66ef95dd2b5 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.c +++ b/drivers/media/platform/vivid/vivid-vbi-cap.c @@ -137,7 +137,7 @@ void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, static int vbi_cap_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, - unsigned sizes[], void *alloc_ctxs[]) + unsigned sizes[], struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); bool is_60hz = dev->std_cap & V4L2_STD_525_60; diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c index 3c5a469e6f49..d2989195cf03 100644 --- a/drivers/media/platform/vivid/vivid-vbi-out.c +++ b/drivers/media/platform/vivid/vivid-vbi-out.c @@ -29,7 +29,7 @@ static int vbi_out_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, - unsigned sizes[], void *alloc_ctxs[]) + unsigned sizes[], struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); bool is_60hz = dev->std_out & V4L2_STD_525_60; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 4f730f355a17..52ea6d74b70b 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -36,8 +36,7 @@ /* timeperframe: min/max and default */ static const struct v4l2_fract tpf_min = {.numerator = 1, .denominator = FPS_MAX}, - tpf_max = {.numerator = FPS_MAX, .denominator = 1}, - tpf_default = {.numerator = 1, .denominator = 30}; + tpf_max = {.numerator = FPS_MAX, .denominator = 1}; static const struct vivid_fmt formats_ovl[] = { { @@ -98,7 +97,7 @@ static const struct v4l2_discrete_probe webcam_probe = { static int vid_cap_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, - unsigned sizes[], void *alloc_ctxs[]) + unsigned sizes[], struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); unsigned buffers = tpg_g_buffers(&dev->tpg); @@ -145,11 +144,6 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, *nplanes = buffers; - /* - * videobuf2-vmalloc allocator is context-less so no need to set - * alloc_ctxs array. - */ - 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]); diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index f92f4496d527..dd609eea4753 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -34,7 +34,7 @@ static int vid_out_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, - unsigned sizes[], void *alloc_ctxs[]) + unsigned sizes[], struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); const struct vivid_fmt *vfmt = dev->fmt_out; @@ -87,11 +87,6 @@ static int vid_out_queue_setup(struct vb2_queue *vq, *nplanes = planes; - /* - * videobuf2-vmalloc allocator is context-less so no need to set - * alloc_ctxs array. - */ - 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]); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a9aec5c0bec6..424a54122190 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -519,8 +519,8 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe) static int vsp1_video_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { struct vsp1_video *video = vb2_get_drv_priv(vq); const struct v4l2_pix_format_mplane *format = &video->rwpf->format; @@ -530,20 +530,16 @@ vsp1_video_queue_setup(struct vb2_queue *vq, if (*nplanes != format->num_planes) return -EINVAL; - for (i = 0; i < *nplanes; i++) { + for (i = 0; i < *nplanes; i++) if (sizes[i] < format->plane_fmt[i].sizeimage) return -EINVAL; - alloc_ctxs[i] = video->alloc_ctx; - } return 0; } *nplanes = format->num_planes; - for (i = 0; i < format->num_planes; ++i) { + for (i = 0; i < format->num_planes; ++i) sizes[i] = format->plane_fmt[i].sizeimage; - alloc_ctxs[i] = video->alloc_ctx; - } return 0; } @@ -982,13 +978,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video_set_drvdata(&video->video, video); - /* ... and the buffers queue... */ - video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev); - if (IS_ERR(video->alloc_ctx)) { - ret = PTR_ERR(video->alloc_ctx); - goto error; - } - video->queue.type = video->type; video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; video->queue.lock = &video->lock; @@ -997,6 +986,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->queue.ops = &vsp1_video_queue_qops; video->queue.mem_ops = &vb2_dma_contig_memops; video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + video->queue.dev = video->vsp1->dev; ret = vb2_queue_init(&video->queue); if (ret < 0) { dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); @@ -1014,7 +1004,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, return video; error: - vb2_dma_contig_cleanup_ctx(video->alloc_ctx); vsp1_video_cleanup(video); return ERR_PTR(ret); } @@ -1024,6 +1013,5 @@ void vsp1_video_cleanup(struct vsp1_video *video) if (video_is_registered(&video->video)) video_unregister_device(&video->video); - vb2_dma_contig_cleanup_ctx(video->alloc_ctx); media_entity_cleanup(&video->video.entity); } diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 867b00807c46..4487dc89f25d 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -46,7 +46,6 @@ struct vsp1_video { unsigned int pipe_index; struct vb2_queue queue; - void *alloc_ctx; spinlock_t irqlock; struct list_head irqqueue; unsigned int sequence; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 7f6898b13cac..7ae1a134b1ff 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -318,11 +318,10 @@ static void xvip_dma_complete(void *param) static int xvip_dma_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { struct xvip_dma *dma = vb2_get_drv_priv(vq); - alloc_ctxs[0] = dma->alloc_ctx; /* Make sure the image size is large enough. */ if (*nplanes) return sizes[0] < dma->format.sizeimage ? -EINVAL : 0; @@ -706,12 +705,6 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, 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)) { - ret = PTR_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 @@ -728,6 +721,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, dma->queue.mem_ops = &vb2_dma_contig_memops; dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; + dma->queue.dev = dma->xdev->dev; ret = vb2_queue_init(&dma->queue); if (ret < 0) { dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n"); @@ -766,9 +760,6 @@ void xvip_dma_cleanup(struct xvip_dma *dma) 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); diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 7a1621a2ef40..e95d136c153a 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -65,7 +65,6 @@ static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) * @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 @@ -88,7 +87,6 @@ struct xvip_dma { const struct xvip_video_format *fmtinfo; struct vb2_queue queue; - void *alloc_ctx; unsigned int sequence; struct list_head queued_bufs; |