429 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2022 MediaTek Inc.4 * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>5 */6 7#include <linux/clk.h>8#include <linux/module.h>9#include <linux/of_platform.h>10#include <linux/platform_device.h>11#include <linux/pm_runtime.h>12#include <linux/remoteproc.h>13#include <linux/remoteproc/mtk_scp.h>14#include <media/videobuf2-dma-contig.h>15 16#include "mtk-mdp3-core.h"17#include "mtk-mdp3-cfg.h"18#include "mtk-mdp3-m2m.h"19 20static const struct of_device_id mdp_of_ids[] = {21 { .compatible = "mediatek,mt8183-mdp3-rdma",22 .data = &mt8183_mdp_driver_data,23 },24 { .compatible = "mediatek,mt8188-mdp3-rdma",25 .data = &mt8188_mdp_driver_data,26 },27 { .compatible = "mediatek,mt8195-mdp3-rdma",28 .data = &mt8195_mdp_driver_data,29 },30 { .compatible = "mediatek,mt8195-mdp3-wrot",31 .data = &mt8195_mdp_driver_data,32 },33 {},34};35MODULE_DEVICE_TABLE(of, mdp_of_ids);36 37static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,38 struct platform_device *from,39 enum mdp_infra_id id)40{41 struct device_node *node, *f = NULL;42 struct platform_device *mdp_pdev = NULL;43 const struct mtk_mdp_driver_data *mdp_data;44 const char *compat;45 46 if (!pdev)47 return NULL;48 49 if (id < MDP_INFRA_MMSYS || id >= MDP_INFRA_MAX) {50 dev_err(&pdev->dev, "Illegal infra id %d\n", id);51 return NULL;52 }53 54 mdp_data = of_device_get_match_data(&pdev->dev);55 if (!mdp_data) {56 dev_err(&pdev->dev, "have no driver data to find node\n");57 return NULL;58 }59 60 compat = mdp_data->mdp_probe_infra[id].compatible;61 if (strlen(compat) == 0)62 return NULL;63 64 if (from)65 f = from->dev.of_node;66 node = of_find_compatible_node(f, NULL, compat);67 if (WARN_ON(!node)) {68 dev_err(&pdev->dev, "find node from id %d failed\n", id);69 return NULL;70 }71 72 mdp_pdev = of_find_device_by_node(node);73 of_node_put(node);74 if (WARN_ON(!mdp_pdev)) {75 dev_err(&pdev->dev, "find pdev from id %d failed\n", id);76 return NULL;77 }78 79 return mdp_pdev;80}81 82struct platform_device *mdp_get_plat_device(struct platform_device *pdev)83{84 struct device *dev = &pdev->dev;85 struct device_node *mdp_node;86 struct platform_device *mdp_pdev;87 88 mdp_node = of_parse_phandle(dev->of_node, MDP_PHANDLE_NAME, 0);89 if (!mdp_node) {90 dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME);91 return NULL;92 }93 94 mdp_pdev = of_find_device_by_node(mdp_node);95 of_node_put(mdp_node);96 97 return mdp_pdev;98}99EXPORT_SYMBOL_GPL(mdp_get_plat_device);100 101int mdp_vpu_get_locked(struct mdp_dev *mdp)102{103 int ret = 0;104 105 if (mdp->vpu_count++ == 0) {106 ret = rproc_boot(mdp->rproc_handle);107 if (ret) {108 dev_err(&mdp->pdev->dev,109 "vpu_load_firmware failed %d\n", ret);110 goto err_load_vpu;111 }112 ret = mdp_vpu_register(mdp);113 if (ret) {114 dev_err(&mdp->pdev->dev,115 "mdp_vpu register failed %d\n", ret);116 goto err_reg_vpu;117 }118 ret = mdp_vpu_dev_init(&mdp->vpu, mdp->scp, &mdp->vpu_lock);119 if (ret) {120 dev_err(&mdp->pdev->dev,121 "mdp_vpu device init failed %d\n", ret);122 goto err_init_vpu;123 }124 }125 return 0;126 127err_init_vpu:128 mdp_vpu_unregister(mdp);129err_reg_vpu:130err_load_vpu:131 mdp->vpu_count--;132 return ret;133}134 135void mdp_vpu_put_locked(struct mdp_dev *mdp)136{137 if (--mdp->vpu_count == 0) {138 mdp_vpu_dev_deinit(&mdp->vpu);139 mdp_vpu_unregister(mdp);140 }141}142 143void mdp_video_device_release(struct video_device *vdev)144{145 struct mdp_dev *mdp = (struct mdp_dev *)video_get_drvdata(vdev);146 int i;147 148 for (i = 0; i < mdp->mdp_data->pp_used; i++)149 if (mdp->cmdq_clt[i])150 cmdq_mbox_destroy(mdp->cmdq_clt[i]);151 152 scp_put(mdp->scp);153 154 destroy_workqueue(mdp->job_wq);155 destroy_workqueue(mdp->clock_wq);156 157 pm_runtime_disable(&mdp->pdev->dev);158 159 vb2_dma_contig_clear_max_seg_size(&mdp->pdev->dev);160 161 mdp_comp_destroy(mdp);162 for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {163 enum mdp_mm_subsys_id idx;164 struct mtk_mutex *m;165 u32 m_id;166 167 idx = mdp->mdp_data->pipe_info[i].sub_id;168 m_id = mdp->mdp_data->pipe_info[i].mutex_id;169 m = mdp->mm_subsys[idx].mdp_mutex[m_id];170 if (!IS_ERR_OR_NULL(m))171 mtk_mutex_put(m);172 }173 174 mdp_vpu_shared_mem_free(&mdp->vpu);175 v4l2_m2m_release(mdp->m2m_dev);176 kfree(mdp);177}178 179static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)180{181 struct platform_device *mm_pdev = NULL;182 struct device **dev;183 int i;184 185 if (!mdp)186 return -EINVAL;187 188 for (i = 0; i < MDP_MM_SUBSYS_MAX; i++) {189 const char *compat;190 enum mdp_infra_id sub_id = id + i;191 192 switch (id) {193 case MDP_INFRA_MMSYS:194 dev = &mdp->mm_subsys[i].mmsys;195 break;196 case MDP_INFRA_MUTEX:197 dev = &mdp->mm_subsys[i].mutex;198 break;199 default:200 dev_err(&mdp->pdev->dev, "Unknown infra id %d", id);201 return -EINVAL;202 }203 204 /*205 * Not every chip has multiple multimedia subsystems, so206 * the config may be null.207 */208 compat = mdp->mdp_data->mdp_probe_infra[sub_id].compatible;209 if (strlen(compat) == 0)210 continue;211 212 mm_pdev = __get_pdev_by_id(mdp->pdev, mm_pdev, sub_id);213 if (WARN_ON(!mm_pdev))214 return -ENODEV;215 216 *dev = &mm_pdev->dev;217 }218 219 return 0;220}221 222static int mdp_probe(struct platform_device *pdev)223{224 struct device *dev = &pdev->dev;225 struct mdp_dev *mdp;226 struct platform_device *mm_pdev;227 struct resource *res;228 int ret, i, mutex_id;229 230 mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);231 if (!mdp) {232 ret = -ENOMEM;233 goto err_return;234 }235 236 mdp->pdev = pdev;237 mdp->mdp_data = of_device_get_match_data(&pdev->dev);238 239 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);240 if (res->start != mdp->mdp_data->mdp_con_res) {241 platform_set_drvdata(pdev, mdp);242 goto success_return;243 }244 245 ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MMSYS);246 if (ret)247 goto err_destroy_device;248 249 ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MUTEX);250 if (ret)251 goto err_destroy_device;252 253 for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {254 enum mdp_mm_subsys_id idx;255 struct mtk_mutex **m;256 257 idx = mdp->mdp_data->pipe_info[i].sub_id;258 mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;259 m = &mdp->mm_subsys[idx].mdp_mutex[mutex_id];260 261 if (!IS_ERR_OR_NULL(*m))262 continue;263 264 *m = mtk_mutex_get(mdp->mm_subsys[idx].mutex);265 if (IS_ERR(*m)) {266 ret = PTR_ERR(*m);267 goto err_free_mutex;268 }269 }270 271 ret = mdp_comp_config(mdp);272 if (ret) {273 dev_err(dev, "Failed to config mdp components\n");274 goto err_free_mutex;275 }276 277 mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 0);278 if (!mdp->job_wq) {279 dev_err(dev, "Unable to create job workqueue\n");280 ret = -ENOMEM;281 goto err_deinit_comp;282 }283 284 mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", WQ_FREEZABLE,285 0);286 if (!mdp->clock_wq) {287 dev_err(dev, "Unable to create clock workqueue\n");288 ret = -ENOMEM;289 goto err_destroy_job_wq;290 }291 292 mdp->scp = scp_get(pdev);293 if (!mdp->scp) {294 mm_pdev = __get_pdev_by_id(pdev, NULL, MDP_INFRA_SCP);295 if (WARN_ON(!mm_pdev)) {296 dev_err(&pdev->dev, "Could not get scp device\n");297 ret = -ENODEV;298 goto err_destroy_clock_wq;299 }300 mdp->scp = platform_get_drvdata(mm_pdev);301 }302 303 mdp->rproc_handle = scp_get_rproc(mdp->scp);304 dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle);305 306 mutex_init(&mdp->vpu_lock);307 mutex_init(&mdp->m2m_lock);308 309 for (i = 0; i < mdp->mdp_data->pp_used; i++) {310 mdp->cmdq_clt[i] = cmdq_mbox_create(dev, i);311 if (IS_ERR(mdp->cmdq_clt[i])) {312 ret = PTR_ERR(mdp->cmdq_clt[i]);313 goto err_mbox_destroy;314 }315 }316 317 init_waitqueue_head(&mdp->callback_wq);318 ida_init(&mdp->mdp_ida);319 platform_set_drvdata(pdev, mdp);320 321 vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));322 323 ret = v4l2_device_register(dev, &mdp->v4l2_dev);324 if (ret) {325 dev_err(dev, "Failed to register v4l2 device\n");326 ret = -EINVAL;327 goto err_mbox_destroy;328 }329 330 ret = mdp_m2m_device_register(mdp);331 if (ret) {332 v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");333 goto err_unregister_device;334 }335 336success_return:337 dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);338 return 0;339 340err_unregister_device:341 v4l2_device_unregister(&mdp->v4l2_dev);342err_mbox_destroy:343 while (--i >= 0)344 cmdq_mbox_destroy(mdp->cmdq_clt[i]);345 scp_put(mdp->scp);346err_destroy_clock_wq:347 destroy_workqueue(mdp->clock_wq);348err_destroy_job_wq:349 destroy_workqueue(mdp->job_wq);350err_deinit_comp:351 mdp_comp_destroy(mdp);352err_free_mutex:353 for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {354 enum mdp_mm_subsys_id idx;355 struct mtk_mutex *m;356 357 idx = mdp->mdp_data->pipe_info[i].sub_id;358 mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;359 m = mdp->mm_subsys[idx].mdp_mutex[mutex_id];360 if (!IS_ERR_OR_NULL(m))361 mtk_mutex_put(m);362 }363err_destroy_device:364 kfree(mdp);365err_return:366 dev_dbg(dev, "Errno %d\n", ret);367 return ret;368}369 370static void mdp_remove(struct platform_device *pdev)371{372 struct mdp_dev *mdp = platform_get_drvdata(pdev);373 374 v4l2_device_unregister(&mdp->v4l2_dev);375 376 dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);377}378 379static int __maybe_unused mdp_suspend(struct device *dev)380{381 struct mdp_dev *mdp = dev_get_drvdata(dev);382 int ret;383 384 atomic_set(&mdp->suspended, 1);385 386 if (refcount_read(&mdp->job_count)) {387 ret = wait_event_timeout(mdp->callback_wq,388 !refcount_read(&mdp->job_count),389 2 * HZ);390 if (ret == 0) {391 dev_err(dev,392 "%s:flushed cmdq task incomplete, count=%d\n",393 __func__, refcount_read(&mdp->job_count));394 return -EBUSY;395 }396 }397 398 return 0;399}400 401static int __maybe_unused mdp_resume(struct device *dev)402{403 struct mdp_dev *mdp = dev_get_drvdata(dev);404 405 atomic_set(&mdp->suspended, 0);406 407 return 0;408}409 410static const struct dev_pm_ops mdp_pm_ops = {411 SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)412};413 414static struct platform_driver mdp_driver = {415 .probe = mdp_probe,416 .remove_new = mdp_remove,417 .driver = {418 .name = MDP_MODULE_NAME,419 .pm = &mdp_pm_ops,420 .of_match_table = mdp_of_ids,421 },422};423 424module_platform_driver(mdp_driver);425 426MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@mediatek.com>");427MODULE_DESCRIPTION("MediaTek image processor 3 driver");428MODULE_LICENSE("GPL");429