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