// SPDX-License-Identifier: GPL-2.0 
 | 
/* 
 | 
 * Copyright (c) 2016 MediaTek Inc. 
 | 
 * Author: PC Chen <pc.chen@mediatek.com> 
 | 
 *         Tiffany Lin <tiffany.lin@mediatek.com> 
 | 
 */ 
 | 
  
 | 
#include <linux/slab.h> 
 | 
#include <linux/interrupt.h> 
 | 
#include <linux/irq.h> 
 | 
#include <linux/module.h> 
 | 
#include <linux/of_device.h> 
 | 
#include <linux/of.h> 
 | 
#include <media/v4l2-event.h> 
 | 
#include <media/v4l2-mem2mem.h> 
 | 
#include <media/videobuf2-dma-contig.h> 
 | 
  
 | 
#include "mtk_vcodec_drv.h" 
 | 
#include "mtk_vcodec_dec.h" 
 | 
#include "mtk_vcodec_dec_pm.h" 
 | 
#include "mtk_vcodec_intr.h" 
 | 
#include "mtk_vcodec_util.h" 
 | 
#include "mtk_vcodec_fw.h" 
 | 
  
 | 
#define VDEC_HW_ACTIVE    0x10 
 | 
#define VDEC_IRQ_CFG    0x11 
 | 
#define VDEC_IRQ_CLR    0x10 
 | 
#define VDEC_IRQ_CFG_REG    0xa4 
 | 
  
 | 
module_param(mtk_v4l2_dbg_level, int, 0644); 
 | 
module_param(mtk_vcodec_dbg, bool, 0644); 
 | 
  
 | 
/* Wake up context wait_queue */ 
 | 
static void wake_up_ctx(struct mtk_vcodec_ctx *ctx) 
 | 
{ 
 | 
    ctx->int_cond = 1; 
 | 
    wake_up_interruptible(&ctx->queue); 
 | 
} 
 | 
  
 | 
static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) 
 | 
{ 
 | 
    struct mtk_vcodec_dev *dev = priv; 
 | 
    struct mtk_vcodec_ctx *ctx; 
 | 
    u32 cg_status = 0; 
 | 
    unsigned int dec_done_status = 0; 
 | 
    void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] + 
 | 
                    VDEC_IRQ_CFG_REG; 
 | 
  
 | 
    ctx = mtk_vcodec_get_curr_ctx(dev); 
 | 
  
 | 
    /* check if HW active or not */ 
 | 
    cg_status = readl(dev->reg_base[0]); 
 | 
    if ((cg_status & VDEC_HW_ACTIVE) != 0) { 
 | 
        mtk_v4l2_err("DEC ISR, VDEC active is not 0x0 (0x%08x)", 
 | 
                 cg_status); 
 | 
        return IRQ_HANDLED; 
 | 
    } 
 | 
  
 | 
    dec_done_status = readl(vdec_misc_addr); 
 | 
    ctx->irq_status = dec_done_status; 
 | 
    if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != 
 | 
        MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) 
 | 
        return IRQ_HANDLED; 
 | 
  
 | 
    /* clear interrupt */ 
 | 
    writel((readl(vdec_misc_addr) | VDEC_IRQ_CFG), 
 | 
        dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); 
 | 
    writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR), 
 | 
        dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); 
 | 
  
 | 
    wake_up_ctx(ctx); 
 | 
  
 | 
    mtk_v4l2_debug(3, 
 | 
            "mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x", 
 | 
            ctx->id, dec_done_status); 
 | 
  
 | 
    return IRQ_HANDLED; 
 | 
} 
 | 
  
 | 
