// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022 MediaTek Inc. * Author: Ping-Hsun Wu */ #include #include #include "mtk-mdp3-vpu.h" #include "mtk-mdp3-core.h" #define MDP_VPU_MESSAGE_TIMEOUT 500U #define vpu_alloc_size 0x600000 static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu) { return container_of(vpu, struct mdp_dev, vpu); } static int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu) { if (vpu->work && vpu->work_addr) return 0; vpu->work = dma_alloc_coherent(scp_get_device(vpu->scp), vpu_alloc_size, &vpu->work_addr, GFP_KERNEL); if (!vpu->work) return -ENOMEM; else return 0; } void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu) { if (vpu->work && vpu->work_addr) dma_free_coherent(scp_get_device(vpu->scp), vpu_alloc_size, vpu->work, vpu->work_addr); } static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len, void *priv) { struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data; struct mdp_vpu_dev *vpu = (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; if (!vpu->work_size) vpu->work_size = msg->work_size; vpu->status = msg->status; complete(&vpu->ipi_acked); } static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len, void *priv) { struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data; struct mdp_vpu_dev *vpu = (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; vpu->status = msg->status; complete(&vpu->ipi_acked); } static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len, void *priv) { struct img_sw_addr *addr = (struct img_sw_addr *)data; struct img_ipi_frameparam *param = (struct img_ipi_frameparam *)(unsigned long)addr->va; struct mdp_vpu_ctx *ctx = (struct mdp_vpu_ctx *)(unsigned long)param->drv_data; if (param->state) { struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev); dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state); } ctx->vpu_dev->status = param->state; complete(&ctx->vpu_dev->ipi_acked); } int mdp_vpu_register(struct mdp_dev *mdp) { int err; struct mtk_scp *scp = mdp->scp; struct device *dev = &mdp->pdev->dev; err = scp_ipi_register(scp, SCP_IPI_MDP_INIT, mdp_vpu_ipi_handle_init_ack, NULL); if (err) { dev_err(dev, "scp_ipi_register failed %d\n", err); goto err_ipi_init; } err = scp_ipi_register(scp, SCP_IPI_MDP_DEINIT, mdp_vpu_ipi_handle_deinit_ack, NULL); if (err) { dev_err(dev, "scp_ipi_register failed %d\n", err); goto err_ipi_deinit; } err = scp_ipi_register(scp, SCP_IPI_MDP_FRAME, mdp_vpu_ipi_handle_frame_ack, NULL); if (err) { dev_err(dev, "scp_ipi_register failed %d\n", err); goto err_ipi_frame; } return 0; err_ipi_frame: scp_ipi_unregister(scp, SCP_IPI_MDP_DEINIT); err_ipi_deinit: scp_ipi_unregister(scp, SCP_IPI_MDP_INIT); err_ipi_init: return err; } void mdp_vpu_unregister(struct mdp_dev *mdp) { scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_INIT); scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_DEINIT); scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_FRAME); } static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id, void *buf, unsigned int len) { struct mdp_dev *mdp = vpu_to_mdp(vpu); unsigned int t = MDP_VPU_MESSAGE_TIMEOUT; int ret; if (!vpu->scp) { dev_dbg(&mdp->pdev->dev, "vpu scp is NULL"); return -EINVAL; } ret = scp_ipi_send(vpu->scp, id, buf, len, 2000); if (ret) { dev_err(&mdp->pdev->dev, "scp_ipi_send failed %d\n", ret); return -EPERM; } ret = wait_for_completion_timeout(&vpu->ipi_acked, msecs_to_jiffies(t)); if (!ret) ret = -ETIME; else if (vpu->status) ret = -EINVAL; else ret = 0; return ret; } int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, struct mutex *lock) { struct mdp_ipi_init_msg msg = { .drv_data = (unsigned long)vpu, }; size_t mem_size; phys_addr_t pool; const size_t pool_size = sizeof(struct mdp_config_pool); struct mdp_dev *mdp = vpu_to_mdp(vpu); int err; init_completion(&vpu->ipi_acked); vpu->scp = scp; vpu->lock = lock; vpu->work_size = 0; err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); if (err) goto err_work_size; /* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */ mem_size = vpu_alloc_size; if (mdp_vpu_shared_mem_alloc(vpu)) { dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); goto err_mem_alloc; } pool = ALIGN((uintptr_t)vpu->work + vpu->work_size, 8); if (pool + pool_size - (uintptr_t)vpu->work > mem_size) { dev_err(&mdp->pdev->dev, "VPU memory insufficient: %zx + %zx > %zx", vpu->work_size, pool_size, mem_size); err = -ENOMEM; goto err_mem_size; } dev_dbg(&mdp->pdev->dev, "VPU work:%pK pa:%pad sz:%zx pool:%pa sz:%zx (mem sz:%zx)", vpu->work, &vpu->work_addr, vpu->work_size, &pool, pool_size, mem_size); vpu->pool = (struct mdp_config_pool *)(uintptr_t)pool; msg.work_addr = vpu->work_addr; msg.work_size = vpu->work_size; err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); if (err) goto err_work_size; memset(vpu->pool, 0, sizeof(*vpu->pool)); return 0; err_work_size: switch (vpu->status) { case -MDP_IPI_EBUSY: err = -EBUSY; break; case -MDP_IPI_ENOMEM: err = -ENOSPC; /* -ENOMEM */ break; } return err; err_mem_size: err_mem_alloc: return err; } int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu) { struct mdp_ipi_deinit_msg msg = { .drv_data = (unsigned long)vpu, .work_addr = vpu->work_addr, }; return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg)); } static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu, enum mdp_config_id id, uint32_t *addr) { struct img_config *config; if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) return ERR_PTR(-EINVAL); mutex_lock(vpu->lock); vpu->pool->cfg_count[id]++; config = &vpu->pool->configs[id]; *addr = vpu->work_addr + ((uintptr_t)config - (uintptr_t)vpu->work); mutex_unlock(vpu->lock); return config; } static int mdp_config_put(struct mdp_vpu_dev *vpu, enum mdp_config_id id, const struct img_config *config) { int err = 0; if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) return -EINVAL; if (vpu->lock) mutex_lock(vpu->lock); if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id]) err = -EINVAL; else vpu->pool->cfg_count[id]--; if (vpu->lock) mutex_unlock(vpu->lock); return err; } int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, enum mdp_config_id id) { ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr); if (IS_ERR(ctx->config)) { int err = PTR_ERR(ctx->config); ctx->config = NULL; return err; } ctx->config_id = id; ctx->vpu_dev = vpu; return 0; } int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx) { int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config); ctx->config_id = 0; ctx->config = NULL; ctx->inst_addr = 0; return err; } int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param) { struct mdp_vpu_dev *vpu = ctx->vpu_dev; struct mdp_dev *mdp = vpu_to_mdp(vpu); struct img_sw_addr addr; if (!ctx->vpu_dev->work || !ctx->vpu_dev->work_addr) { if (mdp_vpu_shared_mem_alloc(vpu)) { dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); return -ENOMEM; } } memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size); memset(ctx->config, 0, sizeof(*ctx->config)); param->config_data.va = (unsigned long)ctx->config; param->config_data.pa = ctx->inst_addr; param->drv_data = (unsigned long)ctx; memcpy((void *)ctx->vpu_dev->work, param, sizeof(*param)); addr.pa = ctx->vpu_dev->work_addr; addr.va = (uintptr_t)ctx->vpu_dev->work; return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME, &addr, sizeof(addr)); }