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,mt8195-mdp3-rdma",
25 .data = &mt8195_mdp_driver_data,
26 },
27 { .compatible = "mediatek,mt8195-mdp3-wrot",
28 .data = &mt8195_mdp_driver_data,
29 },
30 {},
31};
32MODULE_DEVICE_TABLE(of, mdp_of_ids);
33
34static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,
35 struct platform_device *from,
36 enum mdp_infra_id id)
37{
38 struct device_node *node, *f = NULL;
39 struct platform_device *mdp_pdev = NULL;
40 const struct mtk_mdp_driver_data *mdp_data;
41 const char *compat;
42
43 if (!pdev)
44 return NULL;
45
46 if (id < MDP_INFRA_MMSYS || id >= MDP_INFRA_MAX) {
47 dev_err(&pdev->dev, "Illegal infra id %d\n", id);
48 return NULL;
49 }
50
51 mdp_data = of_device_get_match_data(dev: &pdev->dev);
52 if (!mdp_data) {
53 dev_err(&pdev->dev, "have no driver data to find node\n");
54 return NULL;
55 }
56
57 compat = mdp_data->mdp_probe_infra[id].compatible;
58 if (strlen(compat) == 0)
59 return NULL;
60
61 if (from)
62 f = from->dev.of_node;
63 node = of_find_compatible_node(from: f, NULL, compat);
64 if (WARN_ON(!node)) {
65 dev_err(&pdev->dev, "find node from id %d failed\n", id);
66 return NULL;
67 }
68
69 mdp_pdev = of_find_device_by_node(np: node);
70 of_node_put(node);
71 if (WARN_ON(!mdp_pdev)) {
72 dev_err(&pdev->dev, "find pdev from id %d failed\n", id);
73 return NULL;
74 }
75
76 return mdp_pdev;
77}
78
79struct platform_device *mdp_get_plat_device(struct platform_device *pdev)
80{
81 struct device *dev = &pdev->dev;
82 struct device_node *mdp_node;
83 struct platform_device *mdp_pdev;
84
85 mdp_node = of_parse_phandle(np: dev->of_node, MDP_PHANDLE_NAME, index: 0);
86 if (!mdp_node) {
87 dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME);
88 return NULL;
89 }
90
91 mdp_pdev = of_find_device_by_node(np: mdp_node);
92 of_node_put(node: mdp_node);
93
94 return mdp_pdev;
95}
96EXPORT_SYMBOL_GPL(mdp_get_plat_device);
97
98int mdp_vpu_get_locked(struct mdp_dev *mdp)
99{
100 int ret = 0;
101
102 if (mdp->vpu_count++ == 0) {
103 ret = rproc_boot(rproc: mdp->rproc_handle);
104 if (ret) {
105 dev_err(&mdp->pdev->dev,
106 "vpu_load_firmware failed %d\n", ret);
107 goto err_load_vpu;
108 }
109 ret = mdp_vpu_register(mdp);
110 if (ret) {
111 dev_err(&mdp->pdev->dev,
112 "mdp_vpu register failed %d\n", ret);
113 goto err_reg_vpu;
114 }
115 ret = mdp_vpu_dev_init(vpu: &mdp->vpu, scp: mdp->scp, lock: &mdp->vpu_lock);
116 if (ret) {
117 dev_err(&mdp->pdev->dev,
118 "mdp_vpu device init failed %d\n", ret);
119 goto err_init_vpu;
120 }
121 }
122 return 0;
123
124err_init_vpu:
125 mdp_vpu_unregister(mdp);
126err_reg_vpu:
127err_load_vpu:
128 mdp->vpu_count--;
129 return ret;
130}
131
132void mdp_vpu_put_locked(struct mdp_dev *mdp)
133{
134 if (--mdp->vpu_count == 0) {
135 mdp_vpu_dev_deinit(vpu: &mdp->vpu);
136 mdp_vpu_unregister(mdp);
137 }
138}
139
140void mdp_video_device_release(struct video_device *vdev)
141{
142 struct mdp_dev *mdp = (struct mdp_dev *)video_get_drvdata(vdev);
143 int i;
144
145 for (i = 0; i < mdp->mdp_data->pp_used; i++)
146 if (mdp->cmdq_clt[i])
147 cmdq_mbox_destroy(client: mdp->cmdq_clt[i]);
148
149 scp_put(scp: mdp->scp);
150
151 destroy_workqueue(wq: mdp->job_wq);
152 destroy_workqueue(wq: mdp->clock_wq);
153
154 pm_runtime_disable(dev: &mdp->pdev->dev);
155
156 vb2_dma_contig_clear_max_seg_size(dev: &mdp->pdev->dev);
157
158 mdp_comp_destroy(mdp);
159 for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
160 enum mdp_mm_subsys_id idx;
161 struct mtk_mutex *m;
162 u32 m_id;
163
164 idx = mdp->mdp_data->pipe_info[i].sub_id;
165 m_id = mdp->mdp_data->pipe_info[i].mutex_id;
166 m = mdp->mm_subsys[idx].mdp_mutex[m_id];
167 if (!IS_ERR_OR_NULL(ptr: m))
168 mtk_mutex_put(mutex: m);
169 }
170
171 mdp_vpu_shared_mem_free(vpu: &mdp->vpu);
172 v4l2_m2m_release(m2m_dev: mdp->m2m_dev);
173 kfree(objp: mdp);
174}
175
176static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)
177{
178 struct platform_device *mm_pdev = NULL;
179 struct device **dev;
180 int i;
181
182 if (!mdp)
183 return -EINVAL;
184
185 for (i = 0; i < MDP_MM_SUBSYS_MAX; i++) {
186 const char *compat;
187 enum mdp_infra_id sub_id = id + i;
188
189 switch (id) {
190 case MDP_INFRA_MMSYS:
191 dev = &mdp->mm_subsys[i].mmsys;
192 break;
193 case MDP_INFRA_MUTEX:
194 dev = &mdp->mm_subsys[i].mutex;
195 break;
196 default:
197 dev_err(&mdp->pdev->dev, "Unknown infra id %d", id);
198 return -EINVAL;
199 }
200
201 /*
202 * Not every chip has multiple multimedia subsystems, so
203 * the config may be null.
204 */
205 compat = mdp->mdp_data->mdp_probe_infra[sub_id].compatible;
206 if (strlen(compat) == 0)
207 continue;
208
209 mm_pdev = __get_pdev_by_id(pdev: mdp->pdev, from: mm_pdev, id: sub_id);
210 if (WARN_ON(!mm_pdev))
211 return -ENODEV;
212
213 *dev = &mm_pdev->dev;
214 }
215
216 return 0;
217}
218
219static int mdp_probe(struct platform_device *pdev)
220{
221 struct device *dev = &pdev->dev;
222 struct mdp_dev *mdp;
223 struct platform_device *mm_pdev;
224 struct resource *res;
225 int ret, i, mutex_id;
226
227 mdp = kzalloc(size: sizeof(*mdp), GFP_KERNEL);
228 if (!mdp) {
229 ret = -ENOMEM;
230 goto err_return;
231 }
232
233 mdp->pdev = pdev;
234 mdp->mdp_data = of_device_get_match_data(dev: &pdev->dev);
235
236 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
237 if (res->start != mdp->mdp_data->mdp_con_res) {
238 platform_set_drvdata(pdev, data: mdp);
239 goto success_return;
240 }
241
242 ret = mdp_mm_subsys_deploy(mdp, id: MDP_INFRA_MMSYS);
243 if (ret)
244 goto err_destroy_device;
245
246 ret = mdp_mm_subsys_deploy(mdp, id: MDP_INFRA_MUTEX);
247 if (ret)
248 goto err_destroy_device;
249
250 for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
251 enum mdp_mm_subsys_id idx;
252 struct mtk_mutex **m;
253
254 idx = mdp->mdp_data->pipe_info[i].sub_id;
255 mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;
256 m = &mdp->mm_subsys[idx].mdp_mutex[mutex_id];
257
258 if (!IS_ERR_OR_NULL(ptr: *m))
259 continue;
260
261 *m = mtk_mutex_get(dev: mdp->mm_subsys[idx].mutex);
262 if (IS_ERR(ptr: *m)) {
263 ret = PTR_ERR(ptr: *m);
264 goto err_free_mutex;
265 }
266 }
267
268 ret = mdp_comp_config(mdp);
269 if (ret) {
270 dev_err(dev, "Failed to config mdp components\n");
271 goto err_free_mutex;
272 }
273
274 mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, flags: WQ_FREEZABLE, max_active: 0);
275 if (!mdp->job_wq) {
276 dev_err(dev, "Unable to create job workqueue\n");
277 ret = -ENOMEM;
278 goto err_deinit_comp;
279 }
280
281 mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", flags: WQ_FREEZABLE,
282 max_active: 0);
283 if (!mdp->clock_wq) {
284 dev_err(dev, "Unable to create clock workqueue\n");
285 ret = -ENOMEM;
286 goto err_destroy_job_wq;
287 }
288
289 mdp->scp = scp_get(pdev);
290 if (!mdp->scp) {
291 mm_pdev = __get_pdev_by_id(pdev, NULL, id: MDP_INFRA_SCP);
292 if (WARN_ON(!mm_pdev)) {
293 dev_err(&pdev->dev, "Could not get scp device\n");
294 ret = -ENODEV;
295 goto err_destroy_clock_wq;
296 }
297 mdp->scp = platform_get_drvdata(pdev: mm_pdev);
298 }
299
300 mdp->rproc_handle = scp_get_rproc(scp: mdp->scp);
301 dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle);
302
303 mutex_init(&mdp->vpu_lock);
304 mutex_init(&mdp->m2m_lock);
305
306 for (i = 0; i < mdp->mdp_data->pp_used; i++) {
307 mdp->cmdq_clt[i] = cmdq_mbox_create(dev, index: i);
308 if (IS_ERR(ptr: mdp->cmdq_clt[i])) {
309 ret = PTR_ERR(ptr: mdp->cmdq_clt[i]);
310 goto err_mbox_destroy;
311 }
312 }
313
314 init_waitqueue_head(&mdp->callback_wq);
315 ida_init(ida: &mdp->mdp_ida);
316 platform_set_drvdata(pdev, data: mdp);
317
318 vb2_dma_contig_set_max_seg_size(dev: &pdev->dev, DMA_BIT_MASK(32));
319
320 ret = v4l2_device_register(dev, v4l2_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(v4l2_dev: &mdp->v4l2_dev);
339err_mbox_destroy:
340 while (--i >= 0)
341 cmdq_mbox_destroy(client: mdp->cmdq_clt[i]);
342 scp_put(scp: mdp->scp);
343err_destroy_clock_wq:
344 destroy_workqueue(wq: mdp->clock_wq);
345err_destroy_job_wq:
346 destroy_workqueue(wq: 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(ptr: m))
358 mtk_mutex_put(mutex: m);
359 }
360err_destroy_device:
361 kfree(objp: 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(v4l2_dev: &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(v: &mdp->suspended, i: 1);
382
383 if (atomic_read(v: &mdp->job_count)) {
384 ret = wait_event_timeout(mdp->callback_wq,
385 !atomic_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__, atomic_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(v: &mdp->suspended, i: 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_new = 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");
426

source code of linux/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c