static int fops_vcodec_open(struct file *file) 
 | 
{ 
 | 
    struct mtk_vcodec_dev *dev = video_drvdata(file); 
 | 
    struct mtk_vcodec_ctx *ctx = NULL; 
 | 
    struct mtk_video_dec_buf *mtk_buf = NULL; 
 | 
    int ret = 0; 
 | 
    struct vb2_queue *src_vq; 
 | 
  
 | 
    ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 
 | 
    if (!ctx) 
 | 
        return -ENOMEM; 
 | 
    mtk_buf = kzalloc(sizeof(*mtk_buf), GFP_KERNEL); 
 | 
    if (!mtk_buf) { 
 | 
        kfree(ctx); 
 | 
        return -ENOMEM; 
 | 
    } 
 | 
  
 | 
    mutex_lock(&dev->dev_mutex); 
 | 
    ctx->empty_flush_buf = mtk_buf; 
 | 
    ctx->id = dev->id_counter++; 
 | 
    v4l2_fh_init(&ctx->fh, video_devdata(file)); 
 | 
    file->private_data = &ctx->fh; 
 | 
    v4l2_fh_add(&ctx->fh); 
 | 
    INIT_LIST_HEAD(&ctx->list); 
 | 
    ctx->dev = dev; 
 | 
    init_waitqueue_head(&ctx->queue); 
 | 
    mutex_init(&ctx->lock); 
 | 
  
 | 
    ctx->type = MTK_INST_DECODER; 
 | 
    ret = mtk_vcodec_dec_ctrls_setup(ctx); 
 | 
    if (ret) { 
 | 
        mtk_v4l2_err("Failed to setup mt vcodec controls"); 
 | 
        goto err_ctrls_setup; 
 | 
    } 
 | 
    ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, 
 | 
        &mtk_vcodec_dec_queue_init); 
 | 
    if (IS_ERR((__force void *)ctx->m2m_ctx)) { 
 | 
        ret = PTR_ERR((__force void *)ctx->m2m_ctx); 
 | 
        mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", 
 | 
            ret); 
 | 
        goto err_m2m_ctx_init; 
 | 
    } 
 | 
    src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, 
 | 
                V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 
 | 
    ctx->empty_flush_buf->m2m_buf.vb.vb2_buf.vb2_queue = src_vq; 
 | 
    ctx->empty_flush_buf->lastframe = true; 
 | 
    mtk_vcodec_dec_set_default_params(ctx); 
 | 
  
 | 
    if (v4l2_fh_is_singular(&ctx->fh)) { 
 | 
        ret = mtk_vcodec_dec_pw_on(&dev->pm); 
 | 
        if (ret < 0) 
 | 
            goto err_load_fw; 
 | 
        /* 
 | 
         * Does nothing if firmware was already loaded. 
 | 
         */ 
 | 
        ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); 
 | 
        if (ret < 0) { 
 | 
            /* 
 | 
             * Return 0 if downloading firmware successfully, 
 | 
             * otherwise it is failed 
 | 
             */ 
 | 
            mtk_v4l2_err("failed to load firmware!"); 
 | 
            goto err_load_fw; 
 | 
        } 
 | 
  
 | 
        dev->dec_capability = 
 | 
            mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); 
 | 
        mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); 
 | 
    } 
 | 
  
 | 
    list_add(&ctx->list, &dev->ctx_list); 
 | 
  
 | 
    mutex_unlock(&dev->dev_mutex); 
 | 
    mtk_v4l2_debug(0, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), 
 | 
            ctx->id); 
 | 
    return ret; 
 | 
  
 | 
    /* Deinit when failure occurred */ 
 | 
err_load_fw: 
 | 
    v4l2_m2m_ctx_release(ctx->m2m_ctx); 
 | 
err_m2m_ctx_init: 
 | 
    v4l2_ctrl_handler_free(&ctx->ctrl_hdl); 
 | 
err_ctrls_setup: 
 | 
    v4l2_fh_del(&ctx->fh); 
 | 
    v4l2_fh_exit(&ctx->fh); 
 | 
    kfree(ctx->empty_flush_buf); 
 | 
    kfree(ctx); 
 | 
    mutex_unlock(&dev->dev_mutex); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static int fops_vcodec_release(struct file *file) 
 | 
{ 
 | 
    struct mtk_vcodec_dev *dev = video_drvdata(file); 
 | 
    struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); 
 | 
  
 | 
    mtk_v4l2_debug(0, "[%d] decoder", ctx->id); 
 | 
    mutex_lock(&dev->dev_mutex); 
 | 
  
 | 
    /* 
 | 
     * Call v4l2_m2m_ctx_release before mtk_vcodec_dec_release. First, it 
 | 
     * makes sure the worker thread is not running after vdec_if_deinit. 
 | 
     * Second, the decoder will be flushed and all the buffers will be 
 | 
     * returned in stop_streaming. 
 | 
     */ 
 | 
    v4l2_m2m_ctx_release(ctx->m2m_ctx); 
 | 
    mtk_vcodec_dec_release(ctx); 
 | 
  
 | 
    if (v4l2_fh_is_singular(&ctx->fh)) 
 | 
        mtk_vcodec_dec_pw_off(&dev->pm); 
 | 
    v4l2_fh_del(&ctx->fh); 
 | 
    v4l2_fh_exit(&ctx->fh); 
 | 
    v4l2_ctrl_handler_free(&ctx->ctrl_hdl); 
 | 
  
 | 
    list_del_init(&ctx->list); 
 | 
    kfree(ctx->empty_flush_buf); 
 | 
    kfree(ctx); 
 | 
    mutex_unlock(&dev->dev_mutex); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static const struct v4l2_file_operations mtk_vcodec_fops = { 
 | 
    .owner        = THIS_MODULE, 
 | 
    .open        = fops_vcodec_open, 
 | 
    .release    = fops_vcodec_release, 
 | 
    .poll        = v4l2_m2m_fop_poll, 
 | 
    .unlocked_ioctl    = video_ioctl2, 
 | 
    .mmap        = v4l2_m2m_fop_mmap, 
 | 
}; 
 | 
  
 | 
