// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022 MediaTek Inc. * Author: Ping-Hsun Wu */ #include #include #include "mtk-mdp3-cmdq.h" #include "mtk-mdp3-comp.h" #include "mtk-mdp3-core.h" #include "mtk-mdp3-m2m.h" #include "mtk-img-ipi.h" #define MDP_PATH_MAX_COMPS IMG_MAX_COMPONENTS struct mdp_path { struct mdp_dev *mdp_dev; struct mdp_comp_ctx comps[MDP_PATH_MAX_COMPS]; u32 num_comps; const struct img_config *config; const struct img_ipi_frameparam *param; const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS]; struct v4l2_rect bounds[IMG_MAX_HW_OUTPUTS]; }; #define has_op(ctx, op) \ ((ctx)->comp->ops && (ctx)->comp->ops->op) #define call_op(ctx, op, ...) \ (has_op(ctx, op) ? (ctx)->comp->ops->op(ctx, ##__VA_ARGS__) : 0) static bool is_output_disabled(int p_id, const struct img_compparam *param, u32 count) { u32 num = 0; bool dis_output = false; bool dis_tile = false; if (CFG_CHECK(MT8183, p_id)) { num = CFG_COMP(MT8183, param, num_subfrms); dis_output = CFG_COMP(MT8183, param, frame.output_disable); dis_tile = CFG_COMP(MT8183, param, frame.output_disable); } return (count < num) ? (dis_output || dis_tile) : true; } static int mdp_path_subfrm_require(const struct mdp_path *path, struct mdp_cmdq_cmd *cmd, s32 *mutex_id, u32 count) { const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; const struct mdp_comp_ctx *ctx; const struct mtk_mdp_driver_data *data = path->mdp_dev->mdp_data; struct device *dev = &path->mdp_dev->pdev->dev; struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; int id, index; u32 num_comp = 0; if (CFG_CHECK(MT8183, p_id)) num_comp = CFG_GET(MT8183, path->config, num_components); /* Decide which mutex to use based on the current pipeline */ switch (path->comps[0].comp->public_id) { case MDP_COMP_RDMA0: index = MDP_PIPE_RDMA0; break; case MDP_COMP_ISP_IMGI: index = MDP_PIPE_IMGI; break; case MDP_COMP_WPEI: index = MDP_PIPE_WPEI; break; case MDP_COMP_WPEI2: index = MDP_PIPE_WPEI2; break; default: dev_err(dev, "Unknown pipeline and no mutex is assigned"); return -EINVAL; } *mutex_id = data->pipe_info[index].mutex_id; /* Set mutex mod */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; if (is_output_disabled(p_id, ctx->param, count)) continue; id = ctx->comp->public_id; mtk_mutex_write_mod(mutex[*mutex_id], data->mdp_mutex_table_idx[id], false); } mtk_mutex_write_sof(mutex[*mutex_id], MUTEX_SOF_IDX_SINGLE_MODE); return 0; } static int mdp_path_subfrm_run(const struct mdp_path *path, struct mdp_cmdq_cmd *cmd, s32 *mutex_id, u32 count) { const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; const struct mdp_comp_ctx *ctx; struct device *dev = &path->mdp_dev->pdev->dev; struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; int index; u32 num_comp = 0; s32 event; if (-1 == *mutex_id) { dev_err(dev, "Incorrect mutex id"); return -EINVAL; } if (CFG_CHECK(MT8183, p_id)) num_comp = CFG_GET(MT8183, path->config, num_components); /* Wait WROT SRAM shared to DISP RDMA */ /* Clear SOF event for each engine */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; if (is_output_disabled(p_id, ctx->param, count)) continue; event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; if (event != MDP_GCE_NO_EVENT) MM_REG_CLEAR(cmd, event); } /* Enable the mutex */ mtk_mutex_enable_by_cmdq(mutex[*mutex_id], (void *)&cmd->pkt); /* Wait SOF events and clear mutex modules (optional) */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; if (is_output_disabled(p_id, ctx->param, count)) continue; event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; if (event != MDP_GCE_NO_EVENT) MM_REG_WAIT(cmd, event); } return 0; } static int mdp_path_ctx_init(struct mdp_dev *mdp, struct mdp_path *path) { const int p_id = mdp->mdp_data->mdp_plat_id; void *param = NULL; int index, ret; u32 num_comp = 0; if (CFG_CHECK(MT8183, p_id)) num_comp = CFG_GET(MT8183, path->config, num_components); if (num_comp < 1) return -EINVAL; for (index = 0; index < num_comp; index++) { if (CFG_CHECK(MT8183, p_id)) param = (void *)CFG_ADDR(MT8183, path->config, components[index]); ret = mdp_comp_ctx_config(mdp, &path->comps[index], param, path->param); if (ret) return ret; } return 0; } static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, struct mdp_path *path, u32 count) { const int p_id = path->mdp_dev->mdp_data->mdp_plat_id; const struct img_mmsys_ctrl *ctrl = NULL; const struct img_mux *set; struct mdp_comp_ctx *ctx; s32 mutex_id; int index, ret; u32 num_comp = 0; if (CFG_CHECK(MT8183, p_id)) num_comp = CFG_GET(MT8183, path->config, num_components); if (CFG_CHECK(MT8183, p_id)) ctrl = CFG_ADDR(MT8183, path->config, ctrls[count]); /* Acquire components */ ret = mdp_path_subfrm_require(path, cmd, &mutex_id, count); if (ret) return ret; /* Enable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, set->value, 0xFFFFFFFF); } /* Config sub-frame information */ for (index = (num_comp - 1); index >= 0; index--) { ctx = &path->comps[index]; if (is_output_disabled(p_id, ctx->param, count)) continue; ret = call_op(ctx, config_subfrm, cmd, count); if (ret) return ret; } /* Run components */ ret = mdp_path_subfrm_run(path, cmd, &mutex_id, count); if (ret) return ret; /* Wait components done */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; if (is_output_disabled(p_id, ctx->param, count)) continue; ret = call_op(ctx, wait_comp_event, cmd); if (ret) return ret; } /* Advance to the next sub-frame */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, advance_subfrm, cmd, count); if (ret) return ret; } /* Disable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, 0, 0xFFFFFFFF); } return 0; } static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, struct mdp_path *path) { const int p_id = mdp->mdp_data->mdp_plat_id; struct mdp_comp_ctx *ctx; int index, count, ret; u32 num_comp = 0; u32 num_sub = 0; if (CFG_CHECK(MT8183, p_id)) num_comp = CFG_GET(MT8183, path->config, num_components); if (CFG_CHECK(MT8183, p_id)) num_sub = CFG_GET(MT8183, path->config, num_subfrms); /* Config path frame */ /* Reset components */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, init_comp, cmd); if (ret) return ret; } /* Config frame mode */ for (index = 0; index < num_comp; index++) { const struct v4l2_rect *compose; u32 out = 0; ctx = &path->comps[index]; if (CFG_CHECK(MT8183, p_id)) out = CFG_COMP(MT8183, ctx->param, outputs[0]); compose = path->composes[out]; ret = call_op(ctx, config_frame, cmd, compose); if (ret) return ret; } /* Config path sub-frames */ for (count = 0; count < num_sub; count++) { ret = mdp_path_config_subfrm(cmd, path, count); if (ret) return ret; } /* Post processing information */ for (index = 0; index < num_comp; index++) { ctx = &path->comps[index]; ret = call_op(ctx, post_process, cmd); if (ret) return ret; } return 0; } static int mdp_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t size) { struct device *dev; dma_addr_t dma_addr; pkt->va_base = kzalloc(size, GFP_KERNEL); if (!pkt->va_base) return -ENOMEM; pkt->buf_size = size; pkt->cl = (void *)client; dev = client->chan->mbox->dev; dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_addr)) { dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); kfree(pkt->va_base); return -ENOMEM; } pkt->pa_base = dma_addr; return 0; } static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt) { struct cmdq_client *client = (struct cmdq_client *)pkt->cl; dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, DMA_TO_DEVICE); kfree(pkt->va_base); pkt->va_base = NULL; } static void mdp_auto_release_work(struct work_struct *work) { struct mdp_cmdq_cmd *cmd; struct mdp_dev *mdp; int id; cmd = container_of(work, struct mdp_cmdq_cmd, auto_release_work); mdp = cmd->mdp; id = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; mtk_mutex_unprepare(mdp->mdp_mutex[id]); mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); atomic_dec(&mdp->job_count); wake_up(&mdp->callback_wq); mdp_cmdq_pkt_destroy(&cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); cmd = NULL; } static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) { struct mdp_cmdq_cmd *cmd; struct cmdq_cb_data *data; struct mdp_dev *mdp; struct device *dev; int id; if (!mssg) { pr_info("%s:no callback data\n", __func__); return; } data = (struct cmdq_cb_data *)mssg; cmd = container_of(data->pkt, struct mdp_cmdq_cmd, pkt); mdp = cmd->mdp; dev = &mdp->pdev->dev; if (cmd->mdp_ctx) mdp_m2m_job_finish(cmd->mdp_ctx); if (cmd->user_cmdq_cb) { struct cmdq_cb_data user_cb_data; user_cb_data.sta = data->sta; user_cb_data.pkt = data->pkt; cmd->user_cmdq_cb(user_cb_data); } INIT_WORK(&cmd->auto_release_work, mdp_auto_release_work); if (!queue_work(mdp->clock_wq, &cmd->auto_release_work)) { dev_err(dev, "%s:queue_work fail!\n", __func__); id = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; mtk_mutex_unprepare(mdp->mdp_mutex[id]); mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); atomic_dec(&mdp->job_count); wake_up(&mdp->callback_wq); mdp_cmdq_pkt_destroy(&cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); cmd = NULL; } } int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) { struct mdp_path *path = NULL; struct mdp_cmdq_cmd *cmd = NULL; struct mdp_comp *comps = NULL; struct device *dev = &mdp->pdev->dev; const int p_id = mdp->mdp_data->mdp_plat_id; int i, ret; u32 num_comp = 0; atomic_inc(&mdp->job_count); if (atomic_read(&mdp->suspended)) { atomic_dec(&mdp->job_count); return -ECANCELED; } cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto err_cancel_job; } ret = mdp_cmdq_pkt_create(mdp->cmdq_clt, &cmd->pkt, SZ_16K); if (ret) goto err_free_cmd; if (CFG_CHECK(MT8183, p_id)) { num_comp = CFG_GET(MT8183, param->config, num_components); } else { ret = -EINVAL; goto err_destroy_pkt; } comps = kcalloc(num_comp, sizeof(*comps), GFP_KERNEL); if (!comps) { ret = -ENOMEM; goto err_destroy_pkt; } path = kzalloc(sizeof(*path), GFP_KERNEL); if (!path) { ret = -ENOMEM; goto err_free_comps; } i = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; ret = mtk_mutex_prepare(mdp->mdp_mutex[i]); if (ret) { dev_err(dev, "Fail to enable mutex clk\n"); goto err_free_path; } path->mdp_dev = mdp; path->config = param->config; path->param = param->param; for (i = 0; i < param->param->num_outputs; i++) { path->bounds[i].left = 0; path->bounds[i].top = 0; path->bounds[i].width = param->param->outputs[i].buffer.format.width; path->bounds[i].height = param->param->outputs[i].buffer.format.height; path->composes[i] = param->composes[i] ? param->composes[i] : &path->bounds[i]; } ret = mdp_path_ctx_init(mdp, path); if (ret) { dev_err(dev, "mdp_path_ctx_init error\n"); goto err_free_path; } ret = mdp_path_config(mdp, cmd, path); if (ret) { dev_err(dev, "mdp_path_config error\n"); goto err_free_path; } cmdq_pkt_finalize(&cmd->pkt); for (i = 0; i < num_comp; i++) memcpy(&comps[i], path->comps[i].comp, sizeof(struct mdp_comp)); mdp->cmdq_clt->client.rx_callback = mdp_handle_cmdq_callback; cmd->mdp = mdp; cmd->user_cmdq_cb = param->cmdq_cb; cmd->user_cb_data = param->cb_data; cmd->comps = comps; cmd->num_comps = num_comp; cmd->mdp_ctx = param->mdp_ctx; ret = mdp_comp_clocks_on(&mdp->pdev->dev, cmd->comps, cmd->num_comps); if (ret) goto err_free_path; dma_sync_single_for_device(mdp->cmdq_clt->chan->mbox->dev, cmd->pkt.pa_base, cmd->pkt.cmd_buf_size, DMA_TO_DEVICE); ret = mbox_send_message(mdp->cmdq_clt->chan, &cmd->pkt); if (ret < 0) { dev_err(dev, "mbox send message fail %d!\n", ret); goto err_clock_off; } mbox_client_txdone(mdp->cmdq_clt->chan, 0); kfree(path); return 0; err_clock_off: mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, cmd->num_comps); err_free_path: i = mdp->mdp_data->pipe_info[MDP_PIPE_RDMA0].mutex_id; mtk_mutex_unprepare(mdp->mdp_mutex[i]); kfree(path); err_free_comps: kfree(comps); err_destroy_pkt: mdp_cmdq_pkt_destroy(&cmd->pkt); err_free_cmd: kfree(cmd); err_cancel_job: atomic_dec(&mdp->job_count); return ret; } EXPORT_SYMBOL_GPL(mdp_cmdq_send);