From: Jeffy Chen Date: Wed, 1 Jun 2016 08:43:23 +0000 (+0800) Subject: CHROMIUM: [media] rockchip-vpu: rename rk3288-vpu sources to rockchip-vpu X-Git-Tag: firefly_0821_release~2290 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=28641dc3b5c7c14f6d8cb8338f01070d2c183bf3;p=firefly-linux-kernel-4.4.55.git CHROMIUM: [media] rockchip-vpu: rename rk3288-vpu sources to rockchip-vpu Change-Id: I66ba2cde8984e56ffa3a27515e4e6380af8544d8 Signed-off-by: Jeffy Chen Signed-off-by: Yakir Yang --- diff --git a/drivers/media/platform/rockchip-vpu/Makefile b/drivers/media/platform/rockchip-vpu/Makefile index 7fb2433f9f58..5f7eb7433444 100644 --- a/drivers/media/platform/rockchip-vpu/Makefile +++ b/drivers/media/platform/rockchip-vpu/Makefile @@ -1,10 +1,10 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o -rockchip-vpu-y += rk3288_vpu.o \ - rk3288_vpu_dec.o \ - rk3288_vpu_enc.o \ - rk3288_vpu_hw.o \ +rockchip-vpu-y += rockchip_vpu.o \ + rockchip_vpu_dec.o \ + rockchip_vpu_enc.o \ + rockchip_vpu_hw.o \ rk3288_vpu_hw_h264d.o \ rk3288_vpu_hw_vp8d.o \ rk3288_vpu_hw_vp8e.o diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu.c deleted file mode 100644 index 5a4520c46a73..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu.c +++ /dev/null @@ -1,824 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 "rk3288_vpu_common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rk3288_vpu_dec.h" -#include "rk3288_vpu_enc.h" -#include "rk3288_vpu_hw.h" - -int debug; -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, - "Debug level - higher value produces more verbose messages"); - -/* - * DMA coherent helpers. - */ - -int rk3288_vpu_aux_buf_alloc(struct rk3288_vpu_dev *vpu, - struct rk3288_vpu_aux_buf *buf, size_t size) -{ - buf->cpu = dma_alloc_coherent(vpu->dev, size, &buf->dma, GFP_KERNEL); - if (!buf->cpu) - return -ENOMEM; - - buf->size = size; - return 0; -} - -void rk3288_vpu_aux_buf_free(struct rk3288_vpu_dev *vpu, - struct rk3288_vpu_aux_buf *buf) -{ - dma_free_coherent(vpu->dev, buf->size, buf->cpu, buf->dma); - - buf->cpu = NULL; - buf->dma = 0; - buf->size = 0; -} - -/* - * Context scheduling. - */ - -static void rk3288_vpu_prepare_run(struct rk3288_vpu_ctx *ctx) -{ - if (ctx->run_ops->prepare_run) - ctx->run_ops->prepare_run(ctx); -} - -static void __rk3288_vpu_dequeue_run_locked(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_buf *src, *dst; - - /* - * Since ctx was dequeued from ready_ctxs list, we know that it has - * at least one buffer in each queue. - */ - src = list_first_entry(&ctx->src_queue, struct rk3288_vpu_buf, list); - dst = list_first_entry(&ctx->dst_queue, struct rk3288_vpu_buf, list); - - list_del(&src->list); - list_del(&dst->list); - - ctx->run.src = src; - ctx->run.dst = dst; -} - -static struct rk3288_vpu_ctx * -rk3288_vpu_encode_after_decode_war(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *dev = ctx->dev; - - if (dev->was_decoding && rk3288_vpu_ctx_is_encoder(ctx)) - return dev->dummy_encode_ctx; - - return ctx; -} - -static void rk3288_vpu_try_run(struct rk3288_vpu_dev *dev) -{ - struct rk3288_vpu_ctx *ctx = NULL; - unsigned long flags; - - vpu_debug_enter(); - - spin_lock_irqsave(&dev->irqlock, flags); - - if (list_empty(&dev->ready_ctxs) || - test_bit(VPU_SUSPENDED, &dev->state)) - /* Nothing to do. */ - goto out; - - if (test_and_set_bit(VPU_RUNNING, &dev->state)) - /* - * The hardware is already running. We will pick another - * run after we get the notification in rk3288_vpu_run_done(). - */ - goto out; - - ctx = list_entry(dev->ready_ctxs.next, struct rk3288_vpu_ctx, list); - - /* - * WAR for corrupted hardware state when encoding directly after - * certain decoding runs. - * - * If previous context was decoding and currently picked one is - * encoding then we need to execute a dummy encode with proper - * settings to reinitialize certain internal hardware state. - */ - ctx = rk3288_vpu_encode_after_decode_war(ctx); - - if (!rk3288_vpu_ctx_is_dummy_encode(ctx)) { - list_del_init(&ctx->list); - __rk3288_vpu_dequeue_run_locked(ctx); - } - - dev->current_ctx = ctx; - dev->was_decoding = !rk3288_vpu_ctx_is_encoder(ctx); - -out: - spin_unlock_irqrestore(&dev->irqlock, flags); - - if (ctx) { - rk3288_vpu_prepare_run(ctx); - rk3288_vpu_run(ctx); - } - - vpu_debug_leave(); -} - -static void __rk3288_vpu_try_context_locked(struct rk3288_vpu_dev *dev, - struct rk3288_vpu_ctx *ctx) -{ - if (!list_empty(&ctx->list)) - /* Context already queued. */ - return; - - if (!list_empty(&ctx->dst_queue) && !list_empty(&ctx->src_queue)) - list_add_tail(&ctx->list, &dev->ready_ctxs); -} - -void rk3288_vpu_run_done(struct rk3288_vpu_ctx *ctx, - enum vb2_buffer_state result) -{ - struct rk3288_vpu_dev *dev = ctx->dev; - unsigned long flags; - - vpu_debug_enter(); - - if (ctx->run_ops->run_done) - ctx->run_ops->run_done(ctx, result); - - if (!rk3288_vpu_ctx_is_dummy_encode(ctx)) { - struct vb2_v4l2_buffer *src = - to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); - struct vb2_v4l2_buffer *dst = - to_vb2_v4l2_buffer(&ctx->run.dst->vb.vb2_buf); - - dst->timestamp = src->timestamp; - vb2_buffer_done(&ctx->run.src->vb.vb2_buf, result); - vb2_buffer_done(&ctx->run.dst->vb.vb2_buf, result); - } - - dev->current_ctx = NULL; - wake_up_all(&dev->run_wq); - - spin_lock_irqsave(&dev->irqlock, flags); - - __rk3288_vpu_try_context_locked(dev, ctx); - clear_bit(VPU_RUNNING, &dev->state); - - spin_unlock_irqrestore(&dev->irqlock, flags); - - /* Try scheduling another run to see if we have anything left to do. */ - rk3288_vpu_try_run(dev); - - vpu_debug_leave(); -} - -void rk3288_vpu_try_context(struct rk3288_vpu_dev *dev, - struct rk3288_vpu_ctx *ctx) -{ - unsigned long flags; - - vpu_debug_enter(); - - spin_lock_irqsave(&dev->irqlock, flags); - - __rk3288_vpu_try_context_locked(dev, ctx); - - spin_unlock_irqrestore(&dev->irqlock, flags); - - rk3288_vpu_try_run(dev); - - vpu_debug_enter(); -} - -/* - * Control registration. - */ - -#define IS_VPU_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) && \ - V4L2_CTRL_DRIVER_PRIV(x)) - -int rk3288_vpu_ctrls_setup(struct rk3288_vpu_ctx *ctx, - const struct v4l2_ctrl_ops *ctrl_ops, - struct rk3288_vpu_control *controls, - unsigned num_ctrls, - const char *const *(*get_menu)(u32)) -{ - struct v4l2_ctrl_config cfg; - int i; - - if (num_ctrls > ARRAY_SIZE(ctx->ctrls)) { - vpu_err("context control array not large enough\n"); - return -ENOSPC; - } - - v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls); - if (ctx->ctrl_handler.error) { - vpu_err("v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_handler.error; - } - - for (i = 0; i < num_ctrls; i++) { - if (IS_VPU_PRIV(controls[i].id) - || controls[i].id >= V4L2_CID_CUSTOM_BASE - || controls[i].type == V4L2_CTRL_TYPE_PRIVATE) { - memset(&cfg, 0, sizeof(struct v4l2_ctrl_config)); - - cfg.ops = ctrl_ops; - cfg.id = controls[i].id; - cfg.min = controls[i].minimum; - cfg.max = controls[i].maximum; - cfg.max_stores = controls[i].max_stores; - cfg.def = controls[i].default_value; - cfg.name = controls[i].name; - cfg.type = controls[i].type; - cfg.elem_size = controls[i].elem_size; - memcpy(cfg.dims, controls[i].dims, sizeof(cfg.dims)); - - if (cfg.type == V4L2_CTRL_TYPE_MENU) { - cfg.menu_skip_mask = cfg.menu_skip_mask; - cfg.qmenu = get_menu(cfg.id); - } else { - cfg.step = controls[i].step; - } - - ctx->ctrls[i] = v4l2_ctrl_new_custom( - &ctx->ctrl_handler, &cfg, NULL); - } else { - if (controls[i].type == V4L2_CTRL_TYPE_MENU) { - ctx->ctrls[i] = - v4l2_ctrl_new_std_menu - (&ctx->ctrl_handler, - ctrl_ops, - controls[i].id, - controls[i].maximum, - 0, - controls[i]. - default_value); - } else { - ctx->ctrls[i] = - v4l2_ctrl_new_std(&ctx->ctrl_handler, - ctrl_ops, - controls[i].id, - controls[i].minimum, - controls[i].maximum, - controls[i].step, - controls[i]. - default_value); - } - } - - if (ctx->ctrl_handler.error) { - vpu_err("Adding control (%d) failed\n", i); - return ctx->ctrl_handler.error; - } - - if (controls[i].is_volatile && ctx->ctrls[i]) - ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE; - if (controls[i].is_read_only && ctx->ctrls[i]) - ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_READ_ONLY; - if (controls[i].can_store && ctx->ctrls[i]) - ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_CAN_STORE; - } - - v4l2_ctrl_handler_setup(&ctx->ctrl_handler); - ctx->num_ctrls = num_ctrls; - return 0; -} - -void rk3288_vpu_ctrls_delete(struct rk3288_vpu_ctx *ctx) -{ - int i; - - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - for (i = 0; i < ctx->num_ctrls; i++) - ctx->ctrls[i] = NULL; -} - -/* - * V4L2 file operations. - */ - -static int rk3288_vpu_open(struct file *filp) -{ - struct video_device *vdev = video_devdata(filp); - struct rk3288_vpu_dev *dev = video_drvdata(filp); - struct rk3288_vpu_ctx *ctx = NULL; - struct vb2_queue *q; - int ret = 0; - - /* - * We do not need any extra locking here, because we operate only - * on local data here, except reading few fields from dev, which - * do not change through device's lifetime (which is guaranteed by - * reference on module from open()) and V4L2 internal objects (such - * as vdev and ctx->fh), which have proper locking done in respective - * helper functions used here. - */ - - vpu_debug_enter(); - - /* Allocate memory for context */ - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - ret = -ENOMEM; - goto err_leave; - } - - v4l2_fh_init(&ctx->fh, video_devdata(filp)); - filp->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - ctx->dev = dev; - INIT_LIST_HEAD(&ctx->src_queue); - INIT_LIST_HEAD(&ctx->dst_queue); - INIT_LIST_HEAD(&ctx->list); - - if (vdev == dev->vfd_enc) { - /* only for encoder */ - ret = rk3288_vpu_enc_init(ctx); - if (ret) { - vpu_err("Failed to initialize encoder context\n"); - goto err_fh_free; - } - } else if (vdev == dev->vfd_dec) { - /* only for decoder */ - ret = rk3288_vpu_dec_init(ctx); - if (ret) { - vpu_err("Failed to initialize decoder context\n"); - goto err_fh_free; - } - } else { - ret = -ENOENT; - goto err_fh_free; - } - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - - /* Init videobuf2 queue for CAPTURE */ - q = &ctx->vq_dst; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->drv_priv = &ctx->fh; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; - q->lock = &dev->vpu_mutex; - q->buf_struct_size = sizeof(struct rk3288_vpu_buf); - - if (vdev == dev->vfd_enc) { - q->ops = get_enc_queue_ops(); - } else if (vdev == dev->vfd_dec) { - q->ops = get_dec_queue_ops(); - q->use_dma_bidirectional = 1; - } - - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - - ret = vb2_queue_init(q); - if (ret) { - vpu_err("Failed to initialize videobuf2 queue(capture)\n"); - goto err_enc_dec_exit; - } - - /* Init videobuf2 queue for OUTPUT */ - q = &ctx->vq_src; - q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - q->drv_priv = &ctx->fh; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; - q->lock = &dev->vpu_mutex; - q->buf_struct_size = sizeof(struct rk3288_vpu_buf); - - if (vdev == dev->vfd_enc) - q->ops = get_enc_queue_ops(); - else if (vdev == dev->vfd_dec) - q->ops = get_dec_queue_ops(); - - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - - ret = vb2_queue_init(q); - if (ret) { - vpu_err("Failed to initialize videobuf2 queue(output)\n"); - goto err_vq_dst_release; - } - - vpu_debug_leave(); - - return 0; - -err_vq_dst_release: - vb2_queue_release(&ctx->vq_dst); -err_enc_dec_exit: - if (vdev == dev->vfd_enc) - rk3288_vpu_enc_exit(ctx); - else if (vdev == dev->vfd_dec) - rk3288_vpu_dec_exit(ctx); -err_fh_free: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); -err_leave: - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_release(struct file *filp) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); - struct video_device *vdev = video_devdata(filp); - struct rk3288_vpu_dev *dev = ctx->dev; - - /* - * No need for extra locking because this was the last reference - * to this file. - */ - - vpu_debug_enter(); - - /* - * vb2_queue_release() ensures that streaming is stopped, which - * in turn means that there are no frames still being processed - * by hardware. - */ - vb2_queue_release(&ctx->vq_src); - vb2_queue_release(&ctx->vq_dst); - - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - - if (vdev == dev->vfd_enc) - rk3288_vpu_enc_exit(ctx); - else if (vdev == dev->vfd_dec) - rk3288_vpu_dec_exit(ctx); - - kfree(ctx); - - vpu_debug_leave(); - - return 0; -} - -static unsigned int rk3288_vpu_poll(struct file *filp, - struct poll_table_struct *wait) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); - struct vb2_queue *src_q, *dst_q; - struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; - unsigned int rc = 0; - unsigned long flags; - - vpu_debug_enter(); - - src_q = &ctx->vq_src; - dst_q = &ctx->vq_dst; - - /* - * There has to be at least one buffer queued on each queued_list, which - * means either in driver already or waiting for driver to claim it - * and start processing. - */ - if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) && - (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) { - vpu_debug(0, "src q streaming %d, dst q streaming %d, src list empty(%d), dst list empty(%d)\n", - src_q->streaming, dst_q->streaming, - list_empty(&src_q->queued_list), - list_empty(&dst_q->queued_list)); - return POLLERR; - } - - poll_wait(filp, &ctx->fh.wait, wait); - poll_wait(filp, &src_q->done_wq, wait); - poll_wait(filp, &dst_q->done_wq, wait); - - if (v4l2_event_pending(&ctx->fh)) - rc |= POLLPRI; - - spin_lock_irqsave(&src_q->done_lock, flags); - - if (!list_empty(&src_q->done_list)) - src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, - done_entry); - - if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE || - src_vb->state == VB2_BUF_STATE_ERROR)) - rc |= POLLOUT | POLLWRNORM; - - spin_unlock_irqrestore(&src_q->done_lock, flags); - - spin_lock_irqsave(&dst_q->done_lock, flags); - - if (!list_empty(&dst_q->done_list)) - dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, - done_entry); - - if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE || - dst_vb->state == VB2_BUF_STATE_ERROR)) - rc |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&dst_q->done_lock, flags); - - return rc; -} - -static int rk3288_vpu_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - int ret; - - vpu_debug_enter(); - - if (offset < DST_QUEUE_OFF_BASE) { - vpu_debug(4, "mmaping source\n"); - - ret = vb2_mmap(&ctx->vq_src, vma); - } else { /* capture */ - vpu_debug(4, "mmaping destination\n"); - - vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); - ret = vb2_mmap(&ctx->vq_dst, vma); - } - - vpu_debug_leave(); - - return ret; -} - -static const struct v4l2_file_operations rk3288_vpu_fops = { - .owner = THIS_MODULE, - .open = rk3288_vpu_open, - .release = rk3288_vpu_release, - .poll = rk3288_vpu_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = rk3288_vpu_mmap, -}; - -/* - * Platform driver. - */ - -static int rk3288_vpu_probe(struct platform_device *pdev) -{ - struct rk3288_vpu_dev *vpu = NULL; - DEFINE_DMA_ATTRS(attrs_novm); - DEFINE_DMA_ATTRS(attrs_nohugepage); - struct video_device *vfd; - int ret = 0; - - vpu_debug_enter(); - - vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); - if (!vpu) - return -ENOMEM; - - vpu->dev = &pdev->dev; - vpu->pdev = pdev; - mutex_init(&vpu->vpu_mutex); - spin_lock_init(&vpu->irqlock); - INIT_LIST_HEAD(&vpu->ready_ctxs); - init_waitqueue_head(&vpu->run_wq); - - ret = rk3288_vpu_hw_probe(vpu); - if (ret) { - dev_err(&pdev->dev, "vcodec_hw_probe failed\n"); - goto err_hw_probe; - } - - /* - * We'll do mostly sequential access, so sacrifice TLB efficiency for - * faster allocation. - */ - dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs_novm); - - dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs_novm); - vpu->alloc_ctx = vb2_dma_contig_init_ctx_attrs(&pdev->dev, - &attrs_novm); - if (IS_ERR(vpu->alloc_ctx)) { - ret = PTR_ERR(vpu->alloc_ctx); - goto err_dma_contig; - } - - dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs_nohugepage); - vpu->alloc_ctx_vm = vb2_dma_contig_init_ctx_attrs(&pdev->dev, - &attrs_nohugepage); - if (IS_ERR(vpu->alloc_ctx_vm)) { - ret = PTR_ERR(vpu->alloc_ctx_vm); - goto err_dma_contig_vm; - } - - ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - goto err_v4l2_dev_reg; - } - - platform_set_drvdata(pdev, vpu); - - ret = rk3288_vpu_enc_init_dummy_ctx(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to create dummy encode context\n"); - goto err_dummy_enc; - } - - /* encoder */ - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto err_enc_alloc; - } - - vfd->fops = &rk3288_vpu_fops; - vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); - vfd->release = video_device_release; - vfd->lock = &vpu->vpu_mutex; - vfd->v4l2_dev = &vpu->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - snprintf(vfd->name, sizeof(vfd->name), "%s", RK3288_VPU_ENC_NAME); - vpu->vfd_enc = vfd; - - video_set_drvdata(vfd, vpu); - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_enc_reg; - } - - v4l2_info(&vpu->v4l2_dev, - "Rockchip RK3288 VPU encoder registered as /vpu/video%d\n", - vfd->num); - - /* decoder */ - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto err_dec_alloc; - } - - vfd->fops = &rk3288_vpu_fops; - vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); - vfd->release = video_device_release; - vfd->lock = &vpu->vpu_mutex; - vfd->v4l2_dev = &vpu->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - snprintf(vfd->name, sizeof(vfd->name), "%s", RK3288_VPU_DEC_NAME); - vpu->vfd_dec = vfd; - - video_set_drvdata(vfd, vpu); - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_dec_reg; - } - - v4l2_info(&vpu->v4l2_dev, - "Rockchip RK3288 VPU decoder registered as /vpu/video%d\n", - vfd->num); - - vpu_debug_leave(); - - return 0; - -err_dec_reg: - video_device_release(vpu->vfd_dec); -err_dec_alloc: - video_unregister_device(vpu->vfd_enc); -err_enc_reg: - video_device_release(vpu->vfd_enc); -err_enc_alloc: - rk3288_vpu_enc_free_dummy_ctx(vpu); -err_dummy_enc: - v4l2_device_unregister(&vpu->v4l2_dev); -err_v4l2_dev_reg: - vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm); -err_dma_contig_vm: - vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx); -err_dma_contig: - rk3288_vpu_hw_remove(vpu); -err_hw_probe: - pr_debug("%s-- with error\n", __func__); - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_remove(struct platform_device *pdev) -{ - struct rk3288_vpu_dev *vpu = platform_get_drvdata(pdev); - - vpu_debug_enter(); - - v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); - - /* - * We are safe here assuming that .remove() got called as - * a result of module removal, which guarantees that all - * contexts have been released. - */ - - video_unregister_device(vpu->vfd_dec); - video_unregister_device(vpu->vfd_enc); - rk3288_vpu_enc_free_dummy_ctx(vpu); - v4l2_device_unregister(&vpu->v4l2_dev); - vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm); - vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx); - rk3288_vpu_hw_remove(vpu); - - vpu_debug_leave(); - - return 0; -} - -static struct platform_device_id vpu_driver_ids[] = { - { .name = "rk3288-vpu", }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(platform, vpu_driver_ids); - -#ifdef CONFIG_OF -static const struct of_device_id of_rk3288_vpu_match[] = { - { .compatible = "rockchip,rk3288-vpu", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, of_rk3288_vpu_match); -#endif - -#ifdef CONFIG_PM_SLEEP -static int rk3288_vpu_suspend(struct device *dev) -{ - struct rk3288_vpu_dev *vpu = dev_get_drvdata(dev); - - set_bit(VPU_SUSPENDED, &vpu->state); - wait_event(vpu->run_wq, vpu->current_ctx == NULL); - - return 0; -} - -static int rk3288_vpu_resume(struct device *dev) -{ - struct rk3288_vpu_dev *vpu = dev_get_drvdata(dev); - - clear_bit(VPU_SUSPENDED, &vpu->state); - rk3288_vpu_try_run(vpu); - - return 0; -} -#endif - -static const struct dev_pm_ops rk3288_vpu_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rk3288_vpu_suspend, rk3288_vpu_resume) -}; - -static struct platform_driver rk3288_vpu_driver = { - .probe = rk3288_vpu_probe, - .remove = rk3288_vpu_remove, - .id_table = vpu_driver_ids, - .driver = { - .name = RK3288_VPU_NAME, - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(of_rk3288_vpu_match), - .pm = &rk3288_vpu_pm_ops, - }, -}; -module_platform_driver(rk3288_vpu_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Alpha Lin "); -MODULE_AUTHOR("Tomasz Figa "); -MODULE_DESCRIPTION("Rockchip RK3288 VPU codec driver"); diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_common.h b/drivers/media/platform/rockchip-vpu/rk3288_vpu_common.h deleted file mode 100644 index 3ff5c69992b6..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_common.h +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 RK3288_VPU_COMMON_H_ -#define RK3288_VPU_COMMON_H_ - -/* Enable debugging by default for now. */ -#define DEBUG - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "rk3288_vpu_hw.h" - -#define RK3288_VPU_NAME "rk3288-vpu" -#define RK3288_VPU_DEC_NAME "rk3288-vpu-dec" -#define RK3288_VPU_ENC_NAME "rk3288-vpu-enc" - -#define V4L2_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0x1000) - -#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2) - -#define RK3288_VPU_MAX_CTRLS 32 - -#define MB_DIM 16 -#define MB_WIDTH(x_size) DIV_ROUND_UP(x_size, MB_DIM) -#define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, MB_DIM) - -struct rk3288_vpu_variant; -struct rk3288_vpu_ctx; -struct rk3288_vpu_codec_ops; - -/** - * enum rk3288_vpu_codec_mode - codec operating mode. - * @RK_VPU_CODEC_NONE: No operating mode. Used for RAW video formats. - * @RK_VPU_CODEC_H264D: H264 decoder. - * @RK_VPU_CODEC_VP8D: VP8 decoder. - * @RK_VPU_CODEC_H264E: H264 encoder. - * @RK_VPU_CODEC_VP8E: VP8 encoder. - */ -enum rk3288_vpu_codec_mode { - RK_VPU_CODEC_NONE = -1, - RK_VPU_CODEC_H264D, - RK_VPU_CODEC_VP8D, - RK_VPU_CODEC_H264E, - RK_VPU_CODEC_VP8E -}; - -/** - * enum rk3288_vpu_plane - indices of planes inside a VB2 buffer. - * @PLANE_Y: Plane containing luminance data (also denoted as Y). - * @PLANE_CB_CR: Plane containing interleaved chrominance data (also - * denoted as CbCr). - * @PLANE_CB: Plane containing CB part of chrominance data. - * @PLANE_CR: Plane containing CR part of chrominance data. - */ -enum rk3288_vpu_plane { - PLANE_Y = 0, - PLANE_CB_CR = 1, - PLANE_CB = 1, - PLANE_CR = 2, -}; - -/** - * struct rk3288_vpu_vp8e_buf_data - mode-specific per-buffer data - * @dct_offset: Offset inside the buffer to DCT partition. - * @hdr_size: Size of header data in the buffer. - * @ext_hdr_size: Size of ext header data in the buffer. - * @dct_size: Size of DCT partition in the buffer. - * @header: Frame header to copy to destination buffer. - */ -struct rk3288_vpu_vp8e_buf_data { - size_t dct_offset; - size_t hdr_size; - size_t ext_hdr_size; - size_t dct_size; - u8 header[RK3288_HEADER_SIZE]; -}; - -/** - * struct rk3288_vpu_buf - Private data related to each VB2 buffer. - * @vb: Pointer to related VB2 buffer. - * @list: List head for queuing in buffer queue. - * @flags: Buffer state. See enum rk3288_vpu_buf_flags. - */ -struct rk3288_vpu_buf { - struct vb2_v4l2_buffer vb; - struct list_head list; - - /* Mode-specific data. */ - union { - struct rk3288_vpu_vp8e_buf_data vp8e; - }; -}; - -/** - * enum rk3288_vpu_state - bitwise flags indicating hardware state. - * @VPU_RUNNING: The hardware has been programmed for operation - * and is running at the moment. - * @VPU_SUSPENDED: System is entering sleep state and no more runs - * should be executed on hardware. - */ -enum rk3288_vpu_state { - VPU_RUNNING = BIT(0), - VPU_SUSPENDED = BIT(1), -}; - -/** - * struct rk3288_vpu_dev - driver data - * @v4l2_dev: V4L2 device to register video devices for. - * @vfd_dec: Video device for decoder. - * @vfd_enc: Video device for encoder. - * @pdev: Pointer to VPU platform device. - * @dev: Pointer to device for convenient logging using - * dev_ macros. - * @alloc_ctx: VB2 allocator context - * (for allocations without kernel mapping). - * @alloc_ctx_vm: VB2 allocator context - * (for allocations with kernel mapping). - * @aclk_vcodec: Handle of ACLK clock. - * @hclk_vcodec: Handle of HCLK clock. - * @base: Mapped address of VPU registers. - * @enc_base: Mapped address of VPU encoder register for convenience. - * @dec_base: Mapped address of VPU decoder register for convenience. - * @mapping: DMA IOMMU mapping. - * @domain: DMA IOMMU domain. - * @vpu_mutex: Mutex to synchronize V4L2 calls. - * @irqlock: Spinlock to synchronize access to data structures - * shared with interrupt handlers. - * @state: Device state. - * @ready_ctxs: List of contexts ready to run. - * @variant: Hardware variant-specfic parameters. - * @current_ctx: Context being currently processed by hardware. - * @run_wq: Wait queue to wait for run completion. - * @watchdog_work: Delayed work for hardware timeout handling. - * @dummy_encode_ctx: Context used to run dummy frame encoding to initialize - * encoder hardware state. - * @dummy_encode_src: Source buffers used for dummy frame encoding. - * @dummy_encode_dst: Desintation buffer used for dummy frame encoding. - * @was_decoding: Indicates whether last run context was a decoder. - */ -struct rk3288_vpu_dev { - struct v4l2_device v4l2_dev; - struct video_device *vfd_dec; - struct video_device *vfd_enc; - struct platform_device *pdev; - struct device *dev; - void *alloc_ctx; - void *alloc_ctx_vm; - struct clk *aclk_vcodec; - struct clk *hclk_vcodec; - void __iomem *base; - void __iomem *enc_base; - void __iomem *dec_base; - struct dma_iommu_mapping *mapping; - struct iommu_domain *domain; - - struct mutex vpu_mutex; /* video_device lock */ - spinlock_t irqlock; - unsigned long state; - struct list_head ready_ctxs; - const struct rk3288_vpu_variant *variant; - struct rk3288_vpu_ctx *current_ctx; - wait_queue_head_t run_wq; - struct delayed_work watchdog_work; - struct rk3288_vpu_ctx *dummy_encode_ctx; - struct rk3288_vpu_aux_buf dummy_encode_src[VIDEO_MAX_PLANES]; - struct rk3288_vpu_aux_buf dummy_encode_dst; - bool was_decoding; -}; - -/** - * struct rk3288_vpu_run_ops - per context operations on run data. - * @prepare_run: Called when the context was selected for running - * to prepare operating mode specific data. - * @run_done: Called when hardware completed the run to collect - * operating mode specific data from hardware and - * finalize the processing. - */ -struct rk3288_vpu_run_ops { - void (*prepare_run)(struct rk3288_vpu_ctx *); - void (*run_done)(struct rk3288_vpu_ctx *, enum vb2_buffer_state); -}; - -/** - * struct rk3288_vpu_vp8e_run - per-run data specific to VP8 encoding. - * @reg_params: Pointer to a buffer containing register values prepared - * by user space. - */ -struct rk3288_vpu_vp8e_run { - const struct rk3288_vp8e_reg_params *reg_params; -}; - -/** - * struct rk3288_vpu_vp8d_run - per-run data specific to VP8 decoding. - * @frame_hdr: Pointer to a buffer containing per-run frame data which - * is needed by setting vpu register. - */ -struct rk3288_vpu_vp8d_run { - const struct v4l2_ctrl_vp8_frame_hdr *frame_hdr; -}; - -/** - * struct rk3288_vpu_h264d_run - per-run data specific to H264 decoding. - * @sps: Pointer to a buffer containing H264 SPS. - * @pps: Pointer to a buffer containing H264 PPS. - * @scaling_matrix: Pointer to a buffer containing scaling matrix. - * @slice_param: Pointer to a buffer containing slice parameters array. - * @decode_param: Pointer to a buffer containing decode parameters. - * @dpb: Array of DPB entries reordered to keep POC order. - * @dpb_map: Map of indices used in ref_pic_list_* into indices to - * reordered DPB array. - */ -struct rk3288_vpu_h264d_run { - const struct v4l2_ctrl_h264_sps *sps; - const struct v4l2_ctrl_h264_pps *pps; - const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; - const struct v4l2_ctrl_h264_slice_param *slice_param; - const struct v4l2_ctrl_h264_decode_param *decode_param; - struct v4l2_h264_dpb_entry dpb[16]; - u8 dpb_map[16]; -}; - -/** - * struct rk3288_vpu_run - per-run data for hardware code. - * @src: Source buffer to be processed. - * @dst: Destination buffer to be processed. - * @priv_src: Hardware private source buffer. - * @priv_dst: Hardware private destination buffer. - */ -struct rk3288_vpu_run { - /* Generic for more than one operating mode. */ - struct rk3288_vpu_buf *src; - struct rk3288_vpu_buf *dst; - - struct rk3288_vpu_aux_buf priv_src; - struct rk3288_vpu_aux_buf priv_dst; - - /* Specific for particular operating modes. */ - union { - struct rk3288_vpu_vp8e_run vp8e; - struct rk3288_vpu_vp8d_run vp8d; - struct rk3288_vpu_h264d_run h264d; - /* Other modes will need different data. */ - }; -}; - -/** - * struct rk3288_vpu_ctx - Context (instance) private data. - * - * @dev: VPU driver data to which the context belongs. - * @fh: V4L2 file handler. - * - * @vpu_src_fmt: Descriptor of active source format. - * @src_fmt: V4L2 pixel format of active source format. - * @vpu_dst_fmt: Descriptor of active destination format. - * @dst_fmt: V4L2 pixel format of active destination format. - * - * @vq_src: Videobuf2 source queue. - * @src_queue: Internal source buffer queue. - * @src_crop: Configured source crop rectangle (encoder-only). - * @vq_dst: Videobuf2 destination queue - * @dst_queue: Internal destination buffer queue. - * @dst_bufs: Private buffers wrapping VB2 buffers (destination). - * - * @ctrls: Array containing pointer to registered controls. - * @ctrl_handler: Control handler used to register controls. - * @num_ctrls: Number of registered controls. - * - * @list: List head for queue of ready contexts. - * - * @run: Structure containing data about currently scheduled - * processing run. - * @run_ops: Set of operations related to currently scheduled run. - * @hw: Structure containing hardware-related context. - */ -struct rk3288_vpu_ctx { - struct rk3288_vpu_dev *dev; - struct v4l2_fh fh; - - /* Format info */ - struct rk3288_vpu_fmt *vpu_src_fmt; - struct v4l2_pix_format_mplane src_fmt; - struct rk3288_vpu_fmt *vpu_dst_fmt; - struct v4l2_pix_format_mplane dst_fmt; - - /* VB2 queue data */ - struct vb2_queue vq_src; - struct list_head src_queue; - struct v4l2_rect src_crop; - struct vb2_queue vq_dst; - struct list_head dst_queue; - struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME]; - - /* Controls */ - struct v4l2_ctrl *ctrls[RK3288_VPU_MAX_CTRLS]; - struct v4l2_ctrl_handler ctrl_handler; - unsigned num_ctrls; - - /* Various runtime data */ - struct list_head list; - - struct rk3288_vpu_run run; - const struct rk3288_vpu_run_ops *run_ops; - struct rk3288_vpu_hw_ctx hw; -}; - -/** - * struct rk3288_vpu_fmt - information about supported video formats. - * @name: Human readable name of the format. - * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. - * @codec_mode: Codec mode related to this format. See - * enum rk3288_vpu_codec_mode. - * @num_planes: Number of planes used by this format. - * @depth: Depth of each plane in bits per pixel. - * @enc_fmt: Format identifier for encoder registers. - */ -struct rk3288_vpu_fmt { - char *name; - u32 fourcc; - enum rk3288_vpu_codec_mode codec_mode; - int num_planes; - u8 depth[VIDEO_MAX_PLANES]; - enum rk3288_vpu_enc_fmt enc_fmt; -}; - -/** - * struct rk3288_vpu_control - information about controls to be registered. - * @id: Control ID. - * @type: Type of the control. - * @name: Human readable name of the control. - * @minimum: Minimum value of the control. - * @maximum: Maximum value of the control. - * @step: Control value increase step. - * @menu_skip_mask: Mask of invalid menu positions. - * @default_value: Initial value of the control. - * @max_stores: Maximum number of configration stores. - * @dims: Size of each dimension of compound control. - * @elem_size: Size of individual element of compound control. - * @is_volatile: Control is volatile. - * @is_read_only: Control is read-only. - * @can_store: Control uses configuration stores. - * - * See also struct v4l2_ctrl_config. - */ -struct rk3288_vpu_control { - u32 id; - - enum v4l2_ctrl_type type; - const char *name; - s32 minimum; - s32 maximum; - s32 step; - u32 menu_skip_mask; - s32 default_value; - s32 max_stores; - u32 dims[V4L2_CTRL_MAX_DIMS]; - u32 elem_size; - - bool is_volatile:1; - bool is_read_only:1; - bool can_store:1; -}; - -/* Logging helpers */ - -/** - * debug - Module parameter to control level of debugging messages. - * - * Level of debugging messages can be controlled by bits of module parameter - * called "debug". Meaning of particular bits is as follows: - * - * bit 0 - global information: mode, size, init, release - * bit 1 - each run start/result information - * bit 2 - contents of small controls from userspace - * bit 3 - contents of big controls from userspace - * bit 4 - detail fmt, ctrl, buffer q/dq information - * bit 5 - detail function enter/leave trace information - * bit 6 - register write/read information - */ -extern int debug; - -#define vpu_debug(level, fmt, args...) \ - do { \ - if (debug & BIT(level)) \ - pr_debug("%s:%d: " fmt, \ - __func__, __LINE__, ##args); \ - } while (0) - -#define vpu_debug_enter() vpu_debug(5, "enter\n") -#define vpu_debug_leave() vpu_debug(5, "leave\n") - -#define vpu_err(fmt, args...) \ - pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) - -static inline char *fmt2str(u32 fmt, char *str) -{ - char a = fmt & 0xFF; - char b = (fmt >> 8) & 0xFF; - char c = (fmt >> 16) & 0xFF; - char d = (fmt >> 24) & 0xFF; - - sprintf(str, "%c%c%c%c", a, b, c, d); - - return str; -} - -/* Structure access helpers. */ -static inline struct rk3288_vpu_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct rk3288_vpu_ctx, fh); -} - -static inline struct rk3288_vpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct rk3288_vpu_ctx, ctrl_handler); -} - -static inline struct rk3288_vpu_buf *vb_to_buf(struct vb2_buffer *vb) -{ - return container_of(to_vb2_v4l2_buffer(vb), struct rk3288_vpu_buf, vb); -} - -static inline bool rk3288_vpu_ctx_is_encoder(struct rk3288_vpu_ctx *ctx) -{ - return ctx->vpu_dst_fmt->codec_mode != RK_VPU_CODEC_NONE; -} - -static inline bool rk3288_vpu_ctx_is_dummy_encode(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *dev = ctx->dev; - - return ctx == dev->dummy_encode_ctx; -} - -int rk3288_vpu_ctrls_setup(struct rk3288_vpu_ctx *ctx, - const struct v4l2_ctrl_ops *ctrl_ops, - struct rk3288_vpu_control *controls, - unsigned num_ctrls, - const char *const *(*get_menu)(u32)); -void rk3288_vpu_ctrls_delete(struct rk3288_vpu_ctx *ctx); - -void rk3288_vpu_try_context(struct rk3288_vpu_dev *dev, - struct rk3288_vpu_ctx *ctx); - -void rk3288_vpu_run_done(struct rk3288_vpu_ctx *ctx, - enum vb2_buffer_state result); - -int rk3288_vpu_aux_buf_alloc(struct rk3288_vpu_dev *vpu, - struct rk3288_vpu_aux_buf *buf, size_t size); -void rk3288_vpu_aux_buf_free(struct rk3288_vpu_dev *vpu, - struct rk3288_vpu_aux_buf *buf); - -/* Register accessors. */ -static inline void vepu_write_relaxed(struct rk3288_vpu_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); - writel_relaxed(val, vpu->enc_base + reg); -} - -static inline void vepu_write(struct rk3288_vpu_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); - writel(val, vpu->enc_base + reg); -} - -static inline u32 vepu_read(struct rk3288_vpu_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->enc_base + reg); - - vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val); - return val; -} - -static inline void vdpu_write_relaxed(struct rk3288_vpu_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); - writel_relaxed(val, vpu->dec_base + reg); -} - -static inline void vdpu_write(struct rk3288_vpu_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); - writel(val, vpu->dec_base + reg); -} - -static inline u32 vdpu_read(struct rk3288_vpu_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->dec_base + reg); - - vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val); - return val; -} - -#endif /* RK3288_VPU_COMMON_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.c deleted file mode 100644 index 0652ae7a132f..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.c +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 "rk3288_vpu_common.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "rk3288_vpu_dec.h" -#include "rk3288_vpu_hw.h" - -#define DEF_SRC_FMT_DEC V4L2_PIX_FMT_H264_SLICE -#define DEF_DST_FMT_DEC V4L2_PIX_FMT_NV12 - -#define RK3288_DEC_MIN_WIDTH 48U -#define RK3288_DEC_MAX_WIDTH 3840U -#define RK3288_DEC_MIN_HEIGHT 48U -#define RK3288_DEC_MAX_HEIGHT 2160U - -#define RK3288_H264_MAX_SLICES_PER_FRAME 16 - -static struct rk3288_vpu_fmt formats[] = { - { - .name = "4:2:0 1 plane Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = RK_VPU_CODEC_NONE, - .num_planes = 1, - .depth = { 12 }, - }, - { - .name = "Slices of H264 Encoded Stream", - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = RK_VPU_CODEC_H264D, - .num_planes = 1, - }, - { - .name = "Frames of VP8 Encoded Stream", - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = RK_VPU_CODEC_VP8D, - .num_planes = 1, - }, -}; - -static struct rk3288_vpu_fmt *find_format(u32 fourcc, bool bitstream) -{ - unsigned int i; - - vpu_debug_enter(); - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].fourcc == fourcc && - !!bitstream == (formats[i].codec_mode != RK_VPU_CODEC_NONE)) - return &formats[i]; - } - - return NULL; -} - -/* Indices of controls that need to be accessed directly. */ -enum { - RK3288_VPU_DEC_CTRL_H264_SPS, - RK3288_VPU_DEC_CTRL_H264_PPS, - RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX, - RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM, - RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM, - RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR, -}; - -static struct rk3288_vpu_control controls[] = { - /* H264 slice-based interface. */ - [RK3288_VPU_DEC_CTRL_H264_SPS] = { - .id = V4L2_CID_MPEG_VIDEO_H264_SPS, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "H264 SPS Parameters", - .elem_size = sizeof(struct v4l2_ctrl_h264_sps), - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_DEC_CTRL_H264_PPS] = { - .id = V4L2_CID_MPEG_VIDEO_H264_PPS, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "H264 PPS Parameters", - .elem_size = sizeof(struct v4l2_ctrl_h264_pps), - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX] = { - .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "H264 Scaling Matrix", - .elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix), - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM] = { - .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "H264 Slice Parameters", - .max_stores = VIDEO_MAX_FRAME, - .elem_size = sizeof(struct v4l2_ctrl_h264_slice_param), - .dims = { RK3288_H264_MAX_SLICES_PER_FRAME, }, - .can_store = true, - }, - [RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM] = { - .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "H264 Decode Parameters", - .max_stores = VIDEO_MAX_FRAME, - .elem_size = sizeof(struct v4l2_ctrl_h264_decode_param), - .can_store = true, - }, - [RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR] = { - .id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "VP8 Frame Header Parameters", - .max_stores = VIDEO_MAX_FRAME, - .elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr), - .can_store = true, - }, -}; - -static inline const void *get_ctrl_ptr(struct rk3288_vpu_ctx *ctx, unsigned id) -{ - struct v4l2_ctrl *ctrl = ctx->ctrls[id]; - - return ctrl->p_cur.p; -} - -/* Query capabilities of the device */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rk3288_vpu_dev *dev = video_drvdata(file); - - vpu_debug_enter(); - - strlcpy(cap->driver, RK3288_VPU_DEC_NAME, sizeof(cap->driver)); - strlcpy(cap->card, dev->pdev->name, sizeof(cap->card)); - strlcpy(cap->bus_info, "platform:" RK3288_VPU_NAME, - sizeof(cap->bus_info)); - - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - vpu_debug_leave(); - - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *prov, - struct v4l2_frmsizeenum *fsize) -{ - struct v4l2_frmsize_stepwise *s = &fsize->stepwise; - struct rk3288_vpu_fmt *fmt; - - if (fsize->index != 0) { - vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", - fsize->index); - return -EINVAL; - } - - fmt = find_format(fsize->pixel_format, true); - if (!fmt) { - vpu_debug(0, "unsupported bitstream format (%08x)\n", - fsize->pixel_format); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - - s->min_width = RK3288_DEC_MIN_WIDTH; - s->max_width = RK3288_DEC_MAX_WIDTH; - s->step_width = MB_DIM; - s->min_height = RK3288_DEC_MIN_HEIGHT; - s->max_height = RK3288_DEC_MAX_HEIGHT; - s->step_height = MB_DIM; - - return 0; -} - -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out) -{ - struct rk3288_vpu_fmt *fmt; - int i, j = 0; - - vpu_debug_enter(); - - for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (out && formats[i].codec_mode == RK_VPU_CODEC_NONE) - continue; - else if (!out && (formats[i].codec_mode != RK_VPU_CODEC_NONE)) - continue; - - if (j == f->index) { - fmt = &formats[i]; - strlcpy(f->description, fmt->name, - sizeof(f->description)); - f->pixelformat = fmt->fourcc; - - f->flags = 0; - if (formats[i].codec_mode != RK_VPU_CODEC_NONE) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; - - vpu_debug_leave(); - - return 0; - } - - ++j; - } - - vpu_debug_leave(); - - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, false); -} - -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, true); -} - -static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - - vpu_debug_enter(); - - vpu_debug(4, "f->type = %d\n", f->type); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - f->fmt.pix_mp = ctx->dst_fmt; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - f->fmt.pix_mp = ctx->src_fmt; - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - - vpu_debug_leave(); - - return 0; -} - -static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rk3288_vpu_fmt *fmt; - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - char str[5]; - - vpu_debug_enter(); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); - - fmt = find_format(pix_fmt_mp->pixelformat, true); - if (!fmt) { - vpu_err("failed to try output format\n"); - return -EINVAL; - } - - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - vpu_err("sizeimage of output format must be given\n"); - return -EINVAL; - } - - pix_fmt_mp->plane_fmt[0].bytesperline = 0; - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); - - fmt = find_format(pix_fmt_mp->pixelformat, false); - if (!fmt) { - vpu_err("failed to try capture format\n"); - return -EINVAL; - } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - vpu_err("plane number mismatches on capture format\n"); - return -EINVAL; - } - - /* Limit to hardware min/max. */ - pix_fmt_mp->width = clamp(pix_fmt_mp->width, - RK3288_DEC_MIN_WIDTH, RK3288_DEC_MAX_WIDTH); - pix_fmt_mp->height = clamp(pix_fmt_mp->height, - RK3288_DEC_MIN_HEIGHT, RK3288_DEC_MAX_HEIGHT); - - /* Round up to macroblocks. */ - pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM); - pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM); - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - - vpu_debug_leave(); - - return 0; -} - -static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - unsigned int mb_width, mb_height; - struct rk3288_vpu_fmt *fmt; - int ret = 0; - int i; - - vpu_debug_enter(); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - /* Change not allowed if any queue is streaming. */ - if (vb2_is_streaming(&ctx->vq_src) - || vb2_is_streaming(&ctx->vq_dst)) { - ret = -EBUSY; - goto out; - } - /* - * Pixel format change is not allowed when the other queue has - * buffers allocated. - */ - if (vb2_is_busy(&ctx->vq_dst) - && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) { - ret = -EBUSY; - goto out; - } - - ret = vidioc_try_fmt(file, priv, f); - if (ret) - goto out; - - ctx->vpu_src_fmt = find_format(pix_fmt_mp->pixelformat, true); - ctx->src_fmt = *pix_fmt_mp; - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - /* - * Change not allowed if this queue is streaming. - * - * NOTE: We allow changes with source queue streaming - * to support resolution change in decoded stream. - */ - if (vb2_is_streaming(&ctx->vq_dst)) { - ret = -EBUSY; - goto out; - } - /* - * Pixel format change is not allowed when the other queue has - * buffers allocated. - */ - if (vb2_is_busy(&ctx->vq_src) - && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) { - ret = -EBUSY; - goto out; - } - - ret = vidioc_try_fmt(file, priv, f); - if (ret) - goto out; - - fmt = find_format(pix_fmt_mp->pixelformat, false); - ctx->vpu_dst_fmt = fmt; - - mb_width = MB_WIDTH(pix_fmt_mp->width); - mb_height = MB_HEIGHT(pix_fmt_mp->height); - - vpu_debug(0, "CAPTURE codec mode: %d\n", fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", - pix_fmt_mp->width, pix_fmt_mp->height, - mb_width, mb_height); - - for (i = 0; i < fmt->num_planes; ++i) { - pix_fmt_mp->plane_fmt[i].bytesperline = - mb_width * MB_DIM * fmt->depth[i] / 8; - pix_fmt_mp->plane_fmt[i].sizeimage = - pix_fmt_mp->plane_fmt[i].bytesperline - * mb_height * MB_DIM; - /* - * All of multiplanar formats we support have chroma - * planes subsampled by 2. - */ - if (i != 0) - pix_fmt_mp->plane_fmt[i].sizeimage /= 2; - } - - ctx->dst_fmt = *pix_fmt_mp; - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (reqbufs->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_reqbufs(&ctx->vq_src, reqbufs); - if (ret != 0) { - vpu_err("error in vb2_reqbufs() for E(S)\n"); - goto out; - } - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - if (ret != 0) { - vpu_err("error in vb2_reqbufs() for E(D)\n"); - goto out; - } - break; - - default: - vpu_err("invalid buf type\n"); - ret = -EINVAL; - goto out; - } - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_querybuf(&ctx->vq_dst, buf); - if (ret != 0) { - vpu_err("error in vb2_querybuf() for E(D)\n"); - goto out; - } - - buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_querybuf(&ctx->vq_src, buf); - if (ret != 0) { - vpu_err("error in vb2_querybuf() for E(S)\n"); - goto out; - } - break; - - default: - vpu_err("invalid buf type\n"); - ret = -EINVAL; - goto out; - } - -out: - vpu_debug_leave(); - - return ret; -} - -/* Queue a buffer */ -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - int i; - - vpu_debug_enter(); - - for (i = 0; i < buf->length; i++) - vpu_debug(4, "plane[%d]->length %d bytesused %d\n", - i, buf->m.planes[i].length, - buf->m.planes[i].bytesused); - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_qbuf(&ctx->vq_src, buf); - vpu_debug(4, "OUTPUT_MPLANE : vb2_qbuf return %d\n", ret); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_qbuf(&ctx->vq_dst, buf); - vpu_debug(4, "CAPTURE_MPLANE: vb2_qbuf return %d\n", ret); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -/* Dequeue a buffer */ -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -/* Export DMA buffer */ -static int vidioc_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (eb->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_expbuf(&ctx->vq_src, eb); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_expbuf(&ctx->vq_dst, eb); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -/* Stream on */ -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_streamon(&ctx->vq_src, type); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_streamon(&ctx->vq_dst, type); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -/* Stream off, which equals to a pause */ -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_streamoff(&ctx->vq_src, type); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_streamoff(&ctx->vq_dst, type); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static void rk3288_vpu_dec_set_dpb(struct rk3288_vpu_ctx *ctx, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl_h264_decode_param *dec_param = ctrl->p_new.p; - const struct v4l2_h264_dpb_entry *new_dpb_entry; - u8 *dpb_map = ctx->run.h264d.dpb_map; - struct v4l2_h264_dpb_entry *cur_dpb_entry; - DECLARE_BITMAP(used, ARRAY_SIZE(ctx->run.h264d.dpb)) = { 0, }; - DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - int i, j; - - BUILD_BUG_ON(ARRAY_SIZE(ctx->run.h264d.dpb) != - ARRAY_SIZE(dec_param->dpb)); - - /* Disable all entries by default. */ - for (j = 0; j < ARRAY_SIZE(ctx->run.h264d.dpb); ++j) { - cur_dpb_entry = &ctx->run.h264d.dpb[j]; - - cur_dpb_entry->flags &= ~V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; - } - - /* Try to match new DPB entries with existing ones by their POCs. */ - for (i = 0; i < ARRAY_SIZE(dec_param->dpb); ++i) { - new_dpb_entry = &dec_param->dpb[i]; - - if (!(new_dpb_entry->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - continue; - - /* - * To cut off some comparisons, iterate only on target DPB - * entries which are not used yet. - */ - for_each_clear_bit(j, used, ARRAY_SIZE(ctx->run.h264d.dpb)) { - cur_dpb_entry = &ctx->run.h264d.dpb[j]; - - if (new_dpb_entry->top_field_order_cnt == - cur_dpb_entry->top_field_order_cnt && - new_dpb_entry->bottom_field_order_cnt == - cur_dpb_entry->bottom_field_order_cnt) { - memcpy(cur_dpb_entry, new_dpb_entry, - sizeof(*cur_dpb_entry)); - set_bit(j, used); - dpb_map[i] = j; - break; - } - } - - if (j == ARRAY_SIZE(ctx->run.h264d.dpb)) - set_bit(i, new); - } - - /* For entries that could not be matched, use remaining free slots. */ - for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { - new_dpb_entry = &dec_param->dpb[i]; - - j = find_first_zero_bit(used, ARRAY_SIZE(ctx->run.h264d.dpb)); - /* - * Both arrays are of the same sizes, so there is no way - * we can end up with no space in target array, unless - * something is buggy. - */ - if (WARN_ON(j >= ARRAY_SIZE(ctx->run.h264d.dpb))) - return; - - cur_dpb_entry = &ctx->run.h264d.dpb[j]; - memcpy(cur_dpb_entry, new_dpb_entry, sizeof(*cur_dpb_entry)); - set_bit(j, used); - dpb_map[i] = j; - } - - /* - * Verify that reference picture lists are in range, since they - * will be indexing dpb_map[] when programming the hardware. - * - * Fallback to 0 should be safe, as we will get at most corrupt - * decoding result, without any serious side effects. Moreover, - * even if entry 0 is unused, the hardware programming code will - * handle this properly. - */ - for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_b0); ++i) - if (dec_param->ref_pic_list_b0[i] - >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) - dec_param->ref_pic_list_b0[i] = 0; - for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_b1); ++i) - if (dec_param->ref_pic_list_b1[i] - >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) - dec_param->ref_pic_list_b1[i] = 0; - for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_p0); ++i) - if (dec_param->ref_pic_list_p0[i] - >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) - dec_param->ref_pic_list_p0[i] = 0; -} - -static int rk3288_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); - struct rk3288_vpu_dev *dev = ctx->dev; - int ret = 0; - - vpu_debug_enter(); - - vpu_debug(4, "ctrl id %d\n", ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_H264_SPS: - case V4L2_CID_MPEG_VIDEO_H264_PPS: - case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: - case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM: - case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR: - /* These controls are used directly. */ - break; - - case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM: - if (ctrl->store) - break; - rk3288_vpu_dec_set_dpb(ctx, ctrl); - break; - - default: - v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static const struct v4l2_ctrl_ops rk3288_vpu_dec_ctrl_ops = { - .s_ctrl = rk3288_vpu_dec_s_ctrl, -}; - -static const struct v4l2_ioctl_ops rk3288_vpu_dec_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .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_g_fmt_vid_cap_mplane = vidioc_g_fmt, - .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, - .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, - .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_expbuf = vidioc_expbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, -}; - -static int rk3288_vpu_queue_setup(struct vb2_queue *vq, - const void *parg, - unsigned int *buf_count, - unsigned int *plane_count, - unsigned int psize[], void *allocators[]) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - int ret = 0; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - *plane_count = ctx->vpu_src_fmt->num_planes; - - if (*buf_count < 1) - *buf_count = 1; - - if (*buf_count > VIDEO_MAX_FRAME) - *buf_count = VIDEO_MAX_FRAME; - - psize[0] = ctx->src_fmt.plane_fmt[0].sizeimage; - allocators[0] = ctx->dev->alloc_ctx; - vpu_debug(0, "output psize[%d]: %d\n", 0, psize[0]); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - *plane_count = ctx->vpu_dst_fmt->num_planes; - - if (*buf_count < 1) - *buf_count = 1; - - if (*buf_count > VIDEO_MAX_FRAME) - *buf_count = VIDEO_MAX_FRAME; - - psize[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8); - allocators[0] = ctx->dev->alloc_ctx; - - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) - /* Add space for appended motion vectors. */ - psize[0] += 64 * MB_WIDTH(ctx->dst_fmt.width) - * MB_HEIGHT(ctx->dst_fmt.height); - - vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]); - break; - - default: - vpu_err("invalid queue type: %d\n", vq->type); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_buf_init(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - - vpu_debug_enter(); - - if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - ctx->dst_bufs[vb->index] = vb; - - vpu_debug_leave(); - - return 0; -} - -static void rk3288_vpu_buf_cleanup(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - - vpu_debug_enter(); - - if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - ctx->dst_bufs[vb->index] = NULL; - - vpu_debug_leave(); -} - -static int rk3288_vpu_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - int ret = 0; - int i; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_debug(4, "plane size: %ld, dst size: %d\n", - vb2_plane_size(vb, 0), - ctx->src_fmt.plane_fmt[0].sizeimage); - - if (vb2_plane_size(vb, 0) - < ctx->src_fmt.plane_fmt[0].sizeimage) { - vpu_err("plane size is too small for output\n"); - ret = -EINVAL; - } - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) { - vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i, - vb2_plane_size(vb, i), - ctx->dst_fmt.plane_fmt[i].sizeimage); - - if (vb2_plane_size(vb, i) - < ctx->dst_fmt.plane_fmt[i].sizeimage) { - vpu_err("size of plane %d is too small for capture\n", - i); - break; - } - } - - if (i != ctx->vpu_dst_fmt->num_planes) - ret = -EINVAL; - break; - - default: - vpu_err("invalid queue type: %d\n", vq->type); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_start_streaming(struct vb2_queue *q, unsigned int count) -{ - int ret = 0; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - bool ready = false; - - vpu_debug_enter(); - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - ret = rk3288_vpu_init(ctx); - if (ret < 0) { - vpu_err("rk3288_vpu_init failed\n"); - return ret; - } - - ready = vb2_is_streaming(&ctx->vq_src); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ready = vb2_is_streaming(&ctx->vq_dst); - } - - if (ready) - rk3288_vpu_try_context(dev, ctx); - - vpu_debug_leave(); - - return 0; -} - -static void rk3288_vpu_stop_streaming(struct vb2_queue *q) -{ - unsigned long flags; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - struct rk3288_vpu_buf *b; - LIST_HEAD(queue); - int i; - - vpu_debug_enter(); - - spin_lock_irqsave(&dev->irqlock, flags); - - list_del_init(&ctx->list); - - switch (q->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - list_splice_init(&ctx->dst_queue, &queue); - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - list_splice_init(&ctx->src_queue, &queue); - break; - - default: - break; - } - - spin_unlock_irqrestore(&dev->irqlock, flags); - - wait_event(dev->run_wq, dev->current_ctx != ctx); - - while (!list_empty(&queue)) { - b = list_first_entry(&queue, struct rk3288_vpu_buf, list); - for (i = 0; i < b->vb.vb2_buf.num_planes; i++) - vb2_set_plane_payload(&b->vb.vb2_buf, i, 0); - vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); - list_del(&b->list); - } - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - rk3288_vpu_deinit(ctx); - - vpu_debug_leave(); -} - -static void rk3288_vpu_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - struct rk3288_vpu_buf *vpu_buf; - unsigned long flags; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_buf = vb_to_buf(vb); - - /* Mark destination as available for use by VPU */ - spin_lock_irqsave(&dev->irqlock, flags); - - list_add_tail(&vpu_buf->list, &ctx->dst_queue); - - spin_unlock_irqrestore(&dev->irqlock, flags); - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_buf = vb_to_buf(vb); - - spin_lock_irqsave(&dev->irqlock, flags); - - list_add_tail(&vpu_buf->list, &ctx->src_queue); - - spin_unlock_irqrestore(&dev->irqlock, flags); - break; - - default: - vpu_err("unsupported buffer type (%d)\n", vq->type); - } - - if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst)) - rk3288_vpu_try_context(dev, ctx); - - vpu_debug_leave(); -} - -static struct vb2_ops rk3288_vpu_dec_qops = { - .queue_setup = rk3288_vpu_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .buf_init = rk3288_vpu_buf_init, - .buf_prepare = rk3288_vpu_buf_prepare, - .buf_cleanup = rk3288_vpu_buf_cleanup, - .start_streaming = rk3288_vpu_start_streaming, - .stop_streaming = rk3288_vpu_stop_streaming, - .buf_queue = rk3288_vpu_buf_queue, -}; - -struct vb2_ops *get_dec_queue_ops(void) -{ - return &rk3288_vpu_dec_qops; -} - -const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void) -{ - return &rk3288_vpu_dec_ioctl_ops; -} - -static void rk3288_vpu_dec_prepare_run(struct rk3288_vpu_ctx *ctx) -{ - struct vb2_v4l2_buffer *src = to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); - - v4l2_ctrl_apply_store(&ctx->ctrl_handler, src->config_store); - - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) { - ctx->run.h264d.sps = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_H264_SPS); - ctx->run.h264d.pps = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_H264_PPS); - ctx->run.h264d.scaling_matrix = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX); - ctx->run.h264d.slice_param = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM); - ctx->run.h264d.decode_param = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM); - } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP8_FRAME) { - ctx->run.vp8d.frame_hdr = get_ctrl_ptr(ctx, - RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR); - } -} - -static void rk3288_vpu_dec_run_done(struct rk3288_vpu_ctx *ctx, - enum vb2_buffer_state result) -{ - struct v4l2_plane_pix_format *plane_fmts = ctx->dst_fmt.plane_fmt; - struct vb2_buffer *dst = &ctx->run.dst->vb.vb2_buf; - int i; - - if (result != VB2_BUF_STATE_DONE) { - /* Assume no payload after failed run. */ - for (i = 0; i < dst->num_planes; ++i) - vb2_set_plane_payload(dst, i, 0); - return; - } - - for (i = 0; i < dst->num_planes; ++i) - vb2_set_plane_payload(dst, i, plane_fmts[i].sizeimage); -} - -static const struct rk3288_vpu_run_ops rk3288_vpu_dec_run_ops = { - .prepare_run = rk3288_vpu_dec_prepare_run, - .run_done = rk3288_vpu_dec_run_done, -}; - -int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx) -{ - ctx->vpu_src_fmt = find_format(DEF_SRC_FMT_DEC, false); - ctx->vpu_dst_fmt = find_format(DEF_DST_FMT_DEC, true); - - ctx->run_ops = &rk3288_vpu_dec_run_ops; - - return rk3288_vpu_ctrls_setup(ctx, &rk3288_vpu_dec_ctrl_ops, - controls, ARRAY_SIZE(controls), NULL); -} - -void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx) -{ - rk3288_vpu_ctrls_delete(ctx); -} diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.h b/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.h deleted file mode 100644 index ac2598751083..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_dec.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 RK3288_VPU_DEC_H_ -#define RK3288_VPU_DEC_H_ - -struct vb2_ops *get_dec_queue_ops(void); -const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void); -struct rk3288_vpu_fmt *get_dec_def_fmt(bool src); -int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx); - -#endif /* RK3288_VPU_DEC_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.c deleted file mode 100644 index 0c9ce38d7553..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.c +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Rockchip Electronics Co., Ltd. - * Alpha Lin - * Jeffy Chen - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 "rk3288_vpu_common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rk3288_vpu_enc.h" -#include "rk3288_vpu_hw.h" - -#define DEF_SRC_FMT_ENC V4L2_PIX_FMT_NV12 -#define DEF_DST_FMT_ENC V4L2_PIX_FMT_VP8 - -#define RK3288_ENC_MIN_WIDTH 96U -#define RK3288_ENC_MAX_WIDTH 1920U -#define RK3288_ENC_MIN_HEIGHT 96U -#define RK3288_ENC_MAX_HEIGHT 1088U - -#define V4L2_CID_PRIVATE_RK3288_HEADER (V4L2_CID_CUSTOM_BASE + 0) -#define V4L2_CID_PRIVATE_RK3288_REG_PARAMS (V4L2_CID_CUSTOM_BASE + 1) -#define V4L2_CID_PRIVATE_RK3288_HW_PARAMS (V4L2_CID_CUSTOM_BASE + 2) -#define V4L2_CID_PRIVATE_RK3288_RET_PARAMS (V4L2_CID_CUSTOM_BASE + 3) - -static struct rk3288_vpu_fmt formats[] = { - /* Source formats. */ - { - .name = "4:2:0 3 planes Y/Cb/Cr", - .fourcc = V4L2_PIX_FMT_YUV420M, - .codec_mode = RK_VPU_CODEC_NONE, - .num_planes = 3, - .depth = { 8, 4, 4 }, - .enc_fmt = RK3288_VPU_ENC_FMT_YUV420P, - }, - { - .name = "4:2:0 2 plane Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV12M, - .codec_mode = RK_VPU_CODEC_NONE, - .num_planes = 2, - .depth = { 8, 8 }, - .enc_fmt = RK3288_VPU_ENC_FMT_YUV420SP, - }, - { - .name = "4:2:2 1 plane YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = RK_VPU_CODEC_NONE, - .num_planes = 1, - .depth = { 16 }, - .enc_fmt = RK3288_VPU_ENC_FMT_YUYV422, - }, - { - .name = "4:2:2 1 plane UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .codec_mode = RK_VPU_CODEC_NONE, - .num_planes = 1, - .depth = { 16 }, - .enc_fmt = RK3288_VPU_ENC_FMT_UYVY422, - }, - /* Destination formats. */ - { - .name = "VP8 Encoded Stream", - .fourcc = V4L2_PIX_FMT_VP8, - .codec_mode = RK_VPU_CODEC_VP8E, - .num_planes = 1, - }, -}; - -static struct rk3288_vpu_fmt *find_format(u32 fourcc, bool bitstream) -{ - unsigned int i; - - vpu_debug_enter(); - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].fourcc != fourcc) - continue; - if (bitstream && formats[i].codec_mode != RK_VPU_CODEC_NONE) - return &formats[i]; - if (!bitstream && formats[i].codec_mode == RK_VPU_CODEC_NONE) - return &formats[i]; - } - - return NULL; -} - -/* - * Indices of controls that need to be accessed directly, i.e. through - * p_cur.p pointer of their v4l2_ctrl structs. - */ -enum { - RK3288_VPU_ENC_CTRL_HEADER, - RK3288_VPU_ENC_CTRL_REG_PARAMS, - RK3288_VPU_ENC_CTRL_HW_PARAMS, - RK3288_VPU_ENC_CTRL_RET_PARAMS, -}; - -static struct rk3288_vpu_control controls[] = { - /* Private, per-frame controls. */ - [RK3288_VPU_ENC_CTRL_HEADER] = { - .id = V4L2_CID_PRIVATE_RK3288_HEADER, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "Rk3288 Private Header", - .elem_size = RK3288_HEADER_SIZE, - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_ENC_CTRL_REG_PARAMS] = { - .id = V4L2_CID_PRIVATE_RK3288_REG_PARAMS, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "Rk3288 Private Reg Params", - .elem_size = sizeof(struct rk3288_vp8e_reg_params), - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_ENC_CTRL_HW_PARAMS] = { - .id = V4L2_CID_PRIVATE_RK3288_HW_PARAMS, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "Rk3288 Private Hw Params", - .elem_size = RK3288_HW_PARAMS_SIZE, - .max_stores = VIDEO_MAX_FRAME, - .can_store = true, - }, - [RK3288_VPU_ENC_CTRL_RET_PARAMS] = { - .id = V4L2_CID_PRIVATE_RK3288_RET_PARAMS, - .type = V4L2_CTRL_TYPE_PRIVATE, - .name = "Rk3288 Private Ret Params", - .is_volatile = true, - .is_read_only = true, - .max_stores = VIDEO_MAX_FRAME, - .elem_size = RK3288_RET_PARAMS_SIZE, - }, - /* Generic controls. (currently ignored) */ - { - .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 1, - .maximum = 150, - .step = 1, - .default_value = 30, - }, - { - .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_BITRATE, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 10000, - .maximum = 288000000, - .step = 1, - .default_value = 2097152, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, - .type = V4L2_CTRL_TYPE_MENU, - .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, - .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .menu_skip_mask = ~( - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) - ), - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, - .type = V4L2_CTRL_TYPE_MENU, - .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_1, - .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 51, - .step = 1, - .default_value = 30, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 51, - .step = 1, - .default_value = 18, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 288000, - .step = 1, - .default_value = 30000, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, - .type = V4L2_CTRL_TYPE_BUTTON, - }, - /* - * This hardware does not support features provided by controls - * below, but they are required for compatibility with certain - * userspace software. - */ - { - .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Rate Control Reaction Coeff.", - .minimum = 1, - .maximum = (1 << 16) - 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, - .type = V4L2_CTRL_TYPE_MENU, - .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, - .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, - .menu_skip_mask = 0, - }, - { - .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Fixed Target Bit Enable", - .minimum = 0, - .maximum = 1, - .default_value = 0, - .step = 1, - .menu_skip_mask = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 2, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 51, - .step = 1, - .default_value = 1, - }, -}; - -static inline const void *get_ctrl_ptr(struct rk3288_vpu_ctx *ctx, unsigned id) -{ - struct v4l2_ctrl *ctrl = ctx->ctrls[id]; - - return ctrl->p_cur.p; -} - -static const char *const *rk3288_vpu_enc_get_menu(u32 id) -{ - static const char *const vpu_video_frame_skip[] = { - "Disabled", - "Level Limit", - "VBV/CPB Limit", - NULL, - }; - - switch (id) { - case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE: - return vpu_video_frame_skip; - } - - return NULL; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rk3288_vpu_dev *dev = video_drvdata(file); - - vpu_debug_enter(); - - strlcpy(cap->driver, RK3288_VPU_ENC_NAME, sizeof(cap->driver)); - strlcpy(cap->card, dev->pdev->name, sizeof(cap->card)); - strlcpy(cap->bus_info, "platform:" RK3288_VPU_NAME, - sizeof(cap->bus_info)); - - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - vpu_debug_leave(); - - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *prov, - struct v4l2_frmsizeenum *fsize) -{ - struct v4l2_frmsize_stepwise *s = &fsize->stepwise; - struct rk3288_vpu_fmt *fmt; - - if (fsize->index != 0) { - vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", - fsize->index); - return -EINVAL; - } - - fmt = find_format(fsize->pixel_format, true); - if (!fmt) { - vpu_debug(0, "unsupported bitstream format (%08x)\n", - fsize->pixel_format); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - - s->min_width = RK3288_ENC_MIN_WIDTH; - s->max_width = RK3288_ENC_MAX_WIDTH; - s->step_width = MB_DIM; - s->min_height = RK3288_ENC_MIN_HEIGHT; - s->max_height = RK3288_ENC_MAX_HEIGHT; - s->step_height = MB_DIM; - - return 0; -} - -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out) -{ - struct rk3288_vpu_fmt *fmt; - int i, j = 0; - - vpu_debug_enter(); - - for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (out && formats[i].codec_mode != RK_VPU_CODEC_NONE) - continue; - else if (!out && formats[i].codec_mode == RK_VPU_CODEC_NONE) - continue; - - if (j == f->index) { - fmt = &formats[i]; - strlcpy(f->description, fmt->name, - sizeof(f->description)); - f->pixelformat = fmt->fourcc; - - f->flags = 0; - if (formats[i].codec_mode != RK_VPU_CODEC_NONE) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; - - vpu_debug_leave(); - - return 0; - } - - ++j; - } - - vpu_debug_leave(); - - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, false); -} - -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, true); -} - -static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - - vpu_debug_enter(); - - vpu_debug(4, "f->type = %d\n", f->type); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - f->fmt.pix_mp = ctx->dst_fmt; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - f->fmt.pix_mp = ctx->src_fmt; - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - - vpu_debug_leave(); - - return 0; -} - -static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rk3288_vpu_fmt *fmt; - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - char str[5]; - - vpu_debug_enter(); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); - - fmt = find_format(pix_fmt_mp->pixelformat, true); - if (!fmt) { - vpu_err("failed to try capture format\n"); - return -EINVAL; - } - - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - vpu_err("must be set encoding output size\n"); - return -EINVAL; - } - - pix_fmt_mp->plane_fmt[0].bytesperline = 0; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); - - fmt = find_format(pix_fmt_mp->pixelformat, false); - if (!fmt) { - vpu_err("failed to try output format\n"); - return -EINVAL; - } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - vpu_err("plane number mismatches on output format\n"); - return -EINVAL; - } - - /* Limit to hardware min/max. */ - pix_fmt_mp->width = clamp(pix_fmt_mp->width, - RK3288_ENC_MIN_WIDTH, RK3288_ENC_MAX_WIDTH); - pix_fmt_mp->height = clamp(pix_fmt_mp->height, - RK3288_ENC_MIN_HEIGHT, RK3288_ENC_MAX_HEIGHT); - /* Round up to macroblocks. */ - pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM); - pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM); - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - - vpu_debug_leave(); - - return 0; -} - -static void calculate_plane_sizes(struct rk3288_vpu_fmt *fmt, - unsigned int w, unsigned int h, - struct v4l2_pix_format_mplane *pix_fmt_mp) -{ - int i; - - for (i = 0; i < fmt->num_planes; ++i) { - pix_fmt_mp->plane_fmt[i].bytesperline = w * fmt->depth[i] / 8; - pix_fmt_mp->plane_fmt[i].sizeimage = h * - pix_fmt_mp->plane_fmt[i].bytesperline; - /* - * All of multiplanar formats we support have chroma - * planes subsampled by 2 vertically. - */ - if (i != 0) - pix_fmt_mp->plane_fmt[i].sizeimage /= 2; - } -} - -static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - unsigned int mb_width, mb_height; - struct rk3288_vpu_fmt *fmt; - int ret = 0; - - vpu_debug_enter(); - - /* Change not allowed if any queue is streaming. */ - if (vb2_is_streaming(&ctx->vq_src) || vb2_is_streaming(&ctx->vq_dst)) { - ret = -EBUSY; - goto out; - } - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - /* - * Pixel format change is not allowed when the other queue has - * buffers allocated. - */ - if (vb2_is_busy(&ctx->vq_src) - && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) { - ret = -EBUSY; - goto out; - } - - ret = vidioc_try_fmt(file, priv, f); - if (ret) - goto out; - - ctx->vpu_dst_fmt = find_format(pix_fmt_mp->pixelformat, true); - ctx->dst_fmt = *pix_fmt_mp; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - /* - * Pixel format change is not allowed when the other queue has - * buffers allocated. - */ - if (vb2_is_busy(&ctx->vq_dst) - && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) { - ret = -EBUSY; - goto out; - } - - ret = vidioc_try_fmt(file, priv, f); - if (ret) - goto out; - - fmt = find_format(pix_fmt_mp->pixelformat, false); - ctx->vpu_src_fmt = fmt; - - mb_width = MB_WIDTH(pix_fmt_mp->width); - mb_height = MB_HEIGHT(pix_fmt_mp->height); - - vpu_debug(0, "OUTPUT codec mode: %d\n", fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", - pix_fmt_mp->width, pix_fmt_mp->height, - mb_width, mb_height); - - calculate_plane_sizes(fmt, mb_width * MB_DIM, - mb_height * MB_DIM, pix_fmt_mp); - - /* Reset crop rectangle. */ - ctx->src_crop.width = pix_fmt_mp->width; - ctx->src_crop.height = pix_fmt_mp->height; - - ctx->src_fmt = *pix_fmt_mp; - break; - - default: - vpu_err("invalid buf type\n"); - return -EINVAL; - } - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (reqbufs->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_debug(4, "\n"); - - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - if (ret != 0) { - vpu_err("error in vb2_reqbufs() for E(D)\n"); - goto out; - } - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_debug(4, "memory type %d\n", reqbufs->memory); - - ret = vb2_reqbufs(&ctx->vq_src, reqbufs); - if (ret != 0) { - vpu_err("error in vb2_reqbufs() for E(S)\n"); - goto out; - } - break; - - default: - vpu_err("invalid buf type\n"); - ret = -EINVAL; - goto out; - } - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_querybuf(&ctx->vq_dst, buf); - if (ret != 0) { - vpu_err("error in vb2_querybuf() for E(D)\n"); - goto out; - } - - buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE; - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_querybuf(&ctx->vq_src, buf); - if (ret != 0) { - vpu_err("error in vb2_querybuf() for E(S)\n"); - goto out; - } - break; - - default: - vpu_err("invalid buf type\n"); - ret = -EINVAL; - goto out; - } - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - int i; - - vpu_debug_enter(); - - for (i = 0; i < buf->length; i++) { - vpu_debug(4, "plane[%d]->length %d bytesused %d\n", - i, buf->m.planes[i].length, - buf->m.planes[i].bytesused); - } - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_qbuf(&ctx->vq_src, buf); - vpu_debug(4, "vb2_qbuf return %d\n", ret); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_qbuf(&ctx->vq_dst, buf); - vpu_debug(4, "vb2_qbuf return %d\n", ret); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (buf->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int vidioc_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (eb->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_expbuf(&ctx->vq_src, eb); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_expbuf(&ctx->vq_dst, eb); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_streamon(&ctx->vq_src, type); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_streamon(&ctx->vq_dst, type); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret; - - vpu_debug_enter(); - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - ret = vb2_streamoff(&ctx->vq_src, type); - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - ret = vb2_streamoff(&ctx->vq_dst, type); - break; - - default: - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); - struct rk3288_vpu_dev *dev = ctx->dev; - int ret = 0; - - vpu_debug_enter(); - - vpu_debug(4, "ctrl id %d\n", ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - case V4L2_CID_MPEG_VIDEO_BITRATE: - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: - case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: - case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: - case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF: - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: - /* Ignore these controls for now. (FIXME?) */ - break; - - case V4L2_CID_PRIVATE_RK3288_HEADER: - case V4L2_CID_PRIVATE_RK3288_REG_PARAMS: - case V4L2_CID_PRIVATE_RK3288_HW_PARAMS: - /* Nothing to do here. The control is used directly. */ - break; - - default: - v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_enc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); - struct rk3288_vpu_dev *dev = ctx->dev; - int ret = 0; - - vpu_debug_enter(); - - vpu_debug(4, "ctrl id %d\n", ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_PRIVATE_RK3288_RET_PARAMS: - memcpy(ctrl->p_new.p, ctx->run.priv_dst.cpu, - RK3288_RET_PARAMS_SIZE); - break; - - default: - v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", - ctrl->id, ctrl->val); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static const struct v4l2_ctrl_ops rk3288_vpu_enc_ctrl_ops = { - .s_ctrl = rk3288_vpu_enc_s_ctrl, - .g_volatile_ctrl = rk3288_vpu_enc_g_volatile_ctrl, -}; - -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cap) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *fmt = &ctx->src_fmt; - int ret = 0; - - vpu_debug_enter(); - - /* Crop only supported on source. */ - if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = -EINVAL; - goto out; - } - - cap->bounds.left = 0; - cap->bounds.top = 0; - cap->bounds.width = fmt->width; - cap->bounds.height = fmt->height; - cap->defrect = cap->bounds; - cap->pixelaspect.numerator = 1; - cap->pixelaspect.denominator = 1; - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - int ret = 0; - - vpu_debug_enter(); - - /* Crop only supported on source. */ - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = -EINVAL; - goto out; - } - - crop->c = ctx->src_crop; - -out: - vpu_debug_leave(); - - return ret; -} - -static int vidioc_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *fmt = &ctx->src_fmt; - const struct v4l2_rect *rect = &crop->c; - int ret = 0; - - vpu_debug_enter(); - - /* Crop only supported on source. */ - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = -EINVAL; - goto out; - } - - /* Change not allowed if the queue is streaming. */ - if (vb2_is_streaming(&ctx->vq_src)) { - ret = -EBUSY; - goto out; - } - - /* We do not support offsets. */ - if (rect->left != 0 || rect->top != 0) - goto fallback; - - /* We can crop only inside right- or bottom-most macroblocks. */ - if (round_up(rect->width, MB_DIM) != fmt->width - || round_up(rect->height, MB_DIM) != fmt->height) - goto fallback; - - /* We support widths aligned to 4 pixels and arbitrary heights. */ - ctx->src_crop.width = round_up(rect->width, 4); - ctx->src_crop.height = rect->height; - - vpu_debug_leave(); - - return 0; - -fallback: - /* Default to full frame for incorrect settings. */ - ctx->src_crop.width = fmt->width; - ctx->src_crop.height = fmt->height; - -out: - vpu_debug_leave(); - - return ret; -} - -static const struct v4l2_ioctl_ops rk3288_vpu_enc_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .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_g_fmt_vid_cap_mplane = vidioc_g_fmt, - .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, - .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, - .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_expbuf = vidioc_expbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_s_crop = vidioc_s_crop, -}; - -static int rk3288_vpu_queue_setup(struct vb2_queue *vq, - const void *parg, - unsigned int *buf_count, - unsigned int *plane_count, - unsigned int psize[], void *allocators[]) -{ - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - int ret = 0; - int i; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - *plane_count = ctx->vpu_dst_fmt->num_planes; - - if (*buf_count < 1) - *buf_count = 1; - - if (*buf_count > VIDEO_MAX_FRAME) - *buf_count = VIDEO_MAX_FRAME; - - psize[0] = ctx->dst_fmt.plane_fmt[0].sizeimage; - /* Kernel mapping necessary for bitstream post processing. */ - allocators[0] = ctx->dev->alloc_ctx_vm; - vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]); - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - *plane_count = ctx->vpu_src_fmt->num_planes; - - if (*buf_count < 1) - *buf_count = 1; - - if (*buf_count > VIDEO_MAX_FRAME) - *buf_count = VIDEO_MAX_FRAME; - - for (i = 0; i < ctx->vpu_src_fmt->num_planes; ++i) { - psize[i] = ctx->src_fmt.plane_fmt[i].sizeimage; - vpu_debug(0, "output psize[%d]: %d\n", i, psize[i]); - allocators[i] = ctx->dev->alloc_ctx; - } - break; - - default: - vpu_err("invalid queue type: %d\n", vq->type); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static int rk3288_vpu_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - int ret = 0; - int i; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_debug(4, "plane size: %ld, dst size: %d\n", - vb2_plane_size(vb, 0), - ctx->dst_fmt.plane_fmt[0].sizeimage); - - if (vb2_plane_size(vb, 0) - < ctx->dst_fmt.plane_fmt[0].sizeimage) { - vpu_err("plane size is too small for capture\n"); - ret = -EINVAL; - } - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - for (i = 0; i < ctx->vpu_src_fmt->num_planes; ++i) { - vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i, - vb2_plane_size(vb, i), - ctx->src_fmt.plane_fmt[i].sizeimage); - - if (vb2_plane_size(vb, i) - < ctx->src_fmt.plane_fmt[i].sizeimage) { - vpu_err("size of plane %d is too small for output\n", - i); - break; - } - } - - if (i != ctx->vpu_src_fmt->num_planes) - ret = -EINVAL; - break; - - default: - vpu_err("invalid queue type: %d\n", vq->type); - ret = -EINVAL; - } - - vpu_debug_leave(); - - return ret; -} - -static void rk3288_vpu_buf_finish(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - - vpu_debug_enter(); - - if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE - && vb->state == VB2_BUF_STATE_DONE - && ctx->vpu_dst_fmt->fourcc == V4L2_PIX_FMT_VP8) { - struct rk3288_vpu_buf *buf; - - buf = vb_to_buf(vb); - rk3288_vpu_vp8e_assemble_bitstream(ctx, buf); - } - - vpu_debug_leave(); -} - -static int rk3288_vpu_start_streaming(struct vb2_queue *q, unsigned int count) -{ - int ret = 0; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - bool ready = false; - - vpu_debug_enter(); - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - ret = rk3288_vpu_init(ctx); - if (ret < 0) { - vpu_err("rk3288_vpu_init failed\n"); - return ret; - } - - ready = vb2_is_streaming(&ctx->vq_src); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ready = vb2_is_streaming(&ctx->vq_dst); - } - - if (ready) - rk3288_vpu_try_context(dev, ctx); - - vpu_debug_leave(); - - return 0; -} - -static void rk3288_vpu_stop_streaming(struct vb2_queue *q) -{ - unsigned long flags; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - struct rk3288_vpu_buf *b; - LIST_HEAD(queue); - int i; - - vpu_debug_enter(); - - spin_lock_irqsave(&dev->irqlock, flags); - - list_del_init(&ctx->list); - - switch (q->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - list_splice_init(&ctx->dst_queue, &queue); - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - list_splice_init(&ctx->src_queue, &queue); - break; - - default: - break; - } - - spin_unlock_irqrestore(&dev->irqlock, flags); - - wait_event(dev->run_wq, dev->current_ctx != ctx); - - while (!list_empty(&queue)) { - b = list_first_entry(&queue, struct rk3288_vpu_buf, list); - for (i = 0; i < b->vb.vb2_buf.num_planes; i++) - vb2_set_plane_payload(&b->vb.vb2_buf, i, 0); - vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); - list_del(&b->list); - } - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - rk3288_vpu_deinit(ctx); - - vpu_debug_leave(); -} - -static void rk3288_vpu_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); - struct rk3288_vpu_dev *dev = ctx->dev; - struct rk3288_vpu_buf *vpu_buf; - unsigned long flags; - - vpu_debug_enter(); - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - vpu_buf = vb_to_buf(vb); - - /* Mark destination as available for use by VPU */ - spin_lock_irqsave(&dev->irqlock, flags); - - list_add_tail(&vpu_buf->list, &ctx->dst_queue); - - spin_unlock_irqrestore(&dev->irqlock, flags); - break; - - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - vpu_buf = vb_to_buf(vb); - - spin_lock_irqsave(&dev->irqlock, flags); - - list_add_tail(&vpu_buf->list, &ctx->src_queue); - - spin_unlock_irqrestore(&dev->irqlock, flags); - break; - - default: - vpu_err("unsupported buffer type (%d)\n", vq->type); - } - - if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst)) - rk3288_vpu_try_context(dev, ctx); - - vpu_debug_leave(); -} - -static struct vb2_ops rk3288_vpu_enc_qops = { - .queue_setup = rk3288_vpu_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .buf_prepare = rk3288_vpu_buf_prepare, - .buf_finish = rk3288_vpu_buf_finish, - .start_streaming = rk3288_vpu_start_streaming, - .stop_streaming = rk3288_vpu_stop_streaming, - .buf_queue = rk3288_vpu_buf_queue, -}; - -struct vb2_ops *get_enc_queue_ops(void) -{ - return &rk3288_vpu_enc_qops; -} - -const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void) -{ - return &rk3288_vpu_enc_ioctl_ops; -} - -static void rk3288_vpu_enc_prepare_run(struct rk3288_vpu_ctx *ctx) -{ - struct vb2_v4l2_buffer *vb2_src = to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); - unsigned config_store = vb2_src->config_store; - - v4l2_ctrl_apply_store(&ctx->ctrl_handler, config_store); - - memcpy(ctx->run.dst->vp8e.header, - get_ctrl_ptr(ctx, RK3288_VPU_ENC_CTRL_HEADER), - RK3288_HEADER_SIZE); - ctx->run.vp8e.reg_params = get_ctrl_ptr(ctx, - RK3288_VPU_ENC_CTRL_REG_PARAMS); - memcpy(ctx->run.priv_src.cpu, - get_ctrl_ptr(ctx, RK3288_VPU_ENC_CTRL_HW_PARAMS), - RK3288_HW_PARAMS_SIZE); -} - -static const struct rk3288_vpu_run_ops rk3288_vpu_enc_run_ops = { - .prepare_run = rk3288_vpu_enc_prepare_run, -}; - -int rk3288_vpu_enc_init(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *vpu = ctx->dev; - int ret; - - ctx->vpu_src_fmt = find_format(DEF_SRC_FMT_ENC, false); - ctx->vpu_dst_fmt = find_format(DEF_DST_FMT_ENC, true); - - ret = rk3288_vpu_aux_buf_alloc(vpu, &ctx->run.priv_src, - RK3288_HW_PARAMS_SIZE); - if (ret) { - vpu_err("Failed to allocate private source buffer.\n"); - return ret; - } - - - ret = rk3288_vpu_aux_buf_alloc(vpu, &ctx->run.priv_dst, - RK3288_RET_PARAMS_SIZE); - if (ret) { - vpu_err("Failed to allocate private destination buffer.\n"); - goto err_priv_src; - } - - ret = rk3288_vpu_ctrls_setup(ctx, &rk3288_vpu_enc_ctrl_ops, - controls, ARRAY_SIZE(controls), - rk3288_vpu_enc_get_menu); - if (ret) { - vpu_err("Failed to set up controls\n"); - goto err_priv_dst; - } - - ctx->run_ops = &rk3288_vpu_enc_run_ops; - - return 0; - -err_priv_dst: - rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_dst); -err_priv_src: - rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_src); - - return ret; -} - -void rk3288_vpu_enc_exit(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *vpu = ctx->dev; - - rk3288_vpu_ctrls_delete(ctx); - - rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_dst); - rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_src); -}; - -/* - * WAR for encoder state corruption after decoding - */ - -static const struct rk3288_vpu_run_ops dummy_encode_run_ops = { - /* No ops needed for dummy encoding. */ -}; - -#define DUMMY_W 64 -#define DUMMY_H 64 -#define DUMMY_SRC_FMT V4L2_PIX_FMT_YUYV -#define DUMMY_DST_FMT V4L2_PIX_FMT_VP8 -#define DUMMY_DST_SIZE (32 * 1024) - -int rk3288_vpu_enc_init_dummy_ctx(struct rk3288_vpu_dev *dev) -{ - struct rk3288_vpu_ctx *ctx; - int ret; - int i; - - ctx = devm_kzalloc(dev->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = dev; - - ctx->vpu_src_fmt = find_format(DUMMY_SRC_FMT, false); - ctx->src_fmt.width = DUMMY_W; - ctx->src_fmt.height = DUMMY_H; - ctx->src_fmt.pixelformat = ctx->vpu_src_fmt->fourcc; - ctx->src_fmt.num_planes = ctx->vpu_src_fmt->num_planes; - - calculate_plane_sizes(ctx->vpu_src_fmt, ctx->src_fmt.width, - ctx->src_fmt.height, &ctx->src_fmt); - - ctx->vpu_dst_fmt = find_format(DUMMY_DST_FMT, true); - ctx->dst_fmt.width = ctx->src_fmt.width; - ctx->dst_fmt.height = ctx->src_fmt.height; - ctx->dst_fmt.pixelformat = ctx->vpu_dst_fmt->fourcc; - ctx->dst_fmt.plane_fmt[0].sizeimage = DUMMY_DST_SIZE; - ctx->dst_fmt.plane_fmt[0].bytesperline = 0; - ctx->dst_fmt.num_planes = 1; - - INIT_LIST_HEAD(&ctx->src_queue); - - ctx->src_crop.left = 0; - ctx->src_crop.top = 0; - ctx->src_crop.width = ctx->src_fmt.width; - ctx->src_crop.left = ctx->src_fmt.height; - - INIT_LIST_HEAD(&ctx->dst_queue); - INIT_LIST_HEAD(&ctx->list); - - ctx->run.vp8e.reg_params = rk3288_vpu_vp8e_get_dummy_params(); - ctx->run_ops = &dummy_encode_run_ops; - - ctx->run.dst = devm_kzalloc(dev->dev, sizeof(*ctx->run.dst), - GFP_KERNEL); - if (!ctx->run.dst) - return -ENOMEM; - - ret = rk3288_vpu_aux_buf_alloc(dev, &ctx->run.priv_src, - RK3288_HW_PARAMS_SIZE); - if (ret) - return ret; - - ret = rk3288_vpu_aux_buf_alloc(dev, &ctx->run.priv_dst, - RK3288_RET_PARAMS_SIZE); - if (ret) - goto err_free_priv_src; - - for (i = 0; i < ctx->src_fmt.num_planes; ++i) { - ret = rk3288_vpu_aux_buf_alloc(dev, &dev->dummy_encode_src[i], - ctx->src_fmt.plane_fmt[i].sizeimage); - if (ret) - goto err_free_src; - - memset(dev->dummy_encode_src[i].cpu, 0, - dev->dummy_encode_src[i].size); - } - - ret = rk3288_vpu_aux_buf_alloc(dev, &dev->dummy_encode_dst, - ctx->dst_fmt.plane_fmt[0].sizeimage); - if (ret) - goto err_free_src; - - memset(dev->dummy_encode_dst.cpu, 0, dev->dummy_encode_dst.size); - - ret = rk3288_vpu_init(ctx); - if (ret) - goto err_free_dst; - - dev->dummy_encode_ctx = ctx; - - return 0; - -err_free_dst: - rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_dst); -err_free_src: - for (i = 0; i < ctx->src_fmt.num_planes; ++i) - if (dev->dummy_encode_src[i].cpu) - rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_src[i]); - rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_dst); -err_free_priv_src: - rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_src); - - return ret; -} - -void rk3288_vpu_enc_free_dummy_ctx(struct rk3288_vpu_dev *dev) -{ - struct rk3288_vpu_ctx *ctx = dev->dummy_encode_ctx; - int i; - - rk3288_vpu_deinit(ctx); - - for (i = 0; i < ctx->src_fmt.num_planes; ++i) - rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_src[i]); - rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_dst); - rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_src); - rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_dst); -} diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.h b/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.h deleted file mode 100644 index 4b1979d5d2ef..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_enc.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Alpha Lin - * Jeffy Chen - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 RK3288_VPU_ENC_H_ -#define RK3288_VPU_ENC_H_ - -struct vb2_ops *get_enc_queue_ops(void); -const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void); -struct rk3288_vpu_fmt *get_enc_def_fmt(bool src); -int rk3288_vpu_enc_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_enc_exit(struct rk3288_vpu_ctx *ctx); -int rk3288_vpu_enc_init_dummy_ctx(struct rk3288_vpu_dev *dev); -void rk3288_vpu_enc_free_dummy_ctx(struct rk3288_vpu_dev *dev); - -#endif /* RK3288_VPU_ENC_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.c deleted file mode 100644 index 531fc523647d..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 "rk3288_vpu_common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rk3288_vpu_regs.h" - -/** - * struct rk3288_vpu_variant - information about VPU hardware variant - * - * @hw_id: Top 16 bits (product ID) of hardware ID register. - * @enc_offset: Offset from VPU base to encoder registers. - * @enc_reg_num: Number of registers of encoder block. - * @dec_offset: Offset from VPU base to decoder registers. - * @dec_reg_num: Number of registers of decoder block. - */ -struct rk3288_vpu_variant { - u16 hw_id; - unsigned enc_offset; - unsigned enc_reg_num; - unsigned dec_offset; - unsigned dec_reg_num; -}; - -/* Supported VPU variants. */ -static const struct rk3288_vpu_variant rk3288_vpu_variants[] = { - { - .hw_id = 0x4831, - .enc_offset = 0x0, - .enc_reg_num = 164, - .dec_offset = 0x400, - .dec_reg_num = 60 + 41, - }, -}; - -/** - * struct rk3288_vpu_codec_ops - codec mode specific operations - * - * @init: Prepare for streaming. Called from VB2 .start_streaming() - * when streaming from both queues is being enabled. - * @exit: Clean-up after streaming. Called from VB2 .stop_streaming() - * when streaming from first of both enabled queues is being - * disabled. - * @run: Start single {en,de)coding run. Called from non-atomic context - * to indicate that a pair of buffers is ready and the hardware - * should be programmed and started. - * @done: Read back processing results and additional data from hardware. - * @reset: Reset the hardware in case of a timeout. - */ -struct rk3288_vpu_codec_ops { - int (*init)(struct rk3288_vpu_ctx *); - void (*exit)(struct rk3288_vpu_ctx *); - - void (*run)(struct rk3288_vpu_ctx *); - void (*done)(struct rk3288_vpu_ctx *, enum vb2_buffer_state); - void (*reset)(struct rk3288_vpu_ctx *); -}; - -/* - * Hardware control routines. - */ - -static int rk3288_vpu_identify(struct rk3288_vpu_dev *vpu) -{ - u32 hw_id; - int i; - - hw_id = readl(vpu->base) >> 16; - - dev_info(vpu->dev, "Read hardware ID: %x\n", hw_id); - - for (i = 0; i < ARRAY_SIZE(rk3288_vpu_variants); ++i) { - if (hw_id == rk3288_vpu_variants[i].hw_id) { - vpu->variant = &rk3288_vpu_variants[i]; - return 0; - } - } - - return -ENOENT; -} - -void rk3288_vpu_power_on(struct rk3288_vpu_dev *vpu) -{ - vpu_debug_enter(); - - /* TODO: Clock gating. */ - - pm_runtime_get_sync(vpu->dev); - - vpu_debug_leave(); -} - -static void rk3288_vpu_power_off(struct rk3288_vpu_dev *vpu) -{ - vpu_debug_enter(); - - pm_runtime_mark_last_busy(vpu->dev); - pm_runtime_put_autosuspend(vpu->dev); - - /* TODO: Clock gating. */ - - vpu_debug_leave(); -} - -/* - * Interrupt handlers. - */ - -static irqreturn_t vepu_irq(int irq, void *dev_id) -{ - struct rk3288_vpu_dev *vpu = dev_id; - u32 status = vepu_read(vpu, VEPU_REG_INTERRUPT); - - vepu_write(vpu, 0, VEPU_REG_INTERRUPT); - - if (status & VEPU_REG_INTERRUPT_BIT) { - struct rk3288_vpu_ctx *ctx = vpu->current_ctx; - - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - rk3288_vpu_power_off(vpu); - cancel_delayed_work(&vpu->watchdog_work); - - ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE); - } - - return IRQ_HANDLED; -} - -static irqreturn_t vdpu_irq(int irq, void *dev_id) -{ - struct rk3288_vpu_dev *vpu = dev_id; - u32 status = vdpu_read(vpu, VDPU_REG_INTERRUPT); - - vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); - - vpu_debug(3, "vdpu_irq status: %08x\n", status); - - if (status & VDPU_REG_INTERRUPT_DEC_IRQ) { - struct rk3288_vpu_ctx *ctx = vpu->current_ctx; - - vdpu_write(vpu, 0, VDPU_REG_CONFIG); - rk3288_vpu_power_off(vpu); - cancel_delayed_work(&vpu->watchdog_work); - - ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE); - } - - return IRQ_HANDLED; -} - -static void rk3288_vpu_watchdog(struct work_struct *work) -{ - struct rk3288_vpu_dev *vpu = container_of(to_delayed_work(work), - struct rk3288_vpu_dev, watchdog_work); - struct rk3288_vpu_ctx *ctx = vpu->current_ctx; - unsigned long flags; - - spin_lock_irqsave(&vpu->irqlock, flags); - - ctx->hw.codec_ops->reset(ctx); - - spin_unlock_irqrestore(&vpu->irqlock, flags); - - vpu_err("frame processing timed out!\n"); - - rk3288_vpu_power_off(vpu); - ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_ERROR); -} - -/* - * Initialization/clean-up. - */ - -#if defined(CONFIG_ROCKCHIP_IOMMU) -static int rk3288_vpu_iommu_init(struct rk3288_vpu_dev *vpu) -{ - int ret; - - vpu->dev->dma_parms = devm_kzalloc(vpu->dev, - sizeof(*vpu->dev->dma_parms), - GFP_KERNEL); - if (!vpu->dev->dma_parms) - return -ENOMEM; - - vpu->domain = iommu_domain_alloc(vpu->dev->bus); - if (!vpu->domain) - goto err_free_parms; - - ret = iommu_get_dma_cookie(vpu->domain); - if (ret) - goto err_free_domain; - - ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); - if (ret) - goto err_put_cookie; - - dma_set_max_seg_size(vpu->dev, DMA_BIT_MASK(32)); - - ret = iommu_attach_device(vpu->domain, vpu->dev); - if (ret) - goto err_put_cookie; - - common_iommu_setup_dma_ops(vpu->dev, 0x10000000, SZ_2G, - vpu->domain->ops); - - return 0; - -err_put_cookie: - iommu_put_dma_cookie(vpu->domain); -err_free_domain: - iommu_domain_free(vpu->domain); -err_free_parms: - return ret; -} - -static void rk3288_vpu_iommu_cleanup(struct rk3288_vpu_dev *vpu) -{ - iommu_detach_device(vpu->domain, vpu->dev); - iommu_put_dma_cookie(vpu->domain); - iommu_domain_free(vpu->domain); -} -#else /* CONFIG_ROCKCHIP_IOMMU */ -static inline int rk3288_vpu_iommu_init(struct rk3288_vpu_dev *vpu) -{ - return 0; -} - -static inline void rk3288_vpu_iommu_cleanup(struct rk3288_vpu_dev *vpu) { } -#endif /* CONFIG_ROCKCHIP_IOMMU */ - -int rk3288_vpu_hw_probe(struct rk3288_vpu_dev *vpu) -{ - struct resource *res; - int irq_enc, irq_dec; - int ret; - - pr_info("probe device %s\n", dev_name(vpu->dev)); - - INIT_DELAYED_WORK(&vpu->watchdog_work, rk3288_vpu_watchdog); - - vpu->aclk_vcodec = devm_clk_get(vpu->dev, "aclk_vcodec"); - if (IS_ERR(vpu->aclk_vcodec)) { - dev_err(vpu->dev, "failed to get aclk_vcodec\n"); - return PTR_ERR(vpu->aclk_vcodec); - } - - vpu->hclk_vcodec = devm_clk_get(vpu->dev, "hclk_vcodec"); - if (IS_ERR(vpu->hclk_vcodec)) { - dev_err(vpu->dev, "failed to get hclk_vcodec\n"); - return PTR_ERR(vpu->hclk_vcodec); - } - - /* - * Bump ACLK to max. possible freq. (400 MHz) to improve performance. - * - * VP8 encoding 1280x720@1.2Mbps 200 MHz: 39 fps, 400: MHz 77 fps - */ - clk_set_rate(vpu->aclk_vcodec, 400*1000*1000); - - res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); - vpu->base = devm_ioremap_resource(vpu->dev, res); - if (IS_ERR(vpu->base)) - return PTR_ERR(vpu->base); - - clk_prepare_enable(vpu->aclk_vcodec); - clk_prepare_enable(vpu->hclk_vcodec); - - ret = rk3288_vpu_identify(vpu); - if (ret < 0) { - dev_err(vpu->dev, "failed to identify hardware variant\n"); - goto err_power; - } - - vpu->enc_base = vpu->base + vpu->variant->enc_offset; - vpu->dec_base = vpu->base + vpu->variant->dec_offset; - - ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(vpu->dev, "could not set DMA coherent mask\n"); - goto err_power; - } - - ret = rk3288_vpu_iommu_init(vpu); - if (ret) - goto err_power; - - irq_enc = platform_get_irq_byname(vpu->pdev, "vepu"); - if (irq_enc <= 0) { - dev_err(vpu->dev, "could not get vepu IRQ\n"); - ret = -ENXIO; - goto err_iommu; - } - - ret = devm_request_threaded_irq(vpu->dev, irq_enc, NULL, vepu_irq, - IRQF_ONESHOT, dev_name(vpu->dev), vpu); - if (ret) { - dev_err(vpu->dev, "could not request vepu IRQ\n"); - goto err_iommu; - } - - irq_dec = platform_get_irq_byname(vpu->pdev, "vdpu"); - if (irq_dec <= 0) { - dev_err(vpu->dev, "could not get vdpu IRQ\n"); - ret = -ENXIO; - goto err_iommu; - } - - ret = devm_request_threaded_irq(vpu->dev, irq_dec, NULL, vdpu_irq, - IRQF_ONESHOT, dev_name(vpu->dev), vpu); - if (ret) { - dev_err(vpu->dev, "could not request vdpu IRQ\n"); - goto err_iommu; - } - - pm_runtime_set_autosuspend_delay(vpu->dev, 100); - pm_runtime_use_autosuspend(vpu->dev); - pm_runtime_enable(vpu->dev); - - return 0; - -err_iommu: - rk3288_vpu_iommu_cleanup(vpu); -err_power: - clk_disable_unprepare(vpu->hclk_vcodec); - clk_disable_unprepare(vpu->aclk_vcodec); - - return ret; -} - -void rk3288_vpu_hw_remove(struct rk3288_vpu_dev *vpu) -{ - rk3288_vpu_iommu_cleanup(vpu); - - pm_runtime_disable(vpu->dev); - - clk_disable_unprepare(vpu->hclk_vcodec); - clk_disable_unprepare(vpu->aclk_vcodec); -} - -static void rk3288_vpu_enc_reset(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *vpu = ctx->dev; - - vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT); - vepu_write(vpu, 0, VEPU_REG_ENC_CTRL); - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); -} - -static void rk3288_vpu_dec_reset(struct rk3288_vpu_ctx *ctx) -{ - struct rk3288_vpu_dev *vpu = ctx->dev; - - vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); - vdpu_write(vpu, 0, VDPU_REG_CONFIG); -} - -static const struct rk3288_vpu_codec_ops mode_ops[] = { - [RK_VPU_CODEC_VP8E] = { - .init = rk3288_vpu_vp8e_init, - .exit = rk3288_vpu_vp8e_exit, - .run = rk3288_vpu_vp8e_run, - .done = rk3288_vpu_vp8e_done, - .reset = rk3288_vpu_enc_reset, - }, - [RK_VPU_CODEC_VP8D] = { - .init = rk3288_vpu_vp8d_init, - .exit = rk3288_vpu_vp8d_exit, - .run = rk3288_vpu_vp8d_run, - .done = rk3288_vpu_run_done, - .reset = rk3288_vpu_dec_reset, - }, - [RK_VPU_CODEC_H264D] = { - .init = rk3288_vpu_h264d_init, - .exit = rk3288_vpu_h264d_exit, - .run = rk3288_vpu_h264d_run, - .done = rk3288_vpu_run_done, - .reset = rk3288_vpu_dec_reset, - }, -}; - -void rk3288_vpu_run(struct rk3288_vpu_ctx *ctx) -{ - ctx->hw.codec_ops->run(ctx); -} - -int rk3288_vpu_init(struct rk3288_vpu_ctx *ctx) -{ - enum rk3288_vpu_codec_mode codec_mode; - - if (rk3288_vpu_ctx_is_encoder(ctx)) - codec_mode = ctx->vpu_dst_fmt->codec_mode; /* Encoder */ - else - codec_mode = ctx->vpu_src_fmt->codec_mode; /* Decoder */ - - ctx->hw.codec_ops = &mode_ops[codec_mode]; - - return ctx->hw.codec_ops->init(ctx); -} - -void rk3288_vpu_deinit(struct rk3288_vpu_ctx *ctx) -{ - ctx->hw.codec_ops->exit(ctx); -} diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.h b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.h deleted file mode 100644 index f8325536295b..000000000000 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 RK3288_VPU_HW_H_ -#define RK3288_VPU_HW_H_ - -#include - -#define RK3288_HEADER_SIZE 1280 -#define RK3288_HW_PARAMS_SIZE 5487 -#define RK3288_RET_PARAMS_SIZE 488 - -struct rk3288_vpu_dev; -struct rk3288_vpu_ctx; -struct rk3288_vpu_buf; - -struct rk3288_vpu_h264d_priv_tbl; - -/** - * enum rk3288_vpu_enc_fmt - source format ID for hardware registers. - */ -enum rk3288_vpu_enc_fmt { - RK3288_VPU_ENC_FMT_YUV420P = 0, - RK3288_VPU_ENC_FMT_YUV420SP = 1, - RK3288_VPU_ENC_FMT_YUYV422 = 2, - RK3288_VPU_ENC_FMT_UYVY422 = 3, -}; - -/** - * struct rk3288_vp8e_reg_params - low level encoding parameters - * TODO: Create abstract structures for more generic controls or just - * remove unused fields. - */ -struct rk3288_vp8e_reg_params { - u32 unused_00[5]; - u32 hdr_len; - u32 unused_18[8]; - u32 enc_ctrl; - u32 unused_3c; - u32 enc_ctrl0; - u32 enc_ctrl1; - u32 enc_ctrl2; - u32 enc_ctrl3; - u32 enc_ctrl5; - u32 enc_ctrl4; - u32 str_hdr_rem_msb; - u32 str_hdr_rem_lsb; - u32 unused_60; - u32 mad_ctrl; - u32 unused_68; - u32 qp_val[8]; - u32 bool_enc; - u32 vp8_ctrl0; - u32 rlc_ctrl; - u32 mb_ctrl; - u32 unused_9c[14]; - u32 rgb_yuv_coeff[2]; - u32 rgb_mask_msb; - u32 intra_area_ctrl; - u32 cir_intra_ctrl; - u32 unused_e8[2]; - u32 first_roi_area; - u32 second_roi_area; - u32 mvc_ctrl; - u32 unused_fc; - u32 intra_penalty[7]; - u32 unused_11c; - u32 seg_qp[24]; - u32 dmv_4p_1p_penalty[32]; - u32 dmv_qpel_penalty[32]; - u32 vp8_ctrl1; - u32 bit_cost_golden; - u32 loop_flt_delta[2]; -}; - -/** - * struct rk3288_vpu_aux_buf - auxiliary DMA buffer for hardware data - * @cpu: CPU pointer to the buffer. - * @dma: DMA address of the buffer. - * @size: Size of the buffer. - */ -struct rk3288_vpu_aux_buf { - void *cpu; - dma_addr_t dma; - size_t size; -}; - -/** - * struct rk3288_vpu_vp8e_hw_ctx - Context private data specific to codec mode. - * @ctrl_buf: VP8 control buffer. - * @ext_buf: VP8 ext data buffer. - * @mv_buf: VP8 motion vector buffer. - * @ref_rec_ptr: Bit flag for swapping ref and rec buffers every frame. - */ -struct rk3288_vpu_vp8e_hw_ctx { - struct rk3288_vpu_aux_buf ctrl_buf; - struct rk3288_vpu_aux_buf ext_buf; - struct rk3288_vpu_aux_buf mv_buf; - u8 ref_rec_ptr:1; -}; - -/** - * struct rk3288_vpu_vp8d_hw_ctx - Context private data of VP8 decoder. - * @segment_map: Segment map buffer. - * @prob_tbl: Probability table buffer. - */ -struct rk3288_vpu_vp8d_hw_ctx { - struct rk3288_vpu_aux_buf segment_map; - struct rk3288_vpu_aux_buf prob_tbl; -}; - -/** - * struct rk3288_vpu_h264d_hw_ctx - Per context data specific to H264 decoding. - * @priv_tbl: Private auxiliary buffer for hardware. - */ -struct rk3288_vpu_h264d_hw_ctx { - struct rk3288_vpu_aux_buf priv_tbl; -}; - -/** - * struct rk3288_vpu_hw_ctx - Context private data of hardware code. - * @codec_ops: Set of operations associated with current codec mode. - */ -struct rk3288_vpu_hw_ctx { - const struct rk3288_vpu_codec_ops *codec_ops; - - /* Specific for particular codec modes. */ - union { - struct rk3288_vpu_vp8e_hw_ctx vp8e; - struct rk3288_vpu_vp8d_hw_ctx vp8d; - struct rk3288_vpu_h264d_hw_ctx h264d; - /* Other modes will need different data. */ - }; -}; - -int rk3288_vpu_hw_probe(struct rk3288_vpu_dev *vpu); -void rk3288_vpu_hw_remove(struct rk3288_vpu_dev *vpu); - -int rk3288_vpu_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_deinit(struct rk3288_vpu_ctx *ctx); - -void rk3288_vpu_run(struct rk3288_vpu_ctx *ctx); - -/* Run ops for H264 decoder */ -int rk3288_vpu_h264d_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_h264d_exit(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_h264d_run(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_power_on(struct rk3288_vpu_dev *vpu); - -/* Run ops for VP8 decoder */ -int rk3288_vpu_vp8d_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_vp8d_exit(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_vp8d_run(struct rk3288_vpu_ctx *ctx); - -/* Run ops for VP8 encoder */ -int rk3288_vpu_vp8e_init(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_vp8e_exit(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_vp8e_run(struct rk3288_vpu_ctx *ctx); -void rk3288_vpu_vp8e_done(struct rk3288_vpu_ctx *ctx, - enum vb2_buffer_state result); -const struct rk3288_vp8e_reg_params *rk3288_vpu_vp8e_get_dummy_params(void); - -void rk3288_vpu_vp8e_assemble_bitstream(struct rk3288_vpu_ctx *ctx, - struct rk3288_vpu_buf *dst_buf); - -#endif /* RK3288_VPU_HW_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_h264d.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_h264d.c index 3569d9219f8c..3b4ef9504163 100644 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_h264d.c +++ b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_h264d.c @@ -18,12 +18,12 @@ * GNU General Public License for more details. */ -#include "rk3288_vpu_common.h" +#include "rockchip_vpu_common.h" #include #include -#include "rk3288_vpu_hw.h" +#include "rockchip_vpu_hw.h" #include "rk3288_vpu_regs.h" /* Max. number of DPB pictures supported by hardware. */ diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8d.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8d.c index bcb3cadf1861..91cbd86091f1 100644 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8d.c +++ b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8d.c @@ -17,9 +17,9 @@ * GNU General Public License for more details. */ -#include "rk3288_vpu_hw.h" +#include "rockchip_vpu_hw.h" #include "rk3288_vpu_regs.h" -#include "rk3288_vpu_common.h" +#include "rockchip_vpu_common.h" #define DEC_8190_ALIGN_MASK 0x07U diff --git a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8e.c b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8e.c index 5ba84204fed3..4a1271fa0526 100644 --- a/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8e.c +++ b/drivers/media/platform/rockchip-vpu/rk3288_vpu_hw_vp8e.c @@ -18,13 +18,13 @@ * GNU General Public License for more details. */ -#include "rk3288_vpu_common.h" +#include "rockchip_vpu_common.h" #include #include #include "rk3288_vpu_regs.h" -#include "rk3288_vpu_hw.h" +#include "rockchip_vpu_hw.h" /* Various parameters specific to VP8 encoder. */ #define VP8_CABAC_CTX_OFFSET 192 diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c new file mode 100644 index 000000000000..6c67d638cdad --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c @@ -0,0 +1,824 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "rockchip_vpu_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_vpu_dec.h" +#include "rockchip_vpu_enc.h" +#include "rockchip_vpu_hw.h" + +int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, + "Debug level - higher value produces more verbose messages"); + +/* + * DMA coherent helpers. + */ + +int rk3288_vpu_aux_buf_alloc(struct rk3288_vpu_dev *vpu, + struct rk3288_vpu_aux_buf *buf, size_t size) +{ + buf->cpu = dma_alloc_coherent(vpu->dev, size, &buf->dma, GFP_KERNEL); + if (!buf->cpu) + return -ENOMEM; + + buf->size = size; + return 0; +} + +void rk3288_vpu_aux_buf_free(struct rk3288_vpu_dev *vpu, + struct rk3288_vpu_aux_buf *buf) +{ + dma_free_coherent(vpu->dev, buf->size, buf->cpu, buf->dma); + + buf->cpu = NULL; + buf->dma = 0; + buf->size = 0; +} + +/* + * Context scheduling. + */ + +static void rk3288_vpu_prepare_run(struct rk3288_vpu_ctx *ctx) +{ + if (ctx->run_ops->prepare_run) + ctx->run_ops->prepare_run(ctx); +} + +static void __rk3288_vpu_dequeue_run_locked(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_buf *src, *dst; + + /* + * Since ctx was dequeued from ready_ctxs list, we know that it has + * at least one buffer in each queue. + */ + src = list_first_entry(&ctx->src_queue, struct rk3288_vpu_buf, list); + dst = list_first_entry(&ctx->dst_queue, struct rk3288_vpu_buf, list); + + list_del(&src->list); + list_del(&dst->list); + + ctx->run.src = src; + ctx->run.dst = dst; +} + +static struct rk3288_vpu_ctx * +rk3288_vpu_encode_after_decode_war(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *dev = ctx->dev; + + if (dev->was_decoding && rk3288_vpu_ctx_is_encoder(ctx)) + return dev->dummy_encode_ctx; + + return ctx; +} + +static void rk3288_vpu_try_run(struct rk3288_vpu_dev *dev) +{ + struct rk3288_vpu_ctx *ctx = NULL; + unsigned long flags; + + vpu_debug_enter(); + + spin_lock_irqsave(&dev->irqlock, flags); + + if (list_empty(&dev->ready_ctxs) || + test_bit(VPU_SUSPENDED, &dev->state)) + /* Nothing to do. */ + goto out; + + if (test_and_set_bit(VPU_RUNNING, &dev->state)) + /* + * The hardware is already running. We will pick another + * run after we get the notification in rk3288_vpu_run_done(). + */ + goto out; + + ctx = list_entry(dev->ready_ctxs.next, struct rk3288_vpu_ctx, list); + + /* + * WAR for corrupted hardware state when encoding directly after + * certain decoding runs. + * + * If previous context was decoding and currently picked one is + * encoding then we need to execute a dummy encode with proper + * settings to reinitialize certain internal hardware state. + */ + ctx = rk3288_vpu_encode_after_decode_war(ctx); + + if (!rk3288_vpu_ctx_is_dummy_encode(ctx)) { + list_del_init(&ctx->list); + __rk3288_vpu_dequeue_run_locked(ctx); + } + + dev->current_ctx = ctx; + dev->was_decoding = !rk3288_vpu_ctx_is_encoder(ctx); + +out: + spin_unlock_irqrestore(&dev->irqlock, flags); + + if (ctx) { + rk3288_vpu_prepare_run(ctx); + rk3288_vpu_run(ctx); + } + + vpu_debug_leave(); +} + +static void __rk3288_vpu_try_context_locked(struct rk3288_vpu_dev *dev, + struct rk3288_vpu_ctx *ctx) +{ + if (!list_empty(&ctx->list)) + /* Context already queued. */ + return; + + if (!list_empty(&ctx->dst_queue) && !list_empty(&ctx->src_queue)) + list_add_tail(&ctx->list, &dev->ready_ctxs); +} + +void rk3288_vpu_run_done(struct rk3288_vpu_ctx *ctx, + enum vb2_buffer_state result) +{ + struct rk3288_vpu_dev *dev = ctx->dev; + unsigned long flags; + + vpu_debug_enter(); + + if (ctx->run_ops->run_done) + ctx->run_ops->run_done(ctx, result); + + if (!rk3288_vpu_ctx_is_dummy_encode(ctx)) { + struct vb2_v4l2_buffer *src = + to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); + struct vb2_v4l2_buffer *dst = + to_vb2_v4l2_buffer(&ctx->run.dst->vb.vb2_buf); + + dst->timestamp = src->timestamp; + vb2_buffer_done(&ctx->run.src->vb.vb2_buf, result); + vb2_buffer_done(&ctx->run.dst->vb.vb2_buf, result); + } + + dev->current_ctx = NULL; + wake_up_all(&dev->run_wq); + + spin_lock_irqsave(&dev->irqlock, flags); + + __rk3288_vpu_try_context_locked(dev, ctx); + clear_bit(VPU_RUNNING, &dev->state); + + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* Try scheduling another run to see if we have anything left to do. */ + rk3288_vpu_try_run(dev); + + vpu_debug_leave(); +} + +void rk3288_vpu_try_context(struct rk3288_vpu_dev *dev, + struct rk3288_vpu_ctx *ctx) +{ + unsigned long flags; + + vpu_debug_enter(); + + spin_lock_irqsave(&dev->irqlock, flags); + + __rk3288_vpu_try_context_locked(dev, ctx); + + spin_unlock_irqrestore(&dev->irqlock, flags); + + rk3288_vpu_try_run(dev); + + vpu_debug_enter(); +} + +/* + * Control registration. + */ + +#define IS_VPU_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) && \ + V4L2_CTRL_DRIVER_PRIV(x)) + +int rk3288_vpu_ctrls_setup(struct rk3288_vpu_ctx *ctx, + const struct v4l2_ctrl_ops *ctrl_ops, + struct rk3288_vpu_control *controls, + unsigned num_ctrls, + const char *const *(*get_menu)(u32)) +{ + struct v4l2_ctrl_config cfg; + int i; + + if (num_ctrls > ARRAY_SIZE(ctx->ctrls)) { + vpu_err("context control array not large enough\n"); + return -ENOSPC; + } + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls); + if (ctx->ctrl_handler.error) { + vpu_err("v4l2_ctrl_handler_init failed\n"); + return ctx->ctrl_handler.error; + } + + for (i = 0; i < num_ctrls; i++) { + if (IS_VPU_PRIV(controls[i].id) + || controls[i].id >= V4L2_CID_CUSTOM_BASE + || controls[i].type == V4L2_CTRL_TYPE_PRIVATE) { + memset(&cfg, 0, sizeof(struct v4l2_ctrl_config)); + + cfg.ops = ctrl_ops; + cfg.id = controls[i].id; + cfg.min = controls[i].minimum; + cfg.max = controls[i].maximum; + cfg.max_stores = controls[i].max_stores; + cfg.def = controls[i].default_value; + cfg.name = controls[i].name; + cfg.type = controls[i].type; + cfg.elem_size = controls[i].elem_size; + memcpy(cfg.dims, controls[i].dims, sizeof(cfg.dims)); + + if (cfg.type == V4L2_CTRL_TYPE_MENU) { + cfg.menu_skip_mask = cfg.menu_skip_mask; + cfg.qmenu = get_menu(cfg.id); + } else { + cfg.step = controls[i].step; + } + + ctx->ctrls[i] = v4l2_ctrl_new_custom( + &ctx->ctrl_handler, &cfg, NULL); + } else { + if (controls[i].type == V4L2_CTRL_TYPE_MENU) { + ctx->ctrls[i] = + v4l2_ctrl_new_std_menu + (&ctx->ctrl_handler, + ctrl_ops, + controls[i].id, + controls[i].maximum, + 0, + controls[i]. + default_value); + } else { + ctx->ctrls[i] = + v4l2_ctrl_new_std(&ctx->ctrl_handler, + ctrl_ops, + controls[i].id, + controls[i].minimum, + controls[i].maximum, + controls[i].step, + controls[i]. + default_value); + } + } + + if (ctx->ctrl_handler.error) { + vpu_err("Adding control (%d) failed\n", i); + return ctx->ctrl_handler.error; + } + + if (controls[i].is_volatile && ctx->ctrls[i]) + ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (controls[i].is_read_only && ctx->ctrls[i]) + ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (controls[i].can_store && ctx->ctrls[i]) + ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_CAN_STORE; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + ctx->num_ctrls = num_ctrls; + return 0; +} + +void rk3288_vpu_ctrls_delete(struct rk3288_vpu_ctx *ctx) +{ + int i; + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + for (i = 0; i < ctx->num_ctrls; i++) + ctx->ctrls[i] = NULL; +} + +/* + * V4L2 file operations. + */ + +static int rk3288_vpu_open(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct rk3288_vpu_dev *dev = video_drvdata(filp); + struct rk3288_vpu_ctx *ctx = NULL; + struct vb2_queue *q; + int ret = 0; + + /* + * We do not need any extra locking here, because we operate only + * on local data here, except reading few fields from dev, which + * do not change through device's lifetime (which is guaranteed by + * reference on module from open()) and V4L2 internal objects (such + * as vdev and ctx->fh), which have proper locking done in respective + * helper functions used here. + */ + + vpu_debug_enter(); + + /* Allocate memory for context */ + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err_leave; + } + + v4l2_fh_init(&ctx->fh, video_devdata(filp)); + filp->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->dev = dev; + INIT_LIST_HEAD(&ctx->src_queue); + INIT_LIST_HEAD(&ctx->dst_queue); + INIT_LIST_HEAD(&ctx->list); + + if (vdev == dev->vfd_enc) { + /* only for encoder */ + ret = rk3288_vpu_enc_init(ctx); + if (ret) { + vpu_err("Failed to initialize encoder context\n"); + goto err_fh_free; + } + } else if (vdev == dev->vfd_dec) { + /* only for decoder */ + ret = rk3288_vpu_dec_init(ctx); + if (ret) { + vpu_err("Failed to initialize decoder context\n"); + goto err_fh_free; + } + } else { + ret = -ENOENT; + goto err_fh_free; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + /* Init videobuf2 queue for CAPTURE */ + q = &ctx->vq_dst; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->drv_priv = &ctx->fh; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->lock = &dev->vpu_mutex; + q->buf_struct_size = sizeof(struct rk3288_vpu_buf); + + if (vdev == dev->vfd_enc) { + q->ops = get_enc_queue_ops(); + } else if (vdev == dev->vfd_dec) { + q->ops = get_dec_queue_ops(); + q->use_dma_bidirectional = 1; + } + + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + + ret = vb2_queue_init(q); + if (ret) { + vpu_err("Failed to initialize videobuf2 queue(capture)\n"); + goto err_enc_dec_exit; + } + + /* Init videobuf2 queue for OUTPUT */ + q = &ctx->vq_src; + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + q->drv_priv = &ctx->fh; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->lock = &dev->vpu_mutex; + q->buf_struct_size = sizeof(struct rk3288_vpu_buf); + + if (vdev == dev->vfd_enc) + q->ops = get_enc_queue_ops(); + else if (vdev == dev->vfd_dec) + q->ops = get_dec_queue_ops(); + + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + + ret = vb2_queue_init(q); + if (ret) { + vpu_err("Failed to initialize videobuf2 queue(output)\n"); + goto err_vq_dst_release; + } + + vpu_debug_leave(); + + return 0; + +err_vq_dst_release: + vb2_queue_release(&ctx->vq_dst); +err_enc_dec_exit: + if (vdev == dev->vfd_enc) + rk3288_vpu_enc_exit(ctx); + else if (vdev == dev->vfd_dec) + rk3288_vpu_dec_exit(ctx); +err_fh_free: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); +err_leave: + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_release(struct file *filp) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); + struct video_device *vdev = video_devdata(filp); + struct rk3288_vpu_dev *dev = ctx->dev; + + /* + * No need for extra locking because this was the last reference + * to this file. + */ + + vpu_debug_enter(); + + /* + * vb2_queue_release() ensures that streaming is stopped, which + * in turn means that there are no frames still being processed + * by hardware. + */ + vb2_queue_release(&ctx->vq_src); + vb2_queue_release(&ctx->vq_dst); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + if (vdev == dev->vfd_enc) + rk3288_vpu_enc_exit(ctx); + else if (vdev == dev->vfd_dec) + rk3288_vpu_dec_exit(ctx); + + kfree(ctx); + + vpu_debug_leave(); + + return 0; +} + +static unsigned int rk3288_vpu_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); + struct vb2_queue *src_q, *dst_q; + struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; + unsigned int rc = 0; + unsigned long flags; + + vpu_debug_enter(); + + src_q = &ctx->vq_src; + dst_q = &ctx->vq_dst; + + /* + * There has to be at least one buffer queued on each queued_list, which + * means either in driver already or waiting for driver to claim it + * and start processing. + */ + if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) && + (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) { + vpu_debug(0, "src q streaming %d, dst q streaming %d, src list empty(%d), dst list empty(%d)\n", + src_q->streaming, dst_q->streaming, + list_empty(&src_q->queued_list), + list_empty(&dst_q->queued_list)); + return POLLERR; + } + + poll_wait(filp, &ctx->fh.wait, wait); + poll_wait(filp, &src_q->done_wq, wait); + poll_wait(filp, &dst_q->done_wq, wait); + + if (v4l2_event_pending(&ctx->fh)) + rc |= POLLPRI; + + spin_lock_irqsave(&src_q->done_lock, flags); + + if (!list_empty(&src_q->done_list)) + src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, + done_entry); + + if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE || + src_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLOUT | POLLWRNORM; + + spin_unlock_irqrestore(&src_q->done_lock, flags); + + spin_lock_irqsave(&dst_q->done_lock, flags); + + if (!list_empty(&dst_q->done_list)) + dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, + done_entry); + + if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE || + dst_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&dst_q->done_lock, flags); + + return rc; +} + +static int rk3288_vpu_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(filp->private_data); + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int ret; + + vpu_debug_enter(); + + if (offset < DST_QUEUE_OFF_BASE) { + vpu_debug(4, "mmaping source\n"); + + ret = vb2_mmap(&ctx->vq_src, vma); + } else { /* capture */ + vpu_debug(4, "mmaping destination\n"); + + vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); + ret = vb2_mmap(&ctx->vq_dst, vma); + } + + vpu_debug_leave(); + + return ret; +} + +static const struct v4l2_file_operations rk3288_vpu_fops = { + .owner = THIS_MODULE, + .open = rk3288_vpu_open, + .release = rk3288_vpu_release, + .poll = rk3288_vpu_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = rk3288_vpu_mmap, +}; + +/* + * Platform driver. + */ + +static int rk3288_vpu_probe(struct platform_device *pdev) +{ + struct rk3288_vpu_dev *vpu = NULL; + DEFINE_DMA_ATTRS(attrs_novm); + DEFINE_DMA_ATTRS(attrs_nohugepage); + struct video_device *vfd; + int ret = 0; + + vpu_debug_enter(); + + vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); + if (!vpu) + return -ENOMEM; + + vpu->dev = &pdev->dev; + vpu->pdev = pdev; + mutex_init(&vpu->vpu_mutex); + spin_lock_init(&vpu->irqlock); + INIT_LIST_HEAD(&vpu->ready_ctxs); + init_waitqueue_head(&vpu->run_wq); + + ret = rk3288_vpu_hw_probe(vpu); + if (ret) { + dev_err(&pdev->dev, "vcodec_hw_probe failed\n"); + goto err_hw_probe; + } + + /* + * We'll do mostly sequential access, so sacrifice TLB efficiency for + * faster allocation. + */ + dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs_novm); + + dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs_novm); + vpu->alloc_ctx = vb2_dma_contig_init_ctx_attrs(&pdev->dev, + &attrs_novm); + if (IS_ERR(vpu->alloc_ctx)) { + ret = PTR_ERR(vpu->alloc_ctx); + goto err_dma_contig; + } + + dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs_nohugepage); + vpu->alloc_ctx_vm = vb2_dma_contig_init_ctx_attrs(&pdev->dev, + &attrs_nohugepage); + if (IS_ERR(vpu->alloc_ctx_vm)) { + ret = PTR_ERR(vpu->alloc_ctx_vm); + goto err_dma_contig_vm; + } + + ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + goto err_v4l2_dev_reg; + } + + platform_set_drvdata(pdev, vpu); + + ret = rk3288_vpu_enc_init_dummy_ctx(vpu); + if (ret) { + dev_err(&pdev->dev, "Failed to create dummy encode context\n"); + goto err_dummy_enc; + } + + /* encoder */ + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto err_enc_alloc; + } + + vfd->fops = &rk3288_vpu_fops; + vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); + vfd->release = video_device_release; + vfd->lock = &vpu->vpu_mutex; + vfd->v4l2_dev = &vpu->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + snprintf(vfd->name, sizeof(vfd->name), "%s", RK3288_VPU_ENC_NAME); + vpu->vfd_enc = vfd; + + video_set_drvdata(vfd, vpu); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); + video_device_release(vfd); + goto err_enc_reg; + } + + v4l2_info(&vpu->v4l2_dev, + "Rockchip RK3288 VPU encoder registered as /vpu/video%d\n", + vfd->num); + + /* decoder */ + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto err_dec_alloc; + } + + vfd->fops = &rk3288_vpu_fops; + vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); + vfd->release = video_device_release; + vfd->lock = &vpu->vpu_mutex; + vfd->v4l2_dev = &vpu->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + snprintf(vfd->name, sizeof(vfd->name), "%s", RK3288_VPU_DEC_NAME); + vpu->vfd_dec = vfd; + + video_set_drvdata(vfd, vpu); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); + video_device_release(vfd); + goto err_dec_reg; + } + + v4l2_info(&vpu->v4l2_dev, + "Rockchip RK3288 VPU decoder registered as /vpu/video%d\n", + vfd->num); + + vpu_debug_leave(); + + return 0; + +err_dec_reg: + video_device_release(vpu->vfd_dec); +err_dec_alloc: + video_unregister_device(vpu->vfd_enc); +err_enc_reg: + video_device_release(vpu->vfd_enc); +err_enc_alloc: + rk3288_vpu_enc_free_dummy_ctx(vpu); +err_dummy_enc: + v4l2_device_unregister(&vpu->v4l2_dev); +err_v4l2_dev_reg: + vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm); +err_dma_contig_vm: + vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx); +err_dma_contig: + rk3288_vpu_hw_remove(vpu); +err_hw_probe: + pr_debug("%s-- with error\n", __func__); + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_remove(struct platform_device *pdev) +{ + struct rk3288_vpu_dev *vpu = platform_get_drvdata(pdev); + + vpu_debug_enter(); + + v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); + + /* + * We are safe here assuming that .remove() got called as + * a result of module removal, which guarantees that all + * contexts have been released. + */ + + video_unregister_device(vpu->vfd_dec); + video_unregister_device(vpu->vfd_enc); + rk3288_vpu_enc_free_dummy_ctx(vpu); + v4l2_device_unregister(&vpu->v4l2_dev); + vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm); + vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx); + rk3288_vpu_hw_remove(vpu); + + vpu_debug_leave(); + + return 0; +} + +static struct platform_device_id vpu_driver_ids[] = { + { .name = "rk3288-vpu", }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(platform, vpu_driver_ids); + +#ifdef CONFIG_OF +static const struct of_device_id of_rk3288_vpu_match[] = { + { .compatible = "rockchip,rk3288-vpu", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_rk3288_vpu_match); +#endif + +#ifdef CONFIG_PM_SLEEP +static int rk3288_vpu_suspend(struct device *dev) +{ + struct rk3288_vpu_dev *vpu = dev_get_drvdata(dev); + + set_bit(VPU_SUSPENDED, &vpu->state); + wait_event(vpu->run_wq, vpu->current_ctx == NULL); + + return 0; +} + +static int rk3288_vpu_resume(struct device *dev) +{ + struct rk3288_vpu_dev *vpu = dev_get_drvdata(dev); + + clear_bit(VPU_SUSPENDED, &vpu->state); + rk3288_vpu_try_run(vpu); + + return 0; +} +#endif + +static const struct dev_pm_ops rk3288_vpu_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rk3288_vpu_suspend, rk3288_vpu_resume) +}; + +static struct platform_driver rk3288_vpu_driver = { + .probe = rk3288_vpu_probe, + .remove = rk3288_vpu_remove, + .id_table = vpu_driver_ids, + .driver = { + .name = RK3288_VPU_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_rk3288_vpu_match), + .pm = &rk3288_vpu_pm_ops, + }, +}; +module_platform_driver(rk3288_vpu_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alpha Lin "); +MODULE_AUTHOR("Tomasz Figa "); +MODULE_DESCRIPTION("Rockchip VPU codec driver"); diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h new file mode 100644 index 000000000000..a7906efbe03f --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h @@ -0,0 +1,521 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 ROCKCHIP_VPU_COMMON_H_ +#define ROCKCHIP_VPU_COMMON_H_ + +/* Enable debugging by default for now. */ +#define DEBUG + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rockchip_vpu_hw.h" + +#define RK3288_VPU_NAME "rk3288-vpu" +#define RK3288_VPU_DEC_NAME "rk3288-vpu-dec" +#define RK3288_VPU_ENC_NAME "rk3288-vpu-enc" + +#define V4L2_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0x1000) + +#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2) + +#define RK3288_VPU_MAX_CTRLS 32 + +#define MB_DIM 16 +#define MB_WIDTH(x_size) DIV_ROUND_UP(x_size, MB_DIM) +#define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, MB_DIM) + +struct rk3288_vpu_variant; +struct rk3288_vpu_ctx; +struct rk3288_vpu_codec_ops; + +/** + * enum rk3288_vpu_codec_mode - codec operating mode. + * @RK_VPU_CODEC_NONE: No operating mode. Used for RAW video formats. + * @RK_VPU_CODEC_H264D: H264 decoder. + * @RK_VPU_CODEC_VP8D: VP8 decoder. + * @RK_VPU_CODEC_H264E: H264 encoder. + * @RK_VPU_CODEC_VP8E: VP8 encoder. + */ +enum rk3288_vpu_codec_mode { + RK_VPU_CODEC_NONE = -1, + RK_VPU_CODEC_H264D, + RK_VPU_CODEC_VP8D, + RK_VPU_CODEC_H264E, + RK_VPU_CODEC_VP8E +}; + +/** + * enum rk3288_vpu_plane - indices of planes inside a VB2 buffer. + * @PLANE_Y: Plane containing luminance data (also denoted as Y). + * @PLANE_CB_CR: Plane containing interleaved chrominance data (also + * denoted as CbCr). + * @PLANE_CB: Plane containing CB part of chrominance data. + * @PLANE_CR: Plane containing CR part of chrominance data. + */ +enum rk3288_vpu_plane { + PLANE_Y = 0, + PLANE_CB_CR = 1, + PLANE_CB = 1, + PLANE_CR = 2, +}; + +/** + * struct rk3288_vpu_vp8e_buf_data - mode-specific per-buffer data + * @dct_offset: Offset inside the buffer to DCT partition. + * @hdr_size: Size of header data in the buffer. + * @ext_hdr_size: Size of ext header data in the buffer. + * @dct_size: Size of DCT partition in the buffer. + * @header: Frame header to copy to destination buffer. + */ +struct rk3288_vpu_vp8e_buf_data { + size_t dct_offset; + size_t hdr_size; + size_t ext_hdr_size; + size_t dct_size; + u8 header[RK3288_HEADER_SIZE]; +}; + +/** + * struct rk3288_vpu_buf - Private data related to each VB2 buffer. + * @vb: Pointer to related VB2 buffer. + * @list: List head for queuing in buffer queue. + * @flags: Buffer state. See enum rk3288_vpu_buf_flags. + */ +struct rk3288_vpu_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + + /* Mode-specific data. */ + union { + struct rk3288_vpu_vp8e_buf_data vp8e; + }; +}; + +/** + * enum rk3288_vpu_state - bitwise flags indicating hardware state. + * @VPU_RUNNING: The hardware has been programmed for operation + * and is running at the moment. + * @VPU_SUSPENDED: System is entering sleep state and no more runs + * should be executed on hardware. + */ +enum rk3288_vpu_state { + VPU_RUNNING = BIT(0), + VPU_SUSPENDED = BIT(1), +}; + +/** + * struct rk3288_vpu_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_dec: Video device for decoder. + * @vfd_enc: Video device for encoder. + * @pdev: Pointer to VPU platform device. + * @dev: Pointer to device for convenient logging using + * dev_ macros. + * @alloc_ctx: VB2 allocator context + * (for allocations without kernel mapping). + * @alloc_ctx_vm: VB2 allocator context + * (for allocations with kernel mapping). + * @aclk_vcodec: Handle of ACLK clock. + * @hclk_vcodec: Handle of HCLK clock. + * @base: Mapped address of VPU registers. + * @enc_base: Mapped address of VPU encoder register for convenience. + * @dec_base: Mapped address of VPU decoder register for convenience. + * @mapping: DMA IOMMU mapping. + * @domain: DMA IOMMU domain. + * @vpu_mutex: Mutex to synchronize V4L2 calls. + * @irqlock: Spinlock to synchronize access to data structures + * shared with interrupt handlers. + * @state: Device state. + * @ready_ctxs: List of contexts ready to run. + * @variant: Hardware variant-specfic parameters. + * @current_ctx: Context being currently processed by hardware. + * @run_wq: Wait queue to wait for run completion. + * @watchdog_work: Delayed work for hardware timeout handling. + * @dummy_encode_ctx: Context used to run dummy frame encoding to initialize + * encoder hardware state. + * @dummy_encode_src: Source buffers used for dummy frame encoding. + * @dummy_encode_dst: Desintation buffer used for dummy frame encoding. + * @was_decoding: Indicates whether last run context was a decoder. + */ +struct rk3288_vpu_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_dec; + struct video_device *vfd_enc; + struct platform_device *pdev; + struct device *dev; + void *alloc_ctx; + void *alloc_ctx_vm; + struct clk *aclk_vcodec; + struct clk *hclk_vcodec; + void __iomem *base; + void __iomem *enc_base; + void __iomem *dec_base; + struct dma_iommu_mapping *mapping; + struct iommu_domain *domain; + + struct mutex vpu_mutex; /* video_device lock */ + spinlock_t irqlock; + unsigned long state; + struct list_head ready_ctxs; + const struct rk3288_vpu_variant *variant; + struct rk3288_vpu_ctx *current_ctx; + wait_queue_head_t run_wq; + struct delayed_work watchdog_work; + struct rk3288_vpu_ctx *dummy_encode_ctx; + struct rk3288_vpu_aux_buf dummy_encode_src[VIDEO_MAX_PLANES]; + struct rk3288_vpu_aux_buf dummy_encode_dst; + bool was_decoding; +}; + +/** + * struct rk3288_vpu_run_ops - per context operations on run data. + * @prepare_run: Called when the context was selected for running + * to prepare operating mode specific data. + * @run_done: Called when hardware completed the run to collect + * operating mode specific data from hardware and + * finalize the processing. + */ +struct rk3288_vpu_run_ops { + void (*prepare_run)(struct rk3288_vpu_ctx *); + void (*run_done)(struct rk3288_vpu_ctx *, enum vb2_buffer_state); +}; + +/** + * struct rk3288_vpu_vp8e_run - per-run data specific to VP8 encoding. + * @reg_params: Pointer to a buffer containing register values prepared + * by user space. + */ +struct rk3288_vpu_vp8e_run { + const struct rk3288_vp8e_reg_params *reg_params; +}; + +/** + * struct rk3288_vpu_vp8d_run - per-run data specific to VP8 decoding. + * @frame_hdr: Pointer to a buffer containing per-run frame data which + * is needed by setting vpu register. + */ +struct rk3288_vpu_vp8d_run { + const struct v4l2_ctrl_vp8_frame_hdr *frame_hdr; +}; + +/** + * struct rk3288_vpu_h264d_run - per-run data specific to H264 decoding. + * @sps: Pointer to a buffer containing H264 SPS. + * @pps: Pointer to a buffer containing H264 PPS. + * @scaling_matrix: Pointer to a buffer containing scaling matrix. + * @slice_param: Pointer to a buffer containing slice parameters array. + * @decode_param: Pointer to a buffer containing decode parameters. + * @dpb: Array of DPB entries reordered to keep POC order. + * @dpb_map: Map of indices used in ref_pic_list_* into indices to + * reordered DPB array. + */ +struct rk3288_vpu_h264d_run { + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + const struct v4l2_ctrl_h264_slice_param *slice_param; + const struct v4l2_ctrl_h264_decode_param *decode_param; + struct v4l2_h264_dpb_entry dpb[16]; + u8 dpb_map[16]; +}; + +/** + * struct rk3288_vpu_run - per-run data for hardware code. + * @src: Source buffer to be processed. + * @dst: Destination buffer to be processed. + * @priv_src: Hardware private source buffer. + * @priv_dst: Hardware private destination buffer. + */ +struct rk3288_vpu_run { + /* Generic for more than one operating mode. */ + struct rk3288_vpu_buf *src; + struct rk3288_vpu_buf *dst; + + struct rk3288_vpu_aux_buf priv_src; + struct rk3288_vpu_aux_buf priv_dst; + + /* Specific for particular operating modes. */ + union { + struct rk3288_vpu_vp8e_run vp8e; + struct rk3288_vpu_vp8d_run vp8d; + struct rk3288_vpu_h264d_run h264d; + /* Other modes will need different data. */ + }; +}; + +/** + * struct rk3288_vpu_ctx - Context (instance) private data. + * + * @dev: VPU driver data to which the context belongs. + * @fh: V4L2 file handler. + * + * @vpu_src_fmt: Descriptor of active source format. + * @src_fmt: V4L2 pixel format of active source format. + * @vpu_dst_fmt: Descriptor of active destination format. + * @dst_fmt: V4L2 pixel format of active destination format. + * + * @vq_src: Videobuf2 source queue. + * @src_queue: Internal source buffer queue. + * @src_crop: Configured source crop rectangle (encoder-only). + * @vq_dst: Videobuf2 destination queue + * @dst_queue: Internal destination buffer queue. + * @dst_bufs: Private buffers wrapping VB2 buffers (destination). + * + * @ctrls: Array containing pointer to registered controls. + * @ctrl_handler: Control handler used to register controls. + * @num_ctrls: Number of registered controls. + * + * @list: List head for queue of ready contexts. + * + * @run: Structure containing data about currently scheduled + * processing run. + * @run_ops: Set of operations related to currently scheduled run. + * @hw: Structure containing hardware-related context. + */ +struct rk3288_vpu_ctx { + struct rk3288_vpu_dev *dev; + struct v4l2_fh fh; + + /* Format info */ + struct rk3288_vpu_fmt *vpu_src_fmt; + struct v4l2_pix_format_mplane src_fmt; + struct rk3288_vpu_fmt *vpu_dst_fmt; + struct v4l2_pix_format_mplane dst_fmt; + + /* VB2 queue data */ + struct vb2_queue vq_src; + struct list_head src_queue; + struct v4l2_rect src_crop; + struct vb2_queue vq_dst; + struct list_head dst_queue; + struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME]; + + /* Controls */ + struct v4l2_ctrl *ctrls[RK3288_VPU_MAX_CTRLS]; + struct v4l2_ctrl_handler ctrl_handler; + unsigned num_ctrls; + + /* Various runtime data */ + struct list_head list; + + struct rk3288_vpu_run run; + const struct rk3288_vpu_run_ops *run_ops; + struct rk3288_vpu_hw_ctx hw; +}; + +/** + * struct rk3288_vpu_fmt - information about supported video formats. + * @name: Human readable name of the format. + * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. + * @codec_mode: Codec mode related to this format. See + * enum rk3288_vpu_codec_mode. + * @num_planes: Number of planes used by this format. + * @depth: Depth of each plane in bits per pixel. + * @enc_fmt: Format identifier for encoder registers. + */ +struct rk3288_vpu_fmt { + char *name; + u32 fourcc; + enum rk3288_vpu_codec_mode codec_mode; + int num_planes; + u8 depth[VIDEO_MAX_PLANES]; + enum rk3288_vpu_enc_fmt enc_fmt; +}; + +/** + * struct rk3288_vpu_control - information about controls to be registered. + * @id: Control ID. + * @type: Type of the control. + * @name: Human readable name of the control. + * @minimum: Minimum value of the control. + * @maximum: Maximum value of the control. + * @step: Control value increase step. + * @menu_skip_mask: Mask of invalid menu positions. + * @default_value: Initial value of the control. + * @max_stores: Maximum number of configration stores. + * @dims: Size of each dimension of compound control. + * @elem_size: Size of individual element of compound control. + * @is_volatile: Control is volatile. + * @is_read_only: Control is read-only. + * @can_store: Control uses configuration stores. + * + * See also struct v4l2_ctrl_config. + */ +struct rk3288_vpu_control { + u32 id; + + enum v4l2_ctrl_type type; + const char *name; + s32 minimum; + s32 maximum; + s32 step; + u32 menu_skip_mask; + s32 default_value; + s32 max_stores; + u32 dims[V4L2_CTRL_MAX_DIMS]; + u32 elem_size; + + bool is_volatile:1; + bool is_read_only:1; + bool can_store:1; +}; + +/* Logging helpers */ + +/** + * debug - Module parameter to control level of debugging messages. + * + * Level of debugging messages can be controlled by bits of module parameter + * called "debug". Meaning of particular bits is as follows: + * + * bit 0 - global information: mode, size, init, release + * bit 1 - each run start/result information + * bit 2 - contents of small controls from userspace + * bit 3 - contents of big controls from userspace + * bit 4 - detail fmt, ctrl, buffer q/dq information + * bit 5 - detail function enter/leave trace information + * bit 6 - register write/read information + */ +extern int debug; + +#define vpu_debug(level, fmt, args...) \ + do { \ + if (debug & BIT(level)) \ + pr_debug("%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + } while (0) + +#define vpu_debug_enter() vpu_debug(5, "enter\n") +#define vpu_debug_leave() vpu_debug(5, "leave\n") + +#define vpu_err(fmt, args...) \ + pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) + +static inline char *fmt2str(u32 fmt, char *str) +{ + char a = fmt & 0xFF; + char b = (fmt >> 8) & 0xFF; + char c = (fmt >> 16) & 0xFF; + char d = (fmt >> 24) & 0xFF; + + sprintf(str, "%c%c%c%c", a, b, c, d); + + return str; +} + +/* Structure access helpers. */ +static inline struct rk3288_vpu_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct rk3288_vpu_ctx, fh); +} + +static inline struct rk3288_vpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct rk3288_vpu_ctx, ctrl_handler); +} + +static inline struct rk3288_vpu_buf *vb_to_buf(struct vb2_buffer *vb) +{ + return container_of(to_vb2_v4l2_buffer(vb), struct rk3288_vpu_buf, vb); +} + +static inline bool rk3288_vpu_ctx_is_encoder(struct rk3288_vpu_ctx *ctx) +{ + return ctx->vpu_dst_fmt->codec_mode != RK_VPU_CODEC_NONE; +} + +static inline bool rk3288_vpu_ctx_is_dummy_encode(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *dev = ctx->dev; + + return ctx == dev->dummy_encode_ctx; +} + +int rk3288_vpu_ctrls_setup(struct rk3288_vpu_ctx *ctx, + const struct v4l2_ctrl_ops *ctrl_ops, + struct rk3288_vpu_control *controls, + unsigned num_ctrls, + const char *const *(*get_menu)(u32)); +void rk3288_vpu_ctrls_delete(struct rk3288_vpu_ctx *ctx); + +void rk3288_vpu_try_context(struct rk3288_vpu_dev *dev, + struct rk3288_vpu_ctx *ctx); + +void rk3288_vpu_run_done(struct rk3288_vpu_ctx *ctx, + enum vb2_buffer_state result); + +int rk3288_vpu_aux_buf_alloc(struct rk3288_vpu_dev *vpu, + struct rk3288_vpu_aux_buf *buf, size_t size); +void rk3288_vpu_aux_buf_free(struct rk3288_vpu_dev *vpu, + struct rk3288_vpu_aux_buf *buf); + +/* Register accessors. */ +static inline void vepu_write_relaxed(struct rk3288_vpu_dev *vpu, + u32 val, u32 reg) +{ + vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); + writel_relaxed(val, vpu->enc_base + reg); +} + +static inline void vepu_write(struct rk3288_vpu_dev *vpu, u32 val, u32 reg) +{ + vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); + writel(val, vpu->enc_base + reg); +} + +static inline u32 vepu_read(struct rk3288_vpu_dev *vpu, u32 reg) +{ + u32 val = readl(vpu->enc_base + reg); + + vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val); + return val; +} + +static inline void vdpu_write_relaxed(struct rk3288_vpu_dev *vpu, + u32 val, u32 reg) +{ + vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); + writel_relaxed(val, vpu->dec_base + reg); +} + +static inline void vdpu_write(struct rk3288_vpu_dev *vpu, u32 val, u32 reg) +{ + vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val); + writel(val, vpu->dec_base + reg); +} + +static inline u32 vdpu_read(struct rk3288_vpu_dev *vpu, u32 reg) +{ + u32 val = readl(vpu->dec_base + reg); + + vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val); + return val; +} + +#endif /* ROCKCHIP_VPU_COMMON_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c new file mode 100644 index 000000000000..31e4cabbf3b5 --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c @@ -0,0 +1,1153 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "rockchip_vpu_common.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "rockchip_vpu_dec.h" +#include "rockchip_vpu_hw.h" + +#define DEF_SRC_FMT_DEC V4L2_PIX_FMT_H264_SLICE +#define DEF_DST_FMT_DEC V4L2_PIX_FMT_NV12 + +#define RK3288_DEC_MIN_WIDTH 48U +#define RK3288_DEC_MAX_WIDTH 3840U +#define RK3288_DEC_MIN_HEIGHT 48U +#define RK3288_DEC_MAX_HEIGHT 2160U + +#define RK3288_H264_MAX_SLICES_PER_FRAME 16 + +static struct rk3288_vpu_fmt formats[] = { + { + .name = "4:2:0 1 plane Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = RK_VPU_CODEC_NONE, + .num_planes = 1, + .depth = { 12 }, + }, + { + .name = "Slices of H264 Encoded Stream", + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = RK_VPU_CODEC_H264D, + .num_planes = 1, + }, + { + .name = "Frames of VP8 Encoded Stream", + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = RK_VPU_CODEC_VP8D, + .num_planes = 1, + }, +}; + +static struct rk3288_vpu_fmt *find_format(u32 fourcc, bool bitstream) +{ + unsigned int i; + + vpu_debug_enter(); + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == fourcc && + !!bitstream == (formats[i].codec_mode != RK_VPU_CODEC_NONE)) + return &formats[i]; + } + + return NULL; +} + +/* Indices of controls that need to be accessed directly. */ +enum { + RK3288_VPU_DEC_CTRL_H264_SPS, + RK3288_VPU_DEC_CTRL_H264_PPS, + RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX, + RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM, + RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM, + RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR, +}; + +static struct rk3288_vpu_control controls[] = { + /* H264 slice-based interface. */ + [RK3288_VPU_DEC_CTRL_H264_SPS] = { + .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "H264 SPS Parameters", + .elem_size = sizeof(struct v4l2_ctrl_h264_sps), + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_DEC_CTRL_H264_PPS] = { + .id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "H264 PPS Parameters", + .elem_size = sizeof(struct v4l2_ctrl_h264_pps), + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX] = { + .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "H264 Scaling Matrix", + .elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix), + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM] = { + .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "H264 Slice Parameters", + .max_stores = VIDEO_MAX_FRAME, + .elem_size = sizeof(struct v4l2_ctrl_h264_slice_param), + .dims = { RK3288_H264_MAX_SLICES_PER_FRAME, }, + .can_store = true, + }, + [RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM] = { + .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "H264 Decode Parameters", + .max_stores = VIDEO_MAX_FRAME, + .elem_size = sizeof(struct v4l2_ctrl_h264_decode_param), + .can_store = true, + }, + [RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR] = { + .id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "VP8 Frame Header Parameters", + .max_stores = VIDEO_MAX_FRAME, + .elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr), + .can_store = true, + }, +}; + +static inline const void *get_ctrl_ptr(struct rk3288_vpu_ctx *ctx, unsigned id) +{ + struct v4l2_ctrl *ctrl = ctx->ctrls[id]; + + return ctrl->p_cur.p; +} + +/* Query capabilities of the device */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rk3288_vpu_dev *dev = video_drvdata(file); + + vpu_debug_enter(); + + strlcpy(cap->driver, RK3288_VPU_DEC_NAME, sizeof(cap->driver)); + strlcpy(cap->card, dev->pdev->name, sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:" RK3288_VPU_NAME, + sizeof(cap->bus_info)); + + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + vpu_debug_leave(); + + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *prov, + struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_frmsize_stepwise *s = &fsize->stepwise; + struct rk3288_vpu_fmt *fmt; + + if (fsize->index != 0) { + vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", + fsize->index); + return -EINVAL; + } + + fmt = find_format(fsize->pixel_format, true); + if (!fmt) { + vpu_debug(0, "unsupported bitstream format (%08x)\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + s->min_width = RK3288_DEC_MIN_WIDTH; + s->max_width = RK3288_DEC_MAX_WIDTH; + s->step_width = MB_DIM; + s->min_height = RK3288_DEC_MIN_HEIGHT; + s->max_height = RK3288_DEC_MAX_HEIGHT; + s->step_height = MB_DIM; + + return 0; +} + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out) +{ + struct rk3288_vpu_fmt *fmt; + int i, j = 0; + + vpu_debug_enter(); + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (out && formats[i].codec_mode == RK_VPU_CODEC_NONE) + continue; + else if (!out && (formats[i].codec_mode != RK_VPU_CODEC_NONE)) + continue; + + if (j == f->index) { + fmt = &formats[i]; + strlcpy(f->description, fmt->name, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + f->flags = 0; + if (formats[i].codec_mode != RK_VPU_CODEC_NONE) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + + vpu_debug_leave(); + + return 0; + } + + ++j; + } + + vpu_debug_leave(); + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, false); +} + +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, true); +} + +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + + vpu_debug_enter(); + + vpu_debug(4, "f->type = %d\n", f->type); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + f->fmt.pix_mp = ctx->dst_fmt; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + f->fmt.pix_mp = ctx->src_fmt; + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + + vpu_debug_leave(); + + return 0; +} + +static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rk3288_vpu_fmt *fmt; + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + char str[5]; + + vpu_debug_enter(); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); + + fmt = find_format(pix_fmt_mp->pixelformat, true); + if (!fmt) { + vpu_err("failed to try output format\n"); + return -EINVAL; + } + + if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { + vpu_err("sizeimage of output format must be given\n"); + return -EINVAL; + } + + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); + + fmt = find_format(pix_fmt_mp->pixelformat, false); + if (!fmt) { + vpu_err("failed to try capture format\n"); + return -EINVAL; + } + + if (fmt->num_planes != pix_fmt_mp->num_planes) { + vpu_err("plane number mismatches on capture format\n"); + return -EINVAL; + } + + /* Limit to hardware min/max. */ + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + RK3288_DEC_MIN_WIDTH, RK3288_DEC_MAX_WIDTH); + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + RK3288_DEC_MIN_HEIGHT, RK3288_DEC_MAX_HEIGHT); + + /* Round up to macroblocks. */ + pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM); + pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM); + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + + vpu_debug_leave(); + + return 0; +} + +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + unsigned int mb_width, mb_height; + struct rk3288_vpu_fmt *fmt; + int ret = 0; + int i; + + vpu_debug_enter(); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + /* Change not allowed if any queue is streaming. */ + if (vb2_is_streaming(&ctx->vq_src) + || vb2_is_streaming(&ctx->vq_dst)) { + ret = -EBUSY; + goto out; + } + /* + * Pixel format change is not allowed when the other queue has + * buffers allocated. + */ + if (vb2_is_busy(&ctx->vq_dst) + && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) { + ret = -EBUSY; + goto out; + } + + ret = vidioc_try_fmt(file, priv, f); + if (ret) + goto out; + + ctx->vpu_src_fmt = find_format(pix_fmt_mp->pixelformat, true); + ctx->src_fmt = *pix_fmt_mp; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + /* + * Change not allowed if this queue is streaming. + * + * NOTE: We allow changes with source queue streaming + * to support resolution change in decoded stream. + */ + if (vb2_is_streaming(&ctx->vq_dst)) { + ret = -EBUSY; + goto out; + } + /* + * Pixel format change is not allowed when the other queue has + * buffers allocated. + */ + if (vb2_is_busy(&ctx->vq_src) + && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) { + ret = -EBUSY; + goto out; + } + + ret = vidioc_try_fmt(file, priv, f); + if (ret) + goto out; + + fmt = find_format(pix_fmt_mp->pixelformat, false); + ctx->vpu_dst_fmt = fmt; + + mb_width = MB_WIDTH(pix_fmt_mp->width); + mb_height = MB_HEIGHT(pix_fmt_mp->height); + + vpu_debug(0, "CAPTURE codec mode: %d\n", fmt->codec_mode); + vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", + pix_fmt_mp->width, pix_fmt_mp->height, + mb_width, mb_height); + + for (i = 0; i < fmt->num_planes; ++i) { + pix_fmt_mp->plane_fmt[i].bytesperline = + mb_width * MB_DIM * fmt->depth[i] / 8; + pix_fmt_mp->plane_fmt[i].sizeimage = + pix_fmt_mp->plane_fmt[i].bytesperline + * mb_height * MB_DIM; + /* + * All of multiplanar formats we support have chroma + * planes subsampled by 2. + */ + if (i != 0) + pix_fmt_mp->plane_fmt[i].sizeimage /= 2; + } + + ctx->dst_fmt = *pix_fmt_mp; + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (reqbufs->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret != 0) { + vpu_err("error in vb2_reqbufs() for E(S)\n"); + goto out; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret != 0) { + vpu_err("error in vb2_reqbufs() for E(D)\n"); + goto out; + } + break; + + default: + vpu_err("invalid buf type\n"); + ret = -EINVAL; + goto out; + } + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_querybuf(&ctx->vq_dst, buf); + if (ret != 0) { + vpu_err("error in vb2_querybuf() for E(D)\n"); + goto out; + } + + buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_querybuf(&ctx->vq_src, buf); + if (ret != 0) { + vpu_err("error in vb2_querybuf() for E(S)\n"); + goto out; + } + break; + + default: + vpu_err("invalid buf type\n"); + ret = -EINVAL; + goto out; + } + +out: + vpu_debug_leave(); + + return ret; +} + +/* Queue a buffer */ +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + int i; + + vpu_debug_enter(); + + for (i = 0; i < buf->length; i++) + vpu_debug(4, "plane[%d]->length %d bytesused %d\n", + i, buf->m.planes[i].length, + buf->m.planes[i].bytesused); + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_qbuf(&ctx->vq_src, buf); + vpu_debug(4, "OUTPUT_MPLANE : vb2_qbuf return %d\n", ret); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_qbuf(&ctx->vq_dst, buf); + vpu_debug(4, "CAPTURE_MPLANE: vb2_qbuf return %d\n", ret); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +/* Dequeue a buffer */ +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +/* Export DMA buffer */ +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (eb->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_expbuf(&ctx->vq_src, eb); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_expbuf(&ctx->vq_dst, eb); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +/* Stream on */ +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_streamon(&ctx->vq_src, type); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_streamon(&ctx->vq_dst, type); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +/* Stream off, which equals to a pause */ +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_streamoff(&ctx->vq_src, type); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_streamoff(&ctx->vq_dst, type); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static void rk3288_vpu_dec_set_dpb(struct rk3288_vpu_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl_h264_decode_param *dec_param = ctrl->p_new.p; + const struct v4l2_h264_dpb_entry *new_dpb_entry; + u8 *dpb_map = ctx->run.h264d.dpb_map; + struct v4l2_h264_dpb_entry *cur_dpb_entry; + DECLARE_BITMAP(used, ARRAY_SIZE(ctx->run.h264d.dpb)) = { 0, }; + DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + int i, j; + + BUILD_BUG_ON(ARRAY_SIZE(ctx->run.h264d.dpb) != + ARRAY_SIZE(dec_param->dpb)); + + /* Disable all entries by default. */ + for (j = 0; j < ARRAY_SIZE(ctx->run.h264d.dpb); ++j) { + cur_dpb_entry = &ctx->run.h264d.dpb[j]; + + cur_dpb_entry->flags &= ~V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; + } + + /* Try to match new DPB entries with existing ones by their POCs. */ + for (i = 0; i < ARRAY_SIZE(dec_param->dpb); ++i) { + new_dpb_entry = &dec_param->dpb[i]; + + if (!(new_dpb_entry->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + /* + * To cut off some comparisons, iterate only on target DPB + * entries which are not used yet. + */ + for_each_clear_bit(j, used, ARRAY_SIZE(ctx->run.h264d.dpb)) { + cur_dpb_entry = &ctx->run.h264d.dpb[j]; + + if (new_dpb_entry->top_field_order_cnt == + cur_dpb_entry->top_field_order_cnt && + new_dpb_entry->bottom_field_order_cnt == + cur_dpb_entry->bottom_field_order_cnt) { + memcpy(cur_dpb_entry, new_dpb_entry, + sizeof(*cur_dpb_entry)); + set_bit(j, used); + dpb_map[i] = j; + break; + } + } + + if (j == ARRAY_SIZE(ctx->run.h264d.dpb)) + set_bit(i, new); + } + + /* For entries that could not be matched, use remaining free slots. */ + for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { + new_dpb_entry = &dec_param->dpb[i]; + + j = find_first_zero_bit(used, ARRAY_SIZE(ctx->run.h264d.dpb)); + /* + * Both arrays are of the same sizes, so there is no way + * we can end up with no space in target array, unless + * something is buggy. + */ + if (WARN_ON(j >= ARRAY_SIZE(ctx->run.h264d.dpb))) + return; + + cur_dpb_entry = &ctx->run.h264d.dpb[j]; + memcpy(cur_dpb_entry, new_dpb_entry, sizeof(*cur_dpb_entry)); + set_bit(j, used); + dpb_map[i] = j; + } + + /* + * Verify that reference picture lists are in range, since they + * will be indexing dpb_map[] when programming the hardware. + * + * Fallback to 0 should be safe, as we will get at most corrupt + * decoding result, without any serious side effects. Moreover, + * even if entry 0 is unused, the hardware programming code will + * handle this properly. + */ + for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_b0); ++i) + if (dec_param->ref_pic_list_b0[i] + >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) + dec_param->ref_pic_list_b0[i] = 0; + for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_b1); ++i) + if (dec_param->ref_pic_list_b1[i] + >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) + dec_param->ref_pic_list_b1[i] = 0; + for (i = 0; i < ARRAY_SIZE(dec_param->ref_pic_list_p0); ++i) + if (dec_param->ref_pic_list_p0[i] + >= ARRAY_SIZE(ctx->run.h264d.dpb_map)) + dec_param->ref_pic_list_p0[i] = 0; +} + +static int rk3288_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); + struct rk3288_vpu_dev *dev = ctx->dev; + int ret = 0; + + vpu_debug_enter(); + + vpu_debug(4, "ctrl id %d\n", ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_SPS: + case V4L2_CID_MPEG_VIDEO_H264_PPS: + case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: + case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM: + case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR: + /* These controls are used directly. */ + break; + + case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM: + if (ctrl->store) + break; + rk3288_vpu_dec_set_dpb(ctx, ctrl); + break; + + default: + v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static const struct v4l2_ctrl_ops rk3288_vpu_dec_ctrl_ops = { + .s_ctrl = rk3288_vpu_dec_s_ctrl, +}; + +static const struct v4l2_ioctl_ops rk3288_vpu_dec_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .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_g_fmt_vid_cap_mplane = vidioc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static int rk3288_vpu_queue_setup(struct vb2_queue *vq, + const void *parg, + unsigned int *buf_count, + unsigned int *plane_count, + unsigned int psize[], void *allocators[]) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + int ret = 0; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *plane_count = ctx->vpu_src_fmt->num_planes; + + if (*buf_count < 1) + *buf_count = 1; + + if (*buf_count > VIDEO_MAX_FRAME) + *buf_count = VIDEO_MAX_FRAME; + + psize[0] = ctx->src_fmt.plane_fmt[0].sizeimage; + allocators[0] = ctx->dev->alloc_ctx; + vpu_debug(0, "output psize[%d]: %d\n", 0, psize[0]); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *plane_count = ctx->vpu_dst_fmt->num_planes; + + if (*buf_count < 1) + *buf_count = 1; + + if (*buf_count > VIDEO_MAX_FRAME) + *buf_count = VIDEO_MAX_FRAME; + + psize[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8); + allocators[0] = ctx->dev->alloc_ctx; + + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) + /* Add space for appended motion vectors. */ + psize[0] += 64 * MB_WIDTH(ctx->dst_fmt.width) + * MB_HEIGHT(ctx->dst_fmt.height); + + vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]); + break; + + default: + vpu_err("invalid queue type: %d\n", vq->type); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + + vpu_debug_enter(); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ctx->dst_bufs[vb->index] = vb; + + vpu_debug_leave(); + + return 0; +} + +static void rk3288_vpu_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + + vpu_debug_enter(); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ctx->dst_bufs[vb->index] = NULL; + + vpu_debug_leave(); +} + +static int rk3288_vpu_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + int ret = 0; + int i; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_debug(4, "plane size: %ld, dst size: %d\n", + vb2_plane_size(vb, 0), + ctx->src_fmt.plane_fmt[0].sizeimage); + + if (vb2_plane_size(vb, 0) + < ctx->src_fmt.plane_fmt[0].sizeimage) { + vpu_err("plane size is too small for output\n"); + ret = -EINVAL; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) { + vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i, + vb2_plane_size(vb, i), + ctx->dst_fmt.plane_fmt[i].sizeimage); + + if (vb2_plane_size(vb, i) + < ctx->dst_fmt.plane_fmt[i].sizeimage) { + vpu_err("size of plane %d is too small for capture\n", + i); + break; + } + } + + if (i != ctx->vpu_dst_fmt->num_planes) + ret = -EINVAL; + break; + + default: + vpu_err("invalid queue type: %d\n", vq->type); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret = 0; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + bool ready = false; + + vpu_debug_enter(); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = rk3288_vpu_init(ctx); + if (ret < 0) { + vpu_err("rk3288_vpu_init failed\n"); + return ret; + } + + ready = vb2_is_streaming(&ctx->vq_src); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ready = vb2_is_streaming(&ctx->vq_dst); + } + + if (ready) + rk3288_vpu_try_context(dev, ctx); + + vpu_debug_leave(); + + return 0; +} + +static void rk3288_vpu_stop_streaming(struct vb2_queue *q) +{ + unsigned long flags; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + struct rk3288_vpu_buf *b; + LIST_HEAD(queue); + int i; + + vpu_debug_enter(); + + spin_lock_irqsave(&dev->irqlock, flags); + + list_del_init(&ctx->list); + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + list_splice_init(&ctx->dst_queue, &queue); + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + list_splice_init(&ctx->src_queue, &queue); + break; + + default: + break; + } + + spin_unlock_irqrestore(&dev->irqlock, flags); + + wait_event(dev->run_wq, dev->current_ctx != ctx); + + while (!list_empty(&queue)) { + b = list_first_entry(&queue, struct rk3288_vpu_buf, list); + for (i = 0; i < b->vb.vb2_buf.num_planes; i++) + vb2_set_plane_payload(&b->vb.vb2_buf, i, 0); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&b->list); + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + rk3288_vpu_deinit(ctx); + + vpu_debug_leave(); +} + +static void rk3288_vpu_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + struct rk3288_vpu_buf *vpu_buf; + unsigned long flags; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_buf = vb_to_buf(vb); + + /* Mark destination as available for use by VPU */ + spin_lock_irqsave(&dev->irqlock, flags); + + list_add_tail(&vpu_buf->list, &ctx->dst_queue); + + spin_unlock_irqrestore(&dev->irqlock, flags); + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_buf = vb_to_buf(vb); + + spin_lock_irqsave(&dev->irqlock, flags); + + list_add_tail(&vpu_buf->list, &ctx->src_queue); + + spin_unlock_irqrestore(&dev->irqlock, flags); + break; + + default: + vpu_err("unsupported buffer type (%d)\n", vq->type); + } + + if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst)) + rk3288_vpu_try_context(dev, ctx); + + vpu_debug_leave(); +} + +static struct vb2_ops rk3288_vpu_dec_qops = { + .queue_setup = rk3288_vpu_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = rk3288_vpu_buf_init, + .buf_prepare = rk3288_vpu_buf_prepare, + .buf_cleanup = rk3288_vpu_buf_cleanup, + .start_streaming = rk3288_vpu_start_streaming, + .stop_streaming = rk3288_vpu_stop_streaming, + .buf_queue = rk3288_vpu_buf_queue, +}; + +struct vb2_ops *get_dec_queue_ops(void) +{ + return &rk3288_vpu_dec_qops; +} + +const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void) +{ + return &rk3288_vpu_dec_ioctl_ops; +} + +static void rk3288_vpu_dec_prepare_run(struct rk3288_vpu_ctx *ctx) +{ + struct vb2_v4l2_buffer *src = to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); + + v4l2_ctrl_apply_store(&ctx->ctrl_handler, src->config_store); + + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) { + ctx->run.h264d.sps = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_H264_SPS); + ctx->run.h264d.pps = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_H264_PPS); + ctx->run.h264d.scaling_matrix = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX); + ctx->run.h264d.slice_param = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM); + ctx->run.h264d.decode_param = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM); + } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP8_FRAME) { + ctx->run.vp8d.frame_hdr = get_ctrl_ptr(ctx, + RK3288_VPU_DEC_CTRL_VP8_FRAME_HDR); + } +} + +static void rk3288_vpu_dec_run_done(struct rk3288_vpu_ctx *ctx, + enum vb2_buffer_state result) +{ + struct v4l2_plane_pix_format *plane_fmts = ctx->dst_fmt.plane_fmt; + struct vb2_buffer *dst = &ctx->run.dst->vb.vb2_buf; + int i; + + if (result != VB2_BUF_STATE_DONE) { + /* Assume no payload after failed run. */ + for (i = 0; i < dst->num_planes; ++i) + vb2_set_plane_payload(dst, i, 0); + return; + } + + for (i = 0; i < dst->num_planes; ++i) + vb2_set_plane_payload(dst, i, plane_fmts[i].sizeimage); +} + +static const struct rk3288_vpu_run_ops rk3288_vpu_dec_run_ops = { + .prepare_run = rk3288_vpu_dec_prepare_run, + .run_done = rk3288_vpu_dec_run_done, +}; + +int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx) +{ + ctx->vpu_src_fmt = find_format(DEF_SRC_FMT_DEC, false); + ctx->vpu_dst_fmt = find_format(DEF_DST_FMT_DEC, true); + + ctx->run_ops = &rk3288_vpu_dec_run_ops; + + return rk3288_vpu_ctrls_setup(ctx, &rk3288_vpu_dec_ctrl_ops, + controls, ARRAY_SIZE(controls), NULL); +} + +void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx) +{ + rk3288_vpu_ctrls_delete(ctx); +} diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h new file mode 100644 index 000000000000..5d27a9f23c57 --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h @@ -0,0 +1,33 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 ROCKCHIP_VPU_DEC_H_ +#define ROCKCHIP_VPU_DEC_H_ + +struct vb2_ops *get_dec_queue_ops(void); +const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void); +struct rk3288_vpu_fmt *get_dec_def_fmt(bool src); +int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx); + +#endif /* ROCKCHIP_VPU_DEC_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.c new file mode 100644 index 000000000000..01bb4cfc7bfe --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.c @@ -0,0 +1,1504 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * Alpha Lin + * Jeffy Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "rockchip_vpu_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_vpu_enc.h" +#include "rockchip_vpu_hw.h" + +#define DEF_SRC_FMT_ENC V4L2_PIX_FMT_NV12 +#define DEF_DST_FMT_ENC V4L2_PIX_FMT_VP8 + +#define RK3288_ENC_MIN_WIDTH 96U +#define RK3288_ENC_MAX_WIDTH 1920U +#define RK3288_ENC_MIN_HEIGHT 96U +#define RK3288_ENC_MAX_HEIGHT 1088U + +#define V4L2_CID_PRIVATE_RK3288_HEADER (V4L2_CID_CUSTOM_BASE + 0) +#define V4L2_CID_PRIVATE_RK3288_REG_PARAMS (V4L2_CID_CUSTOM_BASE + 1) +#define V4L2_CID_PRIVATE_RK3288_HW_PARAMS (V4L2_CID_CUSTOM_BASE + 2) +#define V4L2_CID_PRIVATE_RK3288_RET_PARAMS (V4L2_CID_CUSTOM_BASE + 3) + +static struct rk3288_vpu_fmt formats[] = { + /* Source formats. */ + { + .name = "4:2:0 3 planes Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = RK_VPU_CODEC_NONE, + .num_planes = 3, + .depth = { 8, 4, 4 }, + .enc_fmt = RK3288_VPU_ENC_FMT_YUV420P, + }, + { + .name = "4:2:0 2 plane Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12M, + .codec_mode = RK_VPU_CODEC_NONE, + .num_planes = 2, + .depth = { 8, 8 }, + .enc_fmt = RK3288_VPU_ENC_FMT_YUV420SP, + }, + { + .name = "4:2:2 1 plane YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = RK_VPU_CODEC_NONE, + .num_planes = 1, + .depth = { 16 }, + .enc_fmt = RK3288_VPU_ENC_FMT_YUYV422, + }, + { + .name = "4:2:2 1 plane UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .codec_mode = RK_VPU_CODEC_NONE, + .num_planes = 1, + .depth = { 16 }, + .enc_fmt = RK3288_VPU_ENC_FMT_UYVY422, + }, + /* Destination formats. */ + { + .name = "VP8 Encoded Stream", + .fourcc = V4L2_PIX_FMT_VP8, + .codec_mode = RK_VPU_CODEC_VP8E, + .num_planes = 1, + }, +}; + +static struct rk3288_vpu_fmt *find_format(u32 fourcc, bool bitstream) +{ + unsigned int i; + + vpu_debug_enter(); + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc != fourcc) + continue; + if (bitstream && formats[i].codec_mode != RK_VPU_CODEC_NONE) + return &formats[i]; + if (!bitstream && formats[i].codec_mode == RK_VPU_CODEC_NONE) + return &formats[i]; + } + + return NULL; +} + +/* + * Indices of controls that need to be accessed directly, i.e. through + * p_cur.p pointer of their v4l2_ctrl structs. + */ +enum { + RK3288_VPU_ENC_CTRL_HEADER, + RK3288_VPU_ENC_CTRL_REG_PARAMS, + RK3288_VPU_ENC_CTRL_HW_PARAMS, + RK3288_VPU_ENC_CTRL_RET_PARAMS, +}; + +static struct rk3288_vpu_control controls[] = { + /* Private, per-frame controls. */ + [RK3288_VPU_ENC_CTRL_HEADER] = { + .id = V4L2_CID_PRIVATE_RK3288_HEADER, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "Rk3288 Private Header", + .elem_size = RK3288_HEADER_SIZE, + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_ENC_CTRL_REG_PARAMS] = { + .id = V4L2_CID_PRIVATE_RK3288_REG_PARAMS, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "Rk3288 Private Reg Params", + .elem_size = sizeof(struct rk3288_vp8e_reg_params), + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_ENC_CTRL_HW_PARAMS] = { + .id = V4L2_CID_PRIVATE_RK3288_HW_PARAMS, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "Rk3288 Private Hw Params", + .elem_size = RK3288_HW_PARAMS_SIZE, + .max_stores = VIDEO_MAX_FRAME, + .can_store = true, + }, + [RK3288_VPU_ENC_CTRL_RET_PARAMS] = { + .id = V4L2_CID_PRIVATE_RK3288_RET_PARAMS, + .type = V4L2_CTRL_TYPE_PRIVATE, + .name = "Rk3288 Private Ret Params", + .is_volatile = true, + .is_read_only = true, + .max_stores = VIDEO_MAX_FRAME, + .elem_size = RK3288_RET_PARAMS_SIZE, + }, + /* Generic controls. (currently ignored) */ + { + .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 150, + .step = 1, + .default_value = 30, + }, + { + .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 10000, + .maximum = 288000000, + .step = 1, + .default_value = 2097152, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, + .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_1, + .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 30, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 18, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 288000, + .step = 1, + .default_value = 30000, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + .type = V4L2_CTRL_TYPE_BUTTON, + }, + /* + * This hardware does not support features provided by controls + * below, but they are required for compatibility with certain + * userspace software. + */ + { + .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rate Control Reaction Coeff.", + .minimum = 1, + .maximum = (1 << 16) - 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Fixed Target Bit Enable", + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 51, + .step = 1, + .default_value = 1, + }, +}; + +static inline const void *get_ctrl_ptr(struct rk3288_vpu_ctx *ctx, unsigned id) +{ + struct v4l2_ctrl *ctrl = ctx->ctrls[id]; + + return ctrl->p_cur.p; +} + +static const char *const *rk3288_vpu_enc_get_menu(u32 id) +{ + static const char *const vpu_video_frame_skip[] = { + "Disabled", + "Level Limit", + "VBV/CPB Limit", + NULL, + }; + + switch (id) { + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE: + return vpu_video_frame_skip; + } + + return NULL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rk3288_vpu_dev *dev = video_drvdata(file); + + vpu_debug_enter(); + + strlcpy(cap->driver, RK3288_VPU_ENC_NAME, sizeof(cap->driver)); + strlcpy(cap->card, dev->pdev->name, sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:" RK3288_VPU_NAME, + sizeof(cap->bus_info)); + + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + vpu_debug_leave(); + + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *prov, + struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_frmsize_stepwise *s = &fsize->stepwise; + struct rk3288_vpu_fmt *fmt; + + if (fsize->index != 0) { + vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", + fsize->index); + return -EINVAL; + } + + fmt = find_format(fsize->pixel_format, true); + if (!fmt) { + vpu_debug(0, "unsupported bitstream format (%08x)\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + s->min_width = RK3288_ENC_MIN_WIDTH; + s->max_width = RK3288_ENC_MAX_WIDTH; + s->step_width = MB_DIM; + s->min_height = RK3288_ENC_MIN_HEIGHT; + s->max_height = RK3288_ENC_MAX_HEIGHT; + s->step_height = MB_DIM; + + return 0; +} + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out) +{ + struct rk3288_vpu_fmt *fmt; + int i, j = 0; + + vpu_debug_enter(); + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (out && formats[i].codec_mode != RK_VPU_CODEC_NONE) + continue; + else if (!out && formats[i].codec_mode == RK_VPU_CODEC_NONE) + continue; + + if (j == f->index) { + fmt = &formats[i]; + strlcpy(f->description, fmt->name, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + f->flags = 0; + if (formats[i].codec_mode != RK_VPU_CODEC_NONE) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + + vpu_debug_leave(); + + return 0; + } + + ++j; + } + + vpu_debug_leave(); + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, false); +} + +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, true); +} + +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + + vpu_debug_enter(); + + vpu_debug(4, "f->type = %d\n", f->type); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + f->fmt.pix_mp = ctx->dst_fmt; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + f->fmt.pix_mp = ctx->src_fmt; + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + + vpu_debug_leave(); + + return 0; +} + +static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rk3288_vpu_fmt *fmt; + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + char str[5]; + + vpu_debug_enter(); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); + + fmt = find_format(pix_fmt_mp->pixelformat, true); + if (!fmt) { + vpu_err("failed to try capture format\n"); + return -EINVAL; + } + + if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { + vpu_err("must be set encoding output size\n"); + return -EINVAL; + } + + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str)); + + fmt = find_format(pix_fmt_mp->pixelformat, false); + if (!fmt) { + vpu_err("failed to try output format\n"); + return -EINVAL; + } + + if (fmt->num_planes != pix_fmt_mp->num_planes) { + vpu_err("plane number mismatches on output format\n"); + return -EINVAL; + } + + /* Limit to hardware min/max. */ + pix_fmt_mp->width = clamp(pix_fmt_mp->width, + RK3288_ENC_MIN_WIDTH, RK3288_ENC_MAX_WIDTH); + pix_fmt_mp->height = clamp(pix_fmt_mp->height, + RK3288_ENC_MIN_HEIGHT, RK3288_ENC_MAX_HEIGHT); + /* Round up to macroblocks. */ + pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM); + pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM); + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + + vpu_debug_leave(); + + return 0; +} + +static void calculate_plane_sizes(struct rk3288_vpu_fmt *fmt, + unsigned int w, unsigned int h, + struct v4l2_pix_format_mplane *pix_fmt_mp) +{ + int i; + + for (i = 0; i < fmt->num_planes; ++i) { + pix_fmt_mp->plane_fmt[i].bytesperline = w * fmt->depth[i] / 8; + pix_fmt_mp->plane_fmt[i].sizeimage = h * + pix_fmt_mp->plane_fmt[i].bytesperline; + /* + * All of multiplanar formats we support have chroma + * planes subsampled by 2 vertically. + */ + if (i != 0) + pix_fmt_mp->plane_fmt[i].sizeimage /= 2; + } +} + +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + unsigned int mb_width, mb_height; + struct rk3288_vpu_fmt *fmt; + int ret = 0; + + vpu_debug_enter(); + + /* Change not allowed if any queue is streaming. */ + if (vb2_is_streaming(&ctx->vq_src) || vb2_is_streaming(&ctx->vq_dst)) { + ret = -EBUSY; + goto out; + } + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + /* + * Pixel format change is not allowed when the other queue has + * buffers allocated. + */ + if (vb2_is_busy(&ctx->vq_src) + && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) { + ret = -EBUSY; + goto out; + } + + ret = vidioc_try_fmt(file, priv, f); + if (ret) + goto out; + + ctx->vpu_dst_fmt = find_format(pix_fmt_mp->pixelformat, true); + ctx->dst_fmt = *pix_fmt_mp; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + /* + * Pixel format change is not allowed when the other queue has + * buffers allocated. + */ + if (vb2_is_busy(&ctx->vq_dst) + && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) { + ret = -EBUSY; + goto out; + } + + ret = vidioc_try_fmt(file, priv, f); + if (ret) + goto out; + + fmt = find_format(pix_fmt_mp->pixelformat, false); + ctx->vpu_src_fmt = fmt; + + mb_width = MB_WIDTH(pix_fmt_mp->width); + mb_height = MB_HEIGHT(pix_fmt_mp->height); + + vpu_debug(0, "OUTPUT codec mode: %d\n", fmt->codec_mode); + vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", + pix_fmt_mp->width, pix_fmt_mp->height, + mb_width, mb_height); + + calculate_plane_sizes(fmt, mb_width * MB_DIM, + mb_height * MB_DIM, pix_fmt_mp); + + /* Reset crop rectangle. */ + ctx->src_crop.width = pix_fmt_mp->width; + ctx->src_crop.height = pix_fmt_mp->height; + + ctx->src_fmt = *pix_fmt_mp; + break; + + default: + vpu_err("invalid buf type\n"); + return -EINVAL; + } + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (reqbufs->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_debug(4, "\n"); + + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret != 0) { + vpu_err("error in vb2_reqbufs() for E(D)\n"); + goto out; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_debug(4, "memory type %d\n", reqbufs->memory); + + ret = vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret != 0) { + vpu_err("error in vb2_reqbufs() for E(S)\n"); + goto out; + } + break; + + default: + vpu_err("invalid buf type\n"); + ret = -EINVAL; + goto out; + } + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_querybuf(&ctx->vq_dst, buf); + if (ret != 0) { + vpu_err("error in vb2_querybuf() for E(D)\n"); + goto out; + } + + buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_querybuf(&ctx->vq_src, buf); + if (ret != 0) { + vpu_err("error in vb2_querybuf() for E(S)\n"); + goto out; + } + break; + + default: + vpu_err("invalid buf type\n"); + ret = -EINVAL; + goto out; + } + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + int i; + + vpu_debug_enter(); + + for (i = 0; i < buf->length; i++) { + vpu_debug(4, "plane[%d]->length %d bytesused %d\n", + i, buf->m.planes[i].length, + buf->m.planes[i].bytesused); + } + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_qbuf(&ctx->vq_src, buf); + vpu_debug(4, "vb2_qbuf return %d\n", ret); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_qbuf(&ctx->vq_dst, buf); + vpu_debug(4, "vb2_qbuf return %d\n", ret); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (eb->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_expbuf(&ctx->vq_src, eb); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_expbuf(&ctx->vq_dst, eb); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_streamon(&ctx->vq_src, type); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_streamon(&ctx->vq_dst, type); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret; + + vpu_debug_enter(); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + ret = vb2_streamoff(&ctx->vq_src, type); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + ret = vb2_streamoff(&ctx->vq_dst, type); + break; + + default: + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); + struct rk3288_vpu_dev *dev = ctx->dev; + int ret = 0; + + vpu_debug_enter(); + + vpu_debug(4, "ctrl id %d\n", ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + case V4L2_CID_MPEG_VIDEO_BITRATE: + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF: + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + /* Ignore these controls for now. (FIXME?) */ + break; + + case V4L2_CID_PRIVATE_RK3288_HEADER: + case V4L2_CID_PRIVATE_RK3288_REG_PARAMS: + case V4L2_CID_PRIVATE_RK3288_HW_PARAMS: + /* Nothing to do here. The control is used directly. */ + break; + + default: + v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_enc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl); + struct rk3288_vpu_dev *dev = ctx->dev; + int ret = 0; + + vpu_debug_enter(); + + vpu_debug(4, "ctrl id %d\n", ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_PRIVATE_RK3288_RET_PARAMS: + memcpy(ctrl->p_new.p, ctx->run.priv_dst.cpu, + RK3288_RET_PARAMS_SIZE); + break; + + default: + v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static const struct v4l2_ctrl_ops rk3288_vpu_enc_ctrl_ops = { + .s_ctrl = rk3288_vpu_enc_s_ctrl, + .g_volatile_ctrl = rk3288_vpu_enc_g_volatile_ctrl, +}; + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cap) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *fmt = &ctx->src_fmt; + int ret = 0; + + vpu_debug_enter(); + + /* Crop only supported on source. */ + if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = -EINVAL; + goto out; + } + + cap->bounds.left = 0; + cap->bounds.top = 0; + cap->bounds.width = fmt->width; + cap->bounds.height = fmt->height; + cap->defrect = cap->bounds; + cap->pixelaspect.numerator = 1; + cap->pixelaspect.denominator = 1; + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + int ret = 0; + + vpu_debug_enter(); + + /* Crop only supported on source. */ + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = -EINVAL; + goto out; + } + + crop->c = ctx->src_crop; + +out: + vpu_debug_leave(); + + return ret; +} + +static int vidioc_s_crop(struct file *file, void *priv, + const struct v4l2_crop *crop) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *fmt = &ctx->src_fmt; + const struct v4l2_rect *rect = &crop->c; + int ret = 0; + + vpu_debug_enter(); + + /* Crop only supported on source. */ + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = -EINVAL; + goto out; + } + + /* Change not allowed if the queue is streaming. */ + if (vb2_is_streaming(&ctx->vq_src)) { + ret = -EBUSY; + goto out; + } + + /* We do not support offsets. */ + if (rect->left != 0 || rect->top != 0) + goto fallback; + + /* We can crop only inside right- or bottom-most macroblocks. */ + if (round_up(rect->width, MB_DIM) != fmt->width + || round_up(rect->height, MB_DIM) != fmt->height) + goto fallback; + + /* We support widths aligned to 4 pixels and arbitrary heights. */ + ctx->src_crop.width = round_up(rect->width, 4); + ctx->src_crop.height = rect->height; + + vpu_debug_leave(); + + return 0; + +fallback: + /* Default to full frame for incorrect settings. */ + ctx->src_crop.width = fmt->width; + ctx->src_crop.height = fmt->height; + +out: + vpu_debug_leave(); + + return ret; +} + +static const struct v4l2_ioctl_ops rk3288_vpu_enc_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .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_g_fmt_vid_cap_mplane = vidioc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, +}; + +static int rk3288_vpu_queue_setup(struct vb2_queue *vq, + const void *parg, + unsigned int *buf_count, + unsigned int *plane_count, + unsigned int psize[], void *allocators[]) +{ + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + int ret = 0; + int i; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *plane_count = ctx->vpu_dst_fmt->num_planes; + + if (*buf_count < 1) + *buf_count = 1; + + if (*buf_count > VIDEO_MAX_FRAME) + *buf_count = VIDEO_MAX_FRAME; + + psize[0] = ctx->dst_fmt.plane_fmt[0].sizeimage; + /* Kernel mapping necessary for bitstream post processing. */ + allocators[0] = ctx->dev->alloc_ctx_vm; + vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]); + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *plane_count = ctx->vpu_src_fmt->num_planes; + + if (*buf_count < 1) + *buf_count = 1; + + if (*buf_count > VIDEO_MAX_FRAME) + *buf_count = VIDEO_MAX_FRAME; + + for (i = 0; i < ctx->vpu_src_fmt->num_planes; ++i) { + psize[i] = ctx->src_fmt.plane_fmt[i].sizeimage; + vpu_debug(0, "output psize[%d]: %d\n", i, psize[i]); + allocators[i] = ctx->dev->alloc_ctx; + } + break; + + default: + vpu_err("invalid queue type: %d\n", vq->type); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static int rk3288_vpu_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + int ret = 0; + int i; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_debug(4, "plane size: %ld, dst size: %d\n", + vb2_plane_size(vb, 0), + ctx->dst_fmt.plane_fmt[0].sizeimage); + + if (vb2_plane_size(vb, 0) + < ctx->dst_fmt.plane_fmt[0].sizeimage) { + vpu_err("plane size is too small for capture\n"); + ret = -EINVAL; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + for (i = 0; i < ctx->vpu_src_fmt->num_planes; ++i) { + vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i, + vb2_plane_size(vb, i), + ctx->src_fmt.plane_fmt[i].sizeimage); + + if (vb2_plane_size(vb, i) + < ctx->src_fmt.plane_fmt[i].sizeimage) { + vpu_err("size of plane %d is too small for output\n", + i); + break; + } + } + + if (i != ctx->vpu_src_fmt->num_planes) + ret = -EINVAL; + break; + + default: + vpu_err("invalid queue type: %d\n", vq->type); + ret = -EINVAL; + } + + vpu_debug_leave(); + + return ret; +} + +static void rk3288_vpu_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + + vpu_debug_enter(); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE + && vb->state == VB2_BUF_STATE_DONE + && ctx->vpu_dst_fmt->fourcc == V4L2_PIX_FMT_VP8) { + struct rk3288_vpu_buf *buf; + + buf = vb_to_buf(vb); + rk3288_vpu_vp8e_assemble_bitstream(ctx, buf); + } + + vpu_debug_leave(); +} + +static int rk3288_vpu_start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret = 0; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + bool ready = false; + + vpu_debug_enter(); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = rk3288_vpu_init(ctx); + if (ret < 0) { + vpu_err("rk3288_vpu_init failed\n"); + return ret; + } + + ready = vb2_is_streaming(&ctx->vq_src); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ready = vb2_is_streaming(&ctx->vq_dst); + } + + if (ready) + rk3288_vpu_try_context(dev, ctx); + + vpu_debug_leave(); + + return 0; +} + +static void rk3288_vpu_stop_streaming(struct vb2_queue *q) +{ + unsigned long flags; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + struct rk3288_vpu_buf *b; + LIST_HEAD(queue); + int i; + + vpu_debug_enter(); + + spin_lock_irqsave(&dev->irqlock, flags); + + list_del_init(&ctx->list); + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + list_splice_init(&ctx->dst_queue, &queue); + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + list_splice_init(&ctx->src_queue, &queue); + break; + + default: + break; + } + + spin_unlock_irqrestore(&dev->irqlock, flags); + + wait_event(dev->run_wq, dev->current_ctx != ctx); + + while (!list_empty(&queue)) { + b = list_first_entry(&queue, struct rk3288_vpu_buf, list); + for (i = 0; i < b->vb.vb2_buf.num_planes; i++) + vb2_set_plane_payload(&b->vb.vb2_buf, i, 0); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&b->list); + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + rk3288_vpu_deinit(ctx); + + vpu_debug_leave(); +} + +static void rk3288_vpu_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv); + struct rk3288_vpu_dev *dev = ctx->dev; + struct rk3288_vpu_buf *vpu_buf; + unsigned long flags; + + vpu_debug_enter(); + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + vpu_buf = vb_to_buf(vb); + + /* Mark destination as available for use by VPU */ + spin_lock_irqsave(&dev->irqlock, flags); + + list_add_tail(&vpu_buf->list, &ctx->dst_queue); + + spin_unlock_irqrestore(&dev->irqlock, flags); + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + vpu_buf = vb_to_buf(vb); + + spin_lock_irqsave(&dev->irqlock, flags); + + list_add_tail(&vpu_buf->list, &ctx->src_queue); + + spin_unlock_irqrestore(&dev->irqlock, flags); + break; + + default: + vpu_err("unsupported buffer type (%d)\n", vq->type); + } + + if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst)) + rk3288_vpu_try_context(dev, ctx); + + vpu_debug_leave(); +} + +static struct vb2_ops rk3288_vpu_enc_qops = { + .queue_setup = rk3288_vpu_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = rk3288_vpu_buf_prepare, + .buf_finish = rk3288_vpu_buf_finish, + .start_streaming = rk3288_vpu_start_streaming, + .stop_streaming = rk3288_vpu_stop_streaming, + .buf_queue = rk3288_vpu_buf_queue, +}; + +struct vb2_ops *get_enc_queue_ops(void) +{ + return &rk3288_vpu_enc_qops; +} + +const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void) +{ + return &rk3288_vpu_enc_ioctl_ops; +} + +static void rk3288_vpu_enc_prepare_run(struct rk3288_vpu_ctx *ctx) +{ + struct vb2_v4l2_buffer *vb2_src = to_vb2_v4l2_buffer(&ctx->run.src->vb.vb2_buf); + unsigned config_store = vb2_src->config_store; + + v4l2_ctrl_apply_store(&ctx->ctrl_handler, config_store); + + memcpy(ctx->run.dst->vp8e.header, + get_ctrl_ptr(ctx, RK3288_VPU_ENC_CTRL_HEADER), + RK3288_HEADER_SIZE); + ctx->run.vp8e.reg_params = get_ctrl_ptr(ctx, + RK3288_VPU_ENC_CTRL_REG_PARAMS); + memcpy(ctx->run.priv_src.cpu, + get_ctrl_ptr(ctx, RK3288_VPU_ENC_CTRL_HW_PARAMS), + RK3288_HW_PARAMS_SIZE); +} + +static const struct rk3288_vpu_run_ops rk3288_vpu_enc_run_ops = { + .prepare_run = rk3288_vpu_enc_prepare_run, +}; + +int rk3288_vpu_enc_init(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *vpu = ctx->dev; + int ret; + + ctx->vpu_src_fmt = find_format(DEF_SRC_FMT_ENC, false); + ctx->vpu_dst_fmt = find_format(DEF_DST_FMT_ENC, true); + + ret = rk3288_vpu_aux_buf_alloc(vpu, &ctx->run.priv_src, + RK3288_HW_PARAMS_SIZE); + if (ret) { + vpu_err("Failed to allocate private source buffer.\n"); + return ret; + } + + + ret = rk3288_vpu_aux_buf_alloc(vpu, &ctx->run.priv_dst, + RK3288_RET_PARAMS_SIZE); + if (ret) { + vpu_err("Failed to allocate private destination buffer.\n"); + goto err_priv_src; + } + + ret = rk3288_vpu_ctrls_setup(ctx, &rk3288_vpu_enc_ctrl_ops, + controls, ARRAY_SIZE(controls), + rk3288_vpu_enc_get_menu); + if (ret) { + vpu_err("Failed to set up controls\n"); + goto err_priv_dst; + } + + ctx->run_ops = &rk3288_vpu_enc_run_ops; + + return 0; + +err_priv_dst: + rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_dst); +err_priv_src: + rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_src); + + return ret; +} + +void rk3288_vpu_enc_exit(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *vpu = ctx->dev; + + rk3288_vpu_ctrls_delete(ctx); + + rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_dst); + rk3288_vpu_aux_buf_free(vpu, &ctx->run.priv_src); +}; + +/* + * WAR for encoder state corruption after decoding + */ + +static const struct rk3288_vpu_run_ops dummy_encode_run_ops = { + /* No ops needed for dummy encoding. */ +}; + +#define DUMMY_W 64 +#define DUMMY_H 64 +#define DUMMY_SRC_FMT V4L2_PIX_FMT_YUYV +#define DUMMY_DST_FMT V4L2_PIX_FMT_VP8 +#define DUMMY_DST_SIZE (32 * 1024) + +int rk3288_vpu_enc_init_dummy_ctx(struct rk3288_vpu_dev *dev) +{ + struct rk3288_vpu_ctx *ctx; + int ret; + int i; + + ctx = devm_kzalloc(dev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + + ctx->vpu_src_fmt = find_format(DUMMY_SRC_FMT, false); + ctx->src_fmt.width = DUMMY_W; + ctx->src_fmt.height = DUMMY_H; + ctx->src_fmt.pixelformat = ctx->vpu_src_fmt->fourcc; + ctx->src_fmt.num_planes = ctx->vpu_src_fmt->num_planes; + + calculate_plane_sizes(ctx->vpu_src_fmt, ctx->src_fmt.width, + ctx->src_fmt.height, &ctx->src_fmt); + + ctx->vpu_dst_fmt = find_format(DUMMY_DST_FMT, true); + ctx->dst_fmt.width = ctx->src_fmt.width; + ctx->dst_fmt.height = ctx->src_fmt.height; + ctx->dst_fmt.pixelformat = ctx->vpu_dst_fmt->fourcc; + ctx->dst_fmt.plane_fmt[0].sizeimage = DUMMY_DST_SIZE; + ctx->dst_fmt.plane_fmt[0].bytesperline = 0; + ctx->dst_fmt.num_planes = 1; + + INIT_LIST_HEAD(&ctx->src_queue); + + ctx->src_crop.left = 0; + ctx->src_crop.top = 0; + ctx->src_crop.width = ctx->src_fmt.width; + ctx->src_crop.left = ctx->src_fmt.height; + + INIT_LIST_HEAD(&ctx->dst_queue); + INIT_LIST_HEAD(&ctx->list); + + ctx->run.vp8e.reg_params = rk3288_vpu_vp8e_get_dummy_params(); + ctx->run_ops = &dummy_encode_run_ops; + + ctx->run.dst = devm_kzalloc(dev->dev, sizeof(*ctx->run.dst), + GFP_KERNEL); + if (!ctx->run.dst) + return -ENOMEM; + + ret = rk3288_vpu_aux_buf_alloc(dev, &ctx->run.priv_src, + RK3288_HW_PARAMS_SIZE); + if (ret) + return ret; + + ret = rk3288_vpu_aux_buf_alloc(dev, &ctx->run.priv_dst, + RK3288_RET_PARAMS_SIZE); + if (ret) + goto err_free_priv_src; + + for (i = 0; i < ctx->src_fmt.num_planes; ++i) { + ret = rk3288_vpu_aux_buf_alloc(dev, &dev->dummy_encode_src[i], + ctx->src_fmt.plane_fmt[i].sizeimage); + if (ret) + goto err_free_src; + + memset(dev->dummy_encode_src[i].cpu, 0, + dev->dummy_encode_src[i].size); + } + + ret = rk3288_vpu_aux_buf_alloc(dev, &dev->dummy_encode_dst, + ctx->dst_fmt.plane_fmt[0].sizeimage); + if (ret) + goto err_free_src; + + memset(dev->dummy_encode_dst.cpu, 0, dev->dummy_encode_dst.size); + + ret = rk3288_vpu_init(ctx); + if (ret) + goto err_free_dst; + + dev->dummy_encode_ctx = ctx; + + return 0; + +err_free_dst: + rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_dst); +err_free_src: + for (i = 0; i < ctx->src_fmt.num_planes; ++i) + if (dev->dummy_encode_src[i].cpu) + rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_src[i]); + rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_dst); +err_free_priv_src: + rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_src); + + return ret; +} + +void rk3288_vpu_enc_free_dummy_ctx(struct rk3288_vpu_dev *dev) +{ + struct rk3288_vpu_ctx *ctx = dev->dummy_encode_ctx; + int i; + + rk3288_vpu_deinit(ctx); + + for (i = 0; i < ctx->src_fmt.num_planes; ++i) + rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_src[i]); + rk3288_vpu_aux_buf_free(dev, &dev->dummy_encode_dst); + rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_src); + rk3288_vpu_aux_buf_free(dev, &ctx->run.priv_dst); +} diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.h new file mode 100644 index 000000000000..d8a728f3467c --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_enc.h @@ -0,0 +1,36 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Alpha Lin + * Jeffy Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 ROCKCHIP_VPU_ENC_H_ +#define ROCKCHIP_VPU_ENC_H_ + +struct vb2_ops *get_enc_queue_ops(void); +const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void); +struct rk3288_vpu_fmt *get_enc_def_fmt(bool src); +int rk3288_vpu_enc_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_enc_exit(struct rk3288_vpu_ctx *ctx); +int rk3288_vpu_enc_init_dummy_ctx(struct rk3288_vpu_dev *dev); +void rk3288_vpu_enc_free_dummy_ctx(struct rk3288_vpu_dev *dev); + +#endif /* ROCKCHIP_VPU_ENC_H_ */ diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c new file mode 100644 index 000000000000..24aa83d301f0 --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c @@ -0,0 +1,430 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "rockchip_vpu_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rk3288_vpu_regs.h" + +/** + * struct rk3288_vpu_variant - information about VPU hardware variant + * + * @hw_id: Top 16 bits (product ID) of hardware ID register. + * @enc_offset: Offset from VPU base to encoder registers. + * @enc_reg_num: Number of registers of encoder block. + * @dec_offset: Offset from VPU base to decoder registers. + * @dec_reg_num: Number of registers of decoder block. + */ +struct rk3288_vpu_variant { + u16 hw_id; + unsigned enc_offset; + unsigned enc_reg_num; + unsigned dec_offset; + unsigned dec_reg_num; +}; + +/* Supported VPU variants. */ +static const struct rk3288_vpu_variant rk3288_vpu_variants[] = { + { + .hw_id = 0x4831, + .enc_offset = 0x0, + .enc_reg_num = 164, + .dec_offset = 0x400, + .dec_reg_num = 60 + 41, + }, +}; + +/** + * struct rk3288_vpu_codec_ops - codec mode specific operations + * + * @init: Prepare for streaming. Called from VB2 .start_streaming() + * when streaming from both queues is being enabled. + * @exit: Clean-up after streaming. Called from VB2 .stop_streaming() + * when streaming from first of both enabled queues is being + * disabled. + * @run: Start single {en,de)coding run. Called from non-atomic context + * to indicate that a pair of buffers is ready and the hardware + * should be programmed and started. + * @done: Read back processing results and additional data from hardware. + * @reset: Reset the hardware in case of a timeout. + */ +struct rk3288_vpu_codec_ops { + int (*init)(struct rk3288_vpu_ctx *); + void (*exit)(struct rk3288_vpu_ctx *); + + void (*run)(struct rk3288_vpu_ctx *); + void (*done)(struct rk3288_vpu_ctx *, enum vb2_buffer_state); + void (*reset)(struct rk3288_vpu_ctx *); +}; + +/* + * Hardware control routines. + */ + +static int rk3288_vpu_identify(struct rk3288_vpu_dev *vpu) +{ + u32 hw_id; + int i; + + hw_id = readl(vpu->base) >> 16; + + dev_info(vpu->dev, "Read hardware ID: %x\n", hw_id); + + for (i = 0; i < ARRAY_SIZE(rk3288_vpu_variants); ++i) { + if (hw_id == rk3288_vpu_variants[i].hw_id) { + vpu->variant = &rk3288_vpu_variants[i]; + return 0; + } + } + + return -ENOENT; +} + +void rk3288_vpu_power_on(struct rk3288_vpu_dev *vpu) +{ + vpu_debug_enter(); + + /* TODO: Clock gating. */ + + pm_runtime_get_sync(vpu->dev); + + vpu_debug_leave(); +} + +static void rk3288_vpu_power_off(struct rk3288_vpu_dev *vpu) +{ + vpu_debug_enter(); + + pm_runtime_mark_last_busy(vpu->dev); + pm_runtime_put_autosuspend(vpu->dev); + + /* TODO: Clock gating. */ + + vpu_debug_leave(); +} + +/* + * Interrupt handlers. + */ + +static irqreturn_t vepu_irq(int irq, void *dev_id) +{ + struct rk3288_vpu_dev *vpu = dev_id; + u32 status = vepu_read(vpu, VEPU_REG_INTERRUPT); + + vepu_write(vpu, 0, VEPU_REG_INTERRUPT); + + if (status & VEPU_REG_INTERRUPT_BIT) { + struct rk3288_vpu_ctx *ctx = vpu->current_ctx; + + vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); + rk3288_vpu_power_off(vpu); + cancel_delayed_work(&vpu->watchdog_work); + + ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE); + } + + return IRQ_HANDLED; +} + +static irqreturn_t vdpu_irq(int irq, void *dev_id) +{ + struct rk3288_vpu_dev *vpu = dev_id; + u32 status = vdpu_read(vpu, VDPU_REG_INTERRUPT); + + vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); + + vpu_debug(3, "vdpu_irq status: %08x\n", status); + + if (status & VDPU_REG_INTERRUPT_DEC_IRQ) { + struct rk3288_vpu_ctx *ctx = vpu->current_ctx; + + vdpu_write(vpu, 0, VDPU_REG_CONFIG); + rk3288_vpu_power_off(vpu); + cancel_delayed_work(&vpu->watchdog_work); + + ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE); + } + + return IRQ_HANDLED; +} + +static void rk3288_vpu_watchdog(struct work_struct *work) +{ + struct rk3288_vpu_dev *vpu = container_of(to_delayed_work(work), + struct rk3288_vpu_dev, watchdog_work); + struct rk3288_vpu_ctx *ctx = vpu->current_ctx; + unsigned long flags; + + spin_lock_irqsave(&vpu->irqlock, flags); + + ctx->hw.codec_ops->reset(ctx); + + spin_unlock_irqrestore(&vpu->irqlock, flags); + + vpu_err("frame processing timed out!\n"); + + rk3288_vpu_power_off(vpu); + ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_ERROR); +} + +/* + * Initialization/clean-up. + */ + +#if defined(CONFIG_ROCKCHIP_IOMMU) +static int rk3288_vpu_iommu_init(struct rk3288_vpu_dev *vpu) +{ + int ret; + + vpu->dev->dma_parms = devm_kzalloc(vpu->dev, + sizeof(*vpu->dev->dma_parms), + GFP_KERNEL); + if (!vpu->dev->dma_parms) + return -ENOMEM; + + vpu->domain = iommu_domain_alloc(vpu->dev->bus); + if (!vpu->domain) + goto err_free_parms; + + ret = iommu_get_dma_cookie(vpu->domain); + if (ret) + goto err_free_domain; + + ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); + if (ret) + goto err_put_cookie; + + dma_set_max_seg_size(vpu->dev, DMA_BIT_MASK(32)); + + ret = iommu_attach_device(vpu->domain, vpu->dev); + if (ret) + goto err_put_cookie; + + common_iommu_setup_dma_ops(vpu->dev, 0x10000000, SZ_2G, + vpu->domain->ops); + + return 0; + +err_put_cookie: + iommu_put_dma_cookie(vpu->domain); +err_free_domain: + iommu_domain_free(vpu->domain); +err_free_parms: + return ret; +} + +static void rk3288_vpu_iommu_cleanup(struct rk3288_vpu_dev *vpu) +{ + iommu_detach_device(vpu->domain, vpu->dev); + iommu_put_dma_cookie(vpu->domain); + iommu_domain_free(vpu->domain); +} +#else /* CONFIG_ROCKCHIP_IOMMU */ +static inline int rk3288_vpu_iommu_init(struct rk3288_vpu_dev *vpu) +{ + return 0; +} + +static inline void rk3288_vpu_iommu_cleanup(struct rk3288_vpu_dev *vpu) { } +#endif /* CONFIG_ROCKCHIP_IOMMU */ + +int rk3288_vpu_hw_probe(struct rk3288_vpu_dev *vpu) +{ + struct resource *res; + int irq_enc, irq_dec; + int ret; + + pr_info("probe device %s\n", dev_name(vpu->dev)); + + INIT_DELAYED_WORK(&vpu->watchdog_work, rk3288_vpu_watchdog); + + vpu->aclk_vcodec = devm_clk_get(vpu->dev, "aclk_vcodec"); + if (IS_ERR(vpu->aclk_vcodec)) { + dev_err(vpu->dev, "failed to get aclk_vcodec\n"); + return PTR_ERR(vpu->aclk_vcodec); + } + + vpu->hclk_vcodec = devm_clk_get(vpu->dev, "hclk_vcodec"); + if (IS_ERR(vpu->hclk_vcodec)) { + dev_err(vpu->dev, "failed to get hclk_vcodec\n"); + return PTR_ERR(vpu->hclk_vcodec); + } + + /* + * Bump ACLK to max. possible freq. (400 MHz) to improve performance. + * + * VP8 encoding 1280x720@1.2Mbps 200 MHz: 39 fps, 400: MHz 77 fps + */ + clk_set_rate(vpu->aclk_vcodec, 400*1000*1000); + + res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); + vpu->base = devm_ioremap_resource(vpu->dev, res); + if (IS_ERR(vpu->base)) + return PTR_ERR(vpu->base); + + clk_prepare_enable(vpu->aclk_vcodec); + clk_prepare_enable(vpu->hclk_vcodec); + + ret = rk3288_vpu_identify(vpu); + if (ret < 0) { + dev_err(vpu->dev, "failed to identify hardware variant\n"); + goto err_power; + } + + vpu->enc_base = vpu->base + vpu->variant->enc_offset; + vpu->dec_base = vpu->base + vpu->variant->dec_offset; + + ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(vpu->dev, "could not set DMA coherent mask\n"); + goto err_power; + } + + ret = rk3288_vpu_iommu_init(vpu); + if (ret) + goto err_power; + + irq_enc = platform_get_irq_byname(vpu->pdev, "vepu"); + if (irq_enc <= 0) { + dev_err(vpu->dev, "could not get vepu IRQ\n"); + ret = -ENXIO; + goto err_iommu; + } + + ret = devm_request_threaded_irq(vpu->dev, irq_enc, NULL, vepu_irq, + IRQF_ONESHOT, dev_name(vpu->dev), vpu); + if (ret) { + dev_err(vpu->dev, "could not request vepu IRQ\n"); + goto err_iommu; + } + + irq_dec = platform_get_irq_byname(vpu->pdev, "vdpu"); + if (irq_dec <= 0) { + dev_err(vpu->dev, "could not get vdpu IRQ\n"); + ret = -ENXIO; + goto err_iommu; + } + + ret = devm_request_threaded_irq(vpu->dev, irq_dec, NULL, vdpu_irq, + IRQF_ONESHOT, dev_name(vpu->dev), vpu); + if (ret) { + dev_err(vpu->dev, "could not request vdpu IRQ\n"); + goto err_iommu; + } + + pm_runtime_set_autosuspend_delay(vpu->dev, 100); + pm_runtime_use_autosuspend(vpu->dev); + pm_runtime_enable(vpu->dev); + + return 0; + +err_iommu: + rk3288_vpu_iommu_cleanup(vpu); +err_power: + clk_disable_unprepare(vpu->hclk_vcodec); + clk_disable_unprepare(vpu->aclk_vcodec); + + return ret; +} + +void rk3288_vpu_hw_remove(struct rk3288_vpu_dev *vpu) +{ + rk3288_vpu_iommu_cleanup(vpu); + + pm_runtime_disable(vpu->dev); + + clk_disable_unprepare(vpu->hclk_vcodec); + clk_disable_unprepare(vpu->aclk_vcodec); +} + +static void rk3288_vpu_enc_reset(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *vpu = ctx->dev; + + vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT); + vepu_write(vpu, 0, VEPU_REG_ENC_CTRL); + vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); +} + +static void rk3288_vpu_dec_reset(struct rk3288_vpu_ctx *ctx) +{ + struct rk3288_vpu_dev *vpu = ctx->dev; + + vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); + vdpu_write(vpu, 0, VDPU_REG_CONFIG); +} + +static const struct rk3288_vpu_codec_ops mode_ops[] = { + [RK_VPU_CODEC_VP8E] = { + .init = rk3288_vpu_vp8e_init, + .exit = rk3288_vpu_vp8e_exit, + .run = rk3288_vpu_vp8e_run, + .done = rk3288_vpu_vp8e_done, + .reset = rk3288_vpu_enc_reset, + }, + [RK_VPU_CODEC_VP8D] = { + .init = rk3288_vpu_vp8d_init, + .exit = rk3288_vpu_vp8d_exit, + .run = rk3288_vpu_vp8d_run, + .done = rk3288_vpu_run_done, + .reset = rk3288_vpu_dec_reset, + }, + [RK_VPU_CODEC_H264D] = { + .init = rk3288_vpu_h264d_init, + .exit = rk3288_vpu_h264d_exit, + .run = rk3288_vpu_h264d_run, + .done = rk3288_vpu_run_done, + .reset = rk3288_vpu_dec_reset, + }, +}; + +void rk3288_vpu_run(struct rk3288_vpu_ctx *ctx) +{ + ctx->hw.codec_ops->run(ctx); +} + +int rk3288_vpu_init(struct rk3288_vpu_ctx *ctx) +{ + enum rk3288_vpu_codec_mode codec_mode; + + if (rk3288_vpu_ctx_is_encoder(ctx)) + codec_mode = ctx->vpu_dst_fmt->codec_mode; /* Encoder */ + else + codec_mode = ctx->vpu_src_fmt->codec_mode; /* Decoder */ + + ctx->hw.codec_ops = &mode_ops[codec_mode]; + + return ctx->hw.codec_ops->init(ctx); +} + +void rk3288_vpu_deinit(struct rk3288_vpu_ctx *ctx) +{ + ctx->hw.codec_ops->exit(ctx); +} diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h new file mode 100644 index 000000000000..54e6ef3b78a0 --- /dev/null +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h @@ -0,0 +1,179 @@ +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 ROCKCHIP_VPU_HW_H_ +#define ROCKCHIP_VPU_HW_H_ + +#include + +#define RK3288_HEADER_SIZE 1280 +#define RK3288_HW_PARAMS_SIZE 5487 +#define RK3288_RET_PARAMS_SIZE 488 + +struct rk3288_vpu_dev; +struct rk3288_vpu_ctx; +struct rk3288_vpu_buf; + +struct rk3288_vpu_h264d_priv_tbl; + +/** + * enum rk3288_vpu_enc_fmt - source format ID for hardware registers. + */ +enum rk3288_vpu_enc_fmt { + RK3288_VPU_ENC_FMT_YUV420P = 0, + RK3288_VPU_ENC_FMT_YUV420SP = 1, + RK3288_VPU_ENC_FMT_YUYV422 = 2, + RK3288_VPU_ENC_FMT_UYVY422 = 3, +}; + +/** + * struct rk3288_vp8e_reg_params - low level encoding parameters + * TODO: Create abstract structures for more generic controls or just + * remove unused fields. + */ +struct rk3288_vp8e_reg_params { + u32 unused_00[5]; + u32 hdr_len; + u32 unused_18[8]; + u32 enc_ctrl; + u32 unused_3c; + u32 enc_ctrl0; + u32 enc_ctrl1; + u32 enc_ctrl2; + u32 enc_ctrl3; + u32 enc_ctrl5; + u32 enc_ctrl4; + u32 str_hdr_rem_msb; + u32 str_hdr_rem_lsb; + u32 unused_60; + u32 mad_ctrl; + u32 unused_68; + u32 qp_val[8]; + u32 bool_enc; + u32 vp8_ctrl0; + u32 rlc_ctrl; + u32 mb_ctrl; + u32 unused_9c[14]; + u32 rgb_yuv_coeff[2]; + u32 rgb_mask_msb; + u32 intra_area_ctrl; + u32 cir_intra_ctrl; + u32 unused_e8[2]; + u32 first_roi_area; + u32 second_roi_area; + u32 mvc_ctrl; + u32 unused_fc; + u32 intra_penalty[7]; + u32 unused_11c; + u32 seg_qp[24]; + u32 dmv_4p_1p_penalty[32]; + u32 dmv_qpel_penalty[32]; + u32 vp8_ctrl1; + u32 bit_cost_golden; + u32 loop_flt_delta[2]; +}; + +/** + * struct rk3288_vpu_aux_buf - auxiliary DMA buffer for hardware data + * @cpu: CPU pointer to the buffer. + * @dma: DMA address of the buffer. + * @size: Size of the buffer. + */ +struct rk3288_vpu_aux_buf { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +/** + * struct rk3288_vpu_vp8e_hw_ctx - Context private data specific to codec mode. + * @ctrl_buf: VP8 control buffer. + * @ext_buf: VP8 ext data buffer. + * @mv_buf: VP8 motion vector buffer. + * @ref_rec_ptr: Bit flag for swapping ref and rec buffers every frame. + */ +struct rk3288_vpu_vp8e_hw_ctx { + struct rk3288_vpu_aux_buf ctrl_buf; + struct rk3288_vpu_aux_buf ext_buf; + struct rk3288_vpu_aux_buf mv_buf; + u8 ref_rec_ptr:1; +}; + +/** + * struct rk3288_vpu_vp8d_hw_ctx - Context private data of VP8 decoder. + * @segment_map: Segment map buffer. + * @prob_tbl: Probability table buffer. + */ +struct rk3288_vpu_vp8d_hw_ctx { + struct rk3288_vpu_aux_buf segment_map; + struct rk3288_vpu_aux_buf prob_tbl; +}; + +/** + * struct rk3288_vpu_h264d_hw_ctx - Per context data specific to H264 decoding. + * @priv_tbl: Private auxiliary buffer for hardware. + */ +struct rk3288_vpu_h264d_hw_ctx { + struct rk3288_vpu_aux_buf priv_tbl; +}; + +/** + * struct rk3288_vpu_hw_ctx - Context private data of hardware code. + * @codec_ops: Set of operations associated with current codec mode. + */ +struct rk3288_vpu_hw_ctx { + const struct rk3288_vpu_codec_ops *codec_ops; + + /* Specific for particular codec modes. */ + union { + struct rk3288_vpu_vp8e_hw_ctx vp8e; + struct rk3288_vpu_vp8d_hw_ctx vp8d; + struct rk3288_vpu_h264d_hw_ctx h264d; + /* Other modes will need different data. */ + }; +}; + +int rk3288_vpu_hw_probe(struct rk3288_vpu_dev *vpu); +void rk3288_vpu_hw_remove(struct rk3288_vpu_dev *vpu); + +int rk3288_vpu_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_deinit(struct rk3288_vpu_ctx *ctx); + +void rk3288_vpu_run(struct rk3288_vpu_ctx *ctx); + +/* Run ops for H264 decoder */ +int rk3288_vpu_h264d_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_h264d_exit(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_h264d_run(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_power_on(struct rk3288_vpu_dev *vpu); + +/* Run ops for VP8 decoder */ +int rk3288_vpu_vp8d_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_vp8d_exit(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_vp8d_run(struct rk3288_vpu_ctx *ctx); + +/* Run ops for VP8 encoder */ +int rk3288_vpu_vp8e_init(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_vp8e_exit(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_vp8e_run(struct rk3288_vpu_ctx *ctx); +void rk3288_vpu_vp8e_done(struct rk3288_vpu_ctx *ctx, + enum vb2_buffer_state result); +const struct rk3288_vp8e_reg_params *rk3288_vpu_vp8e_get_dummy_params(void); + +void rk3288_vpu_vp8e_assemble_bitstream(struct rk3288_vpu_ctx *ctx, + struct rk3288_vpu_buf *dst_buf); + +#endif /* ROCKCHIP_VPU_HW_H_ */