File Index
Files and directories
Sign in
FILE

mtk-mdp3-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  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
 82int mdp_vpu_get_locked(struct mdp_dev *mdp)
 83{
 84	int ret = 0;
 85
 86	if (mdp->vpu_count++ == 0) {
 87		ret = rproc_boot(mdp->rproc_handle);
 88		if (ret) {
 89			dev_err(&mdp->pdev->dev,
 90				"vpu_load_firmware failed %d\n", ret);
 91			goto err_load_vpu;
 92		}
 93		ret = mdp_vpu_register(mdp);
 94		if (ret) {
 95			dev_err(&mdp->pdev->dev,
 96				"mdp_vpu register failed %d\n", ret);
 97			goto err_reg_vpu;
 98		}
 99		ret = mdp_vpu_dev_init(&mdp->vpu, mdp->scp, &mdp->vpu_lock);
100		if (ret) {
101			dev_err(&mdp->pdev->dev,
102				"mdp_vpu device init failed %d\n", ret);
103			goto err_init_vpu;
104		}
105	}
106	return 0;
107
108err_init_vpu:
109	mdp_vpu_unregister(mdp);
110err_reg_vpu:
111err_load_vpu:
112	mdp->vpu_count--;
113	return ret;
114}
115
116void mdp_vpu_put_locked(struct mdp_dev *mdp)
117{
118	if (--mdp->vpu_count == 0) {
119		mdp_vpu_dev_deinit(&mdp->vpu);
120		mdp_vpu_unregister(mdp);
121	}
122}
123
124void mdp_video_device_release(struct video_device *vdev)
125{
126	struct mdp_dev *mdp = (struct mdp_dev *)video_get_drvdata(vdev);
127	int i;
128
129	for (i = 0; i < mdp->mdp_data->pp_used; i++)
130		if (mdp->cmdq_clt[i])
131			cmdq_mbox_destroy(mdp->cmdq_clt[i]);
132
133	scp_put(mdp->scp);
134
135	destroy_workqueue(mdp->job_wq);
136	destroy_workqueue(mdp->clock_wq);
137
138	pm_runtime_disable(&mdp->pdev->dev);
139
140	vb2_dma_contig_clear_max_seg_size(&mdp->pdev->dev);
141
142	mdp_comp_destroy(mdp);
143	for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
144		enum mdp_mm_subsys_id idx;
145		struct mtk_mutex *m;
146		u32 m_id;
147
148		idx = mdp->mdp_data->pipe_info[i].sub_id;
149		m_id = mdp->mdp_data->pipe_info[i].mutex_id;
150		m = mdp->mm_subsys[idx].mdp_mutex[m_id];
151		if (!IS_ERR_OR_NULL(m))
152			mtk_mutex_put(m);
153	}
154
155	mdp_vpu_shared_mem_free(&mdp->vpu);
156	v4l2_m2m_release(mdp->m2m_dev);
157	kfree(mdp);
158}
159
160static void mdp_put_device(void *_dev)
161{
162	struct device *dev = _dev;
163
164	put_device(dev);
165}
166
167static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)
168{
169	struct platform_device *mm_pdev = NULL;
170	struct device **dev;
171	int ret;
172	int i;
173
174	if (!mdp)
175		return -EINVAL;
176
177	for (i = 0; i < MDP_MM_SUBSYS_MAX; i++) {
178		const char *compat;
179		enum mdp_infra_id sub_id = id + i;
180
181		switch (id) {
182		case MDP_INFRA_MMSYS:
183			dev = &mdp->mm_subsys[i].mmsys;
184			break;
185		case MDP_INFRA_MUTEX:
186			dev = &mdp->mm_subsys[i].mutex;
187			break;
188		default:
189			dev_err(&mdp->pdev->dev, "Unknown infra id %d", id);
190			return -EINVAL;
191		}
192
193		/*
194		 * Not every chip has multiple multimedia subsystems, so
195		 * the config may be null.
196		 */
197		compat = mdp->mdp_data->mdp_probe_infra[sub_id].compatible;
198		if (strlen(compat) == 0)
199			continue;
200
201		mm_pdev = __get_pdev_by_id(mdp->pdev, mm_pdev, sub_id);
202		if (WARN_ON(!mm_pdev))
203			return -ENODEV;
204
205		ret = devm_add_action_or_reset(&mdp->pdev->dev, mdp_put_device,
206					       &mm_pdev->dev);
207		if (ret)
208			return ret;
209
210		*dev = &mm_pdev->dev;
211	}
212
213	return 0;
214}
215
216static int mdp_probe(struct platform_device *pdev)
217{
218	struct device *dev = &pdev->dev;
219	struct mdp_dev *mdp;
220	struct platform_device *mm_pdev;
221	struct resource *res;
222	int ret, i, mutex_id;
223
224	mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);
225	if (!mdp) {
226		ret = -ENOMEM;
227		goto err_return;
228	}
229
230	mdp->pdev = pdev;
231	mdp->mdp_data = of_device_get_match_data(&pdev->dev);
232
233	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
234	if (res->start != mdp->mdp_data->mdp_con_res) {
235		platform_set_drvdata(pdev, mdp);
236		goto success_return;
237	}
238
239	ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MMSYS);
240	if (ret)
241		goto err_destroy_device;
242
243	ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MUTEX);
244	if (ret)
245		goto err_destroy_device;
246
247	for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
248		enum mdp_mm_subsys_id idx;
249		struct mtk_mutex **m;
250
251		idx = mdp->mdp_data->pipe_info[i].sub_id;
252		mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;
253		m = &mdp->mm_subsys[idx].mdp_mutex[mutex_id];
254
255		if (!IS_ERR_OR_NULL(*m))
256			continue;
257
258		*m = mtk_mutex_get(mdp->mm_subsys[idx].mutex);
259		if (IS_ERR(*m)) {
260			ret = PTR_ERR(*m);
261			goto err_free_mutex;
262		}
263	}
264
265	ret = mdp_comp_config(mdp);
266	if (ret) {
267		dev_err(dev, "Failed to config mdp components\n");
268		goto err_free_mutex;
269	}
270
271	mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 0);
272	if (!mdp->job_wq) {
273		dev_err(dev, "Unable to create job workqueue\n");
274		ret = -ENOMEM;
275		goto err_deinit_comp;
276	}
277
278	mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", WQ_FREEZABLE,
279					0);
280	if (!mdp->clock_wq) {
281		dev_err(dev, "Unable to create clock workqueue\n");
282		ret = -ENOMEM;
283		goto err_destroy_job_wq;
284	}
285
286	mdp->scp = scp_get(pdev);
287	if (!mdp->scp) {
288		mm_pdev = __get_pdev_by_id(pdev, NULL, MDP_INFRA_SCP);
289		if (WARN_ON(!mm_pdev)) {
290			dev_err(&pdev->dev, "Could not get scp device\n");
291			ret = -ENODEV;
292			goto err_destroy_clock_wq;
293		}
294		mdp->scp = platform_get_drvdata(mm_pdev);
295		put_device(&mm_pdev->dev);
296	}
297
298	mdp->rproc_handle = scp_get_rproc(mdp->scp);
299	dev_dbg(&pdev->dev, "MDP rproc_handle: %p", mdp->rproc_handle);
300
301	mutex_init(&mdp->vpu_lock);
302	mutex_init(&mdp->m2m_lock);
303
304	for (i = 0; i < mdp->mdp_data->pp_used; i++) {
305		mdp->cmdq_clt[i] = cmdq_mbox_create(dev, i);
306		if (IS_ERR(mdp->cmdq_clt[i])) {
307			ret = PTR_ERR(mdp->cmdq_clt[i]);
308			goto err_mbox_destroy;
309		}
310
311		mdp->cmdq_shift_pa[i] = cmdq_get_shift_pa(mdp->cmdq_clt[i]->chan);
312	}
313
314	init_waitqueue_head(&mdp->callback_wq);
315	ida_init(&mdp->mdp_ida);
316	platform_set_drvdata(pdev, mdp);
317
318	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
319
320	ret = v4l2_device_register(dev, &mdp->v4l2_dev);
321	if (ret) {
322		dev_err(dev, "Failed to register v4l2 device\n");
323		ret = -EINVAL;
324		goto err_mbox_destroy;
325	}
326
327	ret = mdp_m2m_device_register(mdp);
328	if (ret) {
329		v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
330		goto err_unregister_device;
331	}
332
333success_return:
334	dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
335	return 0;
336
337err_unregister_device:
338	v4l2_device_unregister(&mdp->v4l2_dev);
339err_mbox_destroy:
340	while (--i >= 0)
341		cmdq_mbox_destroy(mdp->cmdq_clt[i]);
342	scp_put(mdp->scp);
343err_destroy_clock_wq:
344	destroy_workqueue(mdp->clock_wq);
345err_destroy_job_wq:
346	destroy_workqueue(mdp->job_wq);
347err_deinit_comp:
348	mdp_comp_destroy(mdp);
349err_free_mutex:
350	for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
351		enum mdp_mm_subsys_id idx;
352		struct mtk_mutex *m;
353
354		idx = mdp->mdp_data->pipe_info[i].sub_id;
355		mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;
356		m = mdp->mm_subsys[idx].mdp_mutex[mutex_id];
357		if (!IS_ERR_OR_NULL(m))
358			mtk_mutex_put(m);
359	}
360err_destroy_device:
361	kfree(mdp);
362err_return:
363	dev_dbg(dev, "Errno %d\n", ret);
364	return ret;
365}
366
367static void mdp_remove(struct platform_device *pdev)
368{
369	struct mdp_dev *mdp = platform_get_drvdata(pdev);
370
371	v4l2_device_unregister(&mdp->v4l2_dev);
372
373	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
374}
375
376static int __maybe_unused mdp_suspend(struct device *dev)
377{
378	struct mdp_dev *mdp = dev_get_drvdata(dev);
379	int ret;
380
381	atomic_set(&mdp->suspended, 1);
382
383	if (refcount_read(&mdp->job_count)) {
384		ret = wait_event_timeout(mdp->callback_wq,
385					 !refcount_read(&mdp->job_count),
386					 2 * HZ);
387		if (ret == 0) {
388			dev_err(dev,
389				"%s:flushed cmdq task incomplete, count=%d\n",
390				__func__, refcount_read(&mdp->job_count));
391			return -EBUSY;
392		}
393	}
394
395	return 0;
396}
397
398static int __maybe_unused mdp_resume(struct device *dev)
399{
400	struct mdp_dev *mdp = dev_get_drvdata(dev);
401
402	atomic_set(&mdp->suspended, 0);
403
404	return 0;
405}
406
407static const struct dev_pm_ops mdp_pm_ops = {
408	SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
409};
410
411static struct platform_driver mdp_driver = {
412	.probe		= mdp_probe,
413	.remove		= mdp_remove,
414	.driver = {
415		.name	= MDP_MODULE_NAME,
416		.pm	= &mdp_pm_ops,
417		.of_match_table = mdp_of_ids,
418	},
419};
420
421module_platform_driver(mdp_driver);
422
423MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@mediatek.com>");
424MODULE_DESCRIPTION("MediaTek image processor 3 driver");
425MODULE_LICENSE("GPL");
~/ Read-only file preview.