162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NVIDIA Tegra Video decoder driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019-2022 Dmitry Osipenko <digetx@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on Cedrus driver by Bootlin. 862306a36Sopenharmony_ci * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> 962306a36Sopenharmony_ci * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Based on Rockchip driver by Collabora. 1262306a36Sopenharmony_ci * Copyright (C) 2019 Boris Brezillon <boris.brezillon@collabora.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "vde.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const struct v4l2_ctrl_config ctrl_cfgs[] = { 2162306a36Sopenharmony_ci { .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, 2262306a36Sopenharmony_ci { .id = V4L2_CID_STATELESS_H264_SPS, }, 2362306a36Sopenharmony_ci { .id = V4L2_CID_STATELESS_H264_PPS, }, 2462306a36Sopenharmony_ci { 2562306a36Sopenharmony_ci .id = V4L2_CID_STATELESS_H264_DECODE_MODE, 2662306a36Sopenharmony_ci .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 2762306a36Sopenharmony_ci .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 2862306a36Sopenharmony_ci .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 2962306a36Sopenharmony_ci }, 3062306a36Sopenharmony_ci { 3162306a36Sopenharmony_ci .id = V4L2_CID_STATELESS_H264_START_CODE, 3262306a36Sopenharmony_ci .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 3362306a36Sopenharmony_ci .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 3462306a36Sopenharmony_ci .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 3562306a36Sopenharmony_ci }, 3662306a36Sopenharmony_ci { 3762306a36Sopenharmony_ci .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, 3862306a36Sopenharmony_ci .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 3962306a36Sopenharmony_ci .max = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, 4062306a36Sopenharmony_ci .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, 4162306a36Sopenharmony_ci }, 4262306a36Sopenharmony_ci { 4362306a36Sopenharmony_ci .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, 4462306a36Sopenharmony_ci .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 4562306a36Sopenharmony_ci .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 4662306a36Sopenharmony_ci }, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic inline struct tegra_ctx *fh_to_tegra_ctx(struct v4l2_fh *fh) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return container_of(fh, struct tegra_ctx, fh); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void tegra_set_control_data(struct tegra_ctx *ctx, void *data, u32 id) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci switch (id) { 5762306a36Sopenharmony_ci case V4L2_CID_STATELESS_H264_DECODE_PARAMS: 5862306a36Sopenharmony_ci ctx->h264.decode_params = data; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case V4L2_CID_STATELESS_H264_SPS: 6162306a36Sopenharmony_ci ctx->h264.sps = data; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case V4L2_CID_STATELESS_H264_PPS: 6462306a36Sopenharmony_ci ctx->h264.pps = data; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_civoid tegra_vde_prepare_control_data(struct tegra_ctx *ctx, u32 id) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned int i; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) { 7462306a36Sopenharmony_ci if (ctx->ctrls[i]->id == id) { 7562306a36Sopenharmony_ci tegra_set_control_data(ctx, ctx->ctrls[i]->p_cur.p, id); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tegra_set_control_data(ctx, NULL, id); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int tegra_queue_setup(struct vb2_queue *vq, 8462306a36Sopenharmony_ci unsigned int *nbufs, 8562306a36Sopenharmony_ci unsigned int *num_planes, 8662306a36Sopenharmony_ci unsigned int sizes[], 8762306a36Sopenharmony_ci struct device *alloc_devs[]) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vq); 9062306a36Sopenharmony_ci struct v4l2_format *f; 9162306a36Sopenharmony_ci unsigned int i; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) 9462306a36Sopenharmony_ci f = &ctx->coded_fmt; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci f = &ctx->decoded_fmt; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (*num_planes) { 9962306a36Sopenharmony_ci if (*num_planes != f->fmt.pix_mp.num_planes) 10062306a36Sopenharmony_ci return -EINVAL; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { 10362306a36Sopenharmony_ci if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci *num_planes = f->fmt.pix_mp.num_planes; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (i = 0; i < f->fmt.pix_mp.num_planes; i++) 11062306a36Sopenharmony_ci sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int tegra_buf_out_validate(struct vb2_buffer *vb) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void __tegra_buf_cleanup(struct vb2_buffer *vb, unsigned int i) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 12762306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vq); 12862306a36Sopenharmony_ci struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci while (i--) { 13162306a36Sopenharmony_ci if (tb->a[i]) { 13262306a36Sopenharmony_ci tegra_vde_dmabuf_cache_unmap(ctx->vde, tb->a[i], true); 13362306a36Sopenharmony_ci tb->a[i] = NULL; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (tb->iova[i]) { 13762306a36Sopenharmony_ci tegra_vde_iommu_unmap(ctx->vde, tb->iova[i]); 13862306a36Sopenharmony_ci tb->iova[i] = NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (tb->aux) { 14362306a36Sopenharmony_ci tegra_vde_free_bo(tb->aux); 14462306a36Sopenharmony_ci tb->aux = NULL; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int tegra_buf_init(struct vb2_buffer *vb) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 15162306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vq); 15262306a36Sopenharmony_ci struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); 15362306a36Sopenharmony_ci struct tegra_vde *vde = ctx->vde; 15462306a36Sopenharmony_ci enum dma_data_direction dma_dir; 15562306a36Sopenharmony_ci struct sg_table *sgt; 15662306a36Sopenharmony_ci unsigned int i; 15762306a36Sopenharmony_ci int err; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb->num_planes > 1) { 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Tegra decoder writes auxiliary data for I/P frames. 16262306a36Sopenharmony_ci * This data is needed for decoding of B frames. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci err = tegra_vde_alloc_bo(vde, &tb->aux, DMA_FROM_DEVICE, 16562306a36Sopenharmony_ci vb2_plane_size(vb, 1)); 16662306a36Sopenharmony_ci if (err) 16762306a36Sopenharmony_ci return err; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) 17162306a36Sopenharmony_ci dma_dir = DMA_TO_DEVICE; 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci dma_dir = DMA_FROM_DEVICE; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (i = 0; i < vb->num_planes; i++) { 17662306a36Sopenharmony_ci if (vq->memory == VB2_MEMORY_DMABUF) { 17762306a36Sopenharmony_ci get_dma_buf(vb->planes[i].dbuf); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci err = tegra_vde_dmabuf_cache_map(vde, vb->planes[i].dbuf, 18062306a36Sopenharmony_ci dma_dir, &tb->a[i], 18162306a36Sopenharmony_ci &tb->dma_base[i]); 18262306a36Sopenharmony_ci if (err) { 18362306a36Sopenharmony_ci dma_buf_put(vb->planes[i].dbuf); 18462306a36Sopenharmony_ci goto cleanup; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (vde->domain) { 19162306a36Sopenharmony_ci sgt = vb2_dma_sg_plane_desc(vb, i); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci err = tegra_vde_iommu_map(vde, sgt, &tb->iova[i], 19462306a36Sopenharmony_ci vb2_plane_size(vb, i)); 19562306a36Sopenharmony_ci if (err) 19662306a36Sopenharmony_ci goto cleanup; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci tb->dma_base[i] = iova_dma_addr(&vde->iova, tb->iova[i]); 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci tb->dma_base[i] = vb2_dma_contig_plane_dma_addr(vb, i); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cicleanup: 20762306a36Sopenharmony_ci __tegra_buf_cleanup(vb, i); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return err; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void tegra_buf_cleanup(struct vb2_buffer *vb) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci __tegra_buf_cleanup(vb, vb->num_planes); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int tegra_buf_prepare(struct vb2_buffer *vb) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 22062306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vq); 22162306a36Sopenharmony_ci struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); 22262306a36Sopenharmony_ci size_t hw_align, hw_size, hw_payload, size, offset; 22362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixfmt; 22462306a36Sopenharmony_ci unsigned int i; 22562306a36Sopenharmony_ci void *vb_data; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 22862306a36Sopenharmony_ci hw_align = BSEV_ALIGN; 22962306a36Sopenharmony_ci pixfmt = &ctx->coded_fmt.fmt.pix_mp; 23062306a36Sopenharmony_ci } else { 23162306a36Sopenharmony_ci hw_align = FRAMEID_ALIGN; 23262306a36Sopenharmony_ci pixfmt = &ctx->decoded_fmt.fmt.pix_mp; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (i = 0; i < vb->num_planes; i++) { 23662306a36Sopenharmony_ci offset = vb->planes[i].data_offset; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (offset & (hw_align - 1)) 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (V4L2_TYPE_IS_CAPTURE(vq->type)) { 24262306a36Sopenharmony_ci size = pixfmt->plane_fmt[i].sizeimage; 24362306a36Sopenharmony_ci hw_payload = ALIGN(size, VDE_ATOM); 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci size = vb2_get_plane_payload(vb, i) - offset; 24662306a36Sopenharmony_ci hw_payload = ALIGN(size + VDE_ATOM, SXE_BUFFER); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci hw_size = offset + hw_payload; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < hw_size) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, hw_payload); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 25762306a36Sopenharmony_ci vb_data = vb2_plane_vaddr(vb, i); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * Hardware requires zero-padding of coded data. 26162306a36Sopenharmony_ci * Otherwise it will fail to parse the trailing 26262306a36Sopenharmony_ci * data and abort the decoding. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (vb_data) 26562306a36Sopenharmony_ci memset(vb_data + offset + size, 0, 26662306a36Sopenharmony_ci hw_size - offset - size); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci tb->dma_addr[i] = tb->dma_base[i] + offset; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci switch (pixfmt->pixelformat) { 27362306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420M: 27462306a36Sopenharmony_ci swap(tb->dma_addr[1], tb->dma_addr[2]); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void tegra_buf_queue(struct vb2_buffer *vb) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 28462306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void tegra_buf_request_complete(struct vb2_buffer *vb) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int tegra_start_streaming(struct vb2_queue *vq, unsigned int count) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void tegra_stop_streaming(struct vb2_queue *vq) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct tegra_ctx *ctx = vb2_get_drv_priv(vq); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci while (true) { 30662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) 30962306a36Sopenharmony_ci vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!vbuf) 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl); 31762306a36Sopenharmony_ci v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const struct vb2_ops tegra_qops = { 32262306a36Sopenharmony_ci .queue_setup = tegra_queue_setup, 32362306a36Sopenharmony_ci .buf_init = tegra_buf_init, 32462306a36Sopenharmony_ci .buf_cleanup = tegra_buf_cleanup, 32562306a36Sopenharmony_ci .buf_prepare = tegra_buf_prepare, 32662306a36Sopenharmony_ci .buf_queue = tegra_buf_queue, 32762306a36Sopenharmony_ci .buf_out_validate = tegra_buf_out_validate, 32862306a36Sopenharmony_ci .buf_request_complete = tegra_buf_request_complete, 32962306a36Sopenharmony_ci .start_streaming = tegra_start_streaming, 33062306a36Sopenharmony_ci .stop_streaming = tegra_stop_streaming, 33162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 33262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int tegra_queue_init(void *priv, 33662306a36Sopenharmony_ci struct vb2_queue *src_vq, 33762306a36Sopenharmony_ci struct vb2_queue *dst_vq) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct tegra_ctx *ctx = priv; 34062306a36Sopenharmony_ci struct tegra_vde *vde = ctx->vde; 34162306a36Sopenharmony_ci const struct vb2_mem_ops *mem_ops; 34262306a36Sopenharmony_ci unsigned long dma_attrs; 34362306a36Sopenharmony_ci int err; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * TODO: Switch to use of vb2_dma_contig_memops uniformly once we 34762306a36Sopenharmony_ci * will add IOMMU_DOMAIN support for video decoder to tegra-smmu 34862306a36Sopenharmony_ci * driver. For now we need to stick with SG ops in order to be able 34962306a36Sopenharmony_ci * to get SGT table easily. This is suboptimal since SG mappings are 35062306a36Sopenharmony_ci * wasting CPU cache and we don't need that caching. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci if (vde->domain) 35362306a36Sopenharmony_ci mem_ops = &vb2_dma_sg_memops; 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci mem_ops = &vb2_dma_contig_memops; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci dma_attrs = DMA_ATTR_WRITE_COMBINE; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer); 36062306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 36162306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 36262306a36Sopenharmony_ci src_vq->io_modes = VB2_DMABUF | VB2_MMAP; 36362306a36Sopenharmony_ci src_vq->supports_requests = true; 36462306a36Sopenharmony_ci src_vq->requires_requests = true; 36562306a36Sopenharmony_ci src_vq->lock = &vde->v4l2_lock; 36662306a36Sopenharmony_ci src_vq->dma_attrs = dma_attrs; 36762306a36Sopenharmony_ci src_vq->mem_ops = mem_ops; 36862306a36Sopenharmony_ci src_vq->ops = &tegra_qops; 36962306a36Sopenharmony_ci src_vq->drv_priv = ctx; 37062306a36Sopenharmony_ci src_vq->dev = vde->dev; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci err = vb2_queue_init(src_vq); 37362306a36Sopenharmony_ci if (err) { 37462306a36Sopenharmony_ci v4l2_err(&vde->v4l2_dev, 37562306a36Sopenharmony_ci "failed to initialize src queue: %d\n", err); 37662306a36Sopenharmony_ci return err; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * We may need to zero the end of bitstream in kernel if userspace 38162306a36Sopenharmony_ci * doesn't do that, hence kmap is needed for the coded data. It's not 38262306a36Sopenharmony_ci * needed for framebuffers. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer); 38762306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 38862306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 38962306a36Sopenharmony_ci dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; 39062306a36Sopenharmony_ci dst_vq->lock = &vde->v4l2_lock; 39162306a36Sopenharmony_ci dst_vq->dma_attrs = dma_attrs; 39262306a36Sopenharmony_ci dst_vq->mem_ops = mem_ops; 39362306a36Sopenharmony_ci dst_vq->ops = &tegra_qops; 39462306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 39562306a36Sopenharmony_ci dst_vq->dev = vde->dev; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci err = vb2_queue_init(dst_vq); 39862306a36Sopenharmony_ci if (err) { 39962306a36Sopenharmony_ci v4l2_err(&vde->v4l2_dev, 40062306a36Sopenharmony_ci "failed to initialize dst queue: %d\n", err); 40162306a36Sopenharmony_ci return err; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void tegra_reset_fmt(struct tegra_ctx *ctx, struct v4l2_format *f, 40862306a36Sopenharmony_ci u32 fourcc) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci memset(f, 0, sizeof(*f)); 41162306a36Sopenharmony_ci f->fmt.pix_mp.pixelformat = fourcc; 41262306a36Sopenharmony_ci f->fmt.pix_mp.field = V4L2_FIELD_NONE; 41362306a36Sopenharmony_ci f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; 41462306a36Sopenharmony_ci f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 41562306a36Sopenharmony_ci f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; 41662306a36Sopenharmony_ci f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void tegra_reset_coded_fmt(struct tegra_ctx *ctx) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci const struct tegra_vde_soc *soc = ctx->vde->soc; 42262306a36Sopenharmony_ci struct v4l2_format *f = &ctx->coded_fmt; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ctx->coded_fmt_desc = &soc->coded_fmts[0]; 42562306a36Sopenharmony_ci tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 42862306a36Sopenharmony_ci f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; 42962306a36Sopenharmony_ci f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void tegra_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, 43362306a36Sopenharmony_ci u32 pixelformat, u32 width, u32 height) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci const struct v4l2_format_info *info = v4l2_format_info(pixelformat); 43662306a36Sopenharmony_ci struct v4l2_plane_pix_format *plane; 43762306a36Sopenharmony_ci unsigned int i; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci switch (pixelformat) { 44062306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420M: 44162306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420M: 44262306a36Sopenharmony_ci pixfmt->width = width; 44362306a36Sopenharmony_ci pixfmt->height = height; 44462306a36Sopenharmony_ci pixfmt->pixelformat = pixelformat; 44562306a36Sopenharmony_ci pixfmt->num_planes = info->mem_planes; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (i = 0; i < pixfmt->num_planes; i++) { 44862306a36Sopenharmony_ci unsigned int hdiv = (i == 0) ? 1 : 2; 44962306a36Sopenharmony_ci unsigned int vdiv = (i == 0) ? 1 : 2; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * VDE is connected to Graphics Memory using 128bit port, 45362306a36Sopenharmony_ci * all memory accesses are made using 16B atoms. 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * V4L requires Cb/Cr strides to be exactly half of the 45662306a36Sopenharmony_ci * Y stride, hence we're aligning Y to 16B x 2. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci plane = &pixfmt->plane_fmt[i]; 45962306a36Sopenharmony_ci plane->bytesperline = ALIGN(width, VDE_ATOM * 2) / hdiv; 46062306a36Sopenharmony_ci plane->sizeimage = plane->bytesperline * height / vdiv; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void tegra_reset_decoded_fmt(struct tegra_ctx *ctx) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct v4l2_format *f = &ctx->decoded_fmt; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); 47262306a36Sopenharmony_ci f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 47362306a36Sopenharmony_ci tegra_fill_pixfmt_mp(&f->fmt.pix_mp, 47462306a36Sopenharmony_ci ctx->coded_fmt_desc->decoded_fmts[0], 47562306a36Sopenharmony_ci ctx->coded_fmt.fmt.pix_mp.width, 47662306a36Sopenharmony_ci ctx->coded_fmt.fmt.pix_mp.height); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void tegra_job_finish(struct tegra_ctx *ctx, 48062306a36Sopenharmony_ci enum vb2_buffer_state result) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci v4l2_m2m_buf_done_and_job_finish(ctx->vde->m2m, ctx->fh.m2m_ctx, 48362306a36Sopenharmony_ci result); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void tegra_decode_complete(struct work_struct *work) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct tegra_ctx *ctx = container_of(work, struct tegra_ctx, work); 48962306a36Sopenharmony_ci int err; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci err = ctx->coded_fmt_desc->decode_wait(ctx); 49262306a36Sopenharmony_ci if (err) 49362306a36Sopenharmony_ci tegra_job_finish(ctx, VB2_BUF_STATE_ERROR); 49462306a36Sopenharmony_ci else 49562306a36Sopenharmony_ci tegra_job_finish(ctx, VB2_BUF_STATE_DONE); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int tegra_querycap(struct file *file, void *priv, 49962306a36Sopenharmony_ci struct v4l2_capability *cap) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci strscpy(cap->bus_info, "platform:tegra-vde", sizeof(cap->bus_info)); 50262306a36Sopenharmony_ci strscpy(cap->driver, "tegra-vde", sizeof(cap->driver)); 50362306a36Sopenharmony_ci strscpy(cap->card, "tegra-vde", sizeof(cap->card)); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int tegra_enum_decoded_fmt(struct file *file, void *priv, 50962306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (WARN_ON(!ctx->coded_fmt_desc)) 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci f->pixelformat = ctx->coded_fmt_desc->decoded_fmts[f->index]; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int tegra_g_decoded_fmt(struct file *file, void *priv, 52562306a36Sopenharmony_ci struct v4l2_format *f) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci *f = ctx->decoded_fmt; 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int tegra_try_decoded_fmt(struct file *file, void *priv, 53462306a36Sopenharmony_ci struct v4l2_format *f) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; 53762306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 53862306a36Sopenharmony_ci const struct tegra_coded_fmt_desc *coded_desc; 53962306a36Sopenharmony_ci unsigned int i; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * The codec context should point to a coded format desc, if the format 54362306a36Sopenharmony_ci * on the coded end has not been set yet, it should point to the 54462306a36Sopenharmony_ci * default value. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci coded_desc = ctx->coded_fmt_desc; 54762306a36Sopenharmony_ci if (WARN_ON(!coded_desc)) 54862306a36Sopenharmony_ci return -EINVAL; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (!coded_desc->num_decoded_fmts) 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (i = 0; i < coded_desc->num_decoded_fmts; i++) { 55462306a36Sopenharmony_ci if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat) 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (i == coded_desc->num_decoded_fmts) 55962306a36Sopenharmony_ci pix_mp->pixelformat = coded_desc->decoded_fmts[0]; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* always apply the frmsize constraint of the coded end */ 56262306a36Sopenharmony_ci v4l2_apply_frmsize_constraints(&pix_mp->width, 56362306a36Sopenharmony_ci &pix_mp->height, 56462306a36Sopenharmony_ci &coded_desc->frmsize); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci tegra_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, 56762306a36Sopenharmony_ci pix_mp->width, pix_mp->height); 56862306a36Sopenharmony_ci pix_mp->field = V4L2_FIELD_NONE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int tegra_s_decoded_fmt(struct file *file, void *priv, 57462306a36Sopenharmony_ci struct v4l2_format *f) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 57762306a36Sopenharmony_ci struct vb2_queue *vq; 57862306a36Sopenharmony_ci int err; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* change not allowed if queue is busy */ 58162306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 58262306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 58362306a36Sopenharmony_ci if (vb2_is_busy(vq)) 58462306a36Sopenharmony_ci return -EBUSY; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci err = tegra_try_decoded_fmt(file, priv, f); 58762306a36Sopenharmony_ci if (err) 58862306a36Sopenharmony_ci return err; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ctx->decoded_fmt = *f; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int tegra_enum_coded_fmt(struct file *file, void *priv, 59662306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 59962306a36Sopenharmony_ci const struct tegra_vde_soc *soc = ctx->vde->soc; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (f->index >= soc->num_coded_fmts) 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci f->pixelformat = soc->coded_fmts[f->index].fourcc; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int tegra_g_coded_fmt(struct file *file, void *priv, 61062306a36Sopenharmony_ci struct v4l2_format *f) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci *f = ctx->coded_fmt; 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic const struct tegra_coded_fmt_desc * 61962306a36Sopenharmony_citegra_find_coded_fmt_desc(struct tegra_ctx *ctx, u32 fourcc) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci const struct tegra_vde_soc *soc = ctx->vde->soc; 62262306a36Sopenharmony_ci unsigned int i; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci for (i = 0; i < soc->num_coded_fmts; i++) { 62562306a36Sopenharmony_ci if (soc->coded_fmts[i].fourcc == fourcc) 62662306a36Sopenharmony_ci return &soc->coded_fmts[i]; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return NULL; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int tegra_try_coded_fmt(struct file *file, void *priv, 63362306a36Sopenharmony_ci struct v4l2_format *f) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; 63662306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 63762306a36Sopenharmony_ci const struct tegra_vde_soc *soc = ctx->vde->soc; 63862306a36Sopenharmony_ci int size = pix_mp->plane_fmt[0].sizeimage; 63962306a36Sopenharmony_ci const struct tegra_coded_fmt_desc *desc; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci desc = tegra_find_coded_fmt_desc(ctx, pix_mp->pixelformat); 64262306a36Sopenharmony_ci if (!desc) { 64362306a36Sopenharmony_ci pix_mp->pixelformat = soc->coded_fmts[0].fourcc; 64462306a36Sopenharmony_ci desc = &soc->coded_fmts[0]; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci v4l2_apply_frmsize_constraints(&pix_mp->width, 64862306a36Sopenharmony_ci &pix_mp->height, 64962306a36Sopenharmony_ci &desc->frmsize); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci pix_mp->plane_fmt[0].sizeimage = max(ALIGN(size, SXE_BUFFER), SZ_2M); 65262306a36Sopenharmony_ci pix_mp->field = V4L2_FIELD_NONE; 65362306a36Sopenharmony_ci pix_mp->num_planes = 1; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int tegra_s_coded_fmt(struct file *file, void *priv, 65962306a36Sopenharmony_ci struct v4l2_format *f) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 66262306a36Sopenharmony_ci struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; 66362306a36Sopenharmony_ci const struct tegra_coded_fmt_desc *desc; 66462306a36Sopenharmony_ci struct vb2_queue *peer_vq, *vq; 66562306a36Sopenharmony_ci struct v4l2_format *cap_fmt; 66662306a36Sopenharmony_ci int err; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * In order to support dynamic resolution change, the decoder admits 67062306a36Sopenharmony_ci * a resolution change, as long as the pixelformat remains. Can't be 67162306a36Sopenharmony_ci * done if streaming. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 67462306a36Sopenharmony_ci if (vb2_is_streaming(vq) || 67562306a36Sopenharmony_ci (vb2_is_busy(vq) && 67662306a36Sopenharmony_ci f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat)) 67762306a36Sopenharmony_ci return -EBUSY; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * Since format change on the OUTPUT queue will reset the CAPTURE 68162306a36Sopenharmony_ci * queue, we can't allow doing so when the CAPTURE queue has buffers 68262306a36Sopenharmony_ci * allocated. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ci peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 68562306a36Sopenharmony_ci if (vb2_is_busy(peer_vq)) 68662306a36Sopenharmony_ci return -EBUSY; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci err = tegra_try_coded_fmt(file, priv, f); 68962306a36Sopenharmony_ci if (err) 69062306a36Sopenharmony_ci return err; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci desc = tegra_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat); 69362306a36Sopenharmony_ci if (!desc) 69462306a36Sopenharmony_ci return -EINVAL; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ctx->coded_fmt_desc = desc; 69762306a36Sopenharmony_ci ctx->coded_fmt = *f; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * Current decoded format might have become invalid with newly 70162306a36Sopenharmony_ci * selected codec, so reset it to default just to be safe and 70262306a36Sopenharmony_ci * keep internal driver state sane. User is mandated to set 70362306a36Sopenharmony_ci * the decoded format again after we return, so we don't need 70462306a36Sopenharmony_ci * anything smarter. 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci * Note that this will propagates any size changes to the decoded format. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ci tegra_reset_decoded_fmt(ctx); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* propagate colorspace information to capture */ 71162306a36Sopenharmony_ci cap_fmt = &ctx->decoded_fmt; 71262306a36Sopenharmony_ci cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; 71362306a36Sopenharmony_ci cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; 71462306a36Sopenharmony_ci cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; 71562306a36Sopenharmony_ci cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int tegra_enum_framesizes(struct file *file, void *priv, 72162306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); 72462306a36Sopenharmony_ci const struct tegra_coded_fmt_desc *fmt; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (fsize->index) 72762306a36Sopenharmony_ci return -EINVAL; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci fmt = tegra_find_coded_fmt_desc(ctx, fsize->pixel_format); 73062306a36Sopenharmony_ci if (!fmt) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 73462306a36Sopenharmony_ci fsize->stepwise = fmt->frmsize; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops tegra_v4l2_ioctl_ops = { 74062306a36Sopenharmony_ci .vidioc_querycap = tegra_querycap, 74162306a36Sopenharmony_ci .vidioc_enum_framesizes = tegra_enum_framesizes, 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = tegra_try_coded_fmt, 74462306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = tegra_g_coded_fmt, 74562306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = tegra_s_coded_fmt, 74662306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = tegra_enum_coded_fmt, 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = tegra_try_decoded_fmt, 74962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = tegra_g_decoded_fmt, 75062306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = tegra_s_decoded_fmt, 75162306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = tegra_enum_decoded_fmt, 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 75462306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 75562306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 75662306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 75762306a36Sopenharmony_ci .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 75862306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 75962306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 76262306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 76562306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 76662306a36Sopenharmony_ci}; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int tegra_init_ctrls(struct tegra_ctx *ctx) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci unsigned int i; 77162306a36Sopenharmony_ci int err; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci err = v4l2_ctrl_handler_init(&ctx->hdl, ARRAY_SIZE(ctrl_cfgs)); 77462306a36Sopenharmony_ci if (err) 77562306a36Sopenharmony_ci return err; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) { 77862306a36Sopenharmony_ci ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->hdl, &ctrl_cfgs[i], 77962306a36Sopenharmony_ci NULL); 78062306a36Sopenharmony_ci if (ctx->hdl.error) { 78162306a36Sopenharmony_ci err = ctx->hdl.error; 78262306a36Sopenharmony_ci goto free_ctrls; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci err = v4l2_ctrl_handler_setup(&ctx->hdl); 78762306a36Sopenharmony_ci if (err) 78862306a36Sopenharmony_ci goto free_ctrls; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->hdl; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cifree_ctrls: 79562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return err; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int tegra_init_m2m(struct tegra_ctx *ctx) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ctx->vde->m2m, 80362306a36Sopenharmony_ci ctx, tegra_queue_init); 80462306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) 80562306a36Sopenharmony_ci return PTR_ERR(ctx->fh.m2m_ctx); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int tegra_open(struct file *file) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct tegra_vde *vde = video_drvdata(file); 81362306a36Sopenharmony_ci struct tegra_ctx *ctx; 81462306a36Sopenharmony_ci int err; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ctx = kzalloc(offsetof(struct tegra_ctx, ctrls[ARRAY_SIZE(ctrl_cfgs)]), 81762306a36Sopenharmony_ci GFP_KERNEL); 81862306a36Sopenharmony_ci if (!ctx) 81962306a36Sopenharmony_ci return -ENOMEM; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ctx->vde = vde; 82262306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 82362306a36Sopenharmony_ci INIT_WORK(&ctx->work, tegra_decode_complete); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci err = tegra_init_ctrls(ctx); 82662306a36Sopenharmony_ci if (err) { 82762306a36Sopenharmony_ci v4l2_err(&vde->v4l2_dev, "failed to add controls: %d\n", err); 82862306a36Sopenharmony_ci goto free_ctx; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci err = tegra_init_m2m(ctx); 83262306a36Sopenharmony_ci if (err) { 83362306a36Sopenharmony_ci v4l2_err(&vde->v4l2_dev, "failed to initialize m2m: %d\n", err); 83462306a36Sopenharmony_ci goto free_ctrls; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci file->private_data = &ctx->fh; 83862306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci tegra_reset_coded_fmt(ctx); 84162306a36Sopenharmony_ci tegra_try_coded_fmt(file, file->private_data, &ctx->coded_fmt); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci tegra_reset_decoded_fmt(ctx); 84462306a36Sopenharmony_ci tegra_try_decoded_fmt(file, file->private_data, &ctx->decoded_fmt); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cifree_ctrls: 84962306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 85062306a36Sopenharmony_cifree_ctx: 85162306a36Sopenharmony_ci kfree(ctx); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return err; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int tegra_release(struct file *file) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct v4l2_fh *fh = file->private_data; 85962306a36Sopenharmony_ci struct tegra_ctx *ctx = fh_to_tegra_ctx(fh); 86062306a36Sopenharmony_ci struct tegra_vde *vde = ctx->vde; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci v4l2_fh_del(fh); 86362306a36Sopenharmony_ci v4l2_m2m_ctx_release(fh->m2m_ctx); 86462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 86562306a36Sopenharmony_ci v4l2_fh_exit(fh); 86662306a36Sopenharmony_ci kfree(ctx); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci tegra_vde_dmabuf_cache_unmap_sync(vde); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic const struct v4l2_file_operations tegra_v4l2_fops = { 87462306a36Sopenharmony_ci .owner = THIS_MODULE, 87562306a36Sopenharmony_ci .open = tegra_open, 87662306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 87762306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 87862306a36Sopenharmony_ci .release = tegra_release, 87962306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 88062306a36Sopenharmony_ci}; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic void tegra_device_run(void *priv) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct tegra_ctx *ctx = priv; 88562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 88662306a36Sopenharmony_ci struct media_request *src_req = src->vb2_buf.req_obj.req; 88762306a36Sopenharmony_ci int err; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci v4l2_ctrl_request_setup(src_req, &ctx->hdl); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci err = ctx->coded_fmt_desc->decode_run(ctx); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci v4l2_ctrl_request_complete(src_req, &ctx->hdl); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (err) 89662306a36Sopenharmony_ci tegra_job_finish(ctx, VB2_BUF_STATE_ERROR); 89762306a36Sopenharmony_ci else 89862306a36Sopenharmony_ci queue_work(ctx->vde->wq, &ctx->work); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic const struct v4l2_m2m_ops tegra_v4l2_m2m_ops = { 90262306a36Sopenharmony_ci .device_run = tegra_device_run, 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int tegra_request_validate(struct media_request *req) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci unsigned int count; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci count = vb2_request_buffer_cnt(req); 91062306a36Sopenharmony_ci if (!count) 91162306a36Sopenharmony_ci return -ENOENT; 91262306a36Sopenharmony_ci else if (count > 1) 91362306a36Sopenharmony_ci return -EINVAL; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return vb2_request_validate(req); 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic const struct media_device_ops tegra_media_device_ops = { 91962306a36Sopenharmony_ci .req_validate = tegra_request_validate, 92062306a36Sopenharmony_ci .req_queue = v4l2_m2m_request_queue, 92162306a36Sopenharmony_ci}; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ciint tegra_vde_v4l2_init(struct tegra_vde *vde) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct device *dev = vde->dev; 92662306a36Sopenharmony_ci int err; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci mutex_init(&vde->v4l2_lock); 92962306a36Sopenharmony_ci media_device_init(&vde->mdev); 93062306a36Sopenharmony_ci video_set_drvdata(&vde->vdev, vde); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci vde->vdev.lock = &vde->v4l2_lock, 93362306a36Sopenharmony_ci vde->vdev.fops = &tegra_v4l2_fops, 93462306a36Sopenharmony_ci vde->vdev.vfl_dir = VFL_DIR_M2M, 93562306a36Sopenharmony_ci vde->vdev.release = video_device_release_empty, 93662306a36Sopenharmony_ci vde->vdev.v4l2_dev = &vde->v4l2_dev; 93762306a36Sopenharmony_ci vde->vdev.ioctl_ops = &tegra_v4l2_ioctl_ops, 93862306a36Sopenharmony_ci vde->vdev.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci vde->v4l2_dev.mdev = &vde->mdev; 94162306a36Sopenharmony_ci vde->mdev.ops = &tegra_media_device_ops; 94262306a36Sopenharmony_ci vde->mdev.dev = dev; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci strscpy(vde->mdev.model, "tegra-vde", sizeof(vde->mdev.model)); 94562306a36Sopenharmony_ci strscpy(vde->vdev.name, "tegra-vde", sizeof(vde->vdev.name)); 94662306a36Sopenharmony_ci strscpy(vde->mdev.bus_info, "platform:tegra-vde", 94762306a36Sopenharmony_ci sizeof(vde->mdev.bus_info)); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci vde->wq = create_workqueue("tegra-vde"); 95062306a36Sopenharmony_ci if (!vde->wq) 95162306a36Sopenharmony_ci return -ENOMEM; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci err = media_device_register(&vde->mdev); 95462306a36Sopenharmony_ci if (err) { 95562306a36Sopenharmony_ci dev_err(dev, "failed to register media device: %d\n", err); 95662306a36Sopenharmony_ci goto clean_up_media_device; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci err = v4l2_device_register(dev, &vde->v4l2_dev); 96062306a36Sopenharmony_ci if (err) { 96162306a36Sopenharmony_ci dev_err(dev, "failed to register v4l2 device: %d\n", err); 96262306a36Sopenharmony_ci goto unreg_media_device; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci err = video_register_device(&vde->vdev, VFL_TYPE_VIDEO, -1); 96662306a36Sopenharmony_ci if (err) { 96762306a36Sopenharmony_ci dev_err(dev, "failed to register video device: %d\n", err); 96862306a36Sopenharmony_ci goto unreg_v4l2; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci vde->m2m = v4l2_m2m_init(&tegra_v4l2_m2m_ops); 97262306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(vde->m2m); 97362306a36Sopenharmony_ci if (err) { 97462306a36Sopenharmony_ci dev_err(dev, "failed to initialize m2m device: %d\n", err); 97562306a36Sopenharmony_ci goto unreg_video_device; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci err = v4l2_m2m_register_media_controller(vde->m2m, &vde->vdev, 97962306a36Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_DECODER); 98062306a36Sopenharmony_ci if (err) { 98162306a36Sopenharmony_ci dev_err(dev, "failed to register media controller: %d\n", err); 98262306a36Sopenharmony_ci goto release_m2m; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci v4l2_info(&vde->v4l2_dev, "v4l2 device registered as /dev/video%d\n", 98662306a36Sopenharmony_ci vde->vdev.num); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return 0; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cirelease_m2m: 99162306a36Sopenharmony_ci v4l2_m2m_release(vde->m2m); 99262306a36Sopenharmony_ciunreg_video_device: 99362306a36Sopenharmony_ci video_unregister_device(&vde->vdev); 99462306a36Sopenharmony_ciunreg_v4l2: 99562306a36Sopenharmony_ci v4l2_device_unregister(&vde->v4l2_dev); 99662306a36Sopenharmony_ciunreg_media_device: 99762306a36Sopenharmony_ci media_device_unregister(&vde->mdev); 99862306a36Sopenharmony_ciclean_up_media_device: 99962306a36Sopenharmony_ci media_device_cleanup(&vde->mdev); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci destroy_workqueue(vde->wq); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return err; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_civoid tegra_vde_v4l2_deinit(struct tegra_vde *vde) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci v4l2_m2m_unregister_media_controller(vde->m2m); 100962306a36Sopenharmony_ci v4l2_m2m_release(vde->m2m); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci video_unregister_device(&vde->vdev); 101262306a36Sopenharmony_ci v4l2_device_unregister(&vde->v4l2_dev); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci media_device_unregister(&vde->mdev); 101562306a36Sopenharmony_ci media_device_cleanup(&vde->mdev); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci destroy_workqueue(vde->wq); 101862306a36Sopenharmony_ci} 1019