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