File Index
Files and directories
Sign in
FILE

mtk_mdp_core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2015-2016 MediaTek Inc.
  4 * Author: Houlong Wei <houlong.wei@mediatek.com>
  5 *         Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/device.h>
 10#include <linux/errno.h>
 11#include <linux/interrupt.h>
 12#include <linux/kernel.h>
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/of_address.h>
 16#include <linux/of_platform.h>
 17#include <linux/platform_device.h>
 18#include <linux/pm_runtime.h>
 19#include <linux/workqueue.h>
 20
 21#include "mtk_mdp_core.h"
 22#include "mtk_mdp_m2m.h"
 23#include "mtk_vpu.h"
 24
 25/* MDP debug log level (0-3). 3 shows all the logs. */
 26int mtk_mdp_dbg_level;
 27EXPORT_SYMBOL(mtk_mdp_dbg_level);
 28
 29module_param(mtk_mdp_dbg_level, int, 0644);
 30
 31static const struct of_device_id mtk_mdp_comp_dt_ids[] __maybe_unused = {
 32	{
 33		.compatible = "mediatek,mt8173-mdp-rdma",
 34		.data = (void *)MTK_MDP_RDMA
 35	}, {
 36		.compatible = "mediatek,mt8173-mdp-rsz",
 37		.data = (void *)MTK_MDP_RSZ
 38	}, {
 39		.compatible = "mediatek,mt8173-mdp-wdma",
 40		.data = (void *)MTK_MDP_WDMA
 41	}, {
 42		.compatible = "mediatek,mt8173-mdp-wrot",
 43		.data = (void *)MTK_MDP_WROT
 44	},
 45	{ },
 46};
 47
 48static const struct of_device_id mtk_mdp_of_ids[] = {
 49	{ .compatible = "mediatek,mt8173-mdp", },
 50	{ },
 51};
 52MODULE_DEVICE_TABLE(of, mtk_mdp_of_ids);
 53
 54static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp)
 55{
 56	struct device *dev = &mdp->pdev->dev;
 57	struct mtk_mdp_comp *comp_node;
 58
 59	list_for_each_entry(comp_node, &mdp->comp_list, node)
 60		mtk_mdp_comp_clock_on(dev, comp_node);
 61}
 62
 63static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp)
 64{
 65	struct device *dev = &mdp->pdev->dev;
 66	struct mtk_mdp_comp *comp_node;
 67
 68	list_for_each_entry(comp_node, &mdp->comp_list, node)
 69		mtk_mdp_comp_clock_off(dev, comp_node);
 70}
 71
 72static void mtk_mdp_wdt_worker(struct work_struct *work)
 73{
 74	struct mtk_mdp_dev *mdp =
 75			container_of(work, struct mtk_mdp_dev, wdt_work);
 76	struct mtk_mdp_ctx *ctx;
 77
 78	mtk_mdp_err("Watchdog timeout");
 79
 80	list_for_each_entry(ctx, &mdp->ctx_list, list) {
 81		mtk_mdp_dbg(0, "[%d] Change as state error", ctx->id);
 82		mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_CTX_ERROR);
 83	}
 84}
 85
 86static void mtk_mdp_reset_handler(void *priv)
 87{
 88	struct mtk_mdp_dev *mdp = priv;
 89
 90	queue_work(mdp->wdt_wq, &mdp->wdt_work);
 91}
 92
 93void mtk_mdp_register_component(struct mtk_mdp_dev *mdp,
 94				struct mtk_mdp_comp *comp)
 95{
 96	list_add(&comp->node, &mdp->comp_list);
 97}
 98
 99void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp,
