Commit | Line | Data |
---|---|---|
9293e39c | 1 | // SPDX-License-Identifier: GPL-2.0 |
4e855a6e TL |
2 | /* |
3 | * Copyright (c) 2016 MediaTek Inc. | |
4 | * Author: PC Chen <pc.chen@mediatek.com> | |
5 | * Tiffany Lin <tiffany.lin@mediatek.com> | |
4e855a6e TL |
6 | */ |
7 | ||
8 | #include <linux/slab.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/irq.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/of_device.h> | |
13 | #include <linux/of.h> | |
14 | #include <media/v4l2-event.h> | |
15 | #include <media/v4l2-mem2mem.h> | |
16 | #include <media/videobuf2-dma-contig.h> | |
17 | #include <linux/pm_runtime.h> | |
18 | ||
19 | #include "mtk_vcodec_drv.h" | |
20 | #include "mtk_vcodec_enc.h" | |
21 | #include "mtk_vcodec_enc_pm.h" | |
22 | #include "mtk_vcodec_intr.h" | |
23 | #include "mtk_vcodec_util.h" | |
24 | #include "mtk_vpu.h" | |
25 | ||
26 | module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR); | |
27 | module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR); | |
28 | ||
29 | /* Wake up context wait_queue */ | |
30 | static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason) | |
31 | { | |
32 | ctx->int_cond = 1; | |
33 | ctx->int_type = reason; | |
34 | wake_up_interruptible(&ctx->queue); | |
35 | } | |
36 | ||
37 | static void clean_irq_status(unsigned int irq_status, void __iomem *addr) | |
38 | { | |
39 | if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) | |
40 | writel(MTK_VENC_IRQ_STATUS_PAUSE, addr); | |
41 | ||
42 | if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH) | |
43 | writel(MTK_VENC_IRQ_STATUS_SWITCH, addr); | |
44 | ||
45 | if (irq_status & MTK_VENC_IRQ_STATUS_DRAM) | |
46 | writel(MTK_VENC_IRQ_STATUS_DRAM, addr); | |
47 | ||
48 | if (irq_status & MTK_VENC_IRQ_STATUS_SPS) | |
49 | writel(MTK_VENC_IRQ_STATUS_SPS, addr); | |
50 | ||
51 | if (irq_status & MTK_VENC_IRQ_STATUS_PPS) | |
52 | writel(MTK_VENC_IRQ_STATUS_PPS, addr); | |
53 | ||
54 | if (irq_status & MTK_VENC_IRQ_STATUS_FRM) | |
55 | writel(MTK_VENC_IRQ_STATUS_FRM, addr); | |
56 | ||
57 | } | |
58 | static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) | |
59 | { | |
60 | struct mtk_vcodec_dev *dev = priv; | |
61 | struct mtk_vcodec_ctx *ctx; | |
62 | unsigned long flags; | |
63 | void __iomem *addr; | |
64 | ||
65 | spin_lock_irqsave(&dev->irqlock, flags); | |
66 | ctx = dev->curr_ctx; | |
67 | spin_unlock_irqrestore(&dev->irqlock, flags); | |
68 | ||
69 | mtk_v4l2_debug(1, "id=%d", ctx->id); | |
70 | addr = dev->reg_base[VENC_SYS] + MTK_VENC_IRQ_ACK_OFFSET; | |
71 | ||
72 | ctx->irq_status = readl(dev->reg_base[VENC_SYS] + | |
73 | (MTK_VENC_IRQ_STATUS_OFFSET)); | |
74 | ||
75 | clean_irq_status(ctx->irq_status, addr); | |
76 | ||
77 | wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); | |
78 | return IRQ_HANDLED; | |
79 | } | |
80 | ||
81 | static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv) | |
82 | { | |
83 | struct mtk_vcodec_dev *dev = priv; | |
84 | struct mtk_vcodec_ctx *ctx; | |
85 | unsigned long flags; | |
86 | void __iomem *addr; | |
87 | ||
88 | spin_lock_irqsave(&dev->irqlock, flags); | |
89 | ctx = dev->curr_ctx; | |
90 | spin_unlock_irqrestore(&dev->irqlock, flags); | |
91 | ||
92 | mtk_v4l2_debug(1, "id=%d", ctx->id); | |
93 | ctx->irq_status = readl(dev->reg_base[VENC_LT_SYS] + | |
94 | (MTK_VENC_IRQ_STATUS_OFFSET)); | |
95 | ||
96 | addr = dev->reg_base[VENC_LT_SYS] + MTK_VENC_IRQ_ACK_OFFSET; | |
97 | ||
98 | clean_irq_status(ctx->irq_status, addr); | |
99 | ||
100 | wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); | |
101 | return IRQ_HANDLED; | |
102 | } | |
103 | ||
104 | static void mtk_vcodec_enc_reset_handler(void *priv) | |
105 | { | |
106 | struct mtk_vcodec_dev *dev = priv; | |
107 | struct mtk_vcodec_ctx *ctx; | |
108 | ||
109 | mtk_v4l2_debug(0, "Watchdog timeout!!"); | |
110 | ||
111 | mutex_lock(&dev->dev_mutex); | |
112 | list_for_each_entry(ctx, &dev->ctx_list, list) { | |
113 | ctx->state = MTK_STATE_ABORT; | |
114 | mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", | |
115 | ctx->id); | |
116 | } | |
117 | mutex_unlock(&dev->dev_mutex); | |
118 | } | |
119 | ||
120 | static int fops_vcodec_open(struct file *file) | |
121 | { | |
122 | struct mtk_vcodec_dev *dev = video_drvdata(file); | |
123 | struct mtk_vcodec_ctx *ctx = NULL; | |
124 | int ret = 0; | |
125 | ||
126 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
127 | if (!ctx) | |
128 | return -ENOMEM; | |
129 | ||
130 | mutex_lock(&dev->dev_mutex); | |
131 | /* | |
132 | * Use simple counter to uniquely identify this context. Only | |
133 | * used for logging. | |
134 | */ | |
135 | ctx->id = dev->id_counter++; | |
136 | v4l2_fh_init(&ctx->fh, video_devdata(file)); | |
137 | file->private_data = &ctx->fh; | |
138 | v4l2_fh_add(&ctx->fh); | |
139 | INIT_LIST_HEAD(&ctx->list); | |
140 | ctx->dev = dev; | |
141 | init_waitqueue_head(&ctx->queue); | |
142 | ||
143 | ctx->type = MTK_INST_ENCODER; | |
144 | ret = mtk_vcodec_enc_ctrls_setup(ctx); | |
145 | if (ret) { | |
146 | mtk_v4l2_err("Failed to setup controls() (%d)", | |
147 | ret); | |
148 | goto err_ctrls_setup; | |
149 | } | |
150 | ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, | |
151 | &mtk_vcodec_enc_queue_init); | |
152 | if (IS_ERR((__force void *)ctx->m2m_ctx)) { | |
153 | ret = PTR_ERR((__force void *)ctx->m2m_ctx); | |
154 | mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", | |
155 | ret); | |
156 | goto err_m2m_ctx_init; | |
157 | } | |
158 | mtk_vcodec_enc_set_default_params(ctx); | |
159 | ||
160 | if (v4l2_fh_is_singular(&ctx->fh)) { | |
161 | /* | |
162 | * vpu_load_firmware checks if it was loaded already and | |
163 | * does nothing in that case | |
164 | */ | |
165 | ret = vpu_load_firmware(dev->vpu_plat_dev); | |
166 | if (ret < 0) { | |
167 | /* | |
168 | * Return 0 if downloading firmware successfully, | |
169 | * otherwise it is failed | |
170 | */ | |
171 | mtk_v4l2_err("vpu_load_firmware failed!"); | |
172 | goto err_load_fw; | |
173 | } | |
174 | ||
175 | dev->enc_capability = | |
176 | vpu_get_venc_hw_capa(dev->vpu_plat_dev); | |
177 | mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); | |
178 | } | |
179 | ||
180 | mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", | |
181 | ctx->id, ctx, ctx->m2m_ctx); | |
182 | ||
4e855a6e TL |
183 | list_add(&ctx->list, &dev->ctx_list); |
184 | ||
185 | mutex_unlock(&dev->dev_mutex); | |
186 | mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), | |
187 | ctx->id); | |
188 | return ret; | |
189 | ||
190 | /* Deinit when failure occurred */ | |
191 | err_load_fw: | |
192 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | |
193 | err_m2m_ctx_init: | |
194 | v4l2_ctrl_handler_free(&ctx->ctrl_hdl); | |
195 | err_ctrls_setup: | |
196 | v4l2_fh_del(&ctx->fh); | |
197 | v4l2_fh_exit(&ctx->fh); | |
198 | kfree(ctx); | |
199 | mutex_unlock(&dev->dev_mutex); | |
200 | ||
201 | return ret; | |
202 | } | |
203 | ||
204 | static int fops_vcodec_release(struct file *file) | |
205 | { | |
206 | struct mtk_vcodec_dev *dev = video_drvdata(file); | |
207 | struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); | |
208 | ||
209 | mtk_v4l2_debug(1, "[%d] encoder", ctx->id); | |
210 | mutex_lock(&dev->dev_mutex); | |
211 | ||
212 | mtk_vcodec_enc_release(ctx); | |
213 | v4l2_fh_del(&ctx->fh); | |
214 | v4l2_fh_exit(&ctx->fh); | |
215 | v4l2_ctrl_handler_free(&ctx->ctrl_hdl); | |
590577a4 | 216 | v4l2_m2m_ctx_release(ctx->m2m_ctx); |
4e855a6e TL |
217 | |
218 | list_del_init(&ctx->list); | |
4e855a6e TL |
219 | kfree(ctx); |
220 | mutex_unlock(&dev->dev_mutex); | |
221 | return 0; | |
222 | } | |
223 | ||
224 | static const struct v4l2_file_operations mtk_vcodec_fops = { | |
225 | .owner = THIS_MODULE, | |
226 | .open = fops_vcodec_open, | |
227 | .release = fops_vcodec_release, | |
228 | .poll = v4l2_m2m_fop_poll, | |
229 | .unlocked_ioctl = video_ioctl2, | |
230 | .mmap = v4l2_m2m_fop_mmap, | |
231 | }; | |
232 | ||
233 | static int mtk_vcodec_probe(struct platform_device *pdev) | |
234 | { | |
235 | struct mtk_vcodec_dev *dev; | |
236 | struct video_device *vfd_enc; | |
237 | struct resource *res; | |
238 | int i, j, ret; | |
4e855a6e TL |
239 | |
240 | dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); | |
241 | if (!dev) | |
242 | return -ENOMEM; | |
243 | ||
244 | INIT_LIST_HEAD(&dev->ctx_list); | |
245 | dev->plat_dev = pdev; | |
246 | ||
247 | dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev); | |
248 | if (dev->vpu_plat_dev == NULL) { | |
249 | mtk_v4l2_err("[VPU] vpu device in not ready"); | |
250 | return -EPROBE_DEFER; | |
251 | } | |
252 | ||
253 | vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler, | |
254 | dev, VPU_RST_ENC); | |
255 | ||
256 | ret = mtk_vcodec_init_enc_pm(dev); | |
257 | if (ret < 0) { | |
258 | dev_err(&pdev->dev, "Failed to get mt vcodec clock source!"); | |
259 | return ret; | |
260 | } | |
261 | ||
262 | for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) { | |
263 | res = platform_get_resource(pdev, IORESOURCE_MEM, j); | |
4e855a6e TL |
264 | dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res); |
265 | if (IS_ERR((__force void *)dev->reg_base[i])) { | |
4e855a6e TL |
266 | ret = PTR_ERR((__force void *)dev->reg_base[i]); |
267 | goto err_res; | |
268 | } | |
269 | mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]); | |
270 | } | |
271 | ||
272 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
273 | if (res == NULL) { | |
274 | dev_err(&pdev->dev, "failed to get irq resource"); | |
275 | ret = -ENOENT; | |
276 | goto err_res; | |
277 | } | |
278 | ||
279 | dev->enc_irq = platform_get_irq(pdev, 0); | |
280 | ret = devm_request_irq(&pdev->dev, dev->enc_irq, | |
281 | mtk_vcodec_enc_irq_handler, | |
282 | 0, pdev->name, dev); | |
283 | if (ret) { | |
284 | dev_err(&pdev->dev, "Failed to install dev->enc_irq %d (%d)", | |
285 | dev->enc_irq, | |
286 | ret); | |
287 | ret = -EINVAL; | |
288 | goto err_res; | |
289 | } | |
290 | ||
291 | dev->enc_lt_irq = platform_get_irq(pdev, 1); | |
292 | ret = devm_request_irq(&pdev->dev, | |
293 | dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler, | |
294 | 0, pdev->name, dev); | |
295 | if (ret) { | |
296 | dev_err(&pdev->dev, | |
297 | "Failed to install dev->enc_lt_irq %d (%d)", | |
298 | dev->enc_lt_irq, ret); | |
299 | ret = -EINVAL; | |
300 | goto err_res; | |
301 | } | |
302 | ||
303 | disable_irq(dev->enc_irq); | |
304 | disable_irq(dev->enc_lt_irq); /* VENC_LT */ | |
305 | mutex_init(&dev->enc_mutex); | |
306 | mutex_init(&dev->dev_mutex); | |
307 | spin_lock_init(&dev->irqlock); | |
308 | ||
309 | snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", | |
310 | "[MTK_V4L2_VENC]"); | |
311 | ||
312 | ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); | |
313 | if (ret) { | |
314 | mtk_v4l2_err("v4l2_device_register err=%d", ret); | |
315 | goto err_res; | |
316 | } | |
317 | ||
318 | init_waitqueue_head(&dev->queue); | |
319 | ||
320 | /* allocate video device for encoder and register it */ | |
321 | vfd_enc = video_device_alloc(); | |
322 | if (!vfd_enc) { | |
323 | mtk_v4l2_err("Failed to allocate video device"); | |
324 | ret = -ENOMEM; | |
325 | goto err_enc_alloc; | |
326 | } | |
327 | vfd_enc->fops = &mtk_vcodec_fops; | |
328 | vfd_enc->ioctl_ops = &mtk_venc_ioctl_ops; | |
329 | vfd_enc->release = video_device_release; | |
330 | vfd_enc->lock = &dev->dev_mutex; | |
331 | vfd_enc->v4l2_dev = &dev->v4l2_dev; | |
332 | vfd_enc->vfl_dir = VFL_DIR_M2M; | |
333 | vfd_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | | |
334 | V4L2_CAP_STREAMING; | |
335 | ||
336 | snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s", | |
337 | MTK_VCODEC_ENC_NAME); | |
338 | video_set_drvdata(vfd_enc, dev); | |
339 | dev->vfd_enc = vfd_enc; | |
340 | platform_set_drvdata(pdev, dev); | |
341 | ||
4e855a6e TL |
342 | dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); |
343 | if (IS_ERR((__force void *)dev->m2m_dev_enc)) { | |
344 | mtk_v4l2_err("Failed to init mem2mem enc device"); | |
345 | ret = PTR_ERR((__force void *)dev->m2m_dev_enc); | |
346 | goto err_enc_mem_init; | |
347 | } | |
348 | ||
349 | dev->encode_workqueue = | |
350 | alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME, | |
351 | WQ_MEM_RECLAIM | | |
352 | WQ_FREEZABLE); | |
353 | if (!dev->encode_workqueue) { | |
354 | mtk_v4l2_err("Failed to create encode workqueue"); | |
355 | ret = -EINVAL; | |
356 | goto err_event_workq; | |
357 | } | |
358 | ||
70cad449 | 359 | ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1); |
4e855a6e TL |
360 | if (ret) { |
361 | mtk_v4l2_err("Failed to register video device"); | |
362 | goto err_enc_reg; | |
363 | } | |
364 | ||
4e855a6e TL |
365 | mtk_v4l2_debug(0, "encoder registered as /dev/video%d", |
366 | vfd_enc->num); | |
367 | ||
368 | return 0; | |
369 | ||
370 | err_enc_reg: | |
371 | destroy_workqueue(dev->encode_workqueue); | |
372 | err_event_workq: | |
373 | v4l2_m2m_release(dev->m2m_dev_enc); | |
374 | err_enc_mem_init: | |
4e855a6e TL |
375 | video_unregister_device(vfd_enc); |
376 | err_enc_alloc: | |
377 | v4l2_device_unregister(&dev->v4l2_dev); | |
378 | err_res: | |
379 | mtk_vcodec_release_enc_pm(dev); | |
380 | return ret; | |
381 | } | |
382 | ||
383 | static const struct of_device_id mtk_vcodec_enc_match[] = { | |
384 | {.compatible = "mediatek,mt8173-vcodec-enc",}, | |
385 | {}, | |
386 | }; | |
387 | MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); | |
388 | ||
389 | static int mtk_vcodec_enc_remove(struct platform_device *pdev) | |
390 | { | |
391 | struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); | |
392 | ||
393 | mtk_v4l2_debug_enter(); | |
394 | flush_workqueue(dev->encode_workqueue); | |
395 | destroy_workqueue(dev->encode_workqueue); | |
396 | if (dev->m2m_dev_enc) | |
397 | v4l2_m2m_release(dev->m2m_dev_enc); | |
4e855a6e TL |
398 | |
399 | if (dev->vfd_enc) | |
400 | video_unregister_device(dev->vfd_enc); | |
401 | ||
402 | v4l2_device_unregister(&dev->v4l2_dev); | |
403 | mtk_vcodec_release_enc_pm(dev); | |
404 | return 0; | |
405 | } | |
406 | ||
407 | static struct platform_driver mtk_vcodec_enc_driver = { | |
408 | .probe = mtk_vcodec_probe, | |
409 | .remove = mtk_vcodec_enc_remove, | |
410 | .driver = { | |
411 | .name = MTK_VCODEC_ENC_NAME, | |
4e855a6e TL |
412 | .of_match_table = mtk_vcodec_enc_match, |
413 | }, | |
414 | }; | |
415 | ||
416 | module_platform_driver(mtk_vcodec_enc_driver); | |
417 | ||
418 | ||
419 | MODULE_LICENSE("GPL v2"); | |
420 | MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver"); |