162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 MediaTek Inc.
462306a36Sopenharmony_ci * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/remoteproc.h>
862306a36Sopenharmony_ci#include <linux/remoteproc/mtk_scp.h>
962306a36Sopenharmony_ci#include "mtk-mdp3-vpu.h"
1062306a36Sopenharmony_ci#include "mtk-mdp3-core.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define MDP_VPU_MESSAGE_TIMEOUT 500U
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	return container_of(vpu, struct mdp_dev, vpu);
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct device *dev;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(vpu))
2462306a36Sopenharmony_ci		goto err_return;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	dev = scp_get_device(vpu->scp);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (!vpu->param) {
2962306a36Sopenharmony_ci		vpu->param = dma_alloc_wc(dev, vpu->param_size,
3062306a36Sopenharmony_ci					  &vpu->param_addr, GFP_KERNEL);
3162306a36Sopenharmony_ci		if (!vpu->param)
3262306a36Sopenharmony_ci			goto err_return;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (!vpu->work) {
3662306a36Sopenharmony_ci		vpu->work = dma_alloc_wc(dev, vpu->work_size,
3762306a36Sopenharmony_ci					 &vpu->work_addr, GFP_KERNEL);
3862306a36Sopenharmony_ci		if (!vpu->work)
3962306a36Sopenharmony_ci			goto err_free_param;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!vpu->config) {
4362306a36Sopenharmony_ci		vpu->config = dma_alloc_wc(dev, vpu->config_size,
4462306a36Sopenharmony_ci					   &vpu->config_addr, GFP_KERNEL);
4562306a36Sopenharmony_ci		if (!vpu->config)
4662306a36Sopenharmony_ci			goto err_free_work;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cierr_free_work:
5262306a36Sopenharmony_ci	dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr);
5362306a36Sopenharmony_ci	vpu->work = NULL;
5462306a36Sopenharmony_cierr_free_param:
5562306a36Sopenharmony_ci	dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr);
5662306a36Sopenharmony_ci	vpu->param = NULL;
5762306a36Sopenharmony_cierr_return:
5862306a36Sopenharmony_ci	return -ENOMEM;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_civoid mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct device *dev;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(vpu))
6662306a36Sopenharmony_ci		return;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	dev = scp_get_device(vpu->scp);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (vpu->param && vpu->param_addr)
7162306a36Sopenharmony_ci		dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (vpu->work && vpu->work_addr)
7462306a36Sopenharmony_ci		dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (vpu->config && vpu->config_addr)
7762306a36Sopenharmony_ci		dma_free_wc(dev, vpu->config_size, vpu->config, vpu->config_addr);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len,
8162306a36Sopenharmony_ci					void *priv)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data;
8462306a36Sopenharmony_ci	struct mdp_vpu_dev *vpu =
8562306a36Sopenharmony_ci		(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (!vpu->work_size)
8862306a36Sopenharmony_ci		vpu->work_size = msg->work_size;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	vpu->status = msg->status;
9162306a36Sopenharmony_ci	complete(&vpu->ipi_acked);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len,
9562306a36Sopenharmony_ci					  void *priv)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data;
9862306a36Sopenharmony_ci	struct mdp_vpu_dev *vpu =
9962306a36Sopenharmony_ci		(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	vpu->status = msg->status;
10262306a36Sopenharmony_ci	complete(&vpu->ipi_acked);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len,
10662306a36Sopenharmony_ci					 void *priv)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct img_sw_addr *addr = (struct img_sw_addr *)data;
10962306a36Sopenharmony_ci	struct img_ipi_frameparam *param =
11062306a36Sopenharmony_ci		(struct img_ipi_frameparam *)(unsigned long)addr->va;
11162306a36Sopenharmony_ci	struct mdp_vpu_dev *vpu =
11262306a36Sopenharmony_ci		(struct mdp_vpu_dev *)(unsigned long)param->drv_data;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (param->state) {
11562306a36Sopenharmony_ci		struct mdp_dev *mdp = vpu_to_mdp(vpu);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	vpu->status = param->state;
12062306a36Sopenharmony_ci	complete(&vpu->ipi_acked);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint mdp_vpu_register(struct mdp_dev *mdp)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int err;
12662306a36Sopenharmony_ci	struct mtk_scp *scp = mdp->scp;
12762306a36Sopenharmony_ci	struct device *dev = &mdp->pdev->dev;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = scp_ipi_register(scp, SCP_IPI_MDP_INIT,
13062306a36Sopenharmony_ci			       mdp_vpu_ipi_handle_init_ack, NULL);
13162306a36Sopenharmony_ci	if (err) {
13262306a36Sopenharmony_ci		dev_err(dev, "scp_ipi_register failed %d\n", err);
13362306a36Sopenharmony_ci		goto err_ipi_init;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	err = scp_ipi_register(scp, SCP_IPI_MDP_DEINIT,
13662306a36Sopenharmony_ci			       mdp_vpu_ipi_handle_deinit_ack, NULL);
13762306a36Sopenharmony_ci	if (err) {
13862306a36Sopenharmony_ci		dev_err(dev, "scp_ipi_register failed %d\n", err);
13962306a36Sopenharmony_ci		goto err_ipi_deinit;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	err = scp_ipi_register(scp, SCP_IPI_MDP_FRAME,
14262306a36Sopenharmony_ci			       mdp_vpu_ipi_handle_frame_ack, NULL);
14362306a36Sopenharmony_ci	if (err) {
14462306a36Sopenharmony_ci		dev_err(dev, "scp_ipi_register failed %d\n", err);
14562306a36Sopenharmony_ci		goto err_ipi_frame;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cierr_ipi_frame:
15062306a36Sopenharmony_ci	scp_ipi_unregister(scp, SCP_IPI_MDP_DEINIT);
15162306a36Sopenharmony_cierr_ipi_deinit:
15262306a36Sopenharmony_ci	scp_ipi_unregister(scp, SCP_IPI_MDP_INIT);
15362306a36Sopenharmony_cierr_ipi_init:
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return err;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_civoid mdp_vpu_unregister(struct mdp_dev *mdp)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_INIT);
16162306a36Sopenharmony_ci	scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_DEINIT);
16262306a36Sopenharmony_ci	scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_FRAME);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id,
16662306a36Sopenharmony_ci			   void *buf, unsigned int len)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct mdp_dev *mdp = vpu_to_mdp(vpu);
16962306a36Sopenharmony_ci	unsigned int t = MDP_VPU_MESSAGE_TIMEOUT;
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!vpu->scp) {
17362306a36Sopenharmony_ci		dev_dbg(&mdp->pdev->dev, "vpu scp is NULL");
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	ret = scp_ipi_send(vpu->scp, id, buf, len, 2000);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (ret) {
17962306a36Sopenharmony_ci		dev_err(&mdp->pdev->dev, "scp_ipi_send failed %d\n", ret);
18062306a36Sopenharmony_ci		return -EPERM;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&vpu->ipi_acked,
18362306a36Sopenharmony_ci					  msecs_to_jiffies(t));
18462306a36Sopenharmony_ci	if (!ret)
18562306a36Sopenharmony_ci		ret = -ETIME;
18662306a36Sopenharmony_ci	else if (vpu->status)
18762306a36Sopenharmony_ci		ret = -EINVAL;
18862306a36Sopenharmony_ci	else
18962306a36Sopenharmony_ci		ret = 0;
19062306a36Sopenharmony_ci	return ret;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciint mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp,
19462306a36Sopenharmony_ci		     struct mutex *lock)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct mdp_ipi_init_msg msg = {
19762306a36Sopenharmony_ci		.drv_data = (unsigned long)vpu,
19862306a36Sopenharmony_ci	};
19962306a36Sopenharmony_ci	struct mdp_dev *mdp = vpu_to_mdp(vpu);
20062306a36Sopenharmony_ci	int err;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	init_completion(&vpu->ipi_acked);
20362306a36Sopenharmony_ci	vpu->scp = scp;
20462306a36Sopenharmony_ci	vpu->lock = lock;
20562306a36Sopenharmony_ci	vpu->work_size = 0;
20662306a36Sopenharmony_ci	err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
20762306a36Sopenharmony_ci	if (err)
20862306a36Sopenharmony_ci		goto err_work_size;
20962306a36Sopenharmony_ci	/* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	mutex_lock(vpu->lock);
21262306a36Sopenharmony_ci	vpu->work_size = ALIGN(vpu->work_size, 64);
21362306a36Sopenharmony_ci	vpu->param_size = ALIGN(sizeof(struct img_ipi_frameparam), 64);
21462306a36Sopenharmony_ci	vpu->config_size = ALIGN(sizeof(struct img_config), 64);
21562306a36Sopenharmony_ci	err = mdp_vpu_shared_mem_alloc(vpu);
21662306a36Sopenharmony_ci	mutex_unlock(vpu->lock);
21762306a36Sopenharmony_ci	if (err) {
21862306a36Sopenharmony_ci		dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
21962306a36Sopenharmony_ci		goto err_mem_alloc;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	dev_dbg(&mdp->pdev->dev,
22362306a36Sopenharmony_ci		"VPU param:%pK pa:%pad sz:%zx, work:%pK pa:%pad sz:%zx, config:%pK pa:%pad sz:%zx",
22462306a36Sopenharmony_ci		vpu->param, &vpu->param_addr, vpu->param_size,
22562306a36Sopenharmony_ci		vpu->work, &vpu->work_addr, vpu->work_size,
22662306a36Sopenharmony_ci		vpu->config, &vpu->config_addr, vpu->config_size);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	msg.work_addr = vpu->work_addr;
22962306a36Sopenharmony_ci	msg.work_size = vpu->work_size;
23062306a36Sopenharmony_ci	err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
23162306a36Sopenharmony_ci	if (err)
23262306a36Sopenharmony_ci		goto err_work_size;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cierr_work_size:
23762306a36Sopenharmony_ci	switch (vpu->status) {
23862306a36Sopenharmony_ci	case -MDP_IPI_EBUSY:
23962306a36Sopenharmony_ci		err = -EBUSY;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	case -MDP_IPI_ENOMEM:
24262306a36Sopenharmony_ci		err = -ENOSPC;	/* -ENOMEM */
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	return err;
24662306a36Sopenharmony_cierr_mem_alloc:
24762306a36Sopenharmony_ci	return err;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct mdp_ipi_deinit_msg msg = {
25362306a36Sopenharmony_ci		.drv_data = (unsigned long)vpu,
25462306a36Sopenharmony_ci		.work_addr = vpu->work_addr,
25562306a36Sopenharmony_ci	};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg));
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciint mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct mdp_dev *mdp = vpu_to_mdp(vpu);
26362306a36Sopenharmony_ci	struct img_sw_addr addr;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	mutex_lock(vpu->lock);
26662306a36Sopenharmony_ci	if (mdp_vpu_shared_mem_alloc(vpu)) {
26762306a36Sopenharmony_ci		dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
26862306a36Sopenharmony_ci		mutex_unlock(vpu->lock);
26962306a36Sopenharmony_ci		return -ENOMEM;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	memset(vpu->param, 0, vpu->param_size);
27362306a36Sopenharmony_ci	memset(vpu->work, 0, vpu->work_size);
27462306a36Sopenharmony_ci	memset(vpu->config, 0, vpu->config_size);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	param->self_data.va = (unsigned long)vpu->work;
27762306a36Sopenharmony_ci	param->self_data.pa = vpu->work_addr;
27862306a36Sopenharmony_ci	param->config_data.va = (unsigned long)vpu->config;
27962306a36Sopenharmony_ci	param->config_data.pa = vpu->config_addr;
28062306a36Sopenharmony_ci	param->drv_data = (unsigned long)vpu;
28162306a36Sopenharmony_ci	memcpy(vpu->param, param, sizeof(*param));
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	addr.pa = vpu->param_addr;
28462306a36Sopenharmony_ci	addr.va = (unsigned long)vpu->param;
28562306a36Sopenharmony_ci	mutex_unlock(vpu->lock);
28662306a36Sopenharmony_ci	return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_FRAME, &addr, sizeof(addr));
28762306a36Sopenharmony_ci}
288