]>
Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7472c1c6 SV |
2 | /* |
3 | * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. | |
4 | * Copyright (C) 2017 Linaro Ltd. | |
7472c1c6 SV |
5 | */ |
6 | #include <linux/clk.h> | |
7 | #include <linux/module.h> | |
1b7369ac | 8 | #include <linux/mod_devicetable.h> |
7472c1c6 SV |
9 | #include <linux/platform_device.h> |
10 | #include <linux/pm_runtime.h> | |
11 | #include <linux/slab.h> | |
12 | #include <media/v4l2-ioctl.h> | |
13 | #include <media/v4l2-event.h> | |
14 | #include <media/v4l2-ctrls.h> | |
15 | #include <media/v4l2-mem2mem.h> | |
16 | #include <media/videobuf2-dma-sg.h> | |
17 | ||
18 | #include "hfi_venus_io.h" | |
1a73374a | 19 | #include "hfi_parser.h" |
7472c1c6 SV |
20 | #include "core.h" |
21 | #include "helpers.h" | |
22 | #include "vdec.h" | |
23 | ||
7472c1c6 SV |
24 | /* |
25 | * Three resons to keep MPLANE formats (despite that the number of planes | |
26 | * currently is one): | |
27 | * - the MPLANE formats allow only one plane to be used | |
28 | * - the downstream driver use MPLANE formats too | |
29 | * - future firmware versions could add support for >1 planes | |
30 | */ | |
31 | static const struct venus_format vdec_formats[] = { | |
32 | { | |
33 | .pixfmt = V4L2_PIX_FMT_NV12, | |
34 | .num_planes = 1, | |
35 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, | |
36 | }, { | |
37 | .pixfmt = V4L2_PIX_FMT_MPEG4, | |
38 | .num_planes = 1, | |
39 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
40 | }, { | |
41 | .pixfmt = V4L2_PIX_FMT_MPEG2, | |
42 | .num_planes = 1, | |
43 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
44 | }, { | |
45 | .pixfmt = V4L2_PIX_FMT_H263, | |
46 | .num_planes = 1, | |
47 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
48 | }, { | |
49 | .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G, | |
50 | .num_planes = 1, | |
51 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
52 | }, { | |
53 | .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, | |
54 | .num_planes = 1, | |
55 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
56 | }, { | |
57 | .pixfmt = V4L2_PIX_FMT_H264, | |
58 | .num_planes = 1, | |
59 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
60 | }, { | |
61 | .pixfmt = V4L2_PIX_FMT_VP8, | |
62 | .num_planes = 1, | |
63 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
64 | }, { | |
65 | .pixfmt = V4L2_PIX_FMT_VP9, | |
66 | .num_planes = 1, | |
67 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
68 | }, { | |
69 | .pixfmt = V4L2_PIX_FMT_XVID, | |
70 | .num_planes = 1, | |
71 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
1fb9a605 SV |
72 | }, { |
73 | .pixfmt = V4L2_PIX_FMT_HEVC, | |
74 | .num_planes = 1, | |
75 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
7472c1c6 SV |
76 | }, |
77 | }; | |
78 | ||
29f0133e SV |
79 | static const struct venus_format * |
80 | find_format(struct venus_inst *inst, u32 pixfmt, u32 type) | |
7472c1c6 SV |
81 | { |
82 | const struct venus_format *fmt = vdec_formats; | |
83 | unsigned int size = ARRAY_SIZE(vdec_formats); | |
84 | unsigned int i; | |
85 | ||
86 | for (i = 0; i < size; i++) { | |
87 | if (fmt[i].pixfmt == pixfmt) | |
88 | break; | |
89 | } | |
90 | ||
91 | if (i == size || fmt[i].type != type) | |
92 | return NULL; | |
93 | ||
29f0133e SV |
94 | if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && |
95 | !venus_helper_check_codec(inst, fmt[i].pixfmt)) | |
96 | return NULL; | |
97 | ||
7472c1c6 SV |
98 | return &fmt[i]; |
99 | } | |
100 | ||
101 | static const struct venus_format * | |
29f0133e | 102 | find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type) |
7472c1c6 SV |
103 | { |
104 | const struct venus_format *fmt = vdec_formats; | |
105 | unsigned int size = ARRAY_SIZE(vdec_formats); | |
106 | unsigned int i, k = 0; | |
107 | ||
108 | if (index > size) | |
109 | return NULL; | |
110 | ||
111 | for (i = 0; i < size; i++) { | |
82e071e2 AC |
112 | bool valid; |
113 | ||
7472c1c6 SV |
114 | if (fmt[i].type != type) |
115 | continue; | |
82e071e2 AC |
116 | valid = type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || |
117 | venus_helper_check_codec(inst, fmt[i].pixfmt); | |
118 | if (k == index && valid) | |
7472c1c6 | 119 | break; |
82e071e2 AC |
120 | if (valid) |
121 | k++; | |
7472c1c6 SV |
122 | } |
123 | ||
124 | if (i == size) | |
125 | return NULL; | |
126 | ||
127 | return &fmt[i]; | |
128 | } | |
129 | ||
130 | static const struct venus_format * | |
131 | vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) | |
132 | { | |
133 | struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; | |
134 | struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; | |
135 | const struct venus_format *fmt; | |
7472c1c6 SV |
136 | |
137 | memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); | |
138 | memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); | |
139 | ||
29f0133e | 140 | fmt = find_format(inst, pixmp->pixelformat, f->type); |
7472c1c6 SV |
141 | if (!fmt) { |
142 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | |
143 | pixmp->pixelformat = V4L2_PIX_FMT_NV12; | |
144 | else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
145 | pixmp->pixelformat = V4L2_PIX_FMT_H264; | |
146 | else | |
147 | return NULL; | |
29f0133e | 148 | fmt = find_format(inst, pixmp->pixelformat, f->type); |
7472c1c6 SV |
149 | } |
150 | ||
1a73374a SV |
151 | pixmp->width = clamp(pixmp->width, frame_width_min(inst), |
152 | frame_width_max(inst)); | |
153 | pixmp->height = clamp(pixmp->height, frame_height_min(inst), | |
154 | frame_height_max(inst)); | |
7472c1c6 SV |
155 | |
156 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | |
157 | pixmp->height = ALIGN(pixmp->height, 32); | |
158 | ||
159 | if (pixmp->field == V4L2_FIELD_ANY) | |
160 | pixmp->field = V4L2_FIELD_NONE; | |
161 | pixmp->num_planes = fmt->num_planes; | |
162 | pixmp->flags = 0; | |
163 | ||
e1cb72de SV |
164 | pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, |
165 | pixmp->width, | |
166 | pixmp->height); | |
167 | ||
168 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | |
169 | pfmt[0].bytesperline = ALIGN(pixmp->width, 128); | |
170 | else | |
7472c1c6 | 171 | pfmt[0].bytesperline = 0; |
7472c1c6 SV |
172 | |
173 | return fmt; | |
174 | } | |
175 | ||
176 | static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) | |
177 | { | |
178 | struct venus_inst *inst = to_inst(file); | |
179 | ||
180 | vdec_try_fmt_common(inst, f); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) | |
186 | { | |
187 | struct venus_inst *inst = to_inst(file); | |
188 | const struct venus_format *fmt = NULL; | |
189 | struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; | |
190 | ||
191 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | |
192 | fmt = inst->fmt_cap; | |
193 | else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
194 | fmt = inst->fmt_out; | |
195 | ||
196 | if (inst->reconfig) { | |
197 | struct v4l2_format format = {}; | |
198 | ||
199 | inst->out_width = inst->reconfig_width; | |
200 | inst->out_height = inst->reconfig_height; | |
201 | inst->reconfig = false; | |
202 | ||
203 | format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
204 | format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; | |
205 | format.fmt.pix_mp.width = inst->out_width; | |
206 | format.fmt.pix_mp.height = inst->out_height; | |
207 | ||
208 | vdec_try_fmt_common(inst, &format); | |
209 | ||
210 | inst->width = format.fmt.pix_mp.width; | |
211 | inst->height = format.fmt.pix_mp.height; | |
212 | } | |
213 | ||
214 | pixmp->pixelformat = fmt->pixfmt; | |
215 | ||
216 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { | |
217 | pixmp->width = inst->width; | |
218 | pixmp->height = inst->height; | |
219 | pixmp->colorspace = inst->colorspace; | |
220 | pixmp->ycbcr_enc = inst->ycbcr_enc; | |
221 | pixmp->quantization = inst->quantization; | |
222 | pixmp->xfer_func = inst->xfer_func; | |
223 | } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | |
224 | pixmp->width = inst->out_width; | |
225 | pixmp->height = inst->out_height; | |
226 | } | |
227 | ||
228 | vdec_try_fmt_common(inst, f); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) | |
234 | { | |
235 | struct venus_inst *inst = to_inst(file); | |
236 | struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; | |
237 | struct v4l2_pix_format_mplane orig_pixmp; | |
238 | const struct venus_format *fmt; | |
239 | struct v4l2_format format; | |
240 | u32 pixfmt_out = 0, pixfmt_cap = 0; | |
241 | ||
242 | orig_pixmp = *pixmp; | |
243 | ||
244 | fmt = vdec_try_fmt_common(inst, f); | |
245 | ||
246 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | |
247 | pixfmt_out = pixmp->pixelformat; | |
248 | pixfmt_cap = inst->fmt_cap->pixfmt; | |
249 | } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { | |
250 | pixfmt_cap = pixmp->pixelformat; | |
251 | pixfmt_out = inst->fmt_out->pixfmt; | |
252 | } | |
253 | ||
254 | memset(&format, 0, sizeof(format)); | |
255 | ||
256 | format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
257 | format.fmt.pix_mp.pixelformat = pixfmt_out; | |
258 | format.fmt.pix_mp.width = orig_pixmp.width; | |
259 | format.fmt.pix_mp.height = orig_pixmp.height; | |
260 | vdec_try_fmt_common(inst, &format); | |
261 | ||
262 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | |
263 | inst->out_width = format.fmt.pix_mp.width; | |
264 | inst->out_height = format.fmt.pix_mp.height; | |
265 | inst->colorspace = pixmp->colorspace; | |
266 | inst->ycbcr_enc = pixmp->ycbcr_enc; | |
267 | inst->quantization = pixmp->quantization; | |
268 | inst->xfer_func = pixmp->xfer_func; | |
269 | } | |
270 | ||
271 | memset(&format, 0, sizeof(format)); | |
272 | ||
273 | format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
274 | format.fmt.pix_mp.pixelformat = pixfmt_cap; | |
275 | format.fmt.pix_mp.width = orig_pixmp.width; | |
276 | format.fmt.pix_mp.height = orig_pixmp.height; | |
277 | vdec_try_fmt_common(inst, &format); | |
278 | ||
279 | inst->width = format.fmt.pix_mp.width; | |
280 | inst->height = format.fmt.pix_mp.height; | |
281 | ||
282 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
283 | inst->fmt_out = fmt; | |
284 | else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | |
285 | inst->fmt_cap = fmt; | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | static int | |
291 | vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) | |
292 | { | |
293 | struct venus_inst *inst = to_inst(file); | |
294 | ||
295 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && | |
296 | s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | |
297 | return -EINVAL; | |
298 | ||
299 | switch (s->target) { | |
300 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
301 | case V4L2_SEL_TGT_CROP_DEFAULT: | |
302 | case V4L2_SEL_TGT_CROP: | |
303 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | |
304 | return -EINVAL; | |
305 | s->r.width = inst->out_width; | |
306 | s->r.height = inst->out_height; | |
307 | break; | |
308 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | |
309 | case V4L2_SEL_TGT_COMPOSE_PADDED: | |
310 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
311 | return -EINVAL; | |
312 | s->r.width = inst->width; | |
313 | s->r.height = inst->height; | |
314 | break; | |
315 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | |
316 | case V4L2_SEL_TGT_COMPOSE: | |
317 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
318 | return -EINVAL; | |
319 | s->r.width = inst->out_width; | |
320 | s->r.height = inst->out_height; | |
321 | break; | |
322 | default: | |
323 | return -EINVAL; | |
324 | } | |
325 | ||
326 | s->r.top = 0; | |
327 | s->r.left = 0; | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
332 | static int | |
333 | vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | |
334 | { | |
c0decac1 MCC |
335 | strscpy(cap->driver, "qcom-venus", sizeof(cap->driver)); |
336 | strscpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card)); | |
337 | strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); | |
7472c1c6 SV |
338 | |
339 | return 0; | |
340 | } | |
341 | ||
342 | static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) | |
343 | { | |
29f0133e | 344 | struct venus_inst *inst = to_inst(file); |
7472c1c6 SV |
345 | const struct venus_format *fmt; |
346 | ||
347 | memset(f->reserved, 0, sizeof(f->reserved)); | |
348 | ||
29f0133e | 349 | fmt = find_format_by_index(inst, f->index, f->type); |
7472c1c6 SV |
350 | if (!fmt) |
351 | return -EINVAL; | |
352 | ||
353 | f->pixelformat = fmt->pixfmt; | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
358 | static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) | |
359 | { | |
360 | struct venus_inst *inst = to_inst(file); | |
361 | struct v4l2_captureparm *cap = &a->parm.capture; | |
362 | struct v4l2_fract *timeperframe = &cap->timeperframe; | |
363 | u64 us_per_frame, fps; | |
364 | ||
365 | if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && | |
366 | a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
367 | return -EINVAL; | |
368 | ||
369 | memset(cap->reserved, 0, sizeof(cap->reserved)); | |
370 | if (!timeperframe->denominator) | |
371 | timeperframe->denominator = inst->timeperframe.denominator; | |
372 | if (!timeperframe->numerator) | |
373 | timeperframe->numerator = inst->timeperframe.numerator; | |
374 | cap->readbuffers = 0; | |
375 | cap->extendedmode = 0; | |
376 | cap->capability = V4L2_CAP_TIMEPERFRAME; | |
377 | us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; | |
378 | do_div(us_per_frame, timeperframe->denominator); | |
379 | ||
380 | if (!us_per_frame) | |
381 | return -EINVAL; | |
382 | ||
383 | fps = (u64)USEC_PER_SEC; | |
384 | do_div(fps, us_per_frame); | |
385 | ||
386 | inst->fps = fps; | |
387 | inst->timeperframe = *timeperframe; | |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
392 | static int vdec_enum_framesizes(struct file *file, void *fh, | |
393 | struct v4l2_frmsizeenum *fsize) | |
394 | { | |
395 | struct venus_inst *inst = to_inst(file); | |
396 | const struct venus_format *fmt; | |
397 | ||
29f0133e | 398 | fmt = find_format(inst, fsize->pixel_format, |
7472c1c6 SV |
399 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); |
400 | if (!fmt) { | |
29f0133e | 401 | fmt = find_format(inst, fsize->pixel_format, |
7472c1c6 SV |
402 | V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); |
403 | if (!fmt) | |
404 | return -EINVAL; | |
405 | } | |
406 | ||
407 | if (fsize->index) | |
408 | return -EINVAL; | |
409 | ||
410 | fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; | |
411 | ||
1a73374a SV |
412 | fsize->stepwise.min_width = frame_width_min(inst); |
413 | fsize->stepwise.max_width = frame_width_max(inst); | |
414 | fsize->stepwise.step_width = frame_width_step(inst); | |
415 | fsize->stepwise.min_height = frame_height_min(inst); | |
416 | fsize->stepwise.max_height = frame_height_max(inst); | |
417 | fsize->stepwise.step_height = frame_height_step(inst); | |
7472c1c6 SV |
418 | |
419 | return 0; | |
420 | } | |
421 | ||
422 | static int vdec_subscribe_event(struct v4l2_fh *fh, | |
423 | const struct v4l2_event_subscription *sub) | |
424 | { | |
425 | switch (sub->type) { | |
426 | case V4L2_EVENT_EOS: | |
427 | return v4l2_event_subscribe(fh, sub, 2, NULL); | |
428 | case V4L2_EVENT_SOURCE_CHANGE: | |
429 | return v4l2_src_change_event_subscribe(fh, sub); | |
430 | case V4L2_EVENT_CTRL: | |
431 | return v4l2_ctrl_subscribe_event(fh, sub); | |
432 | default: | |
433 | return -EINVAL; | |
434 | } | |
435 | } | |
436 | ||
437 | static int | |
438 | vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) | |
439 | { | |
e69b987a SV |
440 | switch (cmd->cmd) { |
441 | case V4L2_DEC_CMD_STOP: | |
442 | if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) | |
443 | return -EINVAL; | |
444 | break; | |
445 | default: | |
7472c1c6 | 446 | return -EINVAL; |
e69b987a | 447 | } |
7472c1c6 SV |
448 | |
449 | return 0; | |
450 | } | |
451 | ||
452 | static int | |
453 | vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) | |
454 | { | |
455 | struct venus_inst *inst = to_inst(file); | |
e69b987a | 456 | struct hfi_frame_data fdata = {0}; |
7472c1c6 SV |
457 | int ret; |
458 | ||
459 | ret = vdec_try_decoder_cmd(file, fh, cmd); | |
460 | if (ret) | |
461 | return ret; | |
462 | ||
463 | mutex_lock(&inst->lock); | |
7472c1c6 | 464 | |
e69b987a SV |
465 | /* |
466 | * Implement V4L2_DEC_CMD_STOP by enqueue an empty buffer on decoder | |
467 | * input to signal EOS. | |
468 | */ | |
469 | if (!(inst->streamon_out & inst->streamon_cap)) | |
470 | goto unlock; | |
7472c1c6 | 471 | |
e69b987a SV |
472 | fdata.buffer_type = HFI_BUFFER_INPUT; |
473 | fdata.flags |= HFI_BUFFERFLAG_EOS; | |
474 | fdata.device_addr = 0xdeadbeef; | |
475 | ||
476 | ret = hfi_session_process_buf(inst, &fdata); | |
477 | ||
478 | unlock: | |
479 | mutex_unlock(&inst->lock); | |
480 | return ret; | |
7472c1c6 SV |
481 | } |
482 | ||
483 | static const struct v4l2_ioctl_ops vdec_ioctl_ops = { | |
484 | .vidioc_querycap = vdec_querycap, | |
485 | .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt, | |
486 | .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt, | |
487 | .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, | |
488 | .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, | |
489 | .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, | |
490 | .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt, | |
491 | .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt, | |
492 | .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt, | |
493 | .vidioc_g_selection = vdec_g_selection, | |
494 | .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, | |
495 | .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, | |
496 | .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, | |
497 | .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, | |
498 | .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, | |
499 | .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, | |
500 | .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, | |
501 | .vidioc_streamon = v4l2_m2m_ioctl_streamon, | |
502 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, | |
503 | .vidioc_s_parm = vdec_s_parm, | |
504 | .vidioc_enum_framesizes = vdec_enum_framesizes, | |
505 | .vidioc_subscribe_event = vdec_subscribe_event, | |
506 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
507 | .vidioc_try_decoder_cmd = vdec_try_decoder_cmd, | |
508 | .vidioc_decoder_cmd = vdec_decoder_cmd, | |
509 | }; | |
510 | ||
511 | static int vdec_set_properties(struct venus_inst *inst) | |
512 | { | |
513 | struct vdec_controls *ctr = &inst->controls.dec; | |
ea8ce235 SV |
514 | struct hfi_enable en = { .enable = 1 }; |
515 | u32 ptype; | |
516 | int ret; | |
517 | ||
518 | if (ctr->post_loop_deb_mode) { | |
519 | ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; | |
520 | ret = hfi_session_set_property(inst, ptype, &en); | |
521 | if (ret) | |
522 | return ret; | |
523 | } | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
f012b23d SV |
528 | #define is_ubwc_fmt(fmt) (!!((fmt) & HFI_COLOR_FORMAT_UBWC_BASE)) |
529 | ||
ea8ce235 SV |
530 | static int vdec_output_conf(struct venus_inst *inst) |
531 | { | |
7472c1c6 SV |
532 | struct venus_core *core = inst->core; |
533 | struct hfi_enable en = { .enable = 1 }; | |
f012b23d SV |
534 | u32 width = inst->out_width; |
535 | u32 height = inst->out_height; | |
536 | u32 out_fmt, out2_fmt; | |
537 | bool ubwc = false; | |
7472c1c6 SV |
538 | u32 ptype; |
539 | int ret; | |
540 | ||
01165b84 SV |
541 | ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); |
542 | if (ret) | |
543 | return ret; | |
544 | ||
545 | ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); | |
546 | if (ret) | |
547 | return ret; | |
548 | ||
7472c1c6 SV |
549 | if (core->res->hfi_version == HFI_VERSION_1XX) { |
550 | ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; | |
551 | ret = hfi_session_set_property(inst, ptype, &en); | |
552 | if (ret) | |
553 | return ret; | |
554 | } | |
555 | ||
f012b23d SV |
556 | /* Force searching UBWC formats for bigger then HD resolutions */ |
557 | if (width > 1920 && height > ALIGN(1080, 32)) | |
558 | ubwc = true; | |
559 | ||
560 | /* For Venus v4 UBWC format is mandatory */ | |
561 | if (IS_V4(core)) | |
562 | ubwc = true; | |
563 | ||
564 | ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt, | |
565 | &out2_fmt, ubwc); | |
566 | if (ret) | |
567 | return ret; | |
568 | ||
569 | inst->output_buf_size = | |
570 | venus_helper_get_framesz_raw(out_fmt, width, height); | |
571 | inst->output2_buf_size = | |
572 | venus_helper_get_framesz_raw(out2_fmt, width, height); | |
573 | ||
574 | if (is_ubwc_fmt(out_fmt)) { | |
575 | inst->opb_buftype = HFI_BUFFER_OUTPUT2; | |
576 | inst->opb_fmt = out2_fmt; | |
577 | inst->dpb_buftype = HFI_BUFFER_OUTPUT; | |
578 | inst->dpb_fmt = out_fmt; | |
579 | } else if (is_ubwc_fmt(out2_fmt)) { | |
580 | inst->opb_buftype = HFI_BUFFER_OUTPUT; | |
581 | inst->opb_fmt = out_fmt; | |
582 | inst->dpb_buftype = HFI_BUFFER_OUTPUT2; | |
583 | inst->dpb_fmt = out2_fmt; | |
584 | } else { | |
585 | inst->opb_buftype = HFI_BUFFER_OUTPUT; | |
586 | inst->opb_fmt = out_fmt; | |
587 | inst->dpb_buftype = 0; | |
588 | inst->dpb_fmt = 0; | |
589 | } | |
590 | ||
591 | ret = venus_helper_set_raw_format(inst, inst->opb_fmt, | |
592 | inst->opb_buftype); | |
593 | if (ret) | |
594 | return ret; | |
595 | ||
596 | if (inst->dpb_fmt) { | |
597 | ret = venus_helper_set_multistream(inst, false, true); | |
598 | if (ret) | |
599 | return ret; | |
600 | ||
601 | ret = venus_helper_set_raw_format(inst, inst->dpb_fmt, | |
602 | inst->dpb_buftype); | |
603 | if (ret) | |
604 | return ret; | |
605 | ||
606 | ret = venus_helper_set_output_resolution(inst, width, height, | |
607 | HFI_BUFFER_OUTPUT2); | |
608 | if (ret) | |
609 | return ret; | |
610 | } | |
611 | ||
612 | if (IS_V3(core) || IS_V4(core)) { | |
613 | if (inst->output2_buf_size) { | |
614 | ret = venus_helper_set_bufsize(inst, | |
615 | inst->output2_buf_size, | |
616 | HFI_BUFFER_OUTPUT2); | |
617 | if (ret) | |
618 | return ret; | |
619 | } | |
620 | ||
621 | if (inst->output_buf_size) { | |
622 | ret = venus_helper_set_bufsize(inst, | |
623 | inst->output_buf_size, | |
624 | HFI_BUFFER_OUTPUT); | |
625 | if (ret) | |
626 | return ret; | |
627 | } | |
628 | } | |
629 | ||
2b0a8517 SV |
630 | ret = venus_helper_set_dyn_bufmode(inst); |
631 | if (ret) | |
632 | return ret; | |
7472c1c6 | 633 | |
7472c1c6 SV |
634 | return 0; |
635 | } | |
636 | ||
637 | static int vdec_init_session(struct venus_inst *inst) | |
638 | { | |
639 | int ret; | |
640 | ||
641 | ret = hfi_session_init(inst, inst->fmt_out->pixfmt); | |
642 | if (ret) | |
643 | return ret; | |
644 | ||
645 | ret = venus_helper_set_input_resolution(inst, inst->out_width, | |
646 | inst->out_height); | |
647 | if (ret) | |
648 | goto deinit; | |
649 | ||
650 | ret = venus_helper_set_color_format(inst, inst->fmt_cap->pixfmt); | |
651 | if (ret) | |
652 | goto deinit; | |
653 | ||
654 | return 0; | |
655 | deinit: | |
656 | hfi_session_deinit(inst); | |
657 | return ret; | |
658 | } | |
659 | ||
7094af54 SV |
660 | static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, |
661 | unsigned int *out_num) | |
7472c1c6 | 662 | { |
7094af54 | 663 | enum hfi_version ver = inst->core->res->hfi_version; |
7472c1c6 SV |
664 | struct hfi_buffer_requirements bufreq; |
665 | int ret; | |
666 | ||
7094af54 SV |
667 | *in_num = *out_num = 0; |
668 | ||
7472c1c6 SV |
669 | ret = vdec_init_session(inst); |
670 | if (ret) | |
671 | return ret; | |
672 | ||
7094af54 SV |
673 | ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); |
674 | if (ret) | |
675 | goto deinit; | |
676 | ||
677 | *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); | |
678 | ||
7472c1c6 | 679 | ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); |
7094af54 SV |
680 | if (ret) |
681 | goto deinit; | |
7472c1c6 | 682 | |
7094af54 | 683 | *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); |
7472c1c6 | 684 | |
7094af54 | 685 | deinit: |
7472c1c6 SV |
686 | hfi_session_deinit(inst); |
687 | ||
688 | return ret; | |
689 | } | |
690 | ||
691 | static int vdec_queue_setup(struct vb2_queue *q, | |
692 | unsigned int *num_buffers, unsigned int *num_planes, | |
693 | unsigned int sizes[], struct device *alloc_devs[]) | |
694 | { | |
695 | struct venus_inst *inst = vb2_get_drv_priv(q); | |
e1cb72de | 696 | unsigned int in_num, out_num; |
7472c1c6 SV |
697 | int ret = 0; |
698 | ||
699 | if (*num_planes) { | |
f012b23d SV |
700 | unsigned int output_buf_size = venus_helper_get_opb_size(inst); |
701 | ||
7472c1c6 SV |
702 | if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && |
703 | *num_planes != inst->fmt_out->num_planes) | |
704 | return -EINVAL; | |
705 | ||
706 | if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && | |
707 | *num_planes != inst->fmt_cap->num_planes) | |
708 | return -EINVAL; | |
709 | ||
710 | if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && | |
711 | sizes[0] < inst->input_buf_size) | |
712 | return -EINVAL; | |
713 | ||
714 | if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && | |
f012b23d | 715 | sizes[0] < output_buf_size) |
7472c1c6 SV |
716 | return -EINVAL; |
717 | ||
718 | return 0; | |
719 | } | |
720 | ||
7094af54 SV |
721 | ret = vdec_num_buffers(inst, &in_num, &out_num); |
722 | if (ret) | |
723 | return ret; | |
724 | ||
7472c1c6 SV |
725 | switch (q->type) { |
726 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
727 | *num_planes = inst->fmt_out->num_planes; | |
e1cb72de SV |
728 | sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, |
729 | inst->out_width, | |
7472c1c6 SV |
730 | inst->out_height); |
731 | inst->input_buf_size = sizes[0]; | |
7094af54 | 732 | *num_buffers = max(*num_buffers, in_num); |
7472c1c6 | 733 | inst->num_input_bufs = *num_buffers; |
7094af54 | 734 | inst->num_output_bufs = out_num; |
7472c1c6 SV |
735 | break; |
736 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
737 | *num_planes = inst->fmt_cap->num_planes; | |
e1cb72de SV |
738 | sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, |
739 | inst->width, | |
740 | inst->height); | |
7472c1c6 | 741 | inst->output_buf_size = sizes[0]; |
7094af54 SV |
742 | *num_buffers = max(*num_buffers, out_num); |
743 | inst->num_output_bufs = *num_buffers; | |
7472c1c6 SV |
744 | break; |
745 | default: | |
746 | ret = -EINVAL; | |
747 | break; | |
748 | } | |
749 | ||
750 | return ret; | |
751 | } | |
752 | ||
753 | static int vdec_verify_conf(struct venus_inst *inst) | |
754 | { | |
f04997bd | 755 | enum hfi_version ver = inst->core->res->hfi_version; |
7472c1c6 SV |
756 | struct hfi_buffer_requirements bufreq; |
757 | int ret; | |
758 | ||
759 | if (!inst->num_input_bufs || !inst->num_output_bufs) | |
760 | return -EINVAL; | |
761 | ||
762 | ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); | |
763 | if (ret) | |
764 | return ret; | |
765 | ||
766 | if (inst->num_output_bufs < bufreq.count_actual || | |
f04997bd | 767 | inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) |
7472c1c6 SV |
768 | return -EINVAL; |
769 | ||
770 | ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); | |
771 | if (ret) | |
772 | return ret; | |
773 | ||
f04997bd | 774 | if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) |
7472c1c6 SV |
775 | return -EINVAL; |
776 | ||
777 | return 0; | |
778 | } | |
779 | ||
780 | static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) | |
781 | { | |
782 | struct venus_inst *inst = vb2_get_drv_priv(q); | |
7472c1c6 SV |
783 | int ret; |
784 | ||
785 | mutex_lock(&inst->lock); | |
786 | ||
787 | if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
788 | inst->streamon_out = 1; | |
789 | else | |
790 | inst->streamon_cap = 1; | |
791 | ||
792 | if (!(inst->streamon_out & inst->streamon_cap)) { | |
793 | mutex_unlock(&inst->lock); | |
794 | return 0; | |
795 | } | |
796 | ||
797 | venus_helper_init_instance(inst); | |
798 | ||
799 | inst->reconfig = false; | |
800 | inst->sequence_cap = 0; | |
801 | inst->sequence_out = 0; | |
7472c1c6 SV |
802 | |
803 | ret = vdec_init_session(inst); | |
804 | if (ret) | |
805 | goto bufs_done; | |
806 | ||
807 | ret = vdec_set_properties(inst); | |
808 | if (ret) | |
809 | goto deinit_sess; | |
810 | ||
ea8ce235 SV |
811 | ret = vdec_output_conf(inst); |
812 | if (ret) | |
813 | goto deinit_sess; | |
7472c1c6 SV |
814 | |
815 | ret = vdec_verify_conf(inst); | |
816 | if (ret) | |
817 | goto deinit_sess; | |
818 | ||
819 | ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, | |
1eb04b2e | 820 | VB2_MAX_FRAME, VB2_MAX_FRAME); |
7472c1c6 SV |
821 | if (ret) |
822 | goto deinit_sess; | |
823 | ||
f012b23d SV |
824 | ret = venus_helper_alloc_dpb_bufs(inst); |
825 | if (ret) | |
826 | goto deinit_sess; | |
827 | ||
7472c1c6 SV |
828 | ret = venus_helper_vb2_start_streaming(inst); |
829 | if (ret) | |
830 | goto deinit_sess; | |
831 | ||
832 | mutex_unlock(&inst->lock); | |
833 | ||
834 | return 0; | |
835 | ||
836 | deinit_sess: | |
837 | hfi_session_deinit(inst); | |
838 | bufs_done: | |
839 | venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); | |
840 | if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
841 | inst->streamon_out = 0; | |
842 | else | |
843 | inst->streamon_cap = 0; | |
844 | mutex_unlock(&inst->lock); | |
845 | return ret; | |
846 | } | |
847 | ||
848 | static const struct vb2_ops vdec_vb2_ops = { | |
849 | .queue_setup = vdec_queue_setup, | |
850 | .buf_init = venus_helper_vb2_buf_init, | |
851 | .buf_prepare = venus_helper_vb2_buf_prepare, | |
852 | .start_streaming = vdec_start_streaming, | |
853 | .stop_streaming = venus_helper_vb2_stop_streaming, | |
854 | .buf_queue = venus_helper_vb2_buf_queue, | |
855 | }; | |
856 | ||
857 | static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, | |
858 | u32 tag, u32 bytesused, u32 data_offset, u32 flags, | |
859 | u32 hfi_flags, u64 timestamp_us) | |
860 | { | |
861 | enum vb2_buffer_state state = VB2_BUF_STATE_DONE; | |
862 | struct vb2_v4l2_buffer *vbuf; | |
863 | struct vb2_buffer *vb; | |
864 | unsigned int type; | |
865 | ||
866 | if (buf_type == HFI_BUFFER_INPUT) | |
867 | type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
868 | else | |
869 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
870 | ||
871 | vbuf = venus_helper_find_buf(inst, type, tag); | |
872 | if (!vbuf) | |
873 | return; | |
874 | ||
875 | vbuf->flags = flags; | |
876 | vbuf->field = V4L2_FIELD_NONE; | |
877 | ||
878 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { | |
879 | vb = &vbuf->vb2_buf; | |
87e25f4b | 880 | vb2_set_plane_payload(vb, 0, bytesused); |
7472c1c6 SV |
881 | vb->planes[0].data_offset = data_offset; |
882 | vb->timestamp = timestamp_us * NSEC_PER_USEC; | |
883 | vbuf->sequence = inst->sequence_cap++; | |
884 | ||
7472c1c6 SV |
885 | if (vbuf->flags & V4L2_BUF_FLAG_LAST) { |
886 | const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; | |
887 | ||
888 | v4l2_event_queue_fh(&inst->fh, &ev); | |
889 | } | |
890 | } else { | |
891 | vbuf->sequence = inst->sequence_out++; | |
892 | } | |
893 | ||
894 | if (hfi_flags & HFI_BUFFERFLAG_READONLY) | |
895 | venus_helper_acquire_buf_ref(vbuf); | |
896 | ||
897 | if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT) | |
898 | state = VB2_BUF_STATE_ERROR; | |
899 | ||
900 | v4l2_m2m_buf_done(vbuf, state); | |
901 | } | |
902 | ||
903 | static void vdec_event_notify(struct venus_inst *inst, u32 event, | |
904 | struct hfi_event_data *data) | |
905 | { | |
906 | struct venus_core *core = inst->core; | |
907 | struct device *dev = core->dev_dec; | |
908 | static const struct v4l2_event ev = { | |
909 | .type = V4L2_EVENT_SOURCE_CHANGE, | |
910 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; | |
911 | ||
912 | switch (event) { | |
913 | case EVT_SESSION_ERROR: | |
914 | inst->session_error = true; | |
915 | dev_err(dev, "dec: event session error %x\n", inst->error); | |
916 | break; | |
917 | case EVT_SYS_EVENT_CHANGE: | |
918 | switch (data->event_type) { | |
919 | case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES: | |
920 | hfi_session_continue(inst); | |
921 | dev_dbg(dev, "event sufficient resources\n"); | |
922 | break; | |
923 | case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES: | |
924 | inst->reconfig_height = data->height; | |
925 | inst->reconfig_width = data->width; | |
926 | inst->reconfig = true; | |
927 | ||
928 | v4l2_event_queue_fh(&inst->fh, &ev); | |
929 | ||
930 | dev_dbg(dev, "event not sufficient resources (%ux%u)\n", | |
931 | data->width, data->height); | |
932 | break; | |
933 | case HFI_EVENT_RELEASE_BUFFER_REFERENCE: | |
934 | venus_helper_release_buf_ref(inst, data->tag); | |
935 | break; | |
936 | default: | |
937 | break; | |
938 | } | |
939 | break; | |
940 | default: | |
941 | break; | |
942 | } | |
943 | } | |
944 | ||
945 | static const struct hfi_inst_ops vdec_hfi_ops = { | |
946 | .buf_done = vdec_buf_done, | |
947 | .event_notify = vdec_event_notify, | |
948 | }; | |
949 | ||
950 | static void vdec_inst_init(struct venus_inst *inst) | |
951 | { | |
952 | inst->fmt_out = &vdec_formats[6]; | |
953 | inst->fmt_cap = &vdec_formats[0]; | |
954 | inst->width = 1280; | |
955 | inst->height = ALIGN(720, 32); | |
956 | inst->out_width = 1280; | |
957 | inst->out_height = 720; | |
958 | inst->fps = 30; | |
959 | inst->timeperframe.numerator = 1; | |
960 | inst->timeperframe.denominator = 30; | |
1a73374a | 961 | inst->hfi_codec = HFI_VIDEO_CODEC_H264; |
7472c1c6 SV |
962 | } |
963 | ||
964 | static const struct v4l2_m2m_ops vdec_m2m_ops = { | |
965 | .device_run = venus_helper_m2m_device_run, | |
966 | .job_abort = venus_helper_m2m_job_abort, | |
967 | }; | |
968 | ||
969 | static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, | |
970 | struct vb2_queue *dst_vq) | |
971 | { | |
972 | struct venus_inst *inst = priv; | |
973 | int ret; | |
974 | ||
975 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
976 | src_vq->io_modes = VB2_MMAP | VB2_DMABUF; | |
977 | src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; | |
978 | src_vq->ops = &vdec_vb2_ops; | |
979 | src_vq->mem_ops = &vb2_dma_sg_memops; | |
980 | src_vq->drv_priv = inst; | |
981 | src_vq->buf_struct_size = sizeof(struct venus_buffer); | |
982 | src_vq->allow_zero_bytesused = 1; | |
983 | src_vq->min_buffers_needed = 1; | |
984 | src_vq->dev = inst->core->dev; | |
985 | ret = vb2_queue_init(src_vq); | |
986 | if (ret) | |
987 | return ret; | |
988 | ||
989 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
990 | dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; | |
991 | dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; | |
992 | dst_vq->ops = &vdec_vb2_ops; | |
993 | dst_vq->mem_ops = &vb2_dma_sg_memops; | |
994 | dst_vq->drv_priv = inst; | |
995 | dst_vq->buf_struct_size = sizeof(struct venus_buffer); | |
996 | dst_vq->allow_zero_bytesused = 1; | |
997 | dst_vq->min_buffers_needed = 1; | |
998 | dst_vq->dev = inst->core->dev; | |
999 | ret = vb2_queue_init(dst_vq); | |
1000 | if (ret) { | |
1001 | vb2_queue_release(src_vq); | |
1002 | return ret; | |
1003 | } | |
1004 | ||
1005 | return 0; | |
1006 | } | |
1007 | ||
1008 | static int vdec_open(struct file *file) | |
1009 | { | |
1010 | struct venus_core *core = video_drvdata(file); | |
1011 | struct venus_inst *inst; | |
1012 | int ret; | |
1013 | ||
1014 | inst = kzalloc(sizeof(*inst), GFP_KERNEL); | |
1015 | if (!inst) | |
1016 | return -ENOMEM; | |
1017 | ||
f012b23d | 1018 | INIT_LIST_HEAD(&inst->dpbbufs); |
7472c1c6 SV |
1019 | INIT_LIST_HEAD(&inst->registeredbufs); |
1020 | INIT_LIST_HEAD(&inst->internalbufs); | |
1021 | INIT_LIST_HEAD(&inst->list); | |
1022 | mutex_init(&inst->lock); | |
1023 | ||
1024 | inst->core = core; | |
1025 | inst->session_type = VIDC_SESSION_TYPE_DEC; | |
ebebc593 | 1026 | inst->num_output_bufs = 1; |
7472c1c6 SV |
1027 | |
1028 | venus_helper_init_instance(inst); | |
1029 | ||
1030 | ret = pm_runtime_get_sync(core->dev_dec); | |
1031 | if (ret < 0) | |
1032 | goto err_free_inst; | |
1033 | ||
1034 | ret = vdec_ctrl_init(inst); | |
1035 | if (ret) | |
1036 | goto err_put_sync; | |
1037 | ||
1038 | ret = hfi_session_create(inst, &vdec_hfi_ops); | |
1039 | if (ret) | |
1040 | goto err_ctrl_deinit; | |
1041 | ||
1042 | vdec_inst_init(inst); | |
1043 | ||
1044 | /* | |
1045 | * create m2m device for every instance, the m2m context scheduling | |
1046 | * is made by firmware side so we do not need to care about. | |
1047 | */ | |
1048 | inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops); | |
1049 | if (IS_ERR(inst->m2m_dev)) { | |
1050 | ret = PTR_ERR(inst->m2m_dev); | |
1051 | goto err_session_destroy; | |
1052 | } | |
1053 | ||
1054 | inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); | |
1055 | if (IS_ERR(inst->m2m_ctx)) { | |
1056 | ret = PTR_ERR(inst->m2m_ctx); | |
1057 | goto err_m2m_release; | |
1058 | } | |
1059 | ||
1060 | v4l2_fh_init(&inst->fh, core->vdev_dec); | |
1061 | ||
1062 | inst->fh.ctrl_handler = &inst->ctrl_handler; | |
1063 | v4l2_fh_add(&inst->fh); | |
1064 | inst->fh.m2m_ctx = inst->m2m_ctx; | |
1065 | file->private_data = &inst->fh; | |
1066 | ||
1067 | return 0; | |
1068 | ||
1069 | err_m2m_release: | |
1070 | v4l2_m2m_release(inst->m2m_dev); | |
1071 | err_session_destroy: | |
1072 | hfi_session_destroy(inst); | |
1073 | err_ctrl_deinit: | |
1074 | vdec_ctrl_deinit(inst); | |
1075 | err_put_sync: | |
1076 | pm_runtime_put_sync(core->dev_dec); | |
1077 | err_free_inst: | |
1078 | kfree(inst); | |
1079 | return ret; | |
1080 | } | |
1081 | ||
1082 | static int vdec_close(struct file *file) | |
1083 | { | |
1084 | struct venus_inst *inst = to_inst(file); | |
1085 | ||
1086 | v4l2_m2m_ctx_release(inst->m2m_ctx); | |
1087 | v4l2_m2m_release(inst->m2m_dev); | |
1088 | vdec_ctrl_deinit(inst); | |
1089 | hfi_session_destroy(inst); | |
1090 | mutex_destroy(&inst->lock); | |
1091 | v4l2_fh_del(&inst->fh); | |
1092 | v4l2_fh_exit(&inst->fh); | |
7472c1c6 SV |
1093 | |
1094 | pm_runtime_put_sync(inst->core->dev_dec); | |
17571ed6 SV |
1095 | |
1096 | kfree(inst); | |
7472c1c6 SV |
1097 | return 0; |
1098 | } | |
1099 | ||
1100 | static const struct v4l2_file_operations vdec_fops = { | |
1101 | .owner = THIS_MODULE, | |
1102 | .open = vdec_open, | |
1103 | .release = vdec_close, | |
1104 | .unlocked_ioctl = video_ioctl2, | |
1105 | .poll = v4l2_m2m_fop_poll, | |
1106 | .mmap = v4l2_m2m_fop_mmap, | |
1107 | #ifdef CONFIG_COMPAT | |
1108 | .compat_ioctl32 = v4l2_compat_ioctl32, | |
1109 | #endif | |
1110 | }; | |
1111 | ||
1112 | static int vdec_probe(struct platform_device *pdev) | |
1113 | { | |
1114 | struct device *dev = &pdev->dev; | |
1115 | struct video_device *vdev; | |
1116 | struct venus_core *core; | |
1117 | int ret; | |
1118 | ||
1119 | if (!dev->parent) | |
1120 | return -EPROBE_DEFER; | |
1121 | ||
1122 | core = dev_get_drvdata(dev->parent); | |
1123 | if (!core) | |
1124 | return -EPROBE_DEFER; | |
1125 | ||
aa3a8414 | 1126 | if (IS_V3(core) || IS_V4(core)) { |
7472c1c6 SV |
1127 | core->core0_clk = devm_clk_get(dev, "core"); |
1128 | if (IS_ERR(core->core0_clk)) | |
1129 | return PTR_ERR(core->core0_clk); | |
1130 | } | |
1131 | ||
aa3a8414 SV |
1132 | if (IS_V4(core)) { |
1133 | core->core0_bus_clk = devm_clk_get(dev, "bus"); | |
1134 | if (IS_ERR(core->core0_bus_clk)) | |
1135 | return PTR_ERR(core->core0_bus_clk); | |
1136 | } | |
1137 | ||
7472c1c6 SV |
1138 | platform_set_drvdata(pdev, core); |
1139 | ||
1140 | vdev = video_device_alloc(); | |
1141 | if (!vdev) | |
1142 | return -ENOMEM; | |
1143 | ||
c0decac1 | 1144 | strscpy(vdev->name, "qcom-venus-decoder", sizeof(vdev->name)); |
7472c1c6 SV |
1145 | vdev->release = video_device_release; |
1146 | vdev->fops = &vdec_fops; | |
1147 | vdev->ioctl_ops = &vdec_ioctl_ops; | |
1148 | vdev->vfl_dir = VFL_DIR_M2M; | |
1149 | vdev->v4l2_dev = &core->v4l2_dev; | |
1150 | vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; | |
1151 | ||
1152 | ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); | |
1153 | if (ret) | |
1154 | goto err_vdev_release; | |
1155 | ||
1156 | core->vdev_dec = vdev; | |
1157 | core->dev_dec = dev; | |
1158 | ||
1159 | video_set_drvdata(vdev, core); | |
1160 | pm_runtime_enable(dev); | |
1161 | ||
1162 | return 0; | |
1163 | ||
1164 | err_vdev_release: | |
1165 | video_device_release(vdev); | |
1166 | return ret; | |
1167 | } | |
1168 | ||
1169 | static int vdec_remove(struct platform_device *pdev) | |
1170 | { | |
1171 | struct venus_core *core = dev_get_drvdata(pdev->dev.parent); | |
1172 | ||
1173 | video_unregister_device(core->vdev_dec); | |
1174 | pm_runtime_disable(core->dev_dec); | |
1175 | ||
1176 | return 0; | |
1177 | } | |
1178 | ||
62d625c9 | 1179 | static __maybe_unused int vdec_runtime_suspend(struct device *dev) |
7472c1c6 SV |
1180 | { |
1181 | struct venus_core *core = dev_get_drvdata(dev); | |
aa3a8414 | 1182 | int ret; |
7472c1c6 | 1183 | |
aa3a8414 | 1184 | if (IS_V1(core)) |
7472c1c6 SV |
1185 | return 0; |
1186 | ||
aa3a8414 SV |
1187 | ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); |
1188 | if (ret) | |
1189 | return ret; | |
1190 | ||
1191 | if (IS_V4(core)) | |
1192 | clk_disable_unprepare(core->core0_bus_clk); | |
1193 | ||
7472c1c6 | 1194 | clk_disable_unprepare(core->core0_clk); |
7472c1c6 | 1195 | |
aa3a8414 | 1196 | return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); |
7472c1c6 SV |
1197 | } |
1198 | ||
62d625c9 | 1199 | static __maybe_unused int vdec_runtime_resume(struct device *dev) |
7472c1c6 SV |
1200 | { |
1201 | struct venus_core *core = dev_get_drvdata(dev); | |
1202 | int ret; | |
1203 | ||
aa3a8414 | 1204 | if (IS_V1(core)) |
7472c1c6 SV |
1205 | return 0; |
1206 | ||
aa3a8414 SV |
1207 | ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); |
1208 | if (ret) | |
1209 | return ret; | |
1210 | ||
7472c1c6 | 1211 | ret = clk_prepare_enable(core->core0_clk); |
aa3a8414 SV |
1212 | if (ret) |
1213 | goto err_power_disable; | |
1214 | ||
1215 | if (IS_V4(core)) | |
1216 | ret = clk_prepare_enable(core->core0_bus_clk); | |
7472c1c6 | 1217 | |
aa3a8414 SV |
1218 | if (ret) |
1219 | goto err_unprepare_core0; | |
1220 | ||
1221 | return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); | |
1222 | ||
1223 | err_unprepare_core0: | |
1224 | clk_disable_unprepare(core->core0_clk); | |
1225 | err_power_disable: | |
1226 | venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); | |
7472c1c6 SV |
1227 | return ret; |
1228 | } | |
7472c1c6 SV |
1229 | |
1230 | static const struct dev_pm_ops vdec_pm_ops = { | |
1231 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
1232 | pm_runtime_force_resume) | |
1233 | SET_RUNTIME_PM_OPS(vdec_runtime_suspend, vdec_runtime_resume, NULL) | |
1234 | }; | |
1235 | ||
1236 | static const struct of_device_id vdec_dt_match[] = { | |
1237 | { .compatible = "venus-decoder" }, | |
1238 | { } | |
1239 | }; | |
1240 | MODULE_DEVICE_TABLE(of, vdec_dt_match); | |
1241 | ||
1242 | static struct platform_driver qcom_venus_dec_driver = { | |
1243 | .probe = vdec_probe, | |
1244 | .remove = vdec_remove, | |
1245 | .driver = { | |
1246 | .name = "qcom-venus-decoder", | |
1247 | .of_match_table = vdec_dt_match, | |
1248 | .pm = &vdec_pm_ops, | |
1249 | }, | |
1250 | }; | |
1251 | module_platform_driver(qcom_venus_dec_driver); | |
1252 | ||
1253 | MODULE_ALIAS("platform:qcom-venus-decoder"); | |
1254 | MODULE_DESCRIPTION("Qualcomm Venus video decoder driver"); | |
1255 | MODULE_LICENSE("GPL v2"); |