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