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