// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2021 MediaTek Inc. * Author: Yunfei Dong */ #include #include #include #include #include #include #include #include "mtk_vcodec_dec.h" #include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_dec_pm.h" #include "../common/mtk_vcodec_intr.h" static const struct of_device_id mtk_vdec_hw_match[] = { { .compatible = "mediatek,mtk-vcodec-lat", .data = (void *)MTK_VDEC_LAT0, }, { .compatible = "mediatek,mtk-vcodec-core", .data = (void *)MTK_VDEC_CORE, }, { .compatible = "mediatek,mtk-vcodec-lat-soc", .data = (void *)MTK_VDEC_LAT_SOC, }, {}, }; MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dec_dev *vdec_dev) { struct platform_device *pdev = vdec_dev->plat_dev; struct device_node *subdev_node; enum mtk_vdec_hw_id hw_idx; const struct of_device_id *of_id; int i; for (i = 0; i < ARRAY_SIZE(mtk_vdec_hw_match); i++) { of_id = &mtk_vdec_hw_match[i]; subdev_node = of_find_compatible_node(NULL, NULL, of_id->compatible); if (!subdev_node) continue; of_node_put(subdev_node); hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; if (!test_bit(hw_idx, vdec_dev->subdev_bitmap)) { dev_err(&pdev->dev, "vdec %d is not ready", hw_idx); return -EAGAIN; } } return 0; } static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) { struct mtk_vdec_hw_dev *dev = priv; struct mtk_vcodec_dec_ctx *ctx; u32 cg_status; unsigned int dec_done_status; void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] + VDEC_IRQ_CFG_REG; ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx); /* check if HW active or not */ cg_status = readl(dev->reg_base[VDEC_HW_SYS] + VDEC_HW_ACTIVE_ADDR); if (cg_status & VDEC_HW_ACTIVE_MASK) { mtk_v4l2_vdec_err(ctx, "vdec active is not 0x0 (0x%08x)", cg_status); return IRQ_HANDLED; } dec_done_status = readl(vdec_misc_addr); if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) return IRQ_HANDLED; /* clear interrupt */ writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr); writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr); wake_up_dec_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); mtk_v4l2_vdec_dbg(3, ctx, "wake up ctx %d, dec_done_status=%x", ctx->id, dec_done_status); return IRQ_HANDLED; } static int mtk_vdec_hw_init_irq(struct mtk_vdec_hw_dev *dev) { struct platform_device *pdev = dev->plat_dev; int ret; dev->dec_irq = platform_get_irq(pdev, 0); if (dev->dec_irq < 0) return dev->dec_irq; irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, dev->dec_irq, mtk_vdec_hw_irq_handler, 0, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", dev->dec_irq, ret); return ret; } return 0; } static int mtk_vdec_hw_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_vdec_hw_dev *subdev_dev; struct mtk_vcodec_dec_dev *main_dev; const struct of_device_id *of_id; int hw_idx; int ret; if (!dev->parent) { dev_err(dev, "no parent for hardware devices.\n"); return -ENODEV; } main_dev = dev_get_drvdata(dev->parent); if (!main_dev) { dev_err(dev, "failed to get parent driver data"); return -EINVAL; } subdev_dev = devm_kzalloc(dev, sizeof(*subdev_dev), GFP_KERNEL); if (!subdev_dev) return -ENOMEM; subdev_dev->plat_dev = pdev; ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm); if (ret) return ret; ret = devm_pm_runtime_enable(&pdev->dev); if (ret) return ret; of_id = of_match_device(mtk_vdec_hw_match, dev); if (!of_id) { dev_err(dev, "Can't get vdec subdev id.\n"); return -EINVAL; } hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; if (hw_idx >= MTK_VDEC_HW_MAX) { dev_err(dev, "Hardware index %d not correct.\n", hw_idx); return -EINVAL; } main_dev->subdev_dev[hw_idx] = subdev_dev; subdev_dev->hw_idx = hw_idx; subdev_dev->main_dev = main_dev; subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS]; set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap); if (IS_SUPPORT_VDEC_HW_IRQ(hw_idx)) { ret = mtk_vdec_hw_init_irq(subdev_dev); if (ret) return ret; } subdev_dev->reg_base[VDEC_HW_MISC] = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC])) { ret = PTR_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC]); return ret; } if (!main_dev->subdev_prob_done) main_dev->subdev_prob_done = mtk_vdec_hw_prob_done; platform_set_drvdata(pdev, subdev_dev); return 0; } static struct platform_driver mtk_vdec_driver = { .probe = mtk_vdec_hw_probe, .driver = { .name = "mtk-vdec-comp", .of_match_table = mtk_vdec_hw_match, }, }; module_platform_driver(mtk_vdec_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Mediatek video decoder hardware driver");