// 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 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) { struct device *dev; if (IS_ERR_OR_NULL(vpu)) goto err_return; dev = scp_get_device(vpu->scp); if (!vpu->param) { vpu->param = dma_alloc_wc(dev, vpu->param_size, &vpu->param_addr, GFP_KERNEL); if (!vpu->param) goto err_return; } if (!vpu->work) { vpu->work = dma_alloc_wc(dev, vpu->work_size, &vpu->work_addr, GFP_KERNEL); if (!vpu->work) goto err_free_param; } if (!vpu->config) { vpu->config = dma_alloc_wc(dev, vpu->config_size, &vpu->config_addr, GFP_KERNEL); if (!vpu->config) goto err_free_work; } return 0; err_free_work: dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr); vpu->work = NULL; err_free_param: dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr); vpu->param = NULL; err_return: return -ENOMEM; } void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu) { struct device *dev; if (IS_ERR_OR_NULL(vpu)) return; dev = scp_get_device(vpu->scp); if (vpu->param && vpu->param_addr) dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr); if (vpu->work && vpu->work_addr) dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr); if (vpu->config && vpu->config_addr) dma_free_wc(dev, vpu->config_size, vpu->config, vpu->config_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_dev *vpu = (struct mdp_vpu_dev *)(unsigned long)param->drv_data; if (param->state) { struct mdp_dev *mdp = vpu_to_mdp(vpu); dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state); } vpu->status = param->state; complete(&vpu->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, }; 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 */ mutex_lock(vpu->lock); vpu->work_size = ALIGN(vpu->work_size, 64); vpu->param_size = ALIGN(sizeof(struct img_ipi_frameparam), 64); vpu->config_size = ALIGN(sizeof(struct img_config), 64); err = mdp_vpu_shared_mem_alloc(vpu); mutex_unlock(vpu->lock); if (err) { dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); goto err_mem_alloc; } dev_dbg(&mdp->pdev->dev, "VPU param:%pK pa:%pad sz:%zx, work:%pK pa:%pad sz:%zx, config:%pK pa:%pad sz:%zx", vpu->param, &vpu->param_addr, vpu->param_size, vpu->work, &vpu->work_addr, vpu->work_size, vpu->config, &vpu->config_addr, vpu->config_size); 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; 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_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)); } int mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param) { struct mdp_dev *mdp = vpu_to_mdp(vpu); struct img_sw_addr addr; mutex_lock(vpu->lock); if (mdp_vpu_shared_mem_alloc(vpu)) { dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); mutex_unlock(vpu->lock); return -ENOMEM; } memset(vpu->param, 0, vpu->param_size); memset(vpu->work, 0, vpu->work_size); memset(vpu->config, 0, vpu->config_size); param->self_data.va = (unsigned long)vpu->work; param->self_data.pa = vpu->work_addr; param->config_data.va = (unsigned long)vpu->config; param->config_data.pa = vpu->config_addr; param->drv_data = (unsigned long)vpu; memcpy(vpu->param, param, sizeof(*param)); addr.pa = vpu->param_addr; addr.va = (unsigned long)vpu->param; mutex_unlock(vpu->lock); return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_FRAME, &addr, sizeof(addr)); }