static int mtk_vcodec_probe(struct platform_device *pdev) 
 | 
{ 
 | 
    struct mtk_vcodec_dev *dev; 
 | 
    struct video_device *vfd_dec; 
 | 
    struct resource *res; 
 | 
    phandle rproc_phandle; 
 | 
    enum mtk_vcodec_fw_type fw_type; 
 | 
    int i, ret; 
 | 
  
 | 
    dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 
 | 
    if (!dev) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    INIT_LIST_HEAD(&dev->ctx_list); 
 | 
    dev->plat_dev = pdev; 
 | 
  
 | 
    if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", 
 | 
                  &rproc_phandle)) { 
 | 
        fw_type = VPU; 
 | 
    } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", 
 | 
                     &rproc_phandle)) { 
 | 
        fw_type = SCP; 
 | 
    } else { 
 | 
        mtk_v4l2_err("Could not get vdec IPI device"); 
 | 
        return -ENODEV; 
 | 
    } 
 | 
    if (!pdev->dev.dma_parms) { 
 | 
        pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, 
 | 
                        sizeof(*pdev->dev.dma_parms), 
 | 
                        GFP_KERNEL); 
 | 
        if (!pdev->dev.dma_parms) 
 | 
            return -ENOMEM; 
 | 
    } 
 | 
    dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); 
 | 
  
 | 
    dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER); 
 | 
    if (IS_ERR(dev->fw_handler)) 
 | 
        return PTR_ERR(dev->fw_handler); 
 | 
  
 | 
    ret = mtk_vcodec_init_dec_pm(dev); 
 | 
    if (ret < 0) { 
 | 
        dev_err(&pdev->dev, "Failed to get mt vcodec clock source"); 
 | 
        goto err_dec_pm; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) { 
 | 
        dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); 
 | 
        if (IS_ERR((__force void *)dev->reg_base[i])) { 
 | 
            ret = PTR_ERR((__force void *)dev->reg_base[i]); 
 | 
            goto err_res; 
 | 
        } 
 | 
        mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); 
 | 
    } 
 | 
  
 | 
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 
 | 
    if (res == NULL) { 
 | 
        dev_err(&pdev->dev, "failed to get irq resource"); 
 | 
        ret = -ENOENT; 
 | 
        goto err_res; 
 | 
    } 
 | 
  
 | 
    dev->dec_irq = platform_get_irq(pdev, 0); 
 | 
    irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); 
 | 
    ret = devm_request_irq(&pdev->dev, dev->dec_irq, 
 | 
            mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); 
 | 
    if (ret) { 
 | 
        dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", 
 | 
            dev->dec_irq, 
 | 
            ret); 
 | 
        goto err_res; 
 | 
    } 
 | 
  
 | 
    mutex_init(&dev->dec_mutex); 
 | 
    mutex_init(&dev->dev_mutex); 
 | 
    spin_lock_init(&dev->irqlock); 
 | 
  
 | 
    snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", 
 | 
        "[/MTK_V4L2_VDEC]"); 
 | 
  
 | 
    ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 
 | 
    if (ret) { 
 | 
        mtk_v4l2_err("v4l2_device_register err=%d", ret); 
 | 
        goto err_res; 
 | 
    } 
 | 
  
 | 
    init_waitqueue_head(&dev->queue); 
 | 
  
 | 
    vfd_dec = video_device_alloc(); 
 | 
    if (!vfd_dec) { 
 | 
        mtk_v4l2_err("Failed to allocate video device"); 
 | 
        ret = -ENOMEM; 
 | 
        goto err_dec_alloc; 
 | 
    } 
 | 
    vfd_dec->fops        = &mtk_vcodec_fops; 
 | 
    vfd_dec->ioctl_ops    = &mtk_vdec_ioctl_ops; 
 | 
    vfd_dec->release    = video_device_release; 
 | 
    vfd_dec->lock        = &dev->dev_mutex; 
 | 
    vfd_dec->v4l2_dev    = &dev->v4l2_dev; 
 | 
    vfd_dec->vfl_dir    = VFL_DIR_M2M; 
 | 
    vfd_dec->device_caps    = V4L2_CAP_VIDEO_M2M_MPLANE | 
 | 
            V4L2_CAP_STREAMING; 
 | 
  
 | 
    snprintf(vfd_dec->name, sizeof(vfd_dec->name), "%s", 
 | 
        MTK_VCODEC_DEC_NAME); 
 | 
    video_set_drvdata(vfd_dec, dev); 
 | 
    dev->vfd_dec = vfd_dec; 
 | 
    platform_set_drvdata(pdev, dev); 
 | 
  
 | 
    dev->m2m_dev_dec = v4l2_m2m_init(&mtk_vdec_m2m_ops); 
 | 
    if (IS_ERR((__force void *)dev->m2m_dev_dec)) { 
 | 
        mtk_v4l2_err("Failed to init mem2mem dec device"); 
 | 
        ret = PTR_ERR((__force void *)dev->m2m_dev_dec); 
 | 
        goto err_dec_mem_init; 
 | 
    } 
 | 
  
 | 
    dev->decode_workqueue = 
 | 
        alloc_ordered_workqueue(MTK_VCODEC_DEC_NAME, 
 | 
            WQ_MEM_RECLAIM | WQ_FREEZABLE); 
 | 
    if (!dev->decode_workqueue) { 
 | 
        mtk_v4l2_err("Failed to create decode workqueue"); 
 | 
        ret = -EINVAL; 
 | 
        goto err_event_workq; 
 | 
    } 
 | 
  
 | 
    ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0); 
 | 
    if (ret) { 
 | 
        mtk_v4l2_err("Failed to register video device"); 
 | 
        goto err_dec_reg; 
 | 
    } 
 | 
  
 | 
    mtk_v4l2_debug(0, "decoder registered as /dev/video%d", 
 | 
        vfd_dec->num); 
 | 
  
 | 
    return 0; 
 | 
  
 | 
