1
2
3
4
5
6
7#include <linux/gcd.h>
8#include <media/v4l2-mem2mem.h>
9#include <media/v4l2-event.h>
10#include <media/videobuf2-dma-contig.h>
11
12#include "vdec_helpers.h"
13
14#define NUM_CANVAS_NV12 2
15#define NUM_CANVAS_YUV420 3
16
17u32 amvdec_read_dos(struct amvdec_core *core, u32 reg)
18{
19 return readl_relaxed(core->dos_base + reg);
20}
21EXPORT_SYMBOL_GPL(amvdec_read_dos);
22
23void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val)
24{
25 writel_relaxed(val, core->dos_base + reg);
26}
27EXPORT_SYMBOL_GPL(amvdec_write_dos);
28
29void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val)
30{
31 amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) | val);
32}
33EXPORT_SYMBOL_GPL(amvdec_write_dos_bits);
34
35void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val)
36{
37 amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) & ~val);
38}
39EXPORT_SYMBOL_GPL(amvdec_clear_dos_bits);
40
41u32 amvdec_read_parser(struct amvdec_core *core, u32 reg)
42{
43 return readl_relaxed(core->esparser_base + reg);
44}
45EXPORT_SYMBOL_GPL(amvdec_read_parser);
46
47void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val)
48{
49 writel_relaxed(val, core->esparser_base + reg);
50}
51EXPORT_SYMBOL_GPL(amvdec_write_parser);
52
53
54u32 amvdec_am21c_body_size(u32 width, u32 height)
55{
56 u32 width_64 = ALIGN(width, 64) / 64;
57 u32 height_32 = ALIGN(height, 32) / 32;
58
59 return SZ_4K * width_64 * height_32;
60}
61EXPORT_SYMBOL_GPL(amvdec_am21c_body_size);
62
63
64u32 amvdec_am21c_head_size(u32 width, u32 height)
65{
66 u32 width_128 = ALIGN(width, 128) / 128;
67 u32 height_64 = ALIGN(height, 64) / 64;
68
69 return 32 * width_128 * height_64;
70}
71EXPORT_SYMBOL_GPL(amvdec_am21c_head_size);
72
73u32 amvdec_am21c_size(u32 width, u32 height)
74{
75 return ALIGN(amvdec_am21c_body_size(width, height) +
76 amvdec_am21c_head_size(width, height), SZ_64K);
77}
78EXPORT_SYMBOL_GPL(amvdec_am21c_size);
79
80static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id)
81{
82 int ret;
83
84 if (sess->canvas_num >= MAX_CANVAS) {
85 dev_err(sess->core->dev, "Reached max number of canvas\n");
86 return -ENOMEM;
87 }
88
89 ret = meson_canvas_alloc(sess->core->canvas, canvas_id);
90 if (ret)
91 return ret;
92
93 sess->canvas_alloc[sess->canvas_num++] = *canvas_id;
94 return 0;
95}
96
97static int set_canvas_yuv420m(struct amvdec_session *sess,
98 struct vb2_buffer *vb, u32 width,
99 u32 height, u32 reg)
100{
101 struct amvdec_core *core = sess->core;
102 u8 canvas_id[NUM_CANVAS_YUV420];
103 dma_addr_t buf_paddr[NUM_CANVAS_YUV420];
104 int ret, i;
105
106 for (i = 0; i < NUM_CANVAS_YUV420; ++i) {
107 ret = canvas_alloc(sess, &canvas_id[i]);
108 if (ret)
109 return ret;
110
111 buf_paddr[i] =
112 vb2_dma_contig_plane_dma_addr(vb, i);
113 }
114
115
116 meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0],
117 width, height, MESON_CANVAS_WRAP_NONE,
118 MESON_CANVAS_BLKMODE_LINEAR,
119 MESON_CANVAS_ENDIAN_SWAP64);
120
121
122 meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1],
123 width / 2, height / 2, MESON_CANVAS_WRAP_NONE,
124 MESON_CANVAS_BLKMODE_LINEAR,
125 MESON_CANVAS_ENDIAN_SWAP64);
126
127
128 meson_canvas_config(core->canvas, canvas_id[2], buf_paddr[2],
129 width / 2, height / 2, MESON_CANVAS_WRAP_NONE,
130 MESON_CANVAS_BLKMODE_LINEAR,
131 MESON_CANVAS_ENDIAN_SWAP64);
132
133 amvdec_write_dos(core, reg,
134 ((canvas_id[2]) << 16) |
135 ((canvas_id[1]) << 8) |
136 (canvas_id[0]));
137
138 return 0;
139}
140
141static int set_canvas_nv12m(struct amvdec_session *sess,
142 struct vb2_buffer *vb, u32 width,
143 u32 height, u32 reg)
144{
145 struct amvdec_core *core = sess->core;
146 u8 canvas_id[NUM_CANVAS_NV12];
147 dma_addr_t buf_paddr[NUM_CANVAS_NV12];
148 int ret, i;
149
150 for (i = 0; i < NUM_CANVAS_NV12; ++i) {
151 ret = canvas_alloc(sess, &canvas_id[i]);
152 if (ret)
153 return ret;
154
155 buf_paddr[i] =
156 vb2_dma_contig_plane_dma_addr(vb, i);
157 }
158
159
160 meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0],
161 width, height, MESON_CANVAS_WRAP_NONE,
162 MESON_CANVAS_BLKMODE_LINEAR,
163 MESON_CANVAS_ENDIAN_SWAP64);
164
165
166 meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1],
167 width, height / 2, MESON_CANVAS_WRAP_NONE,
168 MESON_CANVAS_BLKMODE_LINEAR,
169 MESON_CANVAS_ENDIAN_SWAP64);
170
171 amvdec_write_dos(core, reg,
172 ((canvas_id[1]) << 16) |
173 ((canvas_id[1]) << 8) |
174 (canvas_id[0]));
175
176 return 0;
177}
178
179int amvdec_set_canvases(struct amvdec_session *sess,
180 u32 reg_base[], u32 reg_num[])
181{
182 struct v4l2_m2m_buffer *buf;
183 u32 pixfmt = sess->pixfmt_cap;
184 u32 width = ALIGN(sess->width, 32);
185 u32 height = ALIGN(sess->height, 32);
186 u32 reg_cur = reg_base[0];
187 u32 reg_num_cur = 0;
188 u32 reg_base_cur = 0;
189 int i = 0;
190 int ret;
191
192 v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
193 if (!reg_base[reg_base_cur])
194 return -EINVAL;
195
196 reg_cur = reg_base[reg_base_cur] + reg_num_cur * 4;
197
198 switch (pixfmt) {
199 case V4L2_PIX_FMT_NV12M:
200 ret = set_canvas_nv12m(sess, &buf->vb.vb2_buf, width,
201 height, reg_cur);
202 if (ret)
203 return ret;
204 break;
205 case V4L2_PIX_FMT_YUV420M:
206 ret = set_canvas_yuv420m(sess, &buf->vb.vb2_buf, width,
207 height, reg_cur);
208 if (ret)
209 return ret;
210 break;
211 default:
212 dev_err(sess->core->dev, "Unsupported pixfmt %08X\n",
213 pixfmt);
214 return -EINVAL;
215 }
216
217 reg_num_cur++;
218 if (reg_num_cur >= reg_num[reg_base_cur]) {
219 reg_base_cur++;
220 reg_num_cur = 0;
221 }
222
223 sess->fw_idx_to_vb2_idx[i++] = buf->vb.vb2_buf.index;
224 }
225
226 return 0;
227}
228EXPORT_SYMBOL_GPL(amvdec_set_canvases);
229
230void amvdec_add_ts(struct amvdec_session *sess, u64 ts,
231 struct v4l2_timecode tc, u32 offset, u32 vbuf_flags)
232{
233 struct amvdec_timestamp *new_ts;
234 unsigned long flags;
235
236 new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL);
237 new_ts->ts = ts;
238 new_ts->tc = tc;
239 new_ts->offset = offset;
240 new_ts->flags = vbuf_flags;
241
242 spin_lock_irqsave(&sess->ts_spinlock, flags);
243 list_add_tail(&new_ts->list, &sess->timestamps);
244 spin_unlock_irqrestore(&sess->ts_spinlock, flags);
245}
246EXPORT_SYMBOL_GPL(amvdec_add_ts);
247
248void amvdec_remove_ts(struct amvdec_session *sess, u64 ts)
249{
250 struct amvdec_timestamp *tmp;
251 unsigned long flags;
252
253 spin_lock_irqsave(&sess->ts_spinlock, flags);
254 list_for_each_entry(tmp, &sess->timestamps, list) {
255 if (tmp->ts == ts) {
256 list_del(&tmp->list);
257 kfree(tmp);
258 goto unlock;
259 }
260 }
261 dev_warn(sess->core->dev_dec,
262 "Couldn't remove buffer with timestamp %llu from list\n", ts);
263
264unlock:
265 spin_unlock_irqrestore(&sess->ts_spinlock, flags);
266}
267EXPORT_SYMBOL_GPL(amvdec_remove_ts);
268
269static void dst_buf_done(struct amvdec_session *sess,
270 struct vb2_v4l2_buffer *vbuf,
271 u32 field, u64 timestamp,
272 struct v4l2_timecode timecode, u32 flags)
273{
274 struct device *dev = sess->core->dev_dec;
275 u32 output_size = amvdec_get_output_size(sess);
276
277 switch (sess->pixfmt_cap) {
278 case V4L2_PIX_FMT_NV12M:
279 vbuf->vb2_buf.planes[0].bytesused = output_size;
280 vbuf->vb2_buf.planes[1].bytesused = output_size / 2;
281 break;
282 case V4L2_PIX_FMT_YUV420M:
283 vbuf->vb2_buf.planes[0].bytesused = output_size;
284 vbuf->vb2_buf.planes[1].bytesused = output_size / 4;
285 vbuf->vb2_buf.planes[2].bytesused = output_size / 4;
286 break;
287 }
288
289 vbuf->vb2_buf.timestamp = timestamp;
290 vbuf->sequence = sess->sequence_cap++;
291 vbuf->flags = flags;
292 vbuf->timecode = timecode;
293
294 if (sess->should_stop &&
295 atomic_read(&sess->esparser_queued_bufs) <= 1) {
296 const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
297
298 dev_dbg(dev, "Signaling EOS, sequence_cap = %u\n",
299 sess->sequence_cap - 1);
300 v4l2_event_queue_fh(&sess->fh, &ev);
301 vbuf->flags |= V4L2_BUF_FLAG_LAST;
302 } else if (sess->status == STATUS_NEEDS_RESUME) {
303
304 vbuf->flags |= V4L2_BUF_FLAG_LAST;
305 sess->sequence_cap = 0;
306 } else if (sess->should_stop)
307 dev_dbg(dev, "should_stop, %u bufs remain\n",
308 atomic_read(&sess->esparser_queued_bufs));
309
310 dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
311 vbuf->vb2_buf.index, timestamp, flags);
312 vbuf->field = field;
313 v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
314
315
316 schedule_work(&sess->esparser_queue_work);
317}
318
319void amvdec_dst_buf_done(struct amvdec_session *sess,
320 struct vb2_v4l2_buffer *vbuf, u32 field)
321{
322 struct device *dev = sess->core->dev_dec;
323 struct amvdec_timestamp *tmp;
324 struct list_head *timestamps = &sess->timestamps;
325 struct v4l2_timecode timecode;
326 u64 timestamp;
327 u32 vbuf_flags;
328 unsigned long flags;
329
330 spin_lock_irqsave(&sess->ts_spinlock, flags);
331 if (list_empty(timestamps)) {
332 dev_err(dev, "Buffer %u done but list is empty\n",
333 vbuf->vb2_buf.index);
334
335 v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
336 spin_unlock_irqrestore(&sess->ts_spinlock, flags);
337 return;
338 }
339
340 tmp = list_first_entry(timestamps, struct amvdec_timestamp, list);
341 timestamp = tmp->ts;
342 timecode = tmp->tc;
343 vbuf_flags = tmp->flags;
344 list_del(&tmp->list);
345 kfree(tmp);
346 spin_unlock_irqrestore(&sess->ts_spinlock, flags);
347
348 dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
349 atomic_dec(&sess->esparser_queued_bufs);
350}
351EXPORT_SYMBOL_GPL(amvdec_dst_buf_done);
352
353void amvdec_dst_buf_done_offset(struct amvdec_session *sess,
354 struct vb2_v4l2_buffer *vbuf,
355 u32 offset, u32 field, bool allow_drop)
356{
357 struct device *dev = sess->core->dev_dec;
358 struct amvdec_timestamp *match = NULL;
359 struct amvdec_timestamp *tmp, *n;
360 struct v4l2_timecode timecode = { 0 };
361 u64 timestamp = 0;
362 u32 vbuf_flags = 0;
363 unsigned long flags;
364
365 spin_lock_irqsave(&sess->ts_spinlock, flags);
366
367
368 list_for_each_entry_safe(tmp, n, &sess->timestamps, list) {
369 if (tmp->offset > offset) {
370
371
372
373
374 if (tmp->used_count++ >= 32) {
375 list_del(&tmp->list);
376 kfree(tmp);
377 }
378 break;
379 }
380
381 match = tmp;
382 }
383
384 if (!match) {
385 dev_err(dev, "Buffer %u done but can't match offset (%08X)\n",
386 vbuf->vb2_buf.index, offset);
387 } else {
388 timestamp = match->ts;
389 timecode = match->tc;
390 vbuf_flags = match->flags;
391 list_del(&match->list);
392 kfree(match);
393 }
394 spin_unlock_irqrestore(&sess->ts_spinlock, flags);
395
396 dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
397 if (match)
398 atomic_dec(&sess->esparser_queued_bufs);
399}
400EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_offset);
401
402void amvdec_dst_buf_done_idx(struct amvdec_session *sess,
403 u32 buf_idx, u32 offset, u32 field)
404{
405 struct vb2_v4l2_buffer *vbuf;
406 struct device *dev = sess->core->dev_dec;
407
408 vbuf = v4l2_m2m_dst_buf_remove_by_idx(sess->m2m_ctx,
409 sess->fw_idx_to_vb2_idx[buf_idx]);
410
411 if (!vbuf) {
412 dev_err(dev,
413 "Buffer %u done but it doesn't exist in m2m_ctx\n",
414 buf_idx);
415 return;
416 }
417
418 if (offset != -1)
419 amvdec_dst_buf_done_offset(sess, vbuf, offset, field, true);
420 else
421 amvdec_dst_buf_done(sess, vbuf, field);
422}
423EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_idx);
424
425void amvdec_set_par_from_dar(struct amvdec_session *sess,
426 u32 dar_num, u32 dar_den)
427{
428 u32 div;
429
430 sess->pixelaspect.numerator = sess->height * dar_num;
431 sess->pixelaspect.denominator = sess->width * dar_den;
432 div = gcd(sess->pixelaspect.numerator, sess->pixelaspect.denominator);
433 sess->pixelaspect.numerator /= div;
434 sess->pixelaspect.denominator /= div;
435}
436EXPORT_SYMBOL_GPL(amvdec_set_par_from_dar);
437
438void amvdec_src_change(struct amvdec_session *sess, u32 width,
439 u32 height, u32 dpb_size)
440{
441 static const struct v4l2_event ev = {
442 .type = V4L2_EVENT_SOURCE_CHANGE,
443 .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION };
444
445 v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size);
446
447
448
449
450
451 if (sess->streamon_cap &&
452 sess->width == width &&
453 sess->height == height &&
454 dpb_size <= sess->num_dst_bufs) {
455 sess->fmt_out->codec_ops->resume(sess);
456 return;
457 }
458
459 sess->changed_format = 0;
460 sess->width = width;
461 sess->height = height;
462 sess->status = STATUS_NEEDS_RESUME;
463
464 dev_dbg(sess->core->dev, "Res. changed (%ux%u), DPB size %u\n",
465 width, height, dpb_size);
466 v4l2_event_queue_fh(&sess->fh, &ev);
467}
468EXPORT_SYMBOL_GPL(amvdec_src_change);
469
470void amvdec_abort(struct amvdec_session *sess)
471{
472 dev_info(sess->core->dev, "Aborting decoding session!\n");
473 vb2_queue_error(&sess->m2m_ctx->cap_q_ctx.q);
474 vb2_queue_error(&sess->m2m_ctx->out_q_ctx.q);
475}
476EXPORT_SYMBOL_GPL(amvdec_abort);
477