1d968e50bSDetlev Casanova // SPDX-License-Identifier: GPL-2.0 2d968e50bSDetlev Casanova /* 3d968e50bSDetlev Casanova * Rockchip Video Decoder driver 4d968e50bSDetlev Casanova * 5d968e50bSDetlev Casanova * Copyright (C) 2019 Collabora, Ltd. 6d968e50bSDetlev Casanova * 7d968e50bSDetlev Casanova * Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>) 8d968e50bSDetlev Casanova * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. 9d968e50bSDetlev Casanova * Copyright (C) 2011 Samsung Electronics Co., Ltd. 10d968e50bSDetlev Casanova */ 11d968e50bSDetlev Casanova 12d968e50bSDetlev Casanova #include <linux/clk.h> 13d968e50bSDetlev Casanova #include <linux/interrupt.h> 14d968e50bSDetlev Casanova #include <linux/iommu.h> 15d968e50bSDetlev Casanova #include <linux/module.h> 16d968e50bSDetlev Casanova #include <linux/of.h> 17d968e50bSDetlev Casanova #include <linux/platform_device.h> 18d968e50bSDetlev Casanova #include <linux/pm.h> 19d968e50bSDetlev Casanova #include <linux/pm_runtime.h> 20d968e50bSDetlev Casanova #include <linux/slab.h> 21d968e50bSDetlev Casanova #include <linux/videodev2.h> 22d968e50bSDetlev Casanova #include <linux/workqueue.h> 23d968e50bSDetlev Casanova #include <media/v4l2-event.h> 24d968e50bSDetlev Casanova #include <media/v4l2-mem2mem.h> 25d968e50bSDetlev Casanova #include <media/videobuf2-core.h> 26d968e50bSDetlev Casanova #include <media/videobuf2-vmalloc.h> 27d968e50bSDetlev Casanova 28d968e50bSDetlev Casanova #include "rkvdec.h" 29d968e50bSDetlev Casanova #include "rkvdec-regs.h" 30d968e50bSDetlev Casanova 31d968e50bSDetlev Casanova static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, 32d968e50bSDetlev Casanova enum rkvdec_image_fmt fmt2) 33d968e50bSDetlev Casanova { 34d968e50bSDetlev Casanova return fmt1 == fmt2 || fmt2 == RKVDEC_IMG_FMT_ANY || 35d968e50bSDetlev Casanova fmt1 == RKVDEC_IMG_FMT_ANY; 36d968e50bSDetlev Casanova } 37d968e50bSDetlev Casanova 38d968e50bSDetlev Casanova static bool rkvdec_image_fmt_changed(struct rkvdec_ctx *ctx, 39d968e50bSDetlev Casanova enum rkvdec_image_fmt image_fmt) 40d968e50bSDetlev Casanova { 41d968e50bSDetlev Casanova if (image_fmt == RKVDEC_IMG_FMT_ANY) 42d968e50bSDetlev Casanova return false; 43d968e50bSDetlev Casanova 44d968e50bSDetlev Casanova return ctx->image_fmt != image_fmt; 45d968e50bSDetlev Casanova } 46d968e50bSDetlev Casanova 47d968e50bSDetlev Casanova static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index, 48d968e50bSDetlev Casanova enum rkvdec_image_fmt image_fmt) 49d968e50bSDetlev Casanova { 50d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 51d968e50bSDetlev Casanova int fmt_idx = -1; 52d968e50bSDetlev Casanova unsigned int i; 53d968e50bSDetlev Casanova 54d968e50bSDetlev Casanova if (WARN_ON(!desc)) 55d968e50bSDetlev Casanova return 0; 56d968e50bSDetlev Casanova 57d968e50bSDetlev Casanova for (i = 0; i < desc->num_decoded_fmts; i++) { 58d968e50bSDetlev Casanova if (!rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, 59d968e50bSDetlev Casanova image_fmt)) 60d968e50bSDetlev Casanova continue; 61d968e50bSDetlev Casanova fmt_idx++; 62d968e50bSDetlev Casanova if (index == fmt_idx) 63d968e50bSDetlev Casanova return desc->decoded_fmts[i].fourcc; 64d968e50bSDetlev Casanova } 65d968e50bSDetlev Casanova 66d968e50bSDetlev Casanova return 0; 67d968e50bSDetlev Casanova } 68d968e50bSDetlev Casanova 69d968e50bSDetlev Casanova static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, 70d968e50bSDetlev Casanova enum rkvdec_image_fmt image_fmt) 71d968e50bSDetlev Casanova { 72d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 73d968e50bSDetlev Casanova unsigned int i; 74d968e50bSDetlev Casanova 75d968e50bSDetlev Casanova for (i = 0; i < desc->num_decoded_fmts; i++) { 76d968e50bSDetlev Casanova if (rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, 77d968e50bSDetlev Casanova image_fmt) && 78d968e50bSDetlev Casanova desc->decoded_fmts[i].fourcc == fourcc) 79d968e50bSDetlev Casanova return true; 80d968e50bSDetlev Casanova } 81d968e50bSDetlev Casanova 82d968e50bSDetlev Casanova return false; 83d968e50bSDetlev Casanova } 84d968e50bSDetlev Casanova 85d968e50bSDetlev Casanova static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, 86d968e50bSDetlev Casanova struct v4l2_pix_format_mplane *pix_mp) 87d968e50bSDetlev Casanova { 88d968e50bSDetlev Casanova v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, 89d968e50bSDetlev Casanova pix_mp->width, pix_mp->height); 90d968e50bSDetlev Casanova pix_mp->plane_fmt[0].sizeimage += 128 * 91d968e50bSDetlev Casanova DIV_ROUND_UP(pix_mp->width, 16) * 92d968e50bSDetlev Casanova DIV_ROUND_UP(pix_mp->height, 16); 93d968e50bSDetlev Casanova } 94d968e50bSDetlev Casanova 95d968e50bSDetlev Casanova static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, 96d968e50bSDetlev Casanova u32 fourcc) 97d968e50bSDetlev Casanova { 98d968e50bSDetlev Casanova memset(f, 0, sizeof(*f)); 99d968e50bSDetlev Casanova f->fmt.pix_mp.pixelformat = fourcc; 100d968e50bSDetlev Casanova f->fmt.pix_mp.field = V4L2_FIELD_NONE; 101d968e50bSDetlev Casanova f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; 102d968e50bSDetlev Casanova f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 103d968e50bSDetlev Casanova f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; 104d968e50bSDetlev Casanova f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; 105d968e50bSDetlev Casanova } 106d968e50bSDetlev Casanova 107d968e50bSDetlev Casanova static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) 108d968e50bSDetlev Casanova { 109d968e50bSDetlev Casanova struct v4l2_format *f = &ctx->decoded_fmt; 110d968e50bSDetlev Casanova u32 fourcc; 111d968e50bSDetlev Casanova 112d968e50bSDetlev Casanova fourcc = rkvdec_enum_decoded_fmt(ctx, 0, ctx->image_fmt); 113d968e50bSDetlev Casanova rkvdec_reset_fmt(ctx, f, fourcc); 114d968e50bSDetlev Casanova f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 115d968e50bSDetlev Casanova f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; 116d968e50bSDetlev Casanova f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; 117d968e50bSDetlev Casanova rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); 118d968e50bSDetlev Casanova } 119d968e50bSDetlev Casanova 120d968e50bSDetlev Casanova static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) 121d968e50bSDetlev Casanova { 122d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); 123d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 124d968e50bSDetlev Casanova 125d968e50bSDetlev Casanova if (desc->ops->try_ctrl) 126d968e50bSDetlev Casanova return desc->ops->try_ctrl(ctx, ctrl); 127d968e50bSDetlev Casanova 128d968e50bSDetlev Casanova return 0; 129d968e50bSDetlev Casanova } 130d968e50bSDetlev Casanova 131d968e50bSDetlev Casanova static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) 132d968e50bSDetlev Casanova { 133d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); 134d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 135d968e50bSDetlev Casanova enum rkvdec_image_fmt image_fmt; 136d968e50bSDetlev Casanova struct vb2_queue *vq; 137d968e50bSDetlev Casanova 138d968e50bSDetlev Casanova /* Check if this change requires a capture format reset */ 139d968e50bSDetlev Casanova if (!desc->ops->get_image_fmt) 140d968e50bSDetlev Casanova return 0; 141d968e50bSDetlev Casanova 142d968e50bSDetlev Casanova image_fmt = desc->ops->get_image_fmt(ctx, ctrl); 143d968e50bSDetlev Casanova if (rkvdec_image_fmt_changed(ctx, image_fmt)) { 144d968e50bSDetlev Casanova vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 145d968e50bSDetlev Casanova V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 146d968e50bSDetlev Casanova if (vb2_is_busy(vq)) 147d968e50bSDetlev Casanova return -EBUSY; 148d968e50bSDetlev Casanova 149d968e50bSDetlev Casanova ctx->image_fmt = image_fmt; 150d968e50bSDetlev Casanova rkvdec_reset_decoded_fmt(ctx); 151d968e50bSDetlev Casanova } 152d968e50bSDetlev Casanova 153d968e50bSDetlev Casanova return 0; 154d968e50bSDetlev Casanova } 155d968e50bSDetlev Casanova 156d968e50bSDetlev Casanova static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { 157d968e50bSDetlev Casanova .try_ctrl = rkvdec_try_ctrl, 158d968e50bSDetlev Casanova .s_ctrl = rkvdec_s_ctrl, 159d968e50bSDetlev Casanova }; 160d968e50bSDetlev Casanova 161d968e50bSDetlev Casanova static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { 162d968e50bSDetlev Casanova { 163d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, 164d968e50bSDetlev Casanova }, 165d968e50bSDetlev Casanova { 166d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_SPS, 167d968e50bSDetlev Casanova .cfg.ops = &rkvdec_ctrl_ops, 168d968e50bSDetlev Casanova }, 169d968e50bSDetlev Casanova { 170d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_PPS, 171d968e50bSDetlev Casanova }, 172d968e50bSDetlev Casanova { 173d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, 174d968e50bSDetlev Casanova }, 175d968e50bSDetlev Casanova { 176d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, 177d968e50bSDetlev Casanova .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 178d968e50bSDetlev Casanova .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 179d968e50bSDetlev Casanova .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, 180d968e50bSDetlev Casanova }, 181d968e50bSDetlev Casanova { 182d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, 183d968e50bSDetlev Casanova .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 184d968e50bSDetlev Casanova .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 185d968e50bSDetlev Casanova .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, 186d968e50bSDetlev Casanova }, 187d968e50bSDetlev Casanova { 188d968e50bSDetlev Casanova .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, 189d968e50bSDetlev Casanova .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, 190d968e50bSDetlev Casanova .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, 191d968e50bSDetlev Casanova .cfg.menu_skip_mask = 192d968e50bSDetlev Casanova BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | 193d968e50bSDetlev Casanova BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), 194d968e50bSDetlev Casanova .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, 195d968e50bSDetlev Casanova }, 196d968e50bSDetlev Casanova { 197d968e50bSDetlev Casanova .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, 198d968e50bSDetlev Casanova .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 199d968e50bSDetlev Casanova .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 200d968e50bSDetlev Casanova }, 201d968e50bSDetlev Casanova }; 202d968e50bSDetlev Casanova 203d968e50bSDetlev Casanova static const struct rkvdec_ctrls rkvdec_h264_ctrls = { 204d968e50bSDetlev Casanova .ctrls = rkvdec_h264_ctrl_descs, 205d968e50bSDetlev Casanova .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), 206d968e50bSDetlev Casanova }; 207d968e50bSDetlev Casanova 208d968e50bSDetlev Casanova static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = { 209d968e50bSDetlev Casanova { 210d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_NV12, 211d968e50bSDetlev Casanova .image_fmt = RKVDEC_IMG_FMT_420_8BIT, 212d968e50bSDetlev Casanova }, 213d968e50bSDetlev Casanova { 214d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_NV15, 215d968e50bSDetlev Casanova .image_fmt = RKVDEC_IMG_FMT_420_10BIT, 216d968e50bSDetlev Casanova }, 217d968e50bSDetlev Casanova { 218d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_NV16, 219d968e50bSDetlev Casanova .image_fmt = RKVDEC_IMG_FMT_422_8BIT, 220d968e50bSDetlev Casanova }, 221d968e50bSDetlev Casanova { 222d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_NV20, 223d968e50bSDetlev Casanova .image_fmt = RKVDEC_IMG_FMT_422_10BIT, 224d968e50bSDetlev Casanova }, 225d968e50bSDetlev Casanova }; 226d968e50bSDetlev Casanova 227d968e50bSDetlev Casanova static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = { 228d968e50bSDetlev Casanova { 229d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_VP9_FRAME, 230d968e50bSDetlev Casanova }, 231d968e50bSDetlev Casanova { 232d968e50bSDetlev Casanova .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, 233d968e50bSDetlev Casanova }, 234d968e50bSDetlev Casanova { 235d968e50bSDetlev Casanova .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, 236d968e50bSDetlev Casanova .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, 237d968e50bSDetlev Casanova .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_0, 238d968e50bSDetlev Casanova .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, 239d968e50bSDetlev Casanova }, 240d968e50bSDetlev Casanova }; 241d968e50bSDetlev Casanova 242d968e50bSDetlev Casanova static const struct rkvdec_ctrls rkvdec_vp9_ctrls = { 243d968e50bSDetlev Casanova .ctrls = rkvdec_vp9_ctrl_descs, 244d968e50bSDetlev Casanova .num_ctrls = ARRAY_SIZE(rkvdec_vp9_ctrl_descs), 245d968e50bSDetlev Casanova }; 246d968e50bSDetlev Casanova 247d968e50bSDetlev Casanova static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = { 248d968e50bSDetlev Casanova { 249d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_NV12, 250d968e50bSDetlev Casanova .image_fmt = RKVDEC_IMG_FMT_420_8BIT, 251d968e50bSDetlev Casanova }, 252d968e50bSDetlev Casanova }; 253d968e50bSDetlev Casanova 254d968e50bSDetlev Casanova static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { 255d968e50bSDetlev Casanova { 256d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_H264_SLICE, 257d968e50bSDetlev Casanova .frmsize = { 258d968e50bSDetlev Casanova .min_width = 64, 259d968e50bSDetlev Casanova .max_width = 4096, 260d968e50bSDetlev Casanova .step_width = 64, 261d968e50bSDetlev Casanova .min_height = 48, 262d968e50bSDetlev Casanova .max_height = 2560, 263d968e50bSDetlev Casanova .step_height = 16, 264d968e50bSDetlev Casanova }, 265d968e50bSDetlev Casanova .ctrls = &rkvdec_h264_ctrls, 266d968e50bSDetlev Casanova .ops = &rkvdec_h264_fmt_ops, 267d968e50bSDetlev Casanova .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), 268d968e50bSDetlev Casanova .decoded_fmts = rkvdec_h264_decoded_fmts, 269d968e50bSDetlev Casanova .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, 270d968e50bSDetlev Casanova }, 271d968e50bSDetlev Casanova { 272d968e50bSDetlev Casanova .fourcc = V4L2_PIX_FMT_VP9_FRAME, 273d968e50bSDetlev Casanova .frmsize = { 274d968e50bSDetlev Casanova .min_width = 64, 275d968e50bSDetlev Casanova .max_width = 4096, 276d968e50bSDetlev Casanova .step_width = 64, 277d968e50bSDetlev Casanova .min_height = 64, 278d968e50bSDetlev Casanova .max_height = 2304, 279d968e50bSDetlev Casanova .step_height = 64, 280d968e50bSDetlev Casanova }, 281d968e50bSDetlev Casanova .ctrls = &rkvdec_vp9_ctrls, 282d968e50bSDetlev Casanova .ops = &rkvdec_vp9_fmt_ops, 283d968e50bSDetlev Casanova .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), 284d968e50bSDetlev Casanova .decoded_fmts = rkvdec_vp9_decoded_fmts, 285d968e50bSDetlev Casanova } 286d968e50bSDetlev Casanova }; 287d968e50bSDetlev Casanova 288d968e50bSDetlev Casanova static const struct rkvdec_coded_fmt_desc * 289d968e50bSDetlev Casanova rkvdec_find_coded_fmt_desc(u32 fourcc) 290d968e50bSDetlev Casanova { 291d968e50bSDetlev Casanova unsigned int i; 292d968e50bSDetlev Casanova 293d968e50bSDetlev Casanova for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { 294d968e50bSDetlev Casanova if (rkvdec_coded_fmts[i].fourcc == fourcc) 295d968e50bSDetlev Casanova return &rkvdec_coded_fmts[i]; 296d968e50bSDetlev Casanova } 297d968e50bSDetlev Casanova 298d968e50bSDetlev Casanova return NULL; 299d968e50bSDetlev Casanova } 300d968e50bSDetlev Casanova 301d968e50bSDetlev Casanova static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) 302d968e50bSDetlev Casanova { 303d968e50bSDetlev Casanova struct v4l2_format *f = &ctx->coded_fmt; 304d968e50bSDetlev Casanova 305d968e50bSDetlev Casanova ctx->coded_fmt_desc = &rkvdec_coded_fmts[0]; 306d968e50bSDetlev Casanova rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); 307d968e50bSDetlev Casanova 308d968e50bSDetlev Casanova f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 309d968e50bSDetlev Casanova f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; 310d968e50bSDetlev Casanova f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; 311d968e50bSDetlev Casanova 312d968e50bSDetlev Casanova if (ctx->coded_fmt_desc->ops->adjust_fmt) 313d968e50bSDetlev Casanova ctx->coded_fmt_desc->ops->adjust_fmt(ctx, f); 314d968e50bSDetlev Casanova } 315d968e50bSDetlev Casanova 316d968e50bSDetlev Casanova static int rkvdec_enum_framesizes(struct file *file, void *priv, 317d968e50bSDetlev Casanova struct v4l2_frmsizeenum *fsize) 318d968e50bSDetlev Casanova { 319d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *fmt; 320d968e50bSDetlev Casanova 321d968e50bSDetlev Casanova if (fsize->index != 0) 322d968e50bSDetlev Casanova return -EINVAL; 323d968e50bSDetlev Casanova 324d968e50bSDetlev Casanova fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format); 325d968e50bSDetlev Casanova if (!fmt) 326d968e50bSDetlev Casanova return -EINVAL; 327d968e50bSDetlev Casanova 328d968e50bSDetlev Casanova fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 329d968e50bSDetlev Casanova fsize->stepwise.min_width = 1; 330d968e50bSDetlev Casanova fsize->stepwise.max_width = fmt->frmsize.max_width; 331d968e50bSDetlev Casanova fsize->stepwise.step_width = 1; 332d968e50bSDetlev Casanova fsize->stepwise.min_height = 1; 333d968e50bSDetlev Casanova fsize->stepwise.max_height = fmt->frmsize.max_height; 334d968e50bSDetlev Casanova fsize->stepwise.step_height = 1; 335d968e50bSDetlev Casanova 336d968e50bSDetlev Casanova return 0; 337d968e50bSDetlev Casanova } 338d968e50bSDetlev Casanova 339d968e50bSDetlev Casanova static int rkvdec_querycap(struct file *file, void *priv, 340d968e50bSDetlev Casanova struct v4l2_capability *cap) 341d968e50bSDetlev Casanova { 342d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = video_drvdata(file); 343d968e50bSDetlev Casanova struct video_device *vdev = video_devdata(file); 344d968e50bSDetlev Casanova 345d968e50bSDetlev Casanova strscpy(cap->driver, rkvdec->dev->driver->name, 346d968e50bSDetlev Casanova sizeof(cap->driver)); 347d968e50bSDetlev Casanova strscpy(cap->card, vdev->name, sizeof(cap->card)); 348d968e50bSDetlev Casanova snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 349d968e50bSDetlev Casanova rkvdec->dev->driver->name); 350d968e50bSDetlev Casanova return 0; 351d968e50bSDetlev Casanova } 352d968e50bSDetlev Casanova 353d968e50bSDetlev Casanova static int rkvdec_try_capture_fmt(struct file *file, void *priv, 354d968e50bSDetlev Casanova struct v4l2_format *f) 355d968e50bSDetlev Casanova { 356d968e50bSDetlev Casanova struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; 357d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 358d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *coded_desc; 359d968e50bSDetlev Casanova 360d968e50bSDetlev Casanova /* 361d968e50bSDetlev Casanova * The codec context should point to a coded format desc, if the format 362d968e50bSDetlev Casanova * on the coded end has not been set yet, it should point to the 363d968e50bSDetlev Casanova * default value. 364d968e50bSDetlev Casanova */ 365d968e50bSDetlev Casanova coded_desc = ctx->coded_fmt_desc; 366d968e50bSDetlev Casanova if (WARN_ON(!coded_desc)) 367d968e50bSDetlev Casanova return -EINVAL; 368d968e50bSDetlev Casanova 369d968e50bSDetlev Casanova if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt)) 370d968e50bSDetlev Casanova pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0, 371d968e50bSDetlev Casanova ctx->image_fmt); 372d968e50bSDetlev Casanova 373d968e50bSDetlev Casanova /* Always apply the frmsize constraint of the coded end. */ 374d968e50bSDetlev Casanova pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); 375d968e50bSDetlev Casanova pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height); 376d968e50bSDetlev Casanova v4l2_apply_frmsize_constraints(&pix_mp->width, 377d968e50bSDetlev Casanova &pix_mp->height, 378d968e50bSDetlev Casanova &coded_desc->frmsize); 379d968e50bSDetlev Casanova 380d968e50bSDetlev Casanova rkvdec_fill_decoded_pixfmt(ctx, pix_mp); 381d968e50bSDetlev Casanova pix_mp->field = V4L2_FIELD_NONE; 382d968e50bSDetlev Casanova 383d968e50bSDetlev Casanova return 0; 384d968e50bSDetlev Casanova } 385d968e50bSDetlev Casanova 386d968e50bSDetlev Casanova static int rkvdec_try_output_fmt(struct file *file, void *priv, 387d968e50bSDetlev Casanova struct v4l2_format *f) 388d968e50bSDetlev Casanova { 389d968e50bSDetlev Casanova struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; 390d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 391d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc; 392d968e50bSDetlev Casanova 393d968e50bSDetlev Casanova desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat); 394d968e50bSDetlev Casanova if (!desc) { 395d968e50bSDetlev Casanova pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc; 396d968e50bSDetlev Casanova desc = &rkvdec_coded_fmts[0]; 397d968e50bSDetlev Casanova } 398d968e50bSDetlev Casanova 399d968e50bSDetlev Casanova v4l2_apply_frmsize_constraints(&pix_mp->width, 400d968e50bSDetlev Casanova &pix_mp->height, 401d968e50bSDetlev Casanova &desc->frmsize); 402d968e50bSDetlev Casanova 403d968e50bSDetlev Casanova pix_mp->field = V4L2_FIELD_NONE; 404d968e50bSDetlev Casanova /* All coded formats are considered single planar for now. */ 405d968e50bSDetlev Casanova pix_mp->num_planes = 1; 406d968e50bSDetlev Casanova 407d968e50bSDetlev Casanova if (desc->ops->adjust_fmt) { 408d968e50bSDetlev Casanova int ret; 409d968e50bSDetlev Casanova 410d968e50bSDetlev Casanova ret = desc->ops->adjust_fmt(ctx, f); 411d968e50bSDetlev Casanova if (ret) 412d968e50bSDetlev Casanova return ret; 413d968e50bSDetlev Casanova } 414d968e50bSDetlev Casanova 415d968e50bSDetlev Casanova return 0; 416d968e50bSDetlev Casanova } 417d968e50bSDetlev Casanova 418d968e50bSDetlev Casanova static int rkvdec_s_capture_fmt(struct file *file, void *priv, 419d968e50bSDetlev Casanova struct v4l2_format *f) 420d968e50bSDetlev Casanova { 421d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 422d968e50bSDetlev Casanova struct vb2_queue *vq; 423d968e50bSDetlev Casanova int ret; 424d968e50bSDetlev Casanova 425d968e50bSDetlev Casanova /* Change not allowed if queue is busy */ 426d968e50bSDetlev Casanova vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 427d968e50bSDetlev Casanova V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 428d968e50bSDetlev Casanova if (vb2_is_busy(vq)) 429d968e50bSDetlev Casanova return -EBUSY; 430d968e50bSDetlev Casanova 431d968e50bSDetlev Casanova ret = rkvdec_try_capture_fmt(file, priv, f); 432d968e50bSDetlev Casanova if (ret) 433d968e50bSDetlev Casanova return ret; 434d968e50bSDetlev Casanova 435d968e50bSDetlev Casanova ctx->decoded_fmt = *f; 436d968e50bSDetlev Casanova return 0; 437d968e50bSDetlev Casanova } 438d968e50bSDetlev Casanova 439d968e50bSDetlev Casanova static int rkvdec_s_output_fmt(struct file *file, void *priv, 440d968e50bSDetlev Casanova struct v4l2_format *f) 441d968e50bSDetlev Casanova { 442d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 443d968e50bSDetlev Casanova struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; 444d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc; 445d968e50bSDetlev Casanova struct v4l2_format *cap_fmt; 446d968e50bSDetlev Casanova struct vb2_queue *peer_vq, *vq; 447d968e50bSDetlev Casanova int ret; 448d968e50bSDetlev Casanova 449d968e50bSDetlev Casanova /* 450d968e50bSDetlev Casanova * In order to support dynamic resolution change, the decoder admits 451d968e50bSDetlev Casanova * a resolution change, as long as the pixelformat remains. Can't be 452d968e50bSDetlev Casanova * done if streaming. 453d968e50bSDetlev Casanova */ 454d968e50bSDetlev Casanova vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 455d968e50bSDetlev Casanova if (vb2_is_streaming(vq) || 456d968e50bSDetlev Casanova (vb2_is_busy(vq) && 457d968e50bSDetlev Casanova f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat)) 458d968e50bSDetlev Casanova return -EBUSY; 459d968e50bSDetlev Casanova 460d968e50bSDetlev Casanova /* 461d968e50bSDetlev Casanova * Since format change on the OUTPUT queue will reset the CAPTURE 462d968e50bSDetlev Casanova * queue, we can't allow doing so when the CAPTURE queue has buffers 463d968e50bSDetlev Casanova * allocated. 464d968e50bSDetlev Casanova */ 465d968e50bSDetlev Casanova peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 466d968e50bSDetlev Casanova if (vb2_is_busy(peer_vq)) 467d968e50bSDetlev Casanova return -EBUSY; 468d968e50bSDetlev Casanova 469d968e50bSDetlev Casanova ret = rkvdec_try_output_fmt(file, priv, f); 470d968e50bSDetlev Casanova if (ret) 471d968e50bSDetlev Casanova return ret; 472d968e50bSDetlev Casanova 473d968e50bSDetlev Casanova desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat); 474d968e50bSDetlev Casanova if (!desc) 475d968e50bSDetlev Casanova return -EINVAL; 476d968e50bSDetlev Casanova ctx->coded_fmt_desc = desc; 477d968e50bSDetlev Casanova ctx->coded_fmt = *f; 478d968e50bSDetlev Casanova 479d968e50bSDetlev Casanova /* 480d968e50bSDetlev Casanova * Current decoded format might have become invalid with newly 481d968e50bSDetlev Casanova * selected codec, so reset it to default just to be safe and 482d968e50bSDetlev Casanova * keep internal driver state sane. User is mandated to set 483d968e50bSDetlev Casanova * the decoded format again after we return, so we don't need 484d968e50bSDetlev Casanova * anything smarter. 485d968e50bSDetlev Casanova * 486d968e50bSDetlev Casanova * Note that this will propagates any size changes to the decoded format. 487d968e50bSDetlev Casanova */ 488d968e50bSDetlev Casanova ctx->image_fmt = RKVDEC_IMG_FMT_ANY; 489d968e50bSDetlev Casanova rkvdec_reset_decoded_fmt(ctx); 490d968e50bSDetlev Casanova 491d968e50bSDetlev Casanova /* Propagate colorspace information to capture. */ 492d968e50bSDetlev Casanova cap_fmt = &ctx->decoded_fmt; 493d968e50bSDetlev Casanova cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; 494d968e50bSDetlev Casanova cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; 495d968e50bSDetlev Casanova cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; 496d968e50bSDetlev Casanova cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; 497d968e50bSDetlev Casanova 498d968e50bSDetlev Casanova /* Enable format specific queue features */ 499d968e50bSDetlev Casanova vq->subsystem_flags |= desc->subsystem_flags; 500d968e50bSDetlev Casanova 501d968e50bSDetlev Casanova return 0; 502d968e50bSDetlev Casanova } 503d968e50bSDetlev Casanova 504d968e50bSDetlev Casanova static int rkvdec_g_output_fmt(struct file *file, void *priv, 505d968e50bSDetlev Casanova struct v4l2_format *f) 506d968e50bSDetlev Casanova { 507d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 508d968e50bSDetlev Casanova 509d968e50bSDetlev Casanova *f = ctx->coded_fmt; 510d968e50bSDetlev Casanova return 0; 511d968e50bSDetlev Casanova } 512d968e50bSDetlev Casanova 513d968e50bSDetlev Casanova static int rkvdec_g_capture_fmt(struct file *file, void *priv, 514d968e50bSDetlev Casanova struct v4l2_format *f) 515d968e50bSDetlev Casanova { 516d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 517d968e50bSDetlev Casanova 518d968e50bSDetlev Casanova *f = ctx->decoded_fmt; 519d968e50bSDetlev Casanova return 0; 520d968e50bSDetlev Casanova } 521d968e50bSDetlev Casanova 522d968e50bSDetlev Casanova static int rkvdec_enum_output_fmt(struct file *file, void *priv, 523d968e50bSDetlev Casanova struct v4l2_fmtdesc *f) 524d968e50bSDetlev Casanova { 525d968e50bSDetlev Casanova if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts)) 526d968e50bSDetlev Casanova return -EINVAL; 527d968e50bSDetlev Casanova 528d968e50bSDetlev Casanova f->pixelformat = rkvdec_coded_fmts[f->index].fourcc; 529d968e50bSDetlev Casanova return 0; 530d968e50bSDetlev Casanova } 531d968e50bSDetlev Casanova 532d968e50bSDetlev Casanova static int rkvdec_enum_capture_fmt(struct file *file, void *priv, 533d968e50bSDetlev Casanova struct v4l2_fmtdesc *f) 534d968e50bSDetlev Casanova { 535d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); 536d968e50bSDetlev Casanova u32 fourcc; 537d968e50bSDetlev Casanova 538d968e50bSDetlev Casanova fourcc = rkvdec_enum_decoded_fmt(ctx, f->index, ctx->image_fmt); 539d968e50bSDetlev Casanova if (!fourcc) 540d968e50bSDetlev Casanova return -EINVAL; 541d968e50bSDetlev Casanova 542d968e50bSDetlev Casanova f->pixelformat = fourcc; 543d968e50bSDetlev Casanova return 0; 544d968e50bSDetlev Casanova } 545d968e50bSDetlev Casanova 546d968e50bSDetlev Casanova static const struct v4l2_ioctl_ops rkvdec_ioctl_ops = { 547d968e50bSDetlev Casanova .vidioc_querycap = rkvdec_querycap, 548d968e50bSDetlev Casanova .vidioc_enum_framesizes = rkvdec_enum_framesizes, 549d968e50bSDetlev Casanova 550d968e50bSDetlev Casanova .vidioc_try_fmt_vid_cap_mplane = rkvdec_try_capture_fmt, 551d968e50bSDetlev Casanova .vidioc_try_fmt_vid_out_mplane = rkvdec_try_output_fmt, 552d968e50bSDetlev Casanova .vidioc_s_fmt_vid_out_mplane = rkvdec_s_output_fmt, 553d968e50bSDetlev Casanova .vidioc_s_fmt_vid_cap_mplane = rkvdec_s_capture_fmt, 554d968e50bSDetlev Casanova .vidioc_g_fmt_vid_out_mplane = rkvdec_g_output_fmt, 555d968e50bSDetlev Casanova .vidioc_g_fmt_vid_cap_mplane = rkvdec_g_capture_fmt, 556d968e50bSDetlev Casanova .vidioc_enum_fmt_vid_out = rkvdec_enum_output_fmt, 557d968e50bSDetlev Casanova .vidioc_enum_fmt_vid_cap = rkvdec_enum_capture_fmt, 558d968e50bSDetlev Casanova 559d968e50bSDetlev Casanova .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 560d968e50bSDetlev Casanova .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 561d968e50bSDetlev Casanova .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 562d968e50bSDetlev Casanova .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 563d968e50bSDetlev Casanova .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 564d968e50bSDetlev Casanova .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 565d968e50bSDetlev Casanova .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 566d968e50bSDetlev Casanova 567d968e50bSDetlev Casanova .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 568d968e50bSDetlev Casanova .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 569d968e50bSDetlev Casanova 570d968e50bSDetlev Casanova .vidioc_streamon = v4l2_m2m_ioctl_streamon, 571d968e50bSDetlev Casanova .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 572d968e50bSDetlev Casanova 573d968e50bSDetlev Casanova .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, 574d968e50bSDetlev Casanova .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, 575d968e50bSDetlev Casanova }; 576d968e50bSDetlev Casanova 577d968e50bSDetlev Casanova static int rkvdec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, 578d968e50bSDetlev Casanova unsigned int *num_planes, unsigned int sizes[], 579d968e50bSDetlev Casanova struct device *alloc_devs[]) 580d968e50bSDetlev Casanova { 581d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); 582d968e50bSDetlev Casanova struct v4l2_format *f; 583d968e50bSDetlev Casanova unsigned int i; 584d968e50bSDetlev Casanova 585d968e50bSDetlev Casanova if (V4L2_TYPE_IS_OUTPUT(vq->type)) 586d968e50bSDetlev Casanova f = &ctx->coded_fmt; 587d968e50bSDetlev Casanova else 588d968e50bSDetlev Casanova f = &ctx->decoded_fmt; 589d968e50bSDetlev Casanova 590d968e50bSDetlev Casanova if (*num_planes) { 591d968e50bSDetlev Casanova if (*num_planes != f->fmt.pix_mp.num_planes) 592d968e50bSDetlev Casanova return -EINVAL; 593d968e50bSDetlev Casanova 594d968e50bSDetlev Casanova for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { 595d968e50bSDetlev Casanova if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) 596d968e50bSDetlev Casanova return -EINVAL; 597d968e50bSDetlev Casanova } 598d968e50bSDetlev Casanova } else { 599d968e50bSDetlev Casanova *num_planes = f->fmt.pix_mp.num_planes; 600d968e50bSDetlev Casanova for (i = 0; i < f->fmt.pix_mp.num_planes; i++) 601d968e50bSDetlev Casanova sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; 602d968e50bSDetlev Casanova } 603d968e50bSDetlev Casanova 604d968e50bSDetlev Casanova return 0; 605d968e50bSDetlev Casanova } 606d968e50bSDetlev Casanova 607d968e50bSDetlev Casanova static int rkvdec_buf_prepare(struct vb2_buffer *vb) 608d968e50bSDetlev Casanova { 609d968e50bSDetlev Casanova struct vb2_queue *vq = vb->vb2_queue; 610d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); 611d968e50bSDetlev Casanova struct v4l2_format *f; 612d968e50bSDetlev Casanova unsigned int i; 613d968e50bSDetlev Casanova 614d968e50bSDetlev Casanova if (V4L2_TYPE_IS_OUTPUT(vq->type)) 615d968e50bSDetlev Casanova f = &ctx->coded_fmt; 616d968e50bSDetlev Casanova else 617d968e50bSDetlev Casanova f = &ctx->decoded_fmt; 618d968e50bSDetlev Casanova 619d968e50bSDetlev Casanova for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) { 620d968e50bSDetlev Casanova u32 sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; 621d968e50bSDetlev Casanova 622d968e50bSDetlev Casanova if (vb2_plane_size(vb, i) < sizeimage) 623d968e50bSDetlev Casanova return -EINVAL; 624d968e50bSDetlev Casanova } 625d968e50bSDetlev Casanova 626d968e50bSDetlev Casanova /* 627d968e50bSDetlev Casanova * Buffer's bytesused must be written by driver for CAPTURE buffers. 628d968e50bSDetlev Casanova * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets 629d968e50bSDetlev Casanova * it to buffer length). 630d968e50bSDetlev Casanova */ 631d968e50bSDetlev Casanova if (V4L2_TYPE_IS_CAPTURE(vq->type)) 632d968e50bSDetlev Casanova vb2_set_plane_payload(vb, 0, f->fmt.pix_mp.plane_fmt[0].sizeimage); 633d968e50bSDetlev Casanova 634d968e50bSDetlev Casanova return 0; 635d968e50bSDetlev Casanova } 636d968e50bSDetlev Casanova 637d968e50bSDetlev Casanova static void rkvdec_buf_queue(struct vb2_buffer *vb) 638d968e50bSDetlev Casanova { 639d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 640d968e50bSDetlev Casanova struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 641d968e50bSDetlev Casanova 642d968e50bSDetlev Casanova v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 643d968e50bSDetlev Casanova } 644d968e50bSDetlev Casanova 645d968e50bSDetlev Casanova static int rkvdec_buf_out_validate(struct vb2_buffer *vb) 646d968e50bSDetlev Casanova { 647d968e50bSDetlev Casanova struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 648d968e50bSDetlev Casanova 649d968e50bSDetlev Casanova vbuf->field = V4L2_FIELD_NONE; 650d968e50bSDetlev Casanova return 0; 651d968e50bSDetlev Casanova } 652d968e50bSDetlev Casanova 653d968e50bSDetlev Casanova static void rkvdec_buf_request_complete(struct vb2_buffer *vb) 654d968e50bSDetlev Casanova { 655d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 656d968e50bSDetlev Casanova 657d968e50bSDetlev Casanova v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); 658d968e50bSDetlev Casanova } 659d968e50bSDetlev Casanova 660d968e50bSDetlev Casanova static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) 661d968e50bSDetlev Casanova { 662d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); 663d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc; 664d968e50bSDetlev Casanova int ret; 665d968e50bSDetlev Casanova 666d968e50bSDetlev Casanova if (V4L2_TYPE_IS_CAPTURE(q->type)) 667d968e50bSDetlev Casanova return 0; 668d968e50bSDetlev Casanova 669d968e50bSDetlev Casanova desc = ctx->coded_fmt_desc; 670d968e50bSDetlev Casanova if (WARN_ON(!desc)) 671d968e50bSDetlev Casanova return -EINVAL; 672d968e50bSDetlev Casanova 673d968e50bSDetlev Casanova if (desc->ops->start) { 674d968e50bSDetlev Casanova ret = desc->ops->start(ctx); 675d968e50bSDetlev Casanova if (ret) 676d968e50bSDetlev Casanova return ret; 677d968e50bSDetlev Casanova } 678d968e50bSDetlev Casanova 679d968e50bSDetlev Casanova return 0; 680d968e50bSDetlev Casanova } 681d968e50bSDetlev Casanova 682d968e50bSDetlev Casanova static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state) 683d968e50bSDetlev Casanova { 684d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); 685d968e50bSDetlev Casanova 686d968e50bSDetlev Casanova while (true) { 687d968e50bSDetlev Casanova struct vb2_v4l2_buffer *vbuf; 688d968e50bSDetlev Casanova 689d968e50bSDetlev Casanova if (V4L2_TYPE_IS_OUTPUT(vq->type)) 690d968e50bSDetlev Casanova vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 691d968e50bSDetlev Casanova else 692d968e50bSDetlev Casanova vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 693d968e50bSDetlev Casanova 694d968e50bSDetlev Casanova if (!vbuf) 695d968e50bSDetlev Casanova break; 696d968e50bSDetlev Casanova 697d968e50bSDetlev Casanova v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, 698d968e50bSDetlev Casanova &ctx->ctrl_hdl); 699d968e50bSDetlev Casanova v4l2_m2m_buf_done(vbuf, state); 700d968e50bSDetlev Casanova } 701d968e50bSDetlev Casanova } 702d968e50bSDetlev Casanova 703d968e50bSDetlev Casanova static void rkvdec_stop_streaming(struct vb2_queue *q) 704d968e50bSDetlev Casanova { 705d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); 706d968e50bSDetlev Casanova 707d968e50bSDetlev Casanova if (V4L2_TYPE_IS_OUTPUT(q->type)) { 708d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 709d968e50bSDetlev Casanova 710d968e50bSDetlev Casanova if (WARN_ON(!desc)) 711d968e50bSDetlev Casanova return; 712d968e50bSDetlev Casanova 713d968e50bSDetlev Casanova if (desc->ops->stop) 714d968e50bSDetlev Casanova desc->ops->stop(ctx); 715d968e50bSDetlev Casanova } 716d968e50bSDetlev Casanova 717d968e50bSDetlev Casanova rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR); 718d968e50bSDetlev Casanova } 719d968e50bSDetlev Casanova 720d968e50bSDetlev Casanova static const struct vb2_ops rkvdec_queue_ops = { 721d968e50bSDetlev Casanova .queue_setup = rkvdec_queue_setup, 722d968e50bSDetlev Casanova .buf_prepare = rkvdec_buf_prepare, 723d968e50bSDetlev Casanova .buf_queue = rkvdec_buf_queue, 724d968e50bSDetlev Casanova .buf_out_validate = rkvdec_buf_out_validate, 725d968e50bSDetlev Casanova .buf_request_complete = rkvdec_buf_request_complete, 726d968e50bSDetlev Casanova .start_streaming = rkvdec_start_streaming, 727d968e50bSDetlev Casanova .stop_streaming = rkvdec_stop_streaming, 728d968e50bSDetlev Casanova }; 729d968e50bSDetlev Casanova 730d968e50bSDetlev Casanova static int rkvdec_request_validate(struct media_request *req) 731d968e50bSDetlev Casanova { 732d968e50bSDetlev Casanova unsigned int count; 733d968e50bSDetlev Casanova 734d968e50bSDetlev Casanova count = vb2_request_buffer_cnt(req); 735d968e50bSDetlev Casanova if (!count) 736d968e50bSDetlev Casanova return -ENOENT; 737d968e50bSDetlev Casanova else if (count > 1) 738d968e50bSDetlev Casanova return -EINVAL; 739d968e50bSDetlev Casanova 740d968e50bSDetlev Casanova return vb2_request_validate(req); 741d968e50bSDetlev Casanova } 742d968e50bSDetlev Casanova 743d968e50bSDetlev Casanova static const struct media_device_ops rkvdec_media_ops = { 744d968e50bSDetlev Casanova .req_validate = rkvdec_request_validate, 745d968e50bSDetlev Casanova .req_queue = v4l2_m2m_request_queue, 746d968e50bSDetlev Casanova }; 747d968e50bSDetlev Casanova 748d968e50bSDetlev Casanova static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx, 749d968e50bSDetlev Casanova enum vb2_buffer_state result) 750d968e50bSDetlev Casanova { 751d968e50bSDetlev Casanova if (ctx->coded_fmt_desc->ops->done) { 752d968e50bSDetlev Casanova struct vb2_v4l2_buffer *src_buf, *dst_buf; 753d968e50bSDetlev Casanova 754d968e50bSDetlev Casanova src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 755d968e50bSDetlev Casanova dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 756d968e50bSDetlev Casanova ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result); 757d968e50bSDetlev Casanova } 758d968e50bSDetlev Casanova 759d968e50bSDetlev Casanova v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, 760d968e50bSDetlev Casanova result); 761d968e50bSDetlev Casanova } 762d968e50bSDetlev Casanova 763d968e50bSDetlev Casanova static void rkvdec_job_finish(struct rkvdec_ctx *ctx, 764d968e50bSDetlev Casanova enum vb2_buffer_state result) 765d968e50bSDetlev Casanova { 766d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = ctx->dev; 767d968e50bSDetlev Casanova 768d968e50bSDetlev Casanova pm_runtime_mark_last_busy(rkvdec->dev); 769d968e50bSDetlev Casanova pm_runtime_put_autosuspend(rkvdec->dev); 770d968e50bSDetlev Casanova rkvdec_job_finish_no_pm(ctx, result); 771d968e50bSDetlev Casanova } 772d968e50bSDetlev Casanova 773d968e50bSDetlev Casanova void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) 774d968e50bSDetlev Casanova { 775d968e50bSDetlev Casanova struct media_request *src_req; 776d968e50bSDetlev Casanova 777d968e50bSDetlev Casanova memset(run, 0, sizeof(*run)); 778d968e50bSDetlev Casanova 779d968e50bSDetlev Casanova run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 780d968e50bSDetlev Casanova run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 781d968e50bSDetlev Casanova 782d968e50bSDetlev Casanova /* Apply request(s) controls if needed. */ 783d968e50bSDetlev Casanova src_req = run->bufs.src->vb2_buf.req_obj.req; 784d968e50bSDetlev Casanova if (src_req) 785d968e50bSDetlev Casanova v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl); 786d968e50bSDetlev Casanova 787d968e50bSDetlev Casanova v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true); 788d968e50bSDetlev Casanova } 789d968e50bSDetlev Casanova 790d968e50bSDetlev Casanova void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) 791d968e50bSDetlev Casanova { 792d968e50bSDetlev Casanova struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req; 793d968e50bSDetlev Casanova 794d968e50bSDetlev Casanova if (src_req) 795d968e50bSDetlev Casanova v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl); 796d968e50bSDetlev Casanova } 797d968e50bSDetlev Casanova 798d968e50bSDetlev Casanova static void rkvdec_device_run(void *priv) 799d968e50bSDetlev Casanova { 800d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = priv; 801d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = ctx->dev; 802d968e50bSDetlev Casanova const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; 803d968e50bSDetlev Casanova int ret; 804d968e50bSDetlev Casanova 805d968e50bSDetlev Casanova if (WARN_ON(!desc)) 806d968e50bSDetlev Casanova return; 807d968e50bSDetlev Casanova 808d968e50bSDetlev Casanova ret = pm_runtime_resume_and_get(rkvdec->dev); 809d968e50bSDetlev Casanova if (ret < 0) { 810d968e50bSDetlev Casanova rkvdec_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR); 811d968e50bSDetlev Casanova return; 812d968e50bSDetlev Casanova } 813d968e50bSDetlev Casanova 814d968e50bSDetlev Casanova ret = desc->ops->run(ctx); 815d968e50bSDetlev Casanova if (ret) 816d968e50bSDetlev Casanova rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); 817d968e50bSDetlev Casanova } 818d968e50bSDetlev Casanova 819d968e50bSDetlev Casanova static const struct v4l2_m2m_ops rkvdec_m2m_ops = { 820d968e50bSDetlev Casanova .device_run = rkvdec_device_run, 821d968e50bSDetlev Casanova }; 822d968e50bSDetlev Casanova 823d968e50bSDetlev Casanova static int rkvdec_queue_init(void *priv, 824d968e50bSDetlev Casanova struct vb2_queue *src_vq, 825d968e50bSDetlev Casanova struct vb2_queue *dst_vq) 826d968e50bSDetlev Casanova { 827d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = priv; 828d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = ctx->dev; 829d968e50bSDetlev Casanova int ret; 830d968e50bSDetlev Casanova 831d968e50bSDetlev Casanova src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 832d968e50bSDetlev Casanova src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 833d968e50bSDetlev Casanova src_vq->drv_priv = ctx; 834d968e50bSDetlev Casanova src_vq->ops = &rkvdec_queue_ops; 835d968e50bSDetlev Casanova src_vq->mem_ops = &vb2_dma_contig_memops; 836d968e50bSDetlev Casanova 837d968e50bSDetlev Casanova /* 838d968e50bSDetlev Casanova * Driver does mostly sequential access, so sacrifice TLB efficiency 839d968e50bSDetlev Casanova * for faster allocation. Also, no CPU access on the source queue, 840d968e50bSDetlev Casanova * so no kernel mapping needed. 841d968e50bSDetlev Casanova */ 842d968e50bSDetlev Casanova src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | 843d968e50bSDetlev Casanova DMA_ATTR_NO_KERNEL_MAPPING; 844d968e50bSDetlev Casanova src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 845d968e50bSDetlev Casanova src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 846d968e50bSDetlev Casanova src_vq->lock = &rkvdec->vdev_lock; 847d968e50bSDetlev Casanova src_vq->dev = rkvdec->v4l2_dev.dev; 848d968e50bSDetlev Casanova src_vq->supports_requests = true; 849d968e50bSDetlev Casanova src_vq->requires_requests = true; 850d968e50bSDetlev Casanova 851d968e50bSDetlev Casanova ret = vb2_queue_init(src_vq); 852d968e50bSDetlev Casanova if (ret) 853d968e50bSDetlev Casanova return ret; 854d968e50bSDetlev Casanova 855d968e50bSDetlev Casanova dst_vq->bidirectional = true; 856d968e50bSDetlev Casanova dst_vq->mem_ops = &vb2_dma_contig_memops; 857d968e50bSDetlev Casanova dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | 858d968e50bSDetlev Casanova DMA_ATTR_NO_KERNEL_MAPPING; 859d968e50bSDetlev Casanova dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 860d968e50bSDetlev Casanova dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 861d968e50bSDetlev Casanova dst_vq->drv_priv = ctx; 862d968e50bSDetlev Casanova dst_vq->ops = &rkvdec_queue_ops; 863d968e50bSDetlev Casanova dst_vq->buf_struct_size = sizeof(struct rkvdec_decoded_buffer); 864d968e50bSDetlev Casanova dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 865d968e50bSDetlev Casanova dst_vq->lock = &rkvdec->vdev_lock; 866d968e50bSDetlev Casanova dst_vq->dev = rkvdec->v4l2_dev.dev; 867d968e50bSDetlev Casanova 868d968e50bSDetlev Casanova return vb2_queue_init(dst_vq); 869d968e50bSDetlev Casanova } 870d968e50bSDetlev Casanova 871d968e50bSDetlev Casanova static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx, 872d968e50bSDetlev Casanova const struct rkvdec_ctrls *ctrls) 873d968e50bSDetlev Casanova { 874d968e50bSDetlev Casanova unsigned int i; 875d968e50bSDetlev Casanova 876d968e50bSDetlev Casanova for (i = 0; i < ctrls->num_ctrls; i++) { 877d968e50bSDetlev Casanova const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg; 878d968e50bSDetlev Casanova 879d968e50bSDetlev Casanova v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx); 880d968e50bSDetlev Casanova if (ctx->ctrl_hdl.error) 881d968e50bSDetlev Casanova return ctx->ctrl_hdl.error; 882d968e50bSDetlev Casanova } 883d968e50bSDetlev Casanova 884d968e50bSDetlev Casanova return 0; 885d968e50bSDetlev Casanova } 886d968e50bSDetlev Casanova 887d968e50bSDetlev Casanova static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx) 888d968e50bSDetlev Casanova { 889d968e50bSDetlev Casanova unsigned int i, nctrls = 0; 890d968e50bSDetlev Casanova int ret; 891d968e50bSDetlev Casanova 892d968e50bSDetlev Casanova for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) 893d968e50bSDetlev Casanova nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; 894d968e50bSDetlev Casanova 895d968e50bSDetlev Casanova v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); 896d968e50bSDetlev Casanova 897d968e50bSDetlev Casanova for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { 898d968e50bSDetlev Casanova ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); 899d968e50bSDetlev Casanova if (ret) 900d968e50bSDetlev Casanova goto err_free_handler; 901d968e50bSDetlev Casanova } 902d968e50bSDetlev Casanova 903d968e50bSDetlev Casanova ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); 904d968e50bSDetlev Casanova if (ret) 905d968e50bSDetlev Casanova goto err_free_handler; 906d968e50bSDetlev Casanova 907d968e50bSDetlev Casanova ctx->fh.ctrl_handler = &ctx->ctrl_hdl; 908d968e50bSDetlev Casanova return 0; 909d968e50bSDetlev Casanova 910d968e50bSDetlev Casanova err_free_handler: 911d968e50bSDetlev Casanova v4l2_ctrl_handler_free(&ctx->ctrl_hdl); 912d968e50bSDetlev Casanova return ret; 913d968e50bSDetlev Casanova } 914d968e50bSDetlev Casanova 915d968e50bSDetlev Casanova static int rkvdec_open(struct file *filp) 916d968e50bSDetlev Casanova { 917d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = video_drvdata(filp); 918d968e50bSDetlev Casanova struct rkvdec_ctx *ctx; 919d968e50bSDetlev Casanova int ret; 920d968e50bSDetlev Casanova 921d968e50bSDetlev Casanova ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 922d968e50bSDetlev Casanova if (!ctx) 923d968e50bSDetlev Casanova return -ENOMEM; 924d968e50bSDetlev Casanova 925d968e50bSDetlev Casanova ctx->dev = rkvdec; 926d968e50bSDetlev Casanova rkvdec_reset_coded_fmt(ctx); 927d968e50bSDetlev Casanova rkvdec_reset_decoded_fmt(ctx); 928d968e50bSDetlev Casanova v4l2_fh_init(&ctx->fh, video_devdata(filp)); 929d968e50bSDetlev Casanova 930d968e50bSDetlev Casanova ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rkvdec->m2m_dev, ctx, 931d968e50bSDetlev Casanova rkvdec_queue_init); 932d968e50bSDetlev Casanova if (IS_ERR(ctx->fh.m2m_ctx)) { 933d968e50bSDetlev Casanova ret = PTR_ERR(ctx->fh.m2m_ctx); 934d968e50bSDetlev Casanova goto err_free_ctx; 935d968e50bSDetlev Casanova } 936d968e50bSDetlev Casanova 937d968e50bSDetlev Casanova ret = rkvdec_init_ctrls(ctx); 938d968e50bSDetlev Casanova if (ret) 939d968e50bSDetlev Casanova goto err_cleanup_m2m_ctx; 940d968e50bSDetlev Casanova 941d968e50bSDetlev Casanova filp->private_data = &ctx->fh; 942d968e50bSDetlev Casanova v4l2_fh_add(&ctx->fh); 943d968e50bSDetlev Casanova 944d968e50bSDetlev Casanova return 0; 945d968e50bSDetlev Casanova 946d968e50bSDetlev Casanova err_cleanup_m2m_ctx: 947d968e50bSDetlev Casanova v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 948d968e50bSDetlev Casanova 949d968e50bSDetlev Casanova err_free_ctx: 950d968e50bSDetlev Casanova kfree(ctx); 951d968e50bSDetlev Casanova return ret; 952d968e50bSDetlev Casanova } 953d968e50bSDetlev Casanova 954d968e50bSDetlev Casanova static int rkvdec_release(struct file *filp) 955d968e50bSDetlev Casanova { 956d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(filp->private_data); 957d968e50bSDetlev Casanova 958d968e50bSDetlev Casanova v4l2_fh_del(&ctx->fh); 959d968e50bSDetlev Casanova v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 960d968e50bSDetlev Casanova v4l2_ctrl_handler_free(&ctx->ctrl_hdl); 961d968e50bSDetlev Casanova v4l2_fh_exit(&ctx->fh); 962d968e50bSDetlev Casanova kfree(ctx); 963d968e50bSDetlev Casanova 964d968e50bSDetlev Casanova return 0; 965d968e50bSDetlev Casanova } 966d968e50bSDetlev Casanova 967d968e50bSDetlev Casanova static const struct v4l2_file_operations rkvdec_fops = { 968d968e50bSDetlev Casanova .owner = THIS_MODULE, 969d968e50bSDetlev Casanova .open = rkvdec_open, 970d968e50bSDetlev Casanova .release = rkvdec_release, 971d968e50bSDetlev Casanova .poll = v4l2_m2m_fop_poll, 972d968e50bSDetlev Casanova .unlocked_ioctl = video_ioctl2, 973d968e50bSDetlev Casanova .mmap = v4l2_m2m_fop_mmap, 974d968e50bSDetlev Casanova }; 975d968e50bSDetlev Casanova 976d968e50bSDetlev Casanova static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec) 977d968e50bSDetlev Casanova { 978d968e50bSDetlev Casanova int ret; 979d968e50bSDetlev Casanova 980d968e50bSDetlev Casanova ret = v4l2_device_register(rkvdec->dev, &rkvdec->v4l2_dev); 981d968e50bSDetlev Casanova if (ret) { 982d968e50bSDetlev Casanova dev_err(rkvdec->dev, "Failed to register V4L2 device\n"); 983d968e50bSDetlev Casanova return ret; 984d968e50bSDetlev Casanova } 985d968e50bSDetlev Casanova 986d968e50bSDetlev Casanova rkvdec->m2m_dev = v4l2_m2m_init(&rkvdec_m2m_ops); 987d968e50bSDetlev Casanova if (IS_ERR(rkvdec->m2m_dev)) { 988d968e50bSDetlev Casanova v4l2_err(&rkvdec->v4l2_dev, "Failed to init mem2mem device\n"); 989d968e50bSDetlev Casanova ret = PTR_ERR(rkvdec->m2m_dev); 990d968e50bSDetlev Casanova goto err_unregister_v4l2; 991d968e50bSDetlev Casanova } 992d968e50bSDetlev Casanova 993d968e50bSDetlev Casanova rkvdec->mdev.dev = rkvdec->dev; 994d968e50bSDetlev Casanova strscpy(rkvdec->mdev.model, "rkvdec", sizeof(rkvdec->mdev.model)); 995d968e50bSDetlev Casanova strscpy(rkvdec->mdev.bus_info, "platform:rkvdec", 996d968e50bSDetlev Casanova sizeof(rkvdec->mdev.bus_info)); 997d968e50bSDetlev Casanova media_device_init(&rkvdec->mdev); 998d968e50bSDetlev Casanova rkvdec->mdev.ops = &rkvdec_media_ops; 999d968e50bSDetlev Casanova rkvdec->v4l2_dev.mdev = &rkvdec->mdev; 1000d968e50bSDetlev Casanova 1001d968e50bSDetlev Casanova rkvdec->vdev.lock = &rkvdec->vdev_lock; 1002d968e50bSDetlev Casanova rkvdec->vdev.v4l2_dev = &rkvdec->v4l2_dev; 1003d968e50bSDetlev Casanova rkvdec->vdev.fops = &rkvdec_fops; 1004d968e50bSDetlev Casanova rkvdec->vdev.release = video_device_release_empty; 1005d968e50bSDetlev Casanova rkvdec->vdev.vfl_dir = VFL_DIR_M2M; 1006d968e50bSDetlev Casanova rkvdec->vdev.device_caps = V4L2_CAP_STREAMING | 1007d968e50bSDetlev Casanova V4L2_CAP_VIDEO_M2M_MPLANE; 1008d968e50bSDetlev Casanova rkvdec->vdev.ioctl_ops = &rkvdec_ioctl_ops; 1009d968e50bSDetlev Casanova video_set_drvdata(&rkvdec->vdev, rkvdec); 1010d968e50bSDetlev Casanova strscpy(rkvdec->vdev.name, "rkvdec", sizeof(rkvdec->vdev.name)); 1011d968e50bSDetlev Casanova 1012d968e50bSDetlev Casanova ret = video_register_device(&rkvdec->vdev, VFL_TYPE_VIDEO, -1); 1013d968e50bSDetlev Casanova if (ret) { 1014d968e50bSDetlev Casanova v4l2_err(&rkvdec->v4l2_dev, "Failed to register video device\n"); 1015d968e50bSDetlev Casanova goto err_cleanup_mc; 1016d968e50bSDetlev Casanova } 1017d968e50bSDetlev Casanova 1018d968e50bSDetlev Casanova ret = v4l2_m2m_register_media_controller(rkvdec->m2m_dev, &rkvdec->vdev, 1019d968e50bSDetlev Casanova MEDIA_ENT_F_PROC_VIDEO_DECODER); 1020d968e50bSDetlev Casanova if (ret) { 1021d968e50bSDetlev Casanova v4l2_err(&rkvdec->v4l2_dev, 1022d968e50bSDetlev Casanova "Failed to initialize V4L2 M2M media controller\n"); 1023d968e50bSDetlev Casanova goto err_unregister_vdev; 1024d968e50bSDetlev Casanova } 1025d968e50bSDetlev Casanova 1026d968e50bSDetlev Casanova ret = media_device_register(&rkvdec->mdev); 1027d968e50bSDetlev Casanova if (ret) { 1028d968e50bSDetlev Casanova v4l2_err(&rkvdec->v4l2_dev, "Failed to register media device\n"); 1029d968e50bSDetlev Casanova goto err_unregister_mc; 1030d968e50bSDetlev Casanova } 1031d968e50bSDetlev Casanova 1032d968e50bSDetlev Casanova return 0; 1033d968e50bSDetlev Casanova 1034d968e50bSDetlev Casanova err_unregister_mc: 1035d968e50bSDetlev Casanova v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); 1036d968e50bSDetlev Casanova 1037d968e50bSDetlev Casanova err_unregister_vdev: 1038d968e50bSDetlev Casanova video_unregister_device(&rkvdec->vdev); 1039d968e50bSDetlev Casanova 1040d968e50bSDetlev Casanova err_cleanup_mc: 1041d968e50bSDetlev Casanova media_device_cleanup(&rkvdec->mdev); 1042d968e50bSDetlev Casanova v4l2_m2m_release(rkvdec->m2m_dev); 1043d968e50bSDetlev Casanova 1044d968e50bSDetlev Casanova err_unregister_v4l2: 1045d968e50bSDetlev Casanova v4l2_device_unregister(&rkvdec->v4l2_dev); 1046d968e50bSDetlev Casanova return ret; 1047d968e50bSDetlev Casanova } 1048d968e50bSDetlev Casanova 1049d968e50bSDetlev Casanova static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec) 1050d968e50bSDetlev Casanova { 1051d968e50bSDetlev Casanova media_device_unregister(&rkvdec->mdev); 1052d968e50bSDetlev Casanova v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); 1053d968e50bSDetlev Casanova video_unregister_device(&rkvdec->vdev); 1054d968e50bSDetlev Casanova media_device_cleanup(&rkvdec->mdev); 1055d968e50bSDetlev Casanova v4l2_m2m_release(rkvdec->m2m_dev); 1056d968e50bSDetlev Casanova v4l2_device_unregister(&rkvdec->v4l2_dev); 1057d968e50bSDetlev Casanova } 1058d968e50bSDetlev Casanova 1059d968e50bSDetlev Casanova static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec) 1060d968e50bSDetlev Casanova { 1061d968e50bSDetlev Casanova if (rkvdec->empty_domain) { 1062d968e50bSDetlev Casanova /* 1063d968e50bSDetlev Casanova * To rewrite mapping into the attached IOMMU core, attach a new empty domain that 1064d968e50bSDetlev Casanova * will program an empty table, then detach it to restore the default domain and 1065d968e50bSDetlev Casanova * all cached mappings. 1066d968e50bSDetlev Casanova * This is safely done in this interrupt handler to make sure no memory get mapped 1067d968e50bSDetlev Casanova * through the IOMMU while the empty domain is attached. 1068d968e50bSDetlev Casanova */ 1069d968e50bSDetlev Casanova iommu_attach_device(rkvdec->empty_domain, rkvdec->dev); 1070d968e50bSDetlev Casanova iommu_detach_device(rkvdec->empty_domain, rkvdec->dev); 1071d968e50bSDetlev Casanova } 1072d968e50bSDetlev Casanova } 1073d968e50bSDetlev Casanova 1074d968e50bSDetlev Casanova static irqreturn_t rkvdec_irq_handler(int irq, void *priv) 1075d968e50bSDetlev Casanova { 1076d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = priv; 1077d968e50bSDetlev Casanova struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); 1078d968e50bSDetlev Casanova enum vb2_buffer_state state; 1079d968e50bSDetlev Casanova u32 status; 1080d968e50bSDetlev Casanova 1081d968e50bSDetlev Casanova status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT); 1082d968e50bSDetlev Casanova writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT); 1083d968e50bSDetlev Casanova 1084d968e50bSDetlev Casanova if (status & RKVDEC_RDY_STA) { 1085d968e50bSDetlev Casanova state = VB2_BUF_STATE_DONE; 1086d968e50bSDetlev Casanova } else { 1087d968e50bSDetlev Casanova state = VB2_BUF_STATE_ERROR; 1088d968e50bSDetlev Casanova if (status & RKVDEC_SOFTRESET_RDY) 1089d968e50bSDetlev Casanova rkvdec_iommu_restore(rkvdec); 1090d968e50bSDetlev Casanova } 1091d968e50bSDetlev Casanova 1092d968e50bSDetlev Casanova if (cancel_delayed_work(&rkvdec->watchdog_work)) 1093d968e50bSDetlev Casanova rkvdec_job_finish(ctx, state); 1094d968e50bSDetlev Casanova 1095d968e50bSDetlev Casanova return IRQ_HANDLED; 1096d968e50bSDetlev Casanova } 1097d968e50bSDetlev Casanova 1098d968e50bSDetlev Casanova static void rkvdec_watchdog_func(struct work_struct *work) 1099d968e50bSDetlev Casanova { 1100d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec; 1101d968e50bSDetlev Casanova struct rkvdec_ctx *ctx; 1102d968e50bSDetlev Casanova 1103d968e50bSDetlev Casanova rkvdec = container_of(to_delayed_work(work), struct rkvdec_dev, 1104d968e50bSDetlev Casanova watchdog_work); 1105d968e50bSDetlev Casanova ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); 1106d968e50bSDetlev Casanova if (ctx) { 1107d968e50bSDetlev Casanova dev_err(rkvdec->dev, "Frame processing timed out!\n"); 1108d968e50bSDetlev Casanova writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT); 1109d968e50bSDetlev Casanova writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL); 1110d968e50bSDetlev Casanova rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); 1111d968e50bSDetlev Casanova } 1112d968e50bSDetlev Casanova } 1113d968e50bSDetlev Casanova 1114d968e50bSDetlev Casanova static const struct of_device_id of_rkvdec_match[] = { 1115d968e50bSDetlev Casanova { .compatible = "rockchip,rk3399-vdec" }, 1116d968e50bSDetlev Casanova { /* sentinel */ } 1117d968e50bSDetlev Casanova }; 1118d968e50bSDetlev Casanova MODULE_DEVICE_TABLE(of, of_rkvdec_match); 1119d968e50bSDetlev Casanova 1120d968e50bSDetlev Casanova static const char * const rkvdec_clk_names[] = { 1121d968e50bSDetlev Casanova "axi", "ahb", "cabac", "core" 1122d968e50bSDetlev Casanova }; 1123d968e50bSDetlev Casanova 1124d968e50bSDetlev Casanova static int rkvdec_probe(struct platform_device *pdev) 1125d968e50bSDetlev Casanova { 1126d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec; 1127d968e50bSDetlev Casanova unsigned int i; 1128d968e50bSDetlev Casanova int ret, irq; 1129d968e50bSDetlev Casanova 1130d968e50bSDetlev Casanova rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL); 1131d968e50bSDetlev Casanova if (!rkvdec) 1132d968e50bSDetlev Casanova return -ENOMEM; 1133d968e50bSDetlev Casanova 1134d968e50bSDetlev Casanova platform_set_drvdata(pdev, rkvdec); 1135d968e50bSDetlev Casanova rkvdec->dev = &pdev->dev; 1136d968e50bSDetlev Casanova mutex_init(&rkvdec->vdev_lock); 1137d968e50bSDetlev Casanova INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func); 1138d968e50bSDetlev Casanova 1139d968e50bSDetlev Casanova rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), 1140d968e50bSDetlev Casanova sizeof(*rkvdec->clocks), GFP_KERNEL); 1141d968e50bSDetlev Casanova if (!rkvdec->clocks) 1142d968e50bSDetlev Casanova return -ENOMEM; 1143d968e50bSDetlev Casanova 1144d968e50bSDetlev Casanova for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++) 1145d968e50bSDetlev Casanova rkvdec->clocks[i].id = rkvdec_clk_names[i]; 1146d968e50bSDetlev Casanova 1147d968e50bSDetlev Casanova ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), 1148d968e50bSDetlev Casanova rkvdec->clocks); 1149d968e50bSDetlev Casanova if (ret) 1150d968e50bSDetlev Casanova return ret; 1151d968e50bSDetlev Casanova 1152d968e50bSDetlev Casanova rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); 1153d968e50bSDetlev Casanova if (IS_ERR(rkvdec->regs)) 1154d968e50bSDetlev Casanova return PTR_ERR(rkvdec->regs); 1155d968e50bSDetlev Casanova 1156d968e50bSDetlev Casanova ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 1157d968e50bSDetlev Casanova if (ret) { 1158d968e50bSDetlev Casanova dev_err(&pdev->dev, "Could not set DMA coherent mask.\n"); 1159d968e50bSDetlev Casanova return ret; 1160d968e50bSDetlev Casanova } 1161d968e50bSDetlev Casanova 1162d968e50bSDetlev Casanova vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); 1163d968e50bSDetlev Casanova 1164d968e50bSDetlev Casanova irq = platform_get_irq(pdev, 0); 1165d968e50bSDetlev Casanova if (irq <= 0) 1166d968e50bSDetlev Casanova return -ENXIO; 1167d968e50bSDetlev Casanova 1168d968e50bSDetlev Casanova ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 1169d968e50bSDetlev Casanova rkvdec_irq_handler, IRQF_ONESHOT, 1170d968e50bSDetlev Casanova dev_name(&pdev->dev), rkvdec); 1171d968e50bSDetlev Casanova if (ret) { 1172d968e50bSDetlev Casanova dev_err(&pdev->dev, "Could not request vdec IRQ\n"); 1173d968e50bSDetlev Casanova return ret; 1174d968e50bSDetlev Casanova } 1175d968e50bSDetlev Casanova 1176d968e50bSDetlev Casanova pm_runtime_set_autosuspend_delay(&pdev->dev, 100); 1177d968e50bSDetlev Casanova pm_runtime_use_autosuspend(&pdev->dev); 1178d968e50bSDetlev Casanova pm_runtime_enable(&pdev->dev); 1179d968e50bSDetlev Casanova 1180d968e50bSDetlev Casanova ret = rkvdec_v4l2_init(rkvdec); 1181d968e50bSDetlev Casanova if (ret) 1182d968e50bSDetlev Casanova goto err_disable_runtime_pm; 1183d968e50bSDetlev Casanova 1184*0d58a72bSChristophe JAILLET if (iommu_get_domain_for_dev(&pdev->dev)) { 1185*0d58a72bSChristophe JAILLET rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); 1186*0d58a72bSChristophe JAILLET 1187*0d58a72bSChristophe JAILLET if (IS_ERR(rkvdec->empty_domain)) { 1188*0d58a72bSChristophe JAILLET rkvdec->empty_domain = NULL; 1189*0d58a72bSChristophe JAILLET dev_warn(rkvdec->dev, "cannot alloc new empty domain\n"); 1190*0d58a72bSChristophe JAILLET } 1191*0d58a72bSChristophe JAILLET } 1192*0d58a72bSChristophe JAILLET 1193d968e50bSDetlev Casanova return 0; 1194d968e50bSDetlev Casanova 1195d968e50bSDetlev Casanova err_disable_runtime_pm: 1196d968e50bSDetlev Casanova pm_runtime_dont_use_autosuspend(&pdev->dev); 1197d968e50bSDetlev Casanova pm_runtime_disable(&pdev->dev); 1198d968e50bSDetlev Casanova return ret; 1199d968e50bSDetlev Casanova } 1200d968e50bSDetlev Casanova 1201d968e50bSDetlev Casanova static void rkvdec_remove(struct platform_device *pdev) 1202d968e50bSDetlev Casanova { 1203d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = platform_get_drvdata(pdev); 1204d968e50bSDetlev Casanova 1205d968e50bSDetlev Casanova cancel_delayed_work_sync(&rkvdec->watchdog_work); 1206d968e50bSDetlev Casanova 1207d968e50bSDetlev Casanova rkvdec_v4l2_cleanup(rkvdec); 1208d968e50bSDetlev Casanova pm_runtime_disable(&pdev->dev); 1209d968e50bSDetlev Casanova pm_runtime_dont_use_autosuspend(&pdev->dev); 1210d968e50bSDetlev Casanova 1211d968e50bSDetlev Casanova if (rkvdec->empty_domain) 1212d968e50bSDetlev Casanova iommu_domain_free(rkvdec->empty_domain); 1213d968e50bSDetlev Casanova } 1214d968e50bSDetlev Casanova 1215d968e50bSDetlev Casanova #ifdef CONFIG_PM 1216d968e50bSDetlev Casanova static int rkvdec_runtime_resume(struct device *dev) 1217d968e50bSDetlev Casanova { 1218d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); 1219d968e50bSDetlev Casanova 1220d968e50bSDetlev Casanova return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names), 1221d968e50bSDetlev Casanova rkvdec->clocks); 1222d968e50bSDetlev Casanova } 1223d968e50bSDetlev Casanova 1224d968e50bSDetlev Casanova static int rkvdec_runtime_suspend(struct device *dev) 1225d968e50bSDetlev Casanova { 1226d968e50bSDetlev Casanova struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); 1227d968e50bSDetlev Casanova 1228d968e50bSDetlev Casanova clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names), 1229d968e50bSDetlev Casanova rkvdec->clocks); 1230d968e50bSDetlev Casanova return 0; 1231d968e50bSDetlev Casanova } 1232d968e50bSDetlev Casanova #endif 1233d968e50bSDetlev Casanova 1234d968e50bSDetlev Casanova static const struct dev_pm_ops rkvdec_pm_ops = { 1235d968e50bSDetlev Casanova SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1236d968e50bSDetlev Casanova pm_runtime_force_resume) 1237d968e50bSDetlev Casanova SET_RUNTIME_PM_OPS(rkvdec_runtime_suspend, rkvdec_runtime_resume, NULL) 1238d968e50bSDetlev Casanova }; 1239d968e50bSDetlev Casanova 1240d968e50bSDetlev Casanova static struct platform_driver rkvdec_driver = { 1241d968e50bSDetlev Casanova .probe = rkvdec_probe, 1242d968e50bSDetlev Casanova .remove = rkvdec_remove, 1243d968e50bSDetlev Casanova .driver = { 1244d968e50bSDetlev Casanova .name = "rkvdec", 1245d968e50bSDetlev Casanova .of_match_table = of_rkvdec_match, 1246d968e50bSDetlev Casanova .pm = &rkvdec_pm_ops, 1247d968e50bSDetlev Casanova }, 1248d968e50bSDetlev Casanova }; 1249d968e50bSDetlev Casanova module_platform_driver(rkvdec_driver); 1250d968e50bSDetlev Casanova 1251d968e50bSDetlev Casanova MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>"); 1252d968e50bSDetlev Casanova MODULE_DESCRIPTION("Rockchip Video Decoder driver"); 1253d968e50bSDetlev Casanova MODULE_LICENSE("GPL v2"); 1254