err_dec_reg: 
 | 
    destroy_workqueue(dev->decode_workqueue); 
 | 
err_event_workq: 
 | 
    v4l2_m2m_release(dev->m2m_dev_dec); 
 | 
err_dec_mem_init: 
 | 
    video_unregister_device(vfd_dec); 
 | 
err_dec_alloc: 
 | 
    v4l2_device_unregister(&dev->v4l2_dev); 
 | 
err_res: 
 | 
    mtk_vcodec_release_dec_pm(dev); 
 | 
err_dec_pm: 
 | 
    mtk_vcodec_fw_release(dev->fw_handler); 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static const struct of_device_id mtk_vcodec_match[] = { 
 | 
    {.compatible = "mediatek,mt8173-vcodec-dec",}, 
 | 
    {}, 
 | 
}; 
 | 
  
 | 
MODULE_DEVICE_TABLE(of, mtk_vcodec_match); 
 | 
  
 | 
static int mtk_vcodec_dec_remove(struct platform_device *pdev) 
 | 
{ 
 | 
    struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); 
 | 
  
 | 
    flush_workqueue(dev->decode_workqueue); 
 | 
    destroy_workqueue(dev->decode_workqueue); 
 | 
    if (dev->m2m_dev_dec) 
 | 
        v4l2_m2m_release(dev->m2m_dev_dec); 
 | 
  
 | 
    if (dev->vfd_dec) 
 | 
        video_unregister_device(dev->vfd_dec); 
 | 
  
 | 
    v4l2_device_unregister(&dev->v4l2_dev); 
 | 
    mtk_vcodec_release_dec_pm(dev); 
 | 
    mtk_vcodec_fw_release(dev->fw_handler); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static struct platform_driver mtk_vcodec_dec_driver = { 
 | 
    .probe    = mtk_vcodec_probe, 
 | 
    .remove    = mtk_vcodec_dec_remove, 
 | 
    .driver    = { 
 | 
        .name    = MTK_VCODEC_DEC_NAME, 
 | 
        .of_match_table = mtk_vcodec_match, 
 | 
    }, 
 | 
}; 
 | 
  
 | 
module_platform_driver(mtk_vcodec_dec_driver); 
 | 
  
 | 
MODULE_LICENSE("GPL v2"); 
 | 
MODULE_DESCRIPTION("Mediatek video codec V4L2 decoder driver"); 
 |