100				  struct mtk_mdp_comp *comp)
101{
102	list_del(&comp->node);
103}
104
105static int mtk_mdp_probe(struct platform_device *pdev)
106{
107	struct mtk_mdp_dev *mdp;
108	struct device *dev = &pdev->dev;
109	struct device_node *node, *parent;
110	struct mtk_mdp_comp *comp, *comp_temp;
111	int ret = 0;
112
113	mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
114	if (!mdp)
115		return -ENOMEM;
116
117	mdp->id = pdev->id;
118	mdp->pdev = pdev;
119	INIT_LIST_HEAD(&mdp->comp_list);
120	INIT_LIST_HEAD(&mdp->ctx_list);
121
122	mutex_init(&mdp->lock);
123	mutex_init(&mdp->vpulock);
124
125	/* Old dts had the components as child nodes */
126	node = of_get_next_child(dev->of_node, NULL);
127	if (node) {
128		of_node_put(node);
129		parent = dev->of_node;
130		dev_warn(dev, "device tree is out of date\n");
131	} else {
132		parent = dev->of_node->parent;
133	}
134
135	/* Iterate over sibling MDP function blocks */
136	for_each_child_of_node(parent, node) {
137		const struct of_device_id *of_id;
138		enum mtk_mdp_comp_type comp_type;
139
140		of_id = of_match_node(mtk_mdp_comp_dt_ids, node);
141		if (!of_id)
142			continue;
143
144		if (!of_device_is_available(node)) {
145			dev_err(dev, "Skipping disabled component %pOF\n",
146				node);
147			continue;
148		}
149
150		comp_type = (uintptr_t)of_id->data;
151
152		comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
153		if (!comp) {
154			ret = -ENOMEM;
155			of_node_put(node);
156			goto err_comp;
157		}
158
159		ret = mtk_mdp_comp_init(dev, node, comp, comp_type);
160		if (ret) {
161			of_node_put(node);
162			goto err_comp;
163		}
164
165		mtk_mdp_register_component(mdp, comp);
166	}
167
168	mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME);
169	if (!mdp->job_wq) {
170		dev_err(&pdev->dev, "unable to alloc job workqueue\n");
171		ret = -ENOMEM;
172		goto err_alloc_job_wq;
173	}
174
175	mdp->wdt_wq = create_singlethread_workqueue("mdp_wdt_wq");
176	if (!mdp->wdt_wq) {
177		dev_err(&pdev->dev, "unable to alloc wdt workqueue\n");
178		ret = -ENOMEM;
179		goto err_alloc_wdt_wq;
180	}
181	INIT_WORK(&mdp->wdt_work, mtk_mdp_wdt_worker);
182
183	ret = v4l2_device_register(dev, &mdp->v4l2_dev);
184	if (ret) {
185		dev_err(&pdev->dev, "Failed to register v4l2 device\n");
186		ret = -EINVAL;
187		goto err_dev_register;
188	}
189
190	ret = mtk_mdp_register_m2m_device(mdp);
191	if (ret) {
192		v4l2_err(&mdp->v4l2_dev, "Failed to init mem2mem device\n");
193		goto err_m2m_register;
194	}
195
196	mdp->vpu_dev = vpu_get_plat_device(pdev);
197	if (!mdp->vpu_dev) {
198		dev_err(&pdev->dev, "Failed to get vpu device\n");
199		ret = -ENODEV;
200		goto err_vpu_get_dev;
201	}
202
203	ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp,
204				  VPU_RST_MDP);
205	if (ret) {
206		dev_err(&pdev->dev, "Failed to register reset handler\n");
207		goto err_reg_handler;
208	}
209
210	platform_set_drvdata(pdev, mdp);
211
212	ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
213	if (ret) {
214		dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n");
215		goto err_reg_handler;
216	}
217
218	pm_runtime_enable(dev);
219	dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id);
220
221	return 0;
222
223err_reg_handler:
224	platform_device_put(mdp->vpu_dev);
225
226err_vpu_get_dev:
227	mtk_mdp_unregister_m2m_device(mdp);
228
229err_m2m_register:
230	v4l2_device_unregister(&mdp->v4l2_dev);
231
232err_dev_register:
233	destroy_workqueue(mdp->wdt_wq);
234
235err_alloc_wdt_wq:
236	destroy_workqueue(mdp->job_wq);
237
238err_alloc_job_wq:
239
240err_comp:
241	list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {
242		mtk_mdp_unregister_component(mdp, comp);
243		mtk_mdp_comp_deinit(dev, comp);
244	}
245
246	dev_dbg(dev, "err %d\n", ret);
247	return ret;
248}
249
250static void mtk_mdp_remove(struct platform_device *pdev)
251{
252	struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev);
253	struct mtk_mdp_comp *comp, *comp_temp;
254
255	pm_runtime_disable(&pdev->dev);
256	vb2_dma_contig_clear_max_seg_size(&pdev->dev);
257	platform_device_put(mdp->vpu_dev);
258	mtk_mdp_unregister_m2m_device(mdp);
259	v4l2_device_unregister(&mdp->v4l2_dev);
260
261	destroy_workqueue(mdp->wdt_wq);
262
263	destroy_workqueue(mdp->job_wq);
264
265	list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {
266		mtk_mdp_unregister_component(mdp, comp);
267		mtk_mdp_comp_deinit(&pdev->dev, comp);
268	}
269
270	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
271}
272
273static int __maybe_unused mtk_mdp_pm_suspend(struct device *dev)
274{
275	struct mtk_mdp_dev *mdp = dev_get_drvdata(dev);
276
277	mtk_mdp_clock_off(mdp);
278
279	return 0;
280}
281
282static int __maybe_unused mtk_mdp_pm_resume(struct device *dev)
283{
284	struct mtk_mdp_dev *mdp = dev_get_drvdata(dev);
285
286	mtk_mdp_clock_on(mdp);
287
288	return 0;
289}
290
291static int __maybe_unused mtk_mdp_suspend(struct device *dev)
292{
293	if (pm_runtime_suspended(dev))
294		return 0;
295
296	return mtk_mdp_pm_suspend(dev);
297}
298
299static int __maybe_unused mtk_mdp_resume(struct device *dev)
300{
301	if (pm_runtime_suspended(dev))
302		return 0;
303
304	return mtk_mdp_pm_resume(dev);
305}
306
307static const struct dev_pm_ops mtk_mdp_pm_ops = {
308	SET_SYSTEM_SLEEP_PM_OPS(mtk_mdp_suspend, mtk_mdp_resume)
309	SET_RUNTIME_PM_OPS(mtk_mdp_pm_suspend, mtk_mdp_pm_resume, NULL)
310};
311
312static struct platform_driver mtk_mdp_driver = {
313	.probe		= mtk_mdp_probe,
314	.remove		= mtk_mdp_remove,
315	.driver = {
316		.name	= MTK_MDP_MODULE_NAME,
317		.pm	= &mtk_mdp_pm_ops,
318		.of_match_table = mtk_mdp_of_ids,
319	}
320};
321
322module_platform_driver(mtk_mdp_driver);
323
324MODULE_AUTHOR("Houlong Wei <houlong.wei@mediatek.com>");
325MODULE_DESCRIPTION("Mediatek image processor driver");
326MODULE_LICENSE("GPL v2");
~/ Read-only file preview.