From patchwork Fri Apr 28 09:13:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 98315 Delivered-To: patch@linaro.org Received: by 10.182.236.104 with SMTP id ut8csp233647obc; Fri, 28 Apr 2017 02:16:09 -0700 (PDT) X-Received: by 10.99.102.7 with SMTP id a7mr8895101pgc.216.1493370969010; Fri, 28 Apr 2017 02:16:09 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k65si5781014pgc.142.2017.04.28.02.16.08; Fri, 28 Apr 2017 02:16:09 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-media-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1946002AbdD1JPz (ORCPT + 4 others); Fri, 28 Apr 2017 05:15:55 -0400 Received: from mail-wm0-f54.google.com ([74.125.82.54]:34971 "EHLO mail-wm0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1425388AbdD1JO6 (ORCPT ); Fri, 28 Apr 2017 05:14:58 -0400 Received: by mail-wm0-f54.google.com with SMTP id w64so36912869wma.0 for ; Fri, 28 Apr 2017 02:14:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=w2UW0b1aBbcv/4qoE+VN5FOF9zODjIzN7C7RxcFEIWM=; b=SlA03X4tAsCl94pMK4NBC8RzutaXiW2MUUvEZdFovAWIbm4fw3fB6fJB98CKh0w/TX ZM3IgNaZ4aPkXv/XRaBZcuVQNqYrV0Zaky8vgkH6mw9Y1vKQpWPbCYx37AaCRAU/VlH2 I6dYxqr9RyeQBnR5SuhpOnR3TnkMxZnTEaQ4k= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=w2UW0b1aBbcv/4qoE+VN5FOF9zODjIzN7C7RxcFEIWM=; b=FK0490SEunF7Ys4/IXqvajW4s9SIlB31sdQw94jaq8FUDuza8qSG5RKU0sbYI1MnXS 0qmTNBJBPH3HE3GUZSHbx6Tq+KmhT1qv8aJWiI68oUsr8IjaydvRnDDdkrwzRzbbodDy nDjIMEpAKMIU0dCMOwHMhL6upa2XEdYRg7WSLU+fuq3uMB8pwfwftCMC9Hijbq1TPrHI N44/zuLGgfxECx+zFKPTwft0RSXzU8M+nRUG/YY00ISMPjeAVFzPAvwijnMKwShsHXaS TNMOOiuLY25khrRZ5gbEo4ctql7uepKyz/R8iF5QmS4BFs98TQXa55hShxZid5QsObAq fanQ== X-Gm-Message-State: AN3rC/69aWGPOixnd/Vv/r3ypItzk1ZwMF63cQ+KxrRlM3oQ2zB/vMs2 08noZ3z7uPAwxxQj X-Received: by 10.28.8.83 with SMTP id 80mr4729090wmi.23.1493370888293; Fri, 28 Apr 2017 02:14:48 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id p197sm5653070wmb.34.2017.04.28.02.14.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 28 Apr 2017 02:14:47 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: Andy Gross , Bjorn Andersson , Stephen Boyd , Srinivas Kandagatla , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v8 07/10] media: venus: venc: add video encoder files Date: Fri, 28 Apr 2017 12:13:54 +0300 Message-Id: <1493370837-19793-8-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> References: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org This adds encoder part of the driver plus encoder controls. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/venc.c | 1281 ++++++++++++++++++++++++ drivers/media/platform/qcom/venus/venc.h | 23 + drivers/media/platform/qcom/venus/venc_ctrls.c | 269 +++++ 3 files changed, 1573 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/venc.c create mode 100644 drivers/media/platform/qcom/venus/venc.h create mode 100644 drivers/media/platform/qcom/venus/venc_ctrls.c -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c new file mode 100644 index 000000000000..bb558d408605 --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc.c @@ -0,0 +1,1281 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hfi_venus_io.h" +#include "core.h" +#include "helpers.h" +#include "venc.h" + +#define NUM_B_FRAMES_MAX 4 + +static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + size = ALIGN(size, SZ_4K); + + return size; +} + +static u32 get_framesize_compressed(u32 width, u32 height) +{ + u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + + return ALIGN(sz, SZ_4K); +} + +/* + * Three resons to keep MPLANE formats (despite that the number of planes + * currently is one): + * - the MPLANE formats allow only one plane to be used + * - the downstream driver use MPLANE formats too + * - future firmware versions could add support for >1 planes + */ +static const struct venus_format venc_formats[] = { + { + .pixfmt = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H263, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_VP9, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, +}; + +static const struct venus_format *find_format(u32 pixfmt, int type) +{ + const struct venus_format *fmt = venc_formats; + unsigned int size = ARRAY_SIZE(venc_formats); + unsigned int i; + + for (i = 0; i < size; i++) { + if (fmt[i].pixfmt == pixfmt) + break; + } + + if (i == size || fmt[i].type != type) + return NULL; + + return &fmt[i]; +} + +static const struct venus_format *find_format_by_index(int index, int type) +{ + const struct venus_format *fmt = venc_formats; + unsigned int size = ARRAY_SIZE(venc_formats); + int i, k = 0; + + if (index < 0 || index > size) + return NULL; + + for (i = 0; i < size; i++) { + if (fmt[i].type != type) + continue; + if (k == index) + break; + k++; + } + + if (i == size) + return NULL; + + return &fmt[i]; +} + +static int venc_v4l2_to_hfi(int id, int value) +{ + switch (id) { + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0: + default: + return HFI_MPEG4_LEVEL_0; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B: + return HFI_MPEG4_LEVEL_0b; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1: + return HFI_MPEG4_LEVEL_1; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2: + return HFI_MPEG4_LEVEL_2; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3: + return HFI_MPEG4_LEVEL_3; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4: + return HFI_MPEG4_LEVEL_4; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5: + return HFI_MPEG4_LEVEL_5; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: + default: + return HFI_MPEG4_PROFILE_SIMPLE; + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: + return HFI_MPEG4_PROFILE_ADVANCEDSIMPLE; + } + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return HFI_H264_PROFILE_BASELINE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + return HFI_H264_PROFILE_CONSTRAINED_BASE; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return HFI_H264_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + default: + return HFI_H264_PROFILE_HIGH; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return HFI_H264_LEVEL_1; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return HFI_H264_LEVEL_1b; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return HFI_H264_LEVEL_11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return HFI_H264_LEVEL_12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return HFI_H264_LEVEL_13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return HFI_H264_LEVEL_2; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return HFI_H264_LEVEL_21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return HFI_H264_LEVEL_22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return HFI_H264_LEVEL_3; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return HFI_H264_LEVEL_31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return HFI_H264_LEVEL_32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return HFI_H264_LEVEL_4; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return HFI_H264_LEVEL_41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return HFI_H264_LEVEL_42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + default: + return HFI_H264_LEVEL_5; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return HFI_H264_LEVEL_51; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC: + default: + return HFI_H264_ENTROPY_CAVLC; + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: + return HFI_H264_ENTROPY_CABAC; + } + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + switch (value) { + case 0: + default: + return HFI_VPX_PROFILE_VERSION_0; + case 1: + return HFI_VPX_PROFILE_VERSION_1; + case 2: + return HFI_VPX_PROFILE_VERSION_2; + case 3: + return HFI_VPX_PROFILE_VERSION_3; + } + } + + return 0; +} + +static int +venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver)); + strlcpy(cap->card, "Qualcomm Venus video encoder", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + + return 0; +} + +static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + const struct venus_format *fmt; + + fmt = find_format_by_index(f->index, f->type); + + memset(f->reserved, 0, sizeof(f->reserved)); + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixfmt; + + return 0; +} + +static const struct venus_format * +venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; + const struct venus_format *fmt; + unsigned int p; + + memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); + memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + + fmt = find_format(pixmp->pixelformat, f->type); + if (!fmt) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_H264; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pixmp->pixelformat = V4L2_PIX_FMT_NV12; + else + return NULL; + fmt = find_format(pixmp->pixelformat, f->type); + pixmp->width = 1280; + pixmp->height = 720; + } + + pixmp->width = clamp(pixmp->width, inst->cap_width.min, + inst->cap_width.max); + pixmp->height = clamp(pixmp->height, inst->cap_height.min, + inst->cap_height.max); + + if (inst->core->res->hfi_version == HFI_VERSION_1XX) + pixmp->height = ALIGN(pixmp->height, 32); + + pixmp->width = ALIGN(pixmp->width, 2); + pixmp->height = ALIGN(pixmp->height, 2); + + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + pixmp->num_planes = fmt->num_planes; + pixmp->flags = 0; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + for (p = 0; p < pixmp->num_planes; p++) { + pfmt[p].sizeimage = + get_framesize_uncompressed(p, pixmp->width, + pixmp->height); + + pfmt[p].bytesperline = ALIGN(pixmp->width, 128); + } + } else { + pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, + pixmp->height); + pfmt[0].bytesperline = 0; + } + + return fmt; +} + +static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + + venc_try_fmt_common(inst, f); + + return 0; +} + +static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_pix_format_mplane orig_pixmp; + const struct venus_format *fmt; + struct v4l2_format format; + u32 pixfmt_out = 0, pixfmt_cap = 0; + + orig_pixmp = *pixmp; + + fmt = venc_try_fmt_common(inst, f); + if (!fmt) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixfmt_out = pixmp->pixelformat; + pixfmt_cap = inst->fmt_cap->pixfmt; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixfmt_cap = pixmp->pixelformat; + pixfmt_out = inst->fmt_out->pixfmt; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_out; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + venc_try_fmt_common(inst, &format); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->out_width = format.fmt.pix_mp.width; + inst->out_height = format.fmt.pix_mp.height; + inst->colorspace = pixmp->colorspace; + inst->ycbcr_enc = pixmp->ycbcr_enc; + inst->quantization = pixmp->quantization; + inst->xfer_func = pixmp->xfer_func; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_cap; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + venc_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->fmt_out = fmt; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + inst->fmt_cap = fmt; + + return 0; +} + +static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fmt = inst->fmt_cap; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt = inst->fmt_out; + else + return -EINVAL; + + pixmp->pixelformat = fmt->pixfmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixmp->width = inst->width; + pixmp->height = inst->height; + pixmp->colorspace = inst->colorspace; + pixmp->ycbcr_enc = inst->ycbcr_enc; + pixmp->quantization = inst->quantization; + pixmp->xfer_func = inst->xfer_func; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = inst->out_width; + pixmp->height = inst->out_height; + } + + venc_try_fmt_common(inst, f); + + return 0; +} + +static int +venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct venus_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.width = inst->width; + s->r.height = inst->height; + break; + case V4L2_SEL_TGT_CROP: + s->r.width = inst->out_width; + s->r.height = inst->out_height; + break; + default: + return -EINVAL; + } + + s->r.top = 0; + s->r.left = 0; + + return 0; +} + +static int +venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct venus_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (s->r.width != inst->out_width || + s->r.height != inst->out_height || + s->r.top != 0 || s->r.left != 0) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct venus_inst *inst = to_inst(file); + struct v4l2_outputparm *out = &a->parm.output; + struct v4l2_fract *timeperframe = &out->timeperframe; + u64 us_per_frame, fps; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + memset(out->reserved, 0, sizeof(out->reserved)); + + if (!timeperframe->denominator) + timeperframe->denominator = inst->timeperframe.denominator; + if (!timeperframe->numerator) + timeperframe->numerator = inst->timeperframe.numerator; + + out->capability = V4L2_CAP_TIMEPERFRAME; + + us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; + do_div(us_per_frame, timeperframe->denominator); + + if (!us_per_frame) + return -EINVAL; + + fps = (u64)USEC_PER_SEC; + do_div(fps, us_per_frame); + + inst->timeperframe = *timeperframe; + inst->fps = fps; + + return 0; +} + +static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct venus_inst *inst = to_inst(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability |= V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe = inst->timeperframe; + + return 0; +} + +static int venc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { + fmt = find_format(fsize->pixel_format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->stepwise.min_width = inst->cap_width.min; + fsize->stepwise.max_width = inst->cap_width.max; + fsize->stepwise.step_width = inst->cap_width.step_size; + fsize->stepwise.min_height = inst->cap_height.min; + fsize->stepwise.max_height = inst->cap_height.max; + fsize->stepwise.step_height = inst->cap_height.step_size; + + return 0; +} + +static int venc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct venus_inst *inst = to_inst(file); + const struct venus_format *fmt; + + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fmt = find_format(fival->pixel_format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { + fmt = find_format(fival->pixel_format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) + return -EINVAL; + } + + if (fival->index) + return -EINVAL; + + if (!fival->width || !fival->height) + return -EINVAL; + + if (fival->width > inst->cap_width.max || + fival->width < inst->cap_width.min || + fival->height > inst->cap_height.max || + fival->height < inst->cap_height.min) + return -EINVAL; + + fival->stepwise.min.numerator = 1; + fival->stepwise.min.denominator = inst->cap_framerate.max; + fival->stepwise.max.numerator = 1; + fival->stepwise.max.denominator = inst->cap_framerate.min; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = inst->cap_framerate.max; + + return 0; +} + +static const struct v4l2_ioctl_ops venc_ioctl_ops = { + .vidioc_querycap = venc_querycap, + .vidioc_enum_fmt_vid_cap_mplane = venc_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = venc_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = venc_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = venc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = venc_try_fmt, + .vidioc_g_selection = venc_g_selection, + .vidioc_s_selection = venc_s_selection, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_s_parm = venc_s_parm, + .vidioc_g_parm = venc_g_parm, + .vidioc_enum_framesizes = venc_enum_framesizes, + .vidioc_enum_frameintervals = venc_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int venc_set_properties(struct venus_inst *inst) +{ + struct venc_controls *ctr = &inst->controls.enc; + struct hfi_intra_period intra_period; + struct hfi_profile_level pl; + struct hfi_framerate frate; + struct hfi_bitrate brate; + struct hfi_idr_period idrp; + u32 ptype, rate_control, bitrate, profile = 0, level = 0; + int ret; + + ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; + frate.buffer_type = HFI_BUFFER_OUTPUT; + frate.framerate = inst->fps * (1 << 16); + + ret = hfi_session_set_property(inst, ptype, &frate); + if (ret) + return ret; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) { + struct hfi_h264_vui_timing_info info; + + ptype = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO; + info.enable = 1; + info.fixed_framerate = 1; + info.time_scale = NSEC_PER_SEC; + + ret = hfi_session_set_property(inst, ptype, &info); + if (ret) + return ret; + } + + ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + idrp.idr_period = ctr->gop_size; + ret = hfi_session_set_property(inst, ptype, &idrp); + if (ret) + return ret; + + if (ctr->num_b_frames) { + u32 max_num_b_frames = NUM_B_FRAMES_MAX; + + ptype = HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES; + ret = hfi_session_set_property(inst, ptype, &max_num_b_frames); + if (ret) + return ret; + } + + /* intra_period = pframes + bframes + 1 */ + if (!ctr->num_p_frames) + ctr->num_p_frames = 2 * 15 - 1, + + ptype = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD; + intra_period.pframes = ctr->num_p_frames; + intra_period.bframes = ctr->num_b_frames; + + ret = hfi_session_set_property(inst, ptype, &intra_period); + if (ret) + return ret; + + if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + rate_control = HFI_RATE_CONTROL_VBR_CFR; + else + rate_control = HFI_RATE_CONTROL_CBR_CFR; + + ptype = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; + ret = hfi_session_set_property(inst, ptype, &rate_control); + if (ret) + return ret; + + if (!ctr->bitrate) + bitrate = 64000; + else + bitrate = ctr->bitrate; + + ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + + if (!ctr->bitrate_peak) + bitrate *= 2; + else + bitrate = ctr->bitrate_peak; + + ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; + brate.bitrate = bitrate; + brate.layer_id = 0; + + ret = hfi_session_set_property(inst, ptype, &brate); + if (ret) + return ret; + + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_PROFILE, + ctr->profile.h264); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, + ctr->level.h264); + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + ctr->profile.vpx); + level = 0; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + ctr->profile.mpeg4); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + ctr->level.mpeg4); + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { + profile = 0; + level = 0; + } + + ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + pl.profile = profile; + pl.level = level; + + ret = hfi_session_set_property(inst, ptype, &pl); + if (ret) + return ret; + + return 0; +} + +static int venc_init_session(struct venus_inst *inst) +{ + int ret; + + ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); + if (ret) + return ret; + + ret = helper_set_input_resolution(inst, inst->out_width, + inst->out_height); + if (ret) + goto deinit; + + ret = helper_set_output_resolution(inst, inst->width, inst->height); + if (ret) + goto deinit; + + ret = helper_set_color_format(inst, inst->fmt_out->pixfmt); + if (ret) + goto deinit; + + return 0; +deinit: + hfi_session_deinit(inst); + return ret; +} + +static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + ret = venc_init_session(inst); + if (ret) + return ret; + + ret = helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + + *num = bufreq.count_actual; + + hfi_session_deinit(inst); + + return ret; +} + +static int venc_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + unsigned int p, num, min = 4; + int ret = 0; + + if (*num_planes) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + *num_planes != inst->fmt_out->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + *num_planes != inst->fmt_cap->num_planes) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + sizes[0] < inst->input_buf_size) + return -EINVAL; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + sizes[0] < inst->output_buf_size) + return -EINVAL; + + return 0; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *num_planes = inst->fmt_out->num_planes; + + ret = venc_out_num_buffers(inst, &num); + if (ret) + break; + + num = max(num, min); + *num_buffers = max(*num_buffers, num); + inst->num_input_bufs = *num_buffers; + + for (p = 0; p < *num_planes; ++p) + sizes[p] = get_framesize_uncompressed(p, inst->width, + inst->height); + inst->input_buf_size = sizes[0]; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *num_planes = inst->fmt_cap->num_planes; + *num_buffers = max(*num_buffers, min); + inst->num_output_bufs = *num_buffers; + sizes[0] = get_framesize_compressed(inst->width, inst->height); + inst->output_buf_size = sizes[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int venc_verify_conf(struct venus_inst *inst) +{ + struct hfi_buffer_requirements bufreq; + int ret; + + if (!inst->num_input_bufs || !inst->num_output_bufs) + return -EINVAL; + + ret = helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_output_bufs < bufreq.count_actual || + inst->num_output_bufs < bufreq.count_min) + return -EINVAL; + + ret = helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + return ret; + + if (inst->num_input_bufs < bufreq.count_actual || + inst->num_input_bufs < bufreq.count_min) + return -EINVAL; + + return 0; +} + +static int venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + int ret; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 1; + else + inst->streamon_cap = 1; + + if (!(inst->streamon_out & inst->streamon_cap)) { + mutex_unlock(&inst->lock); + return 0; + } + + helper_init_instance(inst); + + inst->sequence_cap = 0; + inst->sequence_out = 0; + + ret = venc_init_session(inst); + if (ret) + goto bufs_done; + + ret = venc_set_properties(inst); + if (ret) + goto deinit_sess; + + ret = venc_verify_conf(inst); + if (ret) + goto deinit_sess; + + ret = helper_set_num_bufs(inst, inst->num_input_bufs, + inst->num_output_bufs); + if (ret) + goto deinit_sess; + + ret = helper_vb2_start_streaming(inst); + if (ret) + goto deinit_sess; + + mutex_unlock(&inst->lock); + + return 0; + +deinit_sess: + hfi_session_deinit(inst); +bufs_done: + helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->streamon_out = 0; + else + inst->streamon_cap = 0; + mutex_unlock(&inst->lock); + return ret; +} + +static const struct vb2_ops venc_vb2_ops = { + .queue_setup = venc_queue_setup, + .buf_init = helper_vb2_buf_init, + .buf_prepare = helper_vb2_buf_prepare, + .start_streaming = venc_start_streaming, + .stop_streaming = helper_vb2_stop_streaming, + .buf_queue = helper_vb2_buf_queue, +}; + +static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type, + u32 tag, u32 bytesused, u32 data_offset, u32 flags, + u32 hfi_flags, u64 timestamp_us) +{ + struct vb2_v4l2_buffer *vbuf; + struct vb2_buffer *vb; + unsigned int type; + + if (buf_type == HFI_BUFFER_INPUT) + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + vbuf = helper_find_buf(inst, type, tag); + if (!vbuf) + return; + + vb = &vbuf->vb2_buf; + vb->planes[0].bytesused = bytesused; + vb->planes[0].data_offset = data_offset; + + vbuf->flags = flags; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + vb->timestamp = timestamp_us * NSEC_PER_USEC; + vbuf->sequence = inst->sequence_cap++; + } else { + vbuf->sequence = inst->sequence_out++; + } + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); +} + +static void venc_event_notify(struct venus_inst *inst, u32 event, + struct hfi_event_data *data) +{ + struct device *dev = inst->core->dev_enc; + + if (event == EVT_SESSION_ERROR) { + inst->session_error = true; + dev_err(dev, "enc: event session error %x\n", inst->error); + } +} + +static const struct hfi_inst_ops venc_hfi_ops = { + .buf_done = venc_buf_done, + .event_notify = venc_event_notify, +}; + +static const struct v4l2_m2m_ops venc_m2m_ops = { + .device_run = helper_m2m_device_run, + .job_abort = helper_m2m_job_abort, +}; + +static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct venus_inst *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &venc_vb2_ops; + src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->drv_priv = inst; + src_vq->buf_struct_size = sizeof(struct venus_buffer); + src_vq->allow_zero_bytesused = 1; + src_vq->min_buffers_needed = 1; + src_vq->dev = inst->core->dev; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &venc_vb2_ops; + dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->drv_priv = inst; + dst_vq->buf_struct_size = sizeof(struct venus_buffer); + dst_vq->allow_zero_bytesused = 1; + dst_vq->min_buffers_needed = 1; + dst_vq->dev = inst->core->dev; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static void venc_inst_init(struct venus_inst *inst) +{ + inst->fmt_cap = &venc_formats[2]; + inst->fmt_out = &venc_formats[0]; + inst->width = 1280; + inst->height = ALIGN(720, 32); + inst->out_width = 1280; + inst->out_height = 720; + inst->fps = 15; + inst->timeperframe.numerator = 1; + inst->timeperframe.denominator = 15; + + inst->cap_width.min = 96; + inst->cap_width.max = 1920; + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_width.max = 3840; + inst->cap_width.step_size = 2; + inst->cap_height.min = 64; + inst->cap_height.max = ALIGN(1080, 32); + if (inst->core->res->hfi_version == HFI_VERSION_3XX) + inst->cap_height.max = ALIGN(2160, 32); + inst->cap_height.step_size = 2; + inst->cap_framerate.min = 1; + inst->cap_framerate.max = 30; + inst->cap_framerate.step_size = 1; + inst->cap_mbs_per_frame.min = 24; + inst->cap_mbs_per_frame.max = 8160; +} + +static int venc_open(struct file *file) +{ + struct venus_core *core = video_drvdata(file); + struct venus_inst *inst; + int ret; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + INIT_LIST_HEAD(&inst->registeredbufs); + INIT_LIST_HEAD(&inst->internalbufs); + INIT_LIST_HEAD(&inst->list); + mutex_init(&inst->lock); + + inst->core = core; + inst->session_type = VIDC_SESSION_TYPE_ENC; + + helper_init_instance(inst); + + ret = pm_runtime_get_sync(core->dev_enc); + if (ret < 0) + goto err_free_inst; + + ret = venc_ctrl_init(inst); + if (ret) + goto err_put_sync; + + ret = hfi_session_create(inst, &venc_hfi_ops); + if (ret) + goto err_ctrl_deinit; + + venc_inst_init(inst); + + /* + * create m2m device for every instance, the m2m context scheduling + * is made by firmware side so we do not need to care about. + */ + inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops); + if (IS_ERR(inst->m2m_dev)) { + ret = PTR_ERR(inst->m2m_dev); + goto err_session_destroy; + } + + inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); + if (IS_ERR(inst->m2m_ctx)) { + ret = PTR_ERR(inst->m2m_ctx); + goto err_m2m_release; + } + + v4l2_fh_init(&inst->fh, core->vdev_enc); + + inst->fh.ctrl_handler = &inst->ctrl_handler; + v4l2_fh_add(&inst->fh); + inst->fh.m2m_ctx = inst->m2m_ctx; + file->private_data = &inst->fh; + + return 0; + +err_m2m_release: + v4l2_m2m_release(inst->m2m_dev); +err_session_destroy: + hfi_session_destroy(inst); +err_ctrl_deinit: + venc_ctrl_deinit(inst); +err_put_sync: + pm_runtime_put_sync(core->dev_enc); +err_free_inst: + kfree(inst); + return ret; +} + +static int venc_close(struct file *file) +{ + struct venus_inst *inst = to_inst(file); + + v4l2_m2m_ctx_release(inst->m2m_ctx); + v4l2_m2m_release(inst->m2m_dev); + venc_ctrl_deinit(inst); + hfi_session_destroy(inst); + mutex_destroy(&inst->lock); + v4l2_fh_del(&inst->fh); + v4l2_fh_exit(&inst->fh); + kfree(inst); + + pm_runtime_put_sync(inst->core->dev_enc); + + return 0; +} + +static const struct v4l2_file_operations venc_fops = { + .owner = THIS_MODULE, + .open = venc_open, + .release = venc_close, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif +}; + +static int venc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct video_device *vdev; + struct venus_core *core; + int ret; + + if (!dev->parent) + return -EPROBE_DEFER; + + core = dev_get_drvdata(dev->parent); + if (!core) + return -EPROBE_DEFER; + + if (core->res->hfi_version == HFI_VERSION_3XX) { + core->core1_clk = devm_clk_get(dev, "core"); + if (IS_ERR(core->core1_clk)) + return PTR_ERR(core->core1_clk); + } + + platform_set_drvdata(pdev, core); + + vdev = video_device_alloc(); + if (!vdev) + return -ENOMEM; + + vdev->release = video_device_release; + vdev->fops = &venc_fops; + vdev->ioctl_ops = &venc_ioctl_ops; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->v4l2_dev = &core->v4l2_dev; + vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vdev_release; + + core->vdev_enc = vdev; + core->dev_enc = dev; + + video_set_drvdata(vdev, core); + pm_runtime_enable(dev); + + return 0; + +err_vdev_release: + video_device_release(vdev); + return ret; +} + +static int venc_remove(struct platform_device *pdev) +{ + struct venus_core *core = dev_get_drvdata(pdev->dev.parent); + + video_unregister_device(core->vdev_enc); + pm_runtime_disable(core->dev_enc); + + return 0; +} + +#ifdef CONFIG_PM +static int venc_runtime_suspend(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + clk_disable_unprepare(core->core1_clk); + writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + + return 0; +} + +static int venc_runtime_resume(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (core->res->hfi_version == HFI_VERSION_1XX) + return 0; + + writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = clk_prepare_enable(core->core1_clk); + writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + + return ret; +} +#endif + +static const struct dev_pm_ops venc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(venc_runtime_suspend, venc_runtime_resume, NULL) +}; + +static const struct of_device_id venc_dt_match[] = { + { .compatible = "venus-encoder" }, + { } +}; +MODULE_DEVICE_TABLE(of, venc_dt_match); + +static struct platform_driver qcom_venus_enc_driver = { + .probe = venc_probe, + .remove = venc_remove, + .driver = { + .name = "qcom-venus-encoder", + .of_match_table = venc_dt_match, + .pm = &venc_pm_ops, + }, +}; +module_platform_driver(qcom_venus_enc_driver); + +MODULE_ALIAS("platform:qcom-venus-encoder"); +MODULE_DESCRIPTION("Qualcomm Venus video encoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/venc.h b/drivers/media/platform/qcom/venus/venc.h new file mode 100644 index 000000000000..9daca669f307 --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_VENC_H__ +#define __VENUS_VENC_H__ + +struct venus_inst; + +int venc_ctrl_init(struct venus_inst *inst); +void venc_ctrl_deinit(struct venus_inst *inst); + +#endif diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c new file mode 100644 index 000000000000..fcd671bc1ddf --- /dev/null +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include "core.h" + +#define BITRATE_MIN 32000 +#define BITRATE_MAX 160000000 +#define BITRATE_DEFAULT 1000000 +#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2) +#define BITRATE_STEP 100 +#define SLICE_BYTE_SIZE_MAX 1024 +#define SLICE_BYTE_SIZE_MIN 1024 +#define SLICE_MB_SIZE_MAX 300 +#define INTRA_REFRESH_MBS_MAX 300 +#define AT_SLICE_BOUNDARY \ + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY + +static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct venus_inst *inst = ctrl_to_inst(ctrl); + struct venc_controls *ctr = &inst->controls.enc; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctr->bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctr->bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctr->bitrate_peak = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + ctr->h264_entropy_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + ctr->profile.mpeg4 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctr->profile.h264 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + ctr->profile.vpx = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + ctr->level.mpeg4 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctr->level.h264 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + ctr->h264_i_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + ctr->h264_p_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + ctr->h264_b_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctr->h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctr->h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + ctr->multi_slice_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + ctr->multi_slice_max_bytes = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + ctr->multi_slice_max_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctr->h264_loop_filter_alpha = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctr->h264_loop_filter_beta = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctr->h264_loop_filter_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + ctr->header_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctr->gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + ctr->h264_i_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: + ctr->vp8_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: + ctr->vp8_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + ctr->num_b_frames = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops venc_ctrl_ops = { + .s_ctrl = venc_op_s_ctrl, +}; + +int venc_ctrl_init(struct venus_inst *inst) +{ + int ret; + + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 27); + if (ret) + return ret; + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)), + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + 0, V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, + ~((1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE) | + (1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE)), + V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + AT_SLICE_BOUNDARY, + 0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, BITRATE_DEFAULT_PEAK); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN, + SLICE_BYTE_SIZE_MAX, 1, SLICE_BYTE_SIZE_MIN); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, + SLICE_MB_SIZE_MAX, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, + 0, INTRA_REFRESH_MBS_MAX, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 12); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, 1, 128, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, 1, 128, 1, 128); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, 0, (1 << 16) - 1, 1, 0); + + ret = inst->ctrl_handler.error; + if (ret) + goto err; + + ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler); + if (ret) + goto err; + + return 0; +err: + v4l2_ctrl_handler_free(&inst->ctrl_handler); + return ret; +} + +void venc_ctrl_deinit(struct venus_inst *inst) +{ + v4l2_ctrl_handler_free(&inst->ctrl_handler); +} From patchwork Fri Apr 28 09:13:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 98320 Delivered-To: patch@linaro.org Received: by 10.182.236.104 with SMTP id ut8csp233919obc; Fri, 28 Apr 2017 02:16:58 -0700 (PDT) X-Received: by 10.99.172.83 with SMTP id z19mr4597038pgn.238.1493371018723; Fri, 28 Apr 2017 02:16:58 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id r2si6077471plj.37.2017.04.28.02.16.58; Fri, 28 Apr 2017 02:16:58 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-media-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1425427AbdD1JQU (ORCPT + 4 others); Fri, 28 Apr 2017 05:16:20 -0400 Received: from mail-wm0-f46.google.com ([74.125.82.46]:35007 "EHLO mail-wm0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1425406AbdD1JPL (ORCPT ); Fri, 28 Apr 2017 05:15:11 -0400 Received: by mail-wm0-f46.google.com with SMTP id w64so36914320wma.0 for ; Fri, 28 Apr 2017 02:15:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=XdJRDSqVigtrSHEyYP+AZ8ewjCLh0SvqN7EBJVYXThg=; b=kP/7FwD6w7JpGbBgcfQNMwJm8JpKlm7bnXiZVHLXyXCTBPB4AgMuSf+QeigtDBwpHu SdWHh7D6RTzcihaBxlnzesljCxF84lUJxqRqNcFyn95pQdadwM0szErqsrYsk4gVjWIO tznnliEO6I5Cf2ONy7cCumcO8HIBD2Eqkk4Ls= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=XdJRDSqVigtrSHEyYP+AZ8ewjCLh0SvqN7EBJVYXThg=; b=rZUcPukI+DCKj4pF+33ypsQGbwMPUjtjJYeXJ1xc7QrQuAFuMvBXBcQ7MaWmsjAbaT DGAcRTRKE0xlu9AQJum4Ywwbm6qnfgWu8u1o3K5aEnK99WKtv6bQsmgnae4vvxqNEV8e 5cW6NpUQqmBBugFp1zyjA1kuC5C++bofVKe3rQa44VICc1BZy+41ge5pXCjCsV2WJADp IMifl3s+aTEiXZC7VsnTYvUYG3SOwheb9scbPR3ekvm82k1hFbdouTSXziQTPqQjGd2E r+tTT4e1YwJctXC5k3nU4ItAcLYhMY1JXXhfEAGNuOk5nTU29yas6CWniMQd/pkNYQzD Ns4Q== X-Gm-Message-State: AN3rC/4tURtd0nBR92NA2HHCugM0KeASB9uorxFM3EaN3L3Ug6TKWhhi kadyWeqmRCivMUwD X-Received: by 10.28.197.135 with SMTP id v129mr5293220wmf.55.1493370891263; Fri, 28 Apr 2017 02:14:51 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id p197sm5653070wmb.34.2017.04.28.02.14.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 28 Apr 2017 02:14:50 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: Andy Gross , Bjorn Andersson , Stephen Boyd , Srinivas Kandagatla , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v8 08/10] media: venus: hfi: add Host Firmware Interface (HFI) Date: Fri, 28 Apr 2017 12:13:55 +0300 Message-Id: <1493370837-19793-9-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> References: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org This is the implementation of HFI. It is charged with the responsibility to comunicate with the firmware through an interface commands and messages. - hfi.c has interface functions used by the core, decoder and encoder parts to comunicate with the firmware. For example there are functions for session and core initialisation. - hfi_cmds has packetization operations which preparing packets to be send from host to firmware. - hfi_msgs takes care of messages sent from firmware to the host. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/hfi.c | 522 ++++++++++ drivers/media/platform/qcom/venus/hfi.h | 175 ++++ drivers/media/platform/qcom/venus/hfi_cmds.c | 1255 ++++++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_cmds.h | 304 ++++++ drivers/media/platform/qcom/venus/hfi_helper.h | 1050 ++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_msgs.c | 1056 ++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_msgs.h | 283 ++++++ 7 files changed, 4645 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/hfi.c create mode 100644 drivers/media/platform/qcom/venus/hfi.h create mode 100644 drivers/media/platform/qcom/venus/hfi_cmds.c create mode 100644 drivers/media/platform/qcom/venus/hfi_cmds.h create mode 100644 drivers/media/platform/qcom/venus/hfi_helper.h create mode 100644 drivers/media/platform/qcom/venus/hfi_msgs.c create mode 100644 drivers/media/platform/qcom/venus/hfi_msgs.h -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c new file mode 100644 index 000000000000..20c9205fdbb4 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "hfi.h" +#include "hfi_cmds.h" +#include "hfi_venus.h" + +#define TIMEOUT msecs_to_jiffies(1000) + +static u32 to_codec_type(u32 pixfmt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_NO_SC: + return HFI_VIDEO_CODEC_H264; + case V4L2_PIX_FMT_H263: + return HFI_VIDEO_CODEC_H263; + case V4L2_PIX_FMT_MPEG1: + return HFI_VIDEO_CODEC_MPEG1; + case V4L2_PIX_FMT_MPEG2: + return HFI_VIDEO_CODEC_MPEG2; + case V4L2_PIX_FMT_MPEG4: + return HFI_VIDEO_CODEC_MPEG4; + case V4L2_PIX_FMT_VC1_ANNEX_G: + case V4L2_PIX_FMT_VC1_ANNEX_L: + return HFI_VIDEO_CODEC_VC1; + case V4L2_PIX_FMT_VP8: + return HFI_VIDEO_CODEC_VP8; + case V4L2_PIX_FMT_VP9: + return HFI_VIDEO_CODEC_VP9; + case V4L2_PIX_FMT_XVID: + return HFI_VIDEO_CODEC_DIVX; + default: + return 0; + } +} + +int hfi_core_init(struct venus_core *core) +{ + int ret = 0; + + mutex_lock(&core->lock); + + if (core->state >= CORE_INIT) + goto unlock; + + reinit_completion(&core->done); + + ret = core->ops->core_init(core); + if (ret) + goto unlock; + + ret = wait_for_completion_timeout(&core->done, TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto unlock; + } + + ret = 0; + + if (core->error != HFI_ERR_NONE) { + ret = -EIO; + goto unlock; + } + + core->state = CORE_INIT; +unlock: + mutex_unlock(&core->lock); + return ret; +} + +static int core_deinit_wait_atomic_t(atomic_t *p) +{ + schedule(); + return 0; +} + +int hfi_core_deinit(struct venus_core *core, bool blocking) +{ + int ret = 0, empty; + + mutex_lock(&core->lock); + + if (core->state == CORE_UNINIT) + goto unlock; + + empty = list_empty(&core->instances); + + if (!empty && !blocking) { + ret = -EBUSY; + goto unlock; + } + + if (!empty) { + mutex_unlock(&core->lock); + wait_on_atomic_t(&core->insts_count, core_deinit_wait_atomic_t, + TASK_UNINTERRUPTIBLE); + mutex_lock(&core->lock); + } + + ret = core->ops->core_deinit(core); + + if (!ret) + core->state = CORE_UNINIT; + +unlock: + mutex_unlock(&core->lock); + return ret; +} + +int hfi_core_suspend(struct venus_core *core) +{ + if (core->state != CORE_INIT) + return 0; + + return core->ops->suspend(core); +} + +int hfi_core_resume(struct venus_core *core, bool force) +{ + if (!force && core->state != CORE_INIT) + return 0; + + return core->ops->resume(core); +} + +int hfi_core_trigger_ssr(struct venus_core *core, u32 type) +{ + return core->ops->core_trigger_ssr(core, type); +} + +int hfi_core_ping(struct venus_core *core) +{ + int ret; + + mutex_lock(&core->lock); + + ret = core->ops->core_ping(core, 0xbeef); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&core->done, TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto unlock; + } + ret = 0; + if (core->error != HFI_ERR_NONE) + ret = -ENODEV; +unlock: + mutex_unlock(&core->lock); + return ret; +} + +static int wait_session_msg(struct venus_inst *inst) +{ + int ret; + + ret = wait_for_completion_timeout(&inst->done, TIMEOUT); + if (!ret) + return -ETIMEDOUT; + + if (inst->error != HFI_ERR_NONE) + return -EIO; + + return 0; +} + +int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) +{ + struct venus_core *core = inst->core; + + if (!ops) + return -EINVAL; + + inst->state = INST_UNINIT; + init_completion(&inst->done); + inst->ops = ops; + + mutex_lock(&core->lock); + list_add_tail(&inst->list, &core->instances); + atomic_inc(&core->insts_count); + mutex_unlock(&core->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(hfi_session_create); + +int hfi_session_init(struct venus_inst *inst, u32 pixfmt) +{ + struct venus_core *core = inst->core; + const struct hfi_ops *ops = core->ops; + u32 codec; + int ret; + + codec = to_codec_type(pixfmt); + reinit_completion(&inst->done); + + ret = ops->session_init(inst, inst->session_type, codec); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_INIT; + + return 0; +} +EXPORT_SYMBOL_GPL(hfi_session_init); + +void hfi_session_destroy(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + + mutex_lock(&core->lock); + list_del_init(&inst->list); + atomic_dec(&core->insts_count); + wake_up_atomic_t(&core->insts_count); + mutex_unlock(&core->lock); +} +EXPORT_SYMBOL_GPL(hfi_session_destroy); + +int hfi_session_deinit(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state == INST_UNINIT) + return 0; + + if (inst->state < INST_INIT) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_end(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_UNINIT; + + return 0; +} +EXPORT_SYMBOL_GPL(hfi_session_deinit); + +int hfi_session_start(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state != INST_LOAD_RESOURCES) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_start(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_START; + + return 0; +} + +int hfi_session_stop(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state != INST_START) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_stop(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_STOP; + + return 0; +} + +int hfi_session_continue(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + + if (core->res->hfi_version != HFI_VERSION_3XX) + return 0; + + return core->ops->session_continue(inst); +} +EXPORT_SYMBOL_GPL(hfi_session_continue); + +int hfi_session_abort(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + reinit_completion(&inst->done); + + ret = ops->session_abort(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + return 0; +} + +int hfi_session_load_res(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state != INST_INIT) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_load_res(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_LOAD_RESOURCES; + + return 0; +} + +int hfi_session_unload_res(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state != INST_STOP) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_release_res(inst); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + inst->state = INST_RELEASE_RESOURCES; + + return 0; +} + +int hfi_session_flush(struct venus_inst *inst) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + reinit_completion(&inst->done); + + ret = ops->session_flush(inst, HFI_FLUSH_ALL); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(hfi_session_flush); + +int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd) +{ + const struct hfi_ops *ops = inst->core->ops; + + return ops->session_set_buffers(inst, bd); +} + +int hfi_session_unset_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + reinit_completion(&inst->done); + + ret = ops->session_unset_buffers(inst, bd); + if (ret) + return ret; + + if (!bd->response_required) + return 0; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + return 0; +} + +int hfi_session_get_property(struct venus_inst *inst, u32 ptype, + union hfi_get_property *hprop) +{ + const struct hfi_ops *ops = inst->core->ops; + int ret; + + if (inst->state < INST_INIT || inst->state >= INST_STOP) + return -EINVAL; + + reinit_completion(&inst->done); + + ret = ops->session_get_property(inst, ptype); + if (ret) + return ret; + + ret = wait_session_msg(inst); + if (ret) + return ret; + + *hprop = inst->hprop; + + return 0; +} +EXPORT_SYMBOL_GPL(hfi_session_get_property); + +int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata) +{ + const struct hfi_ops *ops = inst->core->ops; + + if (inst->state < INST_INIT || inst->state >= INST_STOP) + return -EINVAL; + + return ops->session_set_property(inst, ptype, pdata); +} +EXPORT_SYMBOL_GPL(hfi_session_set_property); + +int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd) +{ + const struct hfi_ops *ops = inst->core->ops; + + if (fd->buffer_type == HFI_BUFFER_INPUT) + return ops->session_etb(inst, fd); + else if (fd->buffer_type == HFI_BUFFER_OUTPUT) + return ops->session_ftb(inst, fd); + + return -EINVAL; +} + +irqreturn_t hfi_isr_thread(int irq, void *dev_id) +{ + struct venus_core *core = dev_id; + + return core->ops->isr_thread(core); +} + +irqreturn_t hfi_isr(int irq, void *dev) +{ + struct venus_core *core = dev; + + return core->ops->isr(core); +} + +int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops) +{ + int ret; + + if (!ops) + return -EINVAL; + + atomic_set(&core->insts_count, 0); + core->core_ops = ops; + core->state = CORE_UNINIT; + init_completion(&core->done); + pkt_set_version(core->res->hfi_version); + ret = venus_hfi_create(core); + + return ret; +} + +void hfi_destroy(struct venus_core *core) +{ + venus_hfi_destroy(core); +} diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h new file mode 100644 index 000000000000..5466b7d60dd0 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __HFI_H__ +#define __HFI_H__ + +#include + +#include "hfi_helper.h" + +#define VIDC_SESSION_TYPE_VPE 0 +#define VIDC_SESSION_TYPE_ENC 1 +#define VIDC_SESSION_TYPE_DEC 2 + +#define VIDC_RESOURCE_NONE 0 +#define VIDC_RESOURCE_OCMEM 1 +#define VIDC_RESOURCE_VMEM 2 + +struct hfi_buffer_desc { + u32 buffer_type; + u32 buffer_size; + u32 num_buffers; + u32 device_addr; + u32 extradata_addr; + u32 extradata_size; + u32 response_required; +}; + +struct hfi_frame_data { + u32 buffer_type; + u32 device_addr; + u32 extradata_addr; + u64 timestamp; + u32 flags; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 mark_target; + u32 mark_data; + u32 clnt_data; + u32 extradata_size; +}; + +union hfi_get_property { + struct hfi_profile_level profile_level; + struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; +}; + +/* HFI events */ +#define EVT_SYS_EVENT_CHANGE 1 +#define EVT_SYS_WATCHDOG_TIMEOUT 2 +#define EVT_SYS_ERROR 3 +#define EVT_SESSION_ERROR 4 + +/* HFI event callback structure */ +struct hfi_event_data { + u32 error; + u32 height; + u32 width; + u32 event_type; + u32 packet_buffer; + u32 extradata_buffer; + u32 tag; + u32 profile; + u32 level; +}; + +/* define core states */ +#define CORE_UNINIT 0 +#define CORE_INIT 1 + +/* define instance states */ +#define INST_UNINIT 2 +#define INST_INIT 3 +#define INST_LOAD_RESOURCES 4 +#define INST_START 5 +#define INST_STOP 6 +#define INST_RELEASE_RESOURCES 7 + +struct venus_core; +struct venus_inst; + +struct hfi_core_ops { + void (*event_notify)(struct venus_core *core, u32 event); +}; + +struct hfi_inst_ops { + void (*buf_done)(struct venus_inst *inst, unsigned int buf_type, + u32 tag, u32 bytesused, u32 data_offset, u32 flags, + u32 hfi_flags, u64 timestamp_us); + void (*event_notify)(struct venus_inst *inst, u32 event, + struct hfi_event_data *data); +}; + +struct hfi_ops { + int (*core_init)(struct venus_core *core); + int (*core_deinit)(struct venus_core *core); + int (*core_ping)(struct venus_core *core, u32 cookie); + int (*core_trigger_ssr)(struct venus_core *core, u32 trigger_type); + + int (*session_init)(struct venus_inst *inst, u32 session_type, + u32 codec); + int (*session_end)(struct venus_inst *inst); + int (*session_abort)(struct venus_inst *inst); + int (*session_flush)(struct venus_inst *inst, u32 flush_mode); + int (*session_start)(struct venus_inst *inst); + int (*session_stop)(struct venus_inst *inst); + int (*session_continue)(struct venus_inst *inst); + int (*session_etb)(struct venus_inst *inst, struct hfi_frame_data *fd); + int (*session_ftb)(struct venus_inst *inst, struct hfi_frame_data *fd); + int (*session_set_buffers)(struct venus_inst *inst, + struct hfi_buffer_desc *bd); + int (*session_unset_buffers)(struct venus_inst *inst, + struct hfi_buffer_desc *bd); + int (*session_load_res)(struct venus_inst *inst); + int (*session_release_res)(struct venus_inst *inst); + int (*session_parse_seq_hdr)(struct venus_inst *inst, u32 seq_hdr, + u32 seq_hdr_len); + int (*session_get_seq_hdr)(struct venus_inst *inst, u32 seq_hdr, + u32 seq_hdr_len); + int (*session_set_property)(struct venus_inst *inst, u32 ptype, + void *pdata); + int (*session_get_property)(struct venus_inst *inst, u32 ptype); + + int (*resume)(struct venus_core *core); + int (*suspend)(struct venus_core *core); + + /* interrupt operations */ + irqreturn_t (*isr)(struct venus_core *core); + irqreturn_t (*isr_thread)(struct venus_core *core); +}; + +int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops); +void hfi_destroy(struct venus_core *core); + +int hfi_core_init(struct venus_core *core); +int hfi_core_deinit(struct venus_core *core, bool blocking); +int hfi_core_suspend(struct venus_core *core); +int hfi_core_resume(struct venus_core *core, bool force); +int hfi_core_trigger_ssr(struct venus_core *core, u32 type); +int hfi_core_ping(struct venus_core *core); +int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops); +void hfi_session_destroy(struct venus_inst *inst); +int hfi_session_init(struct venus_inst *inst, u32 pixfmt); +int hfi_session_deinit(struct venus_inst *inst); +int hfi_session_start(struct venus_inst *inst); +int hfi_session_stop(struct venus_inst *inst); +int hfi_session_continue(struct venus_inst *inst); +int hfi_session_abort(struct venus_inst *inst); +int hfi_session_load_res(struct venus_inst *inst); +int hfi_session_unload_res(struct venus_inst *inst); +int hfi_session_flush(struct venus_inst *inst); +int hfi_session_set_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd); +int hfi_session_unset_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd); +int hfi_session_get_property(struct venus_inst *inst, u32 ptype, + union hfi_get_property *hprop); +int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata); +int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *f); +irqreturn_t hfi_isr_thread(int irq, void *dev_id); +irqreturn_t hfi_isr(int irq, void *dev); + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c new file mode 100644 index 000000000000..74a52b2bfd35 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -0,0 +1,1255 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include "hfi_cmds.h" + +static enum hfi_version hfi_ver; + +void pkt_sys_init(struct hfi_sys_init_pkt *pkt, u32 arch_type) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_INIT; + pkt->arch_type = arch_type; +} + +void pkt_sys_pc_prep(struct hfi_sys_pc_prep_pkt *pkt) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_PC_PREP; +} + +void pkt_sys_idle_indicator(struct hfi_sys_set_property_pkt *pkt, u32 enable) +{ + struct hfi_enable *hfi = (struct hfi_enable *)&pkt->data[1]; + + pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_IDLE_INDICATOR; + hfi->enable = enable; +} + +void pkt_sys_debug_config(struct hfi_sys_set_property_pkt *pkt, u32 mode, + u32 config) +{ + struct hfi_debug_config *hfi; + + pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG; + hfi = (struct hfi_debug_config *)&pkt->data[1]; + hfi->config = config; + hfi->mode = mode; +} + +void pkt_sys_coverage_config(struct hfi_sys_set_property_pkt *pkt, u32 mode) +{ + pkt->hdr.size = sizeof(*pkt) + sizeof(u32); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE; + pkt->data[1] = mode; +} + +int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size, + u32 addr, void *cookie) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_RESOURCE; + pkt->resource_handle = hash32_ptr(cookie); + + switch (id) { + case VIDC_RESOURCE_OCMEM: + case VIDC_RESOURCE_VMEM: { + struct hfi_resource_ocmem *res = + (struct hfi_resource_ocmem *)&pkt->resource_data[0]; + + res->size = size; + res->mem = addr; + pkt->resource_type = HFI_RESOURCE_OCMEM; + pkt->hdr.size += sizeof(*res) - sizeof(u32); + break; + } + case VIDC_RESOURCE_NONE: + default: + return -ENOTSUPP; + } + + return 0; +} + +int pkt_sys_unset_resource(struct hfi_sys_release_resource_pkt *pkt, u32 id, + u32 size, void *cookie) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_RELEASE_RESOURCE; + pkt->resource_handle = hash32_ptr(cookie); + + switch (id) { + case VIDC_RESOURCE_OCMEM: + case VIDC_RESOURCE_VMEM: + pkt->resource_type = HFI_RESOURCE_OCMEM; + break; + case VIDC_RESOURCE_NONE: + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +void pkt_sys_ping(struct hfi_sys_ping_pkt *pkt, u32 cookie) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_PING; + pkt->client_data = cookie; +} + +void pkt_sys_power_control(struct hfi_sys_set_property_pkt *pkt, u32 enable) +{ + struct hfi_enable *hfi = (struct hfi_enable *)&pkt->data[1]; + + pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL; + hfi->enable = enable; +} + +int pkt_sys_ssr_cmd(struct hfi_sys_test_ssr_pkt *pkt, u32 trigger_type) +{ + switch (trigger_type) { + case HFI_TEST_SSR_SW_ERR_FATAL: + case HFI_TEST_SSR_SW_DIV_BY_ZERO: + case HFI_TEST_SSR_HW_WDOG_IRQ: + break; + default: + return -EINVAL; + } + + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_TEST_SSR; + pkt->trigger_type = trigger_type; + + return 0; +} + +void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt) +{ + pkt->hdr.size = sizeof(*pkt); + pkt->hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION; +} + +int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie, + u32 session_type, u32 codec) +{ + if (!pkt || !cookie || !codec) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->session_domain = session_type; + pkt->session_codec = codec; + + return 0; +} + +void pkt_session_cmd(struct hfi_session_pkt *pkt, u32 pkt_type, void *cookie) +{ + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = pkt_type; + pkt->shdr.session_id = hash32_ptr(cookie); +} + +int pkt_session_set_buffers(struct hfi_session_set_buffers_pkt *pkt, + void *cookie, struct hfi_buffer_desc *bd) +{ + int i; + + if (!cookie || !pkt || !bd) + return -EINVAL; + + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_BUFFERS; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->buffer_size = bd->buffer_size; + pkt->min_buffer_size = bd->buffer_size; + pkt->num_buffers = bd->num_buffers; + + if (bd->buffer_type == HFI_BUFFER_OUTPUT || + bd->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *bi; + + pkt->extradata_size = bd->extradata_size; + pkt->shdr.hdr.size = sizeof(*pkt) - sizeof(u32) + + (bd->num_buffers * sizeof(*bi)); + bi = (struct hfi_buffer_info *)pkt->buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + bi->buffer_addr = bd->device_addr; + bi->extradata_addr = bd->extradata_addr; + } + } else { + pkt->extradata_size = 0; + pkt->shdr.hdr.size = sizeof(*pkt) + + ((bd->num_buffers - 1) * sizeof(u32)); + for (i = 0; i < pkt->num_buffers; i++) + pkt->buffer_info[i] = bd->device_addr; + } + + pkt->buffer_type = bd->buffer_type; + + return 0; +} + +int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt, + void *cookie, struct hfi_buffer_desc *bd) +{ + int i; + + if (!cookie || !pkt || !bd) + return -EINVAL; + + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_RELEASE_BUFFERS; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->buffer_size = bd->buffer_size; + pkt->num_buffers = bd->num_buffers; + + if (bd->buffer_type == HFI_BUFFER_OUTPUT || + bd->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *bi; + + bi = (struct hfi_buffer_info *)pkt->buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + bi->buffer_addr = bd->device_addr; + bi->extradata_addr = bd->extradata_addr; + } + pkt->shdr.hdr.size = + sizeof(struct hfi_session_set_buffers_pkt) - + sizeof(u32) + (bd->num_buffers * sizeof(*bi)); + } else { + for (i = 0; i < pkt->num_buffers; i++) + pkt->buffer_info[i] = bd->device_addr; + + pkt->extradata_size = 0; + pkt->shdr.hdr.size = + sizeof(struct hfi_session_set_buffers_pkt) + + ((bd->num_buffers - 1) * sizeof(u32)); + } + + pkt->response_req = bd->response_required; + pkt->buffer_type = bd->buffer_type; + + return 0; +} + +int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt, + void *cookie, struct hfi_frame_data *in_frame) +{ + if (!cookie || !in_frame->device_addr) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->time_stamp_hi = upper_32_bits(in_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(in_frame->timestamp); + pkt->flags = in_frame->flags; + pkt->mark_target = in_frame->mark_target; + pkt->mark_data = in_frame->mark_data; + pkt->offset = in_frame->offset; + pkt->alloc_len = in_frame->alloc_len; + pkt->filled_len = in_frame->filled_len; + pkt->input_tag = in_frame->clnt_data; + pkt->packet_buffer = in_frame->device_addr; + + return 0; +} + +int pkt_session_etb_encoder( + struct hfi_session_empty_buffer_uncompressed_plane0_pkt *pkt, + void *cookie, struct hfi_frame_data *in_frame) +{ + if (!cookie || !in_frame->device_addr) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->view_id = 0; + pkt->time_stamp_hi = upper_32_bits(in_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(in_frame->timestamp); + pkt->flags = in_frame->flags; + pkt->mark_target = in_frame->mark_target; + pkt->mark_data = in_frame->mark_data; + pkt->offset = in_frame->offset; + pkt->alloc_len = in_frame->alloc_len; + pkt->filled_len = in_frame->filled_len; + pkt->input_tag = in_frame->clnt_data; + pkt->packet_buffer = in_frame->device_addr; + pkt->extradata_buffer = in_frame->extradata_addr; + + return 0; +} + +int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, void *cookie, + struct hfi_frame_data *out_frame) +{ + if (!cookie || !out_frame || !out_frame->device_addr) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_FILL_BUFFER; + pkt->shdr.session_id = hash32_ptr(cookie); + + if (out_frame->buffer_type == HFI_BUFFER_OUTPUT) + pkt->stream_id = 0; + else if (out_frame->buffer_type == HFI_BUFFER_OUTPUT2) + pkt->stream_id = 1; + + pkt->output_tag = out_frame->clnt_data; + pkt->packet_buffer = out_frame->device_addr; + pkt->extradata_buffer = out_frame->extradata_addr; + pkt->alloc_len = out_frame->alloc_len; + pkt->filled_len = out_frame->filled_len; + pkt->offset = out_frame->offset; + pkt->data[0] = out_frame->extradata_size; + + return 0; +} + +int pkt_session_parse_seq_header( + struct hfi_session_parse_sequence_header_pkt *pkt, + void *cookie, u32 seq_hdr, u32 seq_hdr_len) +{ + if (!cookie || !seq_hdr || !seq_hdr_len) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->header_len = seq_hdr_len; + pkt->packet_buffer = seq_hdr; + + return 0; +} + +int pkt_session_get_seq_hdr(struct hfi_session_get_sequence_header_pkt *pkt, + void *cookie, u32 seq_hdr, u32 seq_hdr_len) +{ + if (!cookie || !seq_hdr || !seq_hdr_len) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->buffer_len = seq_hdr_len; + pkt->packet_buffer = seq_hdr; + + return 0; +} + +int pkt_session_flush(struct hfi_session_flush_pkt *pkt, void *cookie, u32 type) +{ + switch (type) { + case HFI_FLUSH_INPUT: + case HFI_FLUSH_OUTPUT: + case HFI_FLUSH_OUTPUT2: + case HFI_FLUSH_ALL: + break; + default: + return -EINVAL; + } + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->flush_type = type; + + return 0; +} + +static int pkt_session_get_property_1x(struct hfi_session_get_property_pkt *pkt, + void *cookie, u32 ptype) +{ + switch (ptype) { + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + break; + default: + return -EINVAL; + } + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + pkt->data[0] = ptype; + + return 0; +} + +static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data = &pkt->data[1]; + int ret = 0; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + + switch (ptype) { + case HFI_PROPERTY_CONFIG_FRAME_RATE: { + struct hfi_framerate *in = pdata, *frate = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE; + frate->buffer_type = in->buffer_type; + frate->framerate = in->framerate; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*frate); + break; + } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: { + struct hfi_uncompressed_format_select *in = pdata; + struct hfi_uncompressed_format_select *hfi = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; + hfi->buffer_type = in->buffer_type; + hfi->format = in->format; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + break; + } + case HFI_PROPERTY_PARAM_FRAME_SIZE: { + struct hfi_framesize *in = pdata, *fsize = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE; + fsize->buffer_type = in->buffer_type; + fsize->height = in->height; + fsize->width = in->width; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*fsize); + break; + } + case HFI_PROPERTY_CONFIG_REALTIME: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_REALTIME; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { + struct hfi_buffer_count_actual *in = pdata, *count = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; + count->count_actual = in->count_actual; + count->type = in->type; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: { + struct hfi_buffer_size_actual *in = pdata, *sz = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + sz->size = in->size; + sz->type = in->type; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*sz); + break; + } + case HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL: { + struct hfi_buffer_display_hold_count_actual *in = pdata; + struct hfi_buffer_display_hold_count_actual *count = prop_data; + + pkt->data[0] = + HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL; + count->hold_count = in->hold_count; + count->type = in->type; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: { + struct hfi_nal_stream_format_select *in = pdata; + struct hfi_nal_stream_format_select *fmt = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT; + fmt->format = in->format; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*fmt); + break; + } + case HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER: { + u32 *in = pdata; + + switch (*in) { + case HFI_OUTPUT_ORDER_DECODE: + case HFI_OUTPUT_ORDER_DISPLAY: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE: { + struct hfi_enable_picture *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE; + en->picture_type = in->picture_type; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = + HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: { + struct hfi_enable *in = pdata; + struct hfi_enable *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: { + struct hfi_multi_stream *in = pdata, *multi = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + multi->buffer_type = in->buffer_type; + multi->enable = in->enable; + multi->width = in->width; + multi->height = in->height; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + break; + } + case HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT: { + struct hfi_display_picture_buffer_count *in = pdata; + struct hfi_display_picture_buffer_count *count = prop_data; + + pkt->data[0] = + HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT; + count->count = in->count; + count->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_DIVX_FORMAT: { + u32 *in = pdata; + + switch (*in) { + case HFI_DIVX_FORMAT_4: + case HFI_DIVX_FORMAT_5: + case HFI_DIVX_FORMAT_6: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = + HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME: + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME; + pkt->shdr.hdr.size += sizeof(u32); + break; + case HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER: + break; + case HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION: + break; + case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { + struct hfi_bitrate *in = pdata, *brate = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + brate->bitrate = in->bitrate; + brate->layer_id = in->layer_id; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*brate); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: { + struct hfi_bitrate *in = pdata, *hfi = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; + hfi->bitrate = in->bitrate; + hfi->layer_id = in->layer_id; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + break; + } + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: { + struct hfi_profile_level *in = pdata, *pl = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + pl->level = in->level; + pl->profile = in->profile; + if (pl->profile <= 0) + /* Profile not supported, falling back to high */ + pl->profile = HFI_H264_PROFILE_HIGH; + + if (!pl->level) + /* Level not supported, falling back to 1 */ + pl->level = 1; + + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*pl); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: { + struct hfi_h264_entropy_control *in = pdata, *hfi = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL; + hfi->entropy_mode = in->entropy_mode; + if (hfi->entropy_mode == HFI_H264_ENTROPY_CABAC) + hfi->cabac_model = in->cabac_model; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + break; + } + case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: { + u32 *in = pdata; + + switch (*in) { + case HFI_RATE_CONTROL_OFF: + case HFI_RATE_CONTROL_CBR_CFR: + case HFI_RATE_CONTROL_CBR_VFR: + case HFI_RATE_CONTROL_VBR_CFR: + case HFI_RATE_CONTROL_VBR_VFR: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION: { + struct hfi_mpeg4_time_resolution *in = pdata, *res = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION; + res->time_increment_resolution = in->time_increment_resolution; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*res); + break; + } + case HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION: { + struct hfi_mpeg4_header_extension *in = pdata, *ext = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION; + ext->header_extension = in->header_extension; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ext); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL: { + struct hfi_h264_db_control *in = pdata, *db = prop_data; + + switch (in->mode) { + case HFI_H264_DB_MODE_DISABLE: + case HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY: + case HFI_H264_DB_MODE_ALL_BOUNDARY: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL; + db->mode = in->mode; + db->slice_alpha_offset = in->slice_alpha_offset; + db->slice_beta_offset = in->slice_beta_offset; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*db); + break; + } + case HFI_PROPERTY_PARAM_VENC_SESSION_QP: { + struct hfi_quantization *in = pdata, *quant = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SESSION_QP; + quant->qp_i = in->qp_i; + quant->qp_p = in->qp_p; + quant->qp_b = in->qp_b; + quant->layer_id = in->layer_id; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*quant); + break; + } + case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE: { + struct hfi_quantization_range *in = pdata, *range = prop_data; + u32 min_qp, max_qp; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; + min_qp = in->min_qp; + max_qp = in->max_qp; + + /* We'll be packing in the qp, so make sure we + * won't be losing data when masking + */ + if (min_qp > 0xff || max_qp > 0xff) { + ret = -ERANGE; + break; + } + + /* When creating the packet, pack the qp value as + * 0xiippbb, where ii = qp range for I-frames, + * pp = qp range for P-frames, etc. + */ + range->min_qp = min_qp | min_qp << 8 | min_qp << 16; + range->max_qp = max_qp | max_qp << 8 | max_qp << 16; + range->layer_id = in->layer_id; + + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*range); + break; + } + case HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG: { + struct hfi_vc1e_perf_cfg_type *in = pdata, *perf = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG; + + memcpy(perf->search_range_x_subsampled, + in->search_range_x_subsampled, + sizeof(perf->search_range_x_subsampled)); + memcpy(perf->search_range_y_subsampled, + in->search_range_y_subsampled, + sizeof(perf->search_range_y_subsampled)); + + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*perf); + break; + } + case HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES: { + struct hfi_max_num_b_frames *bframes = prop_data; + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES; + bframes->max_num_b_frames = *in; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*bframes); + break; + } + case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { + struct hfi_intra_period *in = pdata, *intra = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD; + intra->pframes = in->pframes; + intra->bframes = in->bframes; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra); + break; + } + case HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD: { + struct hfi_idr_period *in = pdata, *idr = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + idr->idr_period = in->idr_period; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*idr); + break; + } + case HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR: { + struct hfi_conceal_color *color = prop_data; + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR; + color->conceal_color = *in; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color); + break; + } + case HFI_PROPERTY_CONFIG_VPE_OPERATIONS: { + struct hfi_operations_type *in = pdata, *ops = prop_data; + + switch (in->rotation) { + case HFI_ROTATE_NONE: + case HFI_ROTATE_90: + case HFI_ROTATE_180: + case HFI_ROTATE_270: + break; + default: + ret = -EINVAL; + break; + } + + switch (in->flip) { + case HFI_FLIP_NONE: + case HFI_FLIP_HORIZONTAL: + case HFI_FLIP_VERTICAL: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_CONFIG_VPE_OPERATIONS; + ops->rotation = in->rotation; + ops->flip = in->flip; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ops); + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + struct hfi_intra_refresh *in = pdata, *intra = prop_data; + + switch (in->mode) { + case HFI_INTRA_REFRESH_NONE: + case HFI_INTRA_REFRESH_ADAPTIVE: + case HFI_INTRA_REFRESH_CYCLIC: + case HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE: + case HFI_INTRA_REFRESH_RANDOM: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + intra->mode = in->mode; + intra->air_mbs = in->air_mbs; + intra->air_ref = in->air_ref; + intra->cir_mbs = in->cir_mbs; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra); + break; + } + case HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL: { + struct hfi_multi_slice_control *in = pdata, *multi = prop_data; + + switch (in->multi_slice) { + case HFI_MULTI_SLICE_OFF: + case HFI_MULTI_SLICE_GOB: + case HFI_MULTI_SLICE_BY_MB_COUNT: + case HFI_MULTI_SLICE_BY_BYTE_COUNT: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL; + multi->multi_slice = in->multi_slice; + multi->slice_size = in->slice_size; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + break; + } + case HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO: { + struct hfi_h264_vui_timing_info *in = pdata, *vui = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO; + vui->enable = in->enable; + vui->fixed_framerate = in->fixed_framerate; + vui->time_scale = in->time_scale; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*vui); + break; + } + case HFI_PROPERTY_CONFIG_VPE_DEINTERLACE: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VPE_DEINTERLACE; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: { + struct hfi_buffer_alloc_mode *in = pdata, *mode = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + mode->type = in->type; + mode->mode = in->mode; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*mode); + break; + } + case HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = + HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD: { + struct hfi_scs_threshold *thres = prop_data; + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD; + thres->threshold_value = *in; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*thres); + break; + } + case HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT: { + struct hfi_mvc_buffer_layout_descp_type *in = pdata; + struct hfi_mvc_buffer_layout_descp_type *mvc = prop_data; + + switch (in->layout_type) { + case HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM: + case HFI_MVC_BUFFER_LAYOUT_SEQ: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT; + mvc->layout_type = in->layout_type; + mvc->bright_view_first = in->bright_view_first; + mvc->ngap = in->ngap; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*mvc); + break; + } + case HFI_PROPERTY_PARAM_VENC_LTRMODE: { + struct hfi_ltr_mode *in = pdata, *ltr = prop_data; + + switch (in->ltr_mode) { + case HFI_LTR_MODE_DISABLE: + case HFI_LTR_MODE_MANUAL: + case HFI_LTR_MODE_PERIODIC: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_LTRMODE; + ltr->ltr_mode = in->ltr_mode; + ltr->ltr_count = in->ltr_count; + ltr->trust_mode = in->trust_mode; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr); + break; + } + case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { + struct hfi_ltr_use *in = pdata, *ltr_use = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME; + ltr_use->frames = in->frames; + ltr_use->ref_ltr = in->ref_ltr; + ltr_use->use_constrnt = in->use_constrnt; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { + struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME; + ltr_mark->mark_frame = in->mark_frame; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); + break; + } + case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_INITIAL_QP: { + struct hfi_initial_quantization *in = pdata, *quant = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INITIAL_QP; + quant->init_qp_enable = in->init_qp_enable; + quant->qp_i = in->qp_i; + quant->qp_p = in->qp_p; + quant->qp_b = in->qp_b; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*quant); + break; + } + case HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION: { + struct hfi_vpe_color_space_conversion *in = pdata; + struct hfi_vpe_color_space_conversion *csc = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION; + memcpy(csc->csc_matrix, in->csc_matrix, + sizeof(csc->csc_matrix)); + memcpy(csc->csc_bias, in->csc_bias, sizeof(csc->csc_bias)); + memcpy(csc->csc_limit, in->csc_limit, sizeof(csc->csc_limit)); + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*csc); + break; + } + case HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = + HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_CONFIG_VENC_PERF_MODE: { + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_PERF_MODE; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER: { + u32 *in = pdata; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER; + pkt->data[1] = *in; + pkt->shdr.hdr.size += sizeof(u32) * 2; + break; + } + case HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2: { + struct hfi_enable *in = pdata, *en = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2; + en->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en); + break; + } + case HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE: { + struct hfi_hybrid_hierp *in = pdata, *hierp = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE; + hierp->layers = in->layers; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hierp); + break; + } + + /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + case HFI_PROPERTY_CONFIG_PRIORITY: + case HFI_PROPERTY_CONFIG_BATCH_INFO: + case HFI_PROPERTY_SYS_IDLE_INDICATOR: + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: + case HFI_PROPERTY_PARAM_CHROMA_SITE: + case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + case HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT: + case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: + case HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT: + case HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION: + case HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB: + case HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING: + case HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO: + default: + return -EINVAL; + } + + return ret; +} + +static int +pkt_session_get_property_3xx(struct hfi_session_get_property_pkt *pkt, + void *cookie, u32 ptype) +{ + int ret = 0; + + if (!pkt || !cookie) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(struct hfi_session_get_property_pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + + switch (ptype) { + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; + break; + default: + ret = pkt_session_get_property_1x(pkt, cookie, ptype); + break; + } + + return ret; +} + +static int +pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data = &pkt->data[1]; + int ret = 0; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + + /* + * Any session set property which is different in 3XX packetization + * should be added as a new case below. All unchanged session set + * properties will be handled in the default case. + */ + switch (ptype) { + case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: { + struct hfi_multi_stream *in = pdata; + struct hfi_multi_stream_3x *multi = prop_data; + + pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + multi->buffer_type = in->buffer_type; + multi->enable = in->enable; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + struct hfi_intra_refresh *in = pdata; + struct hfi_intra_refresh_3x *intra = prop_data; + + switch (in->mode) { + case HFI_INTRA_REFRESH_NONE: + case HFI_INTRA_REFRESH_ADAPTIVE: + case HFI_INTRA_REFRESH_CYCLIC: + case HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE: + case HFI_INTRA_REFRESH_RANDOM: + break; + default: + ret = -EINVAL; + break; + } + + pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + intra->mode = in->mode; + intra->mbs = in->cir_mbs; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra); + break; + } + case HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER: + /* for 3xx fw version session_continue is used */ + break; + default: + ret = pkt_session_set_property_1x(pkt, cookie, ptype, pdata); + break; + } + + return ret; +} + +int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, + void *cookie, u32 ptype) +{ + if (hfi_ver == HFI_VERSION_1XX) + return pkt_session_get_property_1x(pkt, cookie, ptype); + + return pkt_session_get_property_3xx(pkt, cookie, ptype); +} + +int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + if (hfi_ver == HFI_VERSION_1XX) + return pkt_session_set_property_1x(pkt, cookie, ptype, pdata); + + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); +} + +void pkt_set_version(enum hfi_version version) +{ + hfi_ver = version; +} diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h new file mode 100644 index 000000000000..f7617cf59914 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_HFI_CMDS_H__ +#define __VENUS_HFI_CMDS_H__ + +#include "hfi.h" + +/* commands */ +#define HFI_CMD_SYS_INIT 0x10001 +#define HFI_CMD_SYS_PC_PREP 0x10002 +#define HFI_CMD_SYS_SET_RESOURCE 0x10003 +#define HFI_CMD_SYS_RELEASE_RESOURCE 0x10004 +#define HFI_CMD_SYS_SET_PROPERTY 0x10005 +#define HFI_CMD_SYS_GET_PROPERTY 0x10006 +#define HFI_CMD_SYS_SESSION_INIT 0x10007 +#define HFI_CMD_SYS_SESSION_END 0x10008 +#define HFI_CMD_SYS_SET_BUFFERS 0x10009 +#define HFI_CMD_SYS_TEST_SSR 0x10101 + +#define HFI_CMD_SESSION_SET_PROPERTY 0x11001 +#define HFI_CMD_SESSION_SET_BUFFERS 0x11002 +#define HFI_CMD_SESSION_GET_SEQUENCE_HEADER 0x11003 + +#define HFI_CMD_SYS_SESSION_ABORT 0x210001 +#define HFI_CMD_SYS_PING 0x210002 + +#define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001 +#define HFI_CMD_SESSION_START 0x211002 +#define HFI_CMD_SESSION_STOP 0x211003 +#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004 +#define HFI_CMD_SESSION_FILL_BUFFER 0x211005 +#define HFI_CMD_SESSION_SUSPEND 0x211006 +#define HFI_CMD_SESSION_RESUME 0x211007 +#define HFI_CMD_SESSION_FLUSH 0x211008 +#define HFI_CMD_SESSION_GET_PROPERTY 0x211009 +#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER 0x21100a +#define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b +#define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c +#define HFI_CMD_SESSION_CONTINUE 0x21100d +#define HFI_CMD_SESSION_SYNC 0x21100e + +/* command packets */ +struct hfi_sys_init_pkt { + struct hfi_pkt_hdr hdr; + u32 arch_type; +}; + +struct hfi_sys_pc_prep_pkt { + struct hfi_pkt_hdr hdr; +}; + +struct hfi_sys_set_resource_pkt { + struct hfi_pkt_hdr hdr; + u32 resource_handle; + u32 resource_type; + u32 resource_data[1]; +}; + +struct hfi_sys_release_resource_pkt { + struct hfi_pkt_hdr hdr; + u32 resource_type; + u32 resource_handle; +}; + +struct hfi_sys_set_property_pkt { + struct hfi_pkt_hdr hdr; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_sys_get_property_pkt { + struct hfi_pkt_hdr hdr; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_sys_set_buffers_pkt { + struct hfi_pkt_hdr hdr; + u32 buffer_type; + u32 buffer_size; + u32 num_buffers; + u32 buffer_addr[1]; +}; + +struct hfi_sys_ping_pkt { + struct hfi_pkt_hdr hdr; + u32 client_data; +}; + +struct hfi_session_init_pkt { + struct hfi_session_hdr_pkt shdr; + u32 session_domain; + u32 session_codec; +}; + +struct hfi_session_end_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_abort_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_set_property_pkt { + struct hfi_session_hdr_pkt shdr; + u32 num_properties; + u32 data[0]; +}; + +struct hfi_session_set_buffers_pkt { + struct hfi_session_hdr_pkt shdr; + u32 buffer_type; + u32 buffer_size; + u32 extradata_size; + u32 min_buffer_size; + u32 num_buffers; + u32 buffer_info[1]; +}; + +struct hfi_session_get_sequence_header_pkt { + struct hfi_session_hdr_pkt shdr; + u32 buffer_len; + u32 packet_buffer; +}; + +struct hfi_session_load_resources_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_start_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_stop_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_empty_buffer_compressed_pkt { + struct hfi_session_hdr_pkt shdr; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[1]; +}; + +struct hfi_session_empty_buffer_uncompressed_plane0_pkt { + struct hfi_session_hdr_pkt shdr; + u32 view_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 input_tag; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[1]; +}; + +struct hfi_session_empty_buffer_uncompressed_plane1_pkt { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 data[1]; +}; + +struct hfi_session_empty_buffer_uncompressed_plane2_pkt { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 data[1]; +}; + +struct hfi_session_fill_buffer_pkt { + struct hfi_session_hdr_pkt shdr; + u32 stream_id; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 output_tag; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[1]; +}; + +struct hfi_session_flush_pkt { + struct hfi_session_hdr_pkt shdr; + u32 flush_type; +}; + +struct hfi_session_suspend_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_resume_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_get_property_pkt { + struct hfi_session_hdr_pkt shdr; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_session_release_buffer_pkt { + struct hfi_session_hdr_pkt shdr; + u32 buffer_type; + u32 buffer_size; + u32 extradata_size; + u32 response_req; + u32 num_buffers; + u32 buffer_info[1]; +}; + +struct hfi_session_release_resources_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +struct hfi_session_parse_sequence_header_pkt { + struct hfi_session_hdr_pkt shdr; + u32 header_len; + u32 packet_buffer; +}; + +struct hfi_sfr { + u32 buf_size; + u8 data[1]; +}; + +struct hfi_sys_test_ssr_pkt { + struct hfi_pkt_hdr hdr; + u32 trigger_type; +}; + +void pkt_set_version(enum hfi_version version); + +void pkt_sys_init(struct hfi_sys_init_pkt *pkt, u32 arch_type); +void pkt_sys_pc_prep(struct hfi_sys_pc_prep_pkt *pkt); +void pkt_sys_idle_indicator(struct hfi_sys_set_property_pkt *pkt, u32 enable); +void pkt_sys_power_control(struct hfi_sys_set_property_pkt *pkt, u32 enable); +int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size, + u32 addr, void *cookie); +int pkt_sys_unset_resource(struct hfi_sys_release_resource_pkt *pkt, u32 id, + u32 size, void *cookie); +void pkt_sys_debug_config(struct hfi_sys_set_property_pkt *pkt, u32 mode, + u32 config); +void pkt_sys_coverage_config(struct hfi_sys_set_property_pkt *pkt, u32 mode); +void pkt_sys_ping(struct hfi_sys_ping_pkt *pkt, u32 cookie); +void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt); +int pkt_sys_ssr_cmd(struct hfi_sys_test_ssr_pkt *pkt, u32 trigger_type); +int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie, + u32 session_type, u32 codec); +void pkt_session_cmd(struct hfi_session_pkt *pkt, u32 pkt_type, void *cookie); +int pkt_session_set_buffers(struct hfi_session_set_buffers_pkt *pkt, + void *cookie, struct hfi_buffer_desc *bd); +int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt, + void *cookie, struct hfi_buffer_desc *bd); +int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt, + void *cookie, struct hfi_frame_data *input_frame); +int pkt_session_etb_encoder( + struct hfi_session_empty_buffer_uncompressed_plane0_pkt *pkt, + void *cookie, struct hfi_frame_data *input_frame); +int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, + void *cookie, struct hfi_frame_data *output_frame); +int pkt_session_parse_seq_header( + struct hfi_session_parse_sequence_header_pkt *pkt, + void *cookie, u32 seq_hdr, u32 seq_hdr_len); +int pkt_session_get_seq_hdr(struct hfi_session_get_sequence_header_pkt *pkt, + void *cookie, u32 seq_hdr, u32 seq_hdr_len); +int pkt_session_flush(struct hfi_session_flush_pkt *pkt, void *cookie, + u32 flush_mode); +int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, + void *cookie, u32 ptype); +int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata); + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h new file mode 100644 index 000000000000..8d282dba9e57 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -0,0 +1,1050 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_HFI_HELPER_H__ +#define __VENUS_HFI_HELPER_H__ + +#define HFI_DOMAIN_BASE_COMMON 0 + +#define HFI_DOMAIN_BASE_VDEC 0x1000000 +#define HFI_DOMAIN_BASE_VENC 0x2000000 +#define HFI_DOMAIN_BASE_VPE 0x3000000 + +#define HFI_VIDEO_ARCH_OX 0x1 + +#define HFI_ARCH_COMMON_OFFSET 0 +#define HFI_ARCH_OX_OFFSET 0x200000 + +#define HFI_OX_BASE 0x1000000 + +#define HFI_CMD_START_OFFSET 0x10000 +#define HFI_MSG_START_OFFSET 0x20000 + +#define HFI_ERR_NONE 0x0 +#define HFI_ERR_SYS_FATAL 0x1 +#define HFI_ERR_SYS_INVALID_PARAMETER 0x2 +#define HFI_ERR_SYS_VERSION_MISMATCH 0x3 +#define HFI_ERR_SYS_INSUFFICIENT_RESOURCES 0x4 +#define HFI_ERR_SYS_MAX_SESSIONS_REACHED 0x5 +#define HFI_ERR_SYS_UNSUPPORTED_CODEC 0x6 +#define HFI_ERR_SYS_SESSION_IN_USE 0x7 +#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE 0x8 +#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN 0x9 + +#define HFI_ERR_SESSION_FATAL 0x1001 +#define HFI_ERR_SESSION_INVALID_PARAMETER 0x1002 +#define HFI_ERR_SESSION_BAD_POINTER 0x1003 +#define HFI_ERR_SESSION_INVALID_SESSION_ID 0x1004 +#define HFI_ERR_SESSION_INVALID_STREAM_ID 0x1005 +#define HFI_ERR_SESSION_INCORRECT_STATE_OPERATION 0x1006 +#define HFI_ERR_SESSION_UNSUPPORTED_PROPERTY 0x1007 +#define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008 +#define HFI_ERR_SESSION_INSUFFICIENT_RESOURCES 0x1009 +#define HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED 0x100a +#define HFI_ERR_SESSION_STREAM_CORRUPT 0x100b +#define HFI_ERR_SESSION_ENC_OVERFLOW 0x100c +#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d +#define HFI_ERR_SESSION_CMDSIZE 0x100e +#define HFI_ERR_SESSION_UNSUPPORT_CMD 0x100f +#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010 +#define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL 0x1011 +#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012 +#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013 + +#define HFI_EVENT_SYS_ERROR 0x1 +#define HFI_EVENT_SESSION_ERROR 0x2 + +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES 0x1000001 +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002 +#define HFI_EVENT_SESSION_SEQUENCE_CHANGED 0x1000003 +#define HFI_EVENT_SESSION_PROPERTY_CHANGED 0x1000004 +#define HFI_EVENT_SESSION_LTRUSE_FAILED 0x1000005 +#define HFI_EVENT_RELEASE_BUFFER_REFERENCE 0x1000006 + +#define HFI_BUFFERFLAG_EOS 0x00000001 +#define HFI_BUFFERFLAG_STARTTIME 0x00000002 +#define HFI_BUFFERFLAG_DECODEONLY 0x00000004 +#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020 +#define HFI_BUFFERFLAG_EXTRADATA 0x00000040 +#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080 +#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100 +#define HFI_BUFFERFLAG_READONLY 0x00000200 +#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 +#define HFI_BUFFERFLAG_EOSEQ 0x00200000 +#define HFI_BUFFERFLAG_MBAFF 0x08000000 +#define HFI_BUFFERFLAG_VPE_YUV_601_709_CSC_CLAMP 0x10000000 +#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000 +#define HFI_BUFFERFLAG_TEI 0x40000000 +#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000 + +#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING 0x1001001 +#define HFI_ERR_SESSION_SAME_STATE_OPERATION 0x1001002 +#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED 0x1001003 +#define HFI_ERR_SESSION_START_CODE_NOT_FOUND 0x1001004 + +#define HFI_FLUSH_INPUT 0x1000001 +#define HFI_FLUSH_OUTPUT 0x1000002 +#define HFI_FLUSH_OUTPUT2 0x1000003 +#define HFI_FLUSH_ALL 0x1000004 + +#define HFI_EXTRADATA_NONE 0x00000000 +#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001 +#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002 +#define HFI_EXTRADATA_VC1_FRAMEDISP 0x00000003 +#define HFI_EXTRADATA_VC1_SEQDISP 0x00000004 +#define HFI_EXTRADATA_TIMESTAMP 0x00000005 +#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006 +#define HFI_EXTRADATA_FRAME_RATE 0x00000007 +#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008 +#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 +#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000d +#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000e +#define HFI_EXTRADATA_FRAME_QP 0x0000000f +#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010 +#define HFI_EXTRADATA_MULTISLICE_INFO 0x7f100000 +#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7f100001 +#define HFI_EXTRADATA_INDEX 0x7f100002 +#define HFI_EXTRADATA_METADATA_LTR 0x7f100004 +#define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002 + +#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e +#define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010 +#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003 + +#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01 +#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02 +#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04 +#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08 +#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10 + +/* + * HFI_PROPERTY_PARAM_OX_START + * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000 + */ +#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL 0x201001 +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO 0x201002 +#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED 0x201003 +#define HFI_PROPERTY_PARAM_CHROMA_SITE 0x201004 +#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG 0x201005 +#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA 0x201006 +#define HFI_PROPERTY_PARAM_DIVX_FORMAT 0x201007 +#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE 0x201008 +#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA 0x201009 +#define HFI_PROPERTY_PARAM_ERR_DETECTION_CODE_EXTRADATA 0x20100a +#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED 0x20100b +#define HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL 0x20100c +#define HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL 0x20100d + +/* + * HFI_PROPERTY_CONFIG_OX_START + * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x2000 + */ +#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001 +#define HFI_PROPERTY_CONFIG_REALTIME 0x202002 +#define HFI_PROPERTY_CONFIG_PRIORITY 0x202003 +#define HFI_PROPERTY_CONFIG_BATCH_INFO 0x202004 + +/* + * HFI_PROPERTY_PARAM_VDEC_OX_START \ + * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000 + */ +#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER 0x1203001 +#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT 0x1203002 +#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT 0x1203003 +#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE 0x1203004 +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER 0x1203005 +#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION 0x1203006 +#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB 0x1203007 +#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING 0x1203008 +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO 0x1203009 +#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA 0x120300a +#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA 0x120300b +#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA 0x120300c +#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE 0x120300d +#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY 0x120300e +#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA 0x1203011 +#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA 0x1203012 +#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA 0x1203013 +#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA 0x1203014 +#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT 0x1203015 +#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA 0x1203016 +#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA 0x1203017 +#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA 0x1203018 +#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA 0x1203019 +#define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD 0x120301a + +/* + * HFI_PROPERTY_CONFIG_VDEC_OX_START + * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000 + */ +#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001 +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING 0x1200002 +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP 0x1200003 + +#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004 + +/* + * HFI_PROPERTY_PARAM_VENC_OX_START + * HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000 + */ +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO 0x2205001 +#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL 0x2205002 +#define HFI_PROPERTY_PARAM_VENC_LTR_INFO 0x2205003 +#define HFI_PROPERTY_PARAM_VENC_MBI_DUMPING 0x2205005 + +/* + * HFI_PROPERTY_CONFIG_VENC_OX_START + * HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000 + */ +#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP 0x2206001 + +/* + * HFI_PROPERTY_PARAM_VPE_OX_START + * HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000 + */ +#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION 0x3207001 + +#define HFI_PROPERTY_CONFIG_VPE_OX_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000) + +#define HFI_CHROMA_SITE_0 0x1000001 +#define HFI_CHROMA_SITE_1 0x1000002 +#define HFI_CHROMA_SITE_2 0x1000003 +#define HFI_CHROMA_SITE_3 0x1000004 +#define HFI_CHROMA_SITE_4 0x1000005 +#define HFI_CHROMA_SITE_5 0x1000006 + +#define HFI_PRIORITY_LOW 10 +#define HFI_PRIOIRTY_MEDIUM 20 +#define HFI_PRIORITY_HIGH 30 + +#define HFI_OUTPUT_ORDER_DISPLAY 0x1000001 +#define HFI_OUTPUT_ORDER_DECODE 0x1000002 + +#define HFI_RATE_CONTROL_OFF 0x1000001 +#define HFI_RATE_CONTROL_VBR_VFR 0x1000002 +#define HFI_RATE_CONTROL_VBR_CFR 0x1000003 +#define HFI_RATE_CONTROL_CBR_VFR 0x1000004 +#define HFI_RATE_CONTROL_CBR_CFR 0x1000005 + +#define HFI_VIDEO_CODEC_H264 0x00000002 +#define HFI_VIDEO_CODEC_H263 0x00000004 +#define HFI_VIDEO_CODEC_MPEG1 0x00000008 +#define HFI_VIDEO_CODEC_MPEG2 0x00000010 +#define HFI_VIDEO_CODEC_MPEG4 0x00000020 +#define HFI_VIDEO_CODEC_DIVX_311 0x00000040 +#define HFI_VIDEO_CODEC_DIVX 0x00000080 +#define HFI_VIDEO_CODEC_VC1 0x00000100 +#define HFI_VIDEO_CODEC_SPARK 0x00000200 +#define HFI_VIDEO_CODEC_VP8 0x00001000 +#define HFI_VIDEO_CODEC_HEVC 0x00002000 +#define HFI_VIDEO_CODEC_VP9 0x00004000 +#define HFI_VIDEO_CODEC_HEVC_HYBRID 0x80000000 + +#define HFI_H264_PROFILE_BASELINE 0x00000001 +#define HFI_H264_PROFILE_MAIN 0x00000002 +#define HFI_H264_PROFILE_HIGH 0x00000004 +#define HFI_H264_PROFILE_STEREO_HIGH 0x00000008 +#define HFI_H264_PROFILE_MULTIVIEW_HIGH 0x00000010 +#define HFI_H264_PROFILE_CONSTRAINED_BASE 0x00000020 +#define HFI_H264_PROFILE_CONSTRAINED_HIGH 0x00000040 + +#define HFI_H264_LEVEL_1 0x00000001 +#define HFI_H264_LEVEL_1b 0x00000002 +#define HFI_H264_LEVEL_11 0x00000004 +#define HFI_H264_LEVEL_12 0x00000008 +#define HFI_H264_LEVEL_13 0x00000010 +#define HFI_H264_LEVEL_2 0x00000020 +#define HFI_H264_LEVEL_21 0x00000040 +#define HFI_H264_LEVEL_22 0x00000080 +#define HFI_H264_LEVEL_3 0x00000100 +#define HFI_H264_LEVEL_31 0x00000200 +#define HFI_H264_LEVEL_32 0x00000400 +#define HFI_H264_LEVEL_4 0x00000800 +#define HFI_H264_LEVEL_41 0x00001000 +#define HFI_H264_LEVEL_42 0x00002000 +#define HFI_H264_LEVEL_5 0x00004000 +#define HFI_H264_LEVEL_51 0x00008000 +#define HFI_H264_LEVEL_52 0x00010000 + +#define HFI_H263_PROFILE_BASELINE 0x00000001 + +#define HFI_H263_LEVEL_10 0x00000001 +#define HFI_H263_LEVEL_20 0x00000002 +#define HFI_H263_LEVEL_30 0x00000004 +#define HFI_H263_LEVEL_40 0x00000008 +#define HFI_H263_LEVEL_45 0x00000010 +#define HFI_H263_LEVEL_50 0x00000020 +#define HFI_H263_LEVEL_60 0x00000040 +#define HFI_H263_LEVEL_70 0x00000080 + +#define HFI_MPEG2_PROFILE_SIMPLE 0x00000001 +#define HFI_MPEG2_PROFILE_MAIN 0x00000002 +#define HFI_MPEG2_PROFILE_422 0x00000004 +#define HFI_MPEG2_PROFILE_SNR 0x00000008 +#define HFI_MPEG2_PROFILE_SPATIAL 0x00000010 +#define HFI_MPEG2_PROFILE_HIGH 0x00000020 + +#define HFI_MPEG2_LEVEL_LL 0x00000001 +#define HFI_MPEG2_LEVEL_ML 0x00000002 +#define HFI_MPEG2_LEVEL_H14 0x00000004 +#define HFI_MPEG2_LEVEL_HL 0x00000008 + +#define HFI_MPEG4_PROFILE_SIMPLE 0x00000001 +#define HFI_MPEG4_PROFILE_ADVANCEDSIMPLE 0x00000002 + +#define HFI_MPEG4_LEVEL_0 0x00000001 +#define HFI_MPEG4_LEVEL_0b 0x00000002 +#define HFI_MPEG4_LEVEL_1 0x00000004 +#define HFI_MPEG4_LEVEL_2 0x00000008 +#define HFI_MPEG4_LEVEL_3 0x00000010 +#define HFI_MPEG4_LEVEL_4 0x00000020 +#define HFI_MPEG4_LEVEL_4a 0x00000040 +#define HFI_MPEG4_LEVEL_5 0x00000080 +#define HFI_MPEG4_LEVEL_6 0x00000100 +#define HFI_MPEG4_LEVEL_7 0x00000200 +#define HFI_MPEG4_LEVEL_8 0x00000400 +#define HFI_MPEG4_LEVEL_9 0x00000800 +#define HFI_MPEG4_LEVEL_3b 0x00001000 + +#define HFI_VC1_PROFILE_SIMPLE 0x00000001 +#define HFI_VC1_PROFILE_MAIN 0x00000002 +#define HFI_VC1_PROFILE_ADVANCED 0x00000004 + +#define HFI_VC1_LEVEL_LOW 0x00000001 +#define HFI_VC1_LEVEL_MEDIUM 0x00000002 +#define HFI_VC1_LEVEL_HIGH 0x00000004 +#define HFI_VC1_LEVEL_0 0x00000008 +#define HFI_VC1_LEVEL_1 0x00000010 +#define HFI_VC1_LEVEL_2 0x00000020 +#define HFI_VC1_LEVEL_3 0x00000040 +#define HFI_VC1_LEVEL_4 0x00000080 + +#define HFI_VPX_PROFILE_SIMPLE 0x00000001 +#define HFI_VPX_PROFILE_ADVANCED 0x00000002 +#define HFI_VPX_PROFILE_VERSION_0 0x00000004 +#define HFI_VPX_PROFILE_VERSION_1 0x00000008 +#define HFI_VPX_PROFILE_VERSION_2 0x00000010 +#define HFI_VPX_PROFILE_VERSION_3 0x00000020 + +#define HFI_DIVX_FORMAT_4 0x1 +#define HFI_DIVX_FORMAT_5 0x2 +#define HFI_DIVX_FORMAT_6 0x3 + +#define HFI_DIVX_PROFILE_QMOBILE 0x00000001 +#define HFI_DIVX_PROFILE_MOBILE 0x00000002 +#define HFI_DIVX_PROFILE_MT 0x00000004 +#define HFI_DIVX_PROFILE_HT 0x00000008 +#define HFI_DIVX_PROFILE_HD 0x00000010 + +#define HFI_HEVC_PROFILE_MAIN 0x00000001 +#define HFI_HEVC_PROFILE_MAIN10 0x00000002 +#define HFI_HEVC_PROFILE_MAIN_STILL_PIC 0x00000004 + +#define HFI_HEVC_LEVEL_1 0x00000001 +#define HFI_HEVC_LEVEL_2 0x00000002 +#define HFI_HEVC_LEVEL_21 0x00000004 +#define HFI_HEVC_LEVEL_3 0x00000008 +#define HFI_HEVC_LEVEL_31 0x00000010 +#define HFI_HEVC_LEVEL_4 0x00000020 +#define HFI_HEVC_LEVEL_41 0x00000040 +#define HFI_HEVC_LEVEL_5 0x00000080 +#define HFI_HEVC_LEVEL_51 0x00000100 +#define HFI_HEVC_LEVEL_52 0x00000200 +#define HFI_HEVC_LEVEL_6 0x00000400 +#define HFI_HEVC_LEVEL_61 0x00000800 +#define HFI_HEVC_LEVEL_62 0x00001000 + +#define HFI_HEVC_TIER_MAIN 0x1 +#define HFI_HEVC_TIER_HIGH0 0x2 + +#define HFI_BUFFER_INPUT 0x1 +#define HFI_BUFFER_OUTPUT 0x2 +#define HFI_BUFFER_OUTPUT2 0x3 +#define HFI_BUFFER_INTERNAL_PERSIST 0x4 +#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5 +#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001 +#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002 +#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003 +#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004 +#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005 +#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006 + +#define HFI_BUFFER_TYPE_MAX 11 + +#define HFI_BUFFER_MODE_STATIC 0x1000001 +#define HFI_BUFFER_MODE_RING 0x1000002 +#define HFI_BUFFER_MODE_DYNAMIC 0x1000003 + +#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1 +#define HFI_VENC_PERFMODE_POWER_SAVE 0x2 + +/* + * HFI_PROPERTY_SYS_COMMON_START + * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000 + */ +#define HFI_PROPERTY_SYS_DEBUG_CONFIG 0x1 +#define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO 0x2 +#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ 0x3 +#define HFI_PROPERTY_SYS_IDLE_INDICATOR 0x4 +#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5 +#define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6 +#define HFI_PROPERTY_SYS_CONFIG_COVERAGE 0x7 + +/* + * HFI_PROPERTY_PARAM_COMMON_START + * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000 + */ +#define HFI_PROPERTY_PARAM_FRAME_SIZE 0x1001 +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO 0x1002 +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT 0x1003 +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED 0x1004 +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT 0x1005 +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED 0x1006 +#define HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED 0x1007 +#define HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED 0x1008 +#define HFI_PROPERTY_PARAM_CODEC_SUPPORTED 0x1009 +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED 0x100a +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT 0x100b +#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT 0x100c +#define HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE 0x100d +#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e +#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f +#define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010 + +/* + * HFI_PROPERTY_CONFIG_COMMON_START + * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000 + */ +#define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001 + +/* + * HFI_PROPERTY_PARAM_VDEC_COMMON_START + * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x3000 + */ +#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001 +#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002 +#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003 + +/* + * HFI_PROPERTY_CONFIG_VDEC_COMMON_START + * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x4000 + */ + +/* + * HFI_PROPERTY_PARAM_VENC_COMMON_START + * HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x5000 + */ +#define HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE 0x2005001 +#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL 0x2005002 +#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003 +#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004 +#define HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE 0x2005005 +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP 0x2005006 +#define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION 0x2005007 +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE 0x2005008 +#define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION 0x2005009 +#define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER 0x200500a +#define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION 0x200500b +#define HFI_PROPERTY_PARAM_VENC_OPEN_GOP 0x200500c +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL 0x200500e +#define HFI_PROPERTY_PARAM_VENC_VBV_HRD_BUF_SIZE 0x200500f +#define HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED 0x2005010 +#define HFI_PROPERTY_PARAM_VENC_ADVANCED 0x2005012 +#define HFI_PROPERTY_PARAM_VENC_H264_SPS_ID 0x2005014 +#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID 0x2005015 +#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL 0x2005016 +#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO 0x2005017 +#define HFI_PROPERTY_PARAM_VENC_NUMREF 0x2005018 +#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P 0x2005019 +#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT 0x200501b +#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c +#define HFI_PROPERTY_PARAM_VENC_VIDEO_FULL_RANGE 0x200501d +#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO 0x200501e +#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG 0x200501f +#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 +#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC 0x2005021 +#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY 0x2005023 +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 +#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP 0x2005027 +#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP 0x2005028 +#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE 0x2005029 +#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER 0x200502c +#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE 0x200502f + +/* + * HFI_PROPERTY_CONFIG_VENC_COMMON_START + * HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000 + */ +#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD 0x2006002 +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 +#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME 0x2006004 +#define HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE 0x2006005 +#define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE 0x2006007 +#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b +#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD 0x200600c +#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE 0x200600e + +/* + * HFI_PROPERTY_PARAM_VPE_COMMON_START + * HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000 + */ + +/* + * HFI_PROPERTY_CONFIG_VPE_COMMON_START + * HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000 + */ +#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE 0x3008001 +#define HFI_PROPERTY_CONFIG_VPE_OPERATIONS 0x3008002 + +enum hfi_version { + HFI_VERSION_1XX, + HFI_VERSION_3XX, +}; + +struct hfi_buffer_info { + u32 buffer_addr; + u32 extradata_addr; +}; + +struct hfi_bitrate { + u32 bitrate; + u32 layer_id; +}; + +#define HFI_CAPABILITY_FRAME_WIDTH 0x01 +#define HFI_CAPABILITY_FRAME_HEIGHT 0x02 +#define HFI_CAPABILITY_MBS_PER_FRAME 0x03 +#define HFI_CAPABILITY_MBS_PER_SECOND 0x04 +#define HFI_CAPABILITY_FRAMERATE 0x05 +#define HFI_CAPABILITY_SCALE_X 0x06 +#define HFI_CAPABILITY_SCALE_Y 0x07 +#define HFI_CAPABILITY_BITRATE 0x08 +#define HFI_CAPABILITY_BFRAME 0x09 +#define HFI_CAPABILITY_PEAKBITRATE 0x0a +#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS 0x10 +#define HFI_CAPABILITY_ENC_LTR_COUNT 0x11 +#define HFI_CAPABILITY_CP_OUTPUT2_THRESH 0x12 +#define HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS 0x13 +#define HFI_CAPABILITY_LCU_SIZE 0x14 +#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 +#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 + +struct hfi_capability { + u32 capability_type; + u32 min; + u32 max; + u32 step_size; +}; + +struct hfi_capabilities { + u32 num_capabilities; + struct hfi_capability data[1]; +}; + +#define HFI_DEBUG_MSG_LOW 0x01 +#define HFI_DEBUG_MSG_MEDIUM 0x02 +#define HFI_DEBUG_MSG_HIGH 0x04 +#define HFI_DEBUG_MSG_ERROR 0x08 +#define HFI_DEBUG_MSG_FATAL 0x10 +#define HFI_DEBUG_MSG_PERF 0x20 + +#define HFI_DEBUG_MODE_QUEUE 0x01 +#define HFI_DEBUG_MODE_QDSS 0x02 + +struct hfi_debug_config { + u32 config; + u32 mode; +}; + +struct hfi_enable { + u32 enable; +}; + +#define HFI_H264_DB_MODE_DISABLE 0x1 +#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY 0x2 +#define HFI_H264_DB_MODE_ALL_BOUNDARY 0x3 + +struct hfi_h264_db_control { + u32 mode; + u32 slice_alpha_offset; + u32 slice_beta_offset; +}; + +#define HFI_H264_ENTROPY_CAVLC 0x1 +#define HFI_H264_ENTROPY_CABAC 0x2 + +#define HFI_H264_CABAC_MODEL_0 0x1 +#define HFI_H264_CABAC_MODEL_1 0x2 +#define HFI_H264_CABAC_MODEL_2 0x3 + +struct hfi_h264_entropy_control { + u32 entropy_mode; + u32 cabac_model; +}; + +struct hfi_framerate { + u32 buffer_type; + u32 framerate; +}; + +#define HFI_INTRA_REFRESH_NONE 0x1 +#define HFI_INTRA_REFRESH_CYCLIC 0x2 +#define HFI_INTRA_REFRESH_ADAPTIVE 0x3 +#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4 +#define HFI_INTRA_REFRESH_RANDOM 0x5 + +struct hfi_intra_refresh { + u32 mode; + u32 air_mbs; + u32 air_ref; + u32 cir_mbs; +}; + +struct hfi_intra_refresh_3x { + u32 mode; + u32 mbs; +}; + +struct hfi_idr_period { + u32 idr_period; +}; + +struct hfi_operations_type { + u32 rotation; + u32 flip; +}; + +struct hfi_max_num_b_frames { + u32 max_num_b_frames; +}; + +struct hfi_vc1e_perf_cfg_type { + u32 search_range_x_subsampled[3]; + u32 search_range_y_subsampled[3]; +}; + +struct hfi_conceal_color { + u32 conceal_color; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + +struct hfi_mpeg4_header_extension { + u32 header_extension; +}; + +struct hfi_mpeg4_time_resolution { + u32 time_increment_resolution; +}; + +struct hfi_multi_stream { + u32 buffer_type; + u32 enable; + u32 width; + u32 height; +}; + +struct hfi_multi_stream_3x { + u32 buffer_type; + u32 enable; +}; + +struct hfi_multi_view_format { + u32 views; + u32 view_order[1]; +}; + +#define HFI_MULTI_SLICE_OFF 0x1 +#define HFI_MULTI_SLICE_BY_MB_COUNT 0x2 +#define HFI_MULTI_SLICE_BY_BYTE_COUNT 0x3 +#define HFI_MULTI_SLICE_GOB 0x4 + +struct hfi_multi_slice_control { + u32 multi_slice; + u32 slice_size; +}; + +#define HFI_NAL_FORMAT_STARTCODES 0x01 +#define HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER 0x02 +#define HFI_NAL_FORMAT_ONE_BYTE_LENGTH 0x04 +#define HFI_NAL_FORMAT_TWO_BYTE_LENGTH 0x08 +#define HFI_NAL_FORMAT_FOUR_BYTE_LENGTH 0x10 + +struct hfi_nal_stream_format { + u32 format; +}; + +struct hfi_nal_stream_format_select { + u32 format; +}; + +#define HFI_PICTURE_TYPE_I 0x01 +#define HFI_PICTURE_TYPE_P 0x02 +#define HFI_PICTURE_TYPE_B 0x04 +#define HFI_PICTURE_TYPE_IDR 0x08 + +struct hfi_profile_level { + u32 profile; + u32 level; +}; + +#define HFI_MAX_PROFILE_COUNT 16 + +struct hfi_profile_level_supported { + u32 profile_count; + struct hfi_profile_level profile_level[1]; +}; + +struct hfi_quality_vs_speed { + u32 quality_vs_speed; +}; + +struct hfi_quantization { + u32 qp_i; + u32 qp_p; + u32 qp_b; + u32 layer_id; +}; + +struct hfi_initial_quantization { + u32 qp_i; + u32 qp_p; + u32 qp_b; + u32 init_qp_enable; +}; + +struct hfi_quantization_range { + u32 min_qp; + u32 max_qp; + u32 layer_id; +}; + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 +#define HFI_LTR_MODE_PERIODIC 0x2 + +struct hfi_ltr_mode { + u32 ltr_mode; + u32 ltr_count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + +struct hfi_framesize { + u32 buffer_type; + u32 width; + u32 height; +}; + +struct hfi_h264_vui_timing_info { + u32 enable; + u32 fixed_framerate; + u32 time_scale; +}; + +#define HFI_COLOR_FORMAT_MONOCHROME 0x01 +#define HFI_COLOR_FORMAT_NV12 0x02 +#define HFI_COLOR_FORMAT_NV21 0x03 +#define HFI_COLOR_FORMAT_NV12_4x4TILE 0x04 +#define HFI_COLOR_FORMAT_NV21_4x4TILE 0x05 +#define HFI_COLOR_FORMAT_YUYV 0x06 +#define HFI_COLOR_FORMAT_YVYU 0x07 +#define HFI_COLOR_FORMAT_UYVY 0x08 +#define HFI_COLOR_FORMAT_VYUY 0x09 +#define HFI_COLOR_FORMAT_RGB565 0x0a +#define HFI_COLOR_FORMAT_BGR565 0x0b +#define HFI_COLOR_FORMAT_RGB888 0x0c +#define HFI_COLOR_FORMAT_BGR888 0x0d +#define HFI_COLOR_FORMAT_YUV444 0x0e +#define HFI_COLOR_FORMAT_RGBA8888 0x10 + +#define HFI_COLOR_FORMAT_UBWC_BASE 0x8000 +#define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000 + +#define HFI_COLOR_FORMAT_YUV420_TP10 0x4002 +#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002 +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002 +#define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010 + +struct hfi_uncompressed_format_select { + u32 buffer_type; + u32 format; +}; + +struct hfi_uncompressed_format_supported { + u32 buffer_type; + u32 format_entries; + u32 format_info[1]; +}; + +struct hfi_uncompressed_plane_actual { + int actual_stride; + u32 actual_plane_buffer_height; +}; + +struct hfi_uncompressed_plane_actual_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_actual plane_format[1]; +}; + +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints plane_format[1]; +}; + +struct hfi_uncompressed_plane_actual_constraints_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_constraints plane_format[1]; +}; + +struct hfi_codec_supported { + u32 dec_codecs; + u32 enc_codecs; +}; + +struct hfi_properties_supported { + u32 num_properties; + u32 properties[1]; +}; + +struct hfi_max_sessions_supported { + u32 max_sessions; +}; + +#define HFI_MAX_MATRIX_COEFFS 9 +#define HFI_MAX_BIAS_COEFFS 3 +#define HFI_MAX_LIMIT_COEFFS 6 + +struct hfi_vpe_color_space_conversion { + u32 csc_matrix[HFI_MAX_MATRIX_COEFFS]; + u32 csc_bias[HFI_MAX_BIAS_COEFFS]; + u32 csc_limit[HFI_MAX_LIMIT_COEFFS]; +}; + +#define HFI_ROTATE_NONE 0x1 +#define HFI_ROTATE_90 0x2 +#define HFI_ROTATE_180 0x3 +#define HFI_ROTATE_270 0x4 + +#define HFI_FLIP_NONE 0x1 +#define HFI_FLIP_HORIZONTAL 0x2 +#define HFI_FLIP_VERTICAL 0x3 + +struct hfi_operations { + u32 rotate; + u32 flip; +}; + +#define HFI_RESOURCE_OCMEM 0x1 + +struct hfi_resource_ocmem { + u32 size; + u32 mem; +}; + +struct hfi_resource_ocmem_requirement { + u32 session_domain; + u32 width; + u32 height; + u32 size; +}; + +struct hfi_resource_ocmem_requirement_info { + u32 num_entries; + struct hfi_resource_ocmem_requirement requirements[1]; +}; + +struct hfi_property_sys_image_version_info_type { + u32 string_size; + u8 str_image_version[1]; +}; + +struct hfi_codec_mask_supported { + u32 codecs; + u32 video_domains; +}; + +struct hfi_seq_header_info { + u32 max_hader_len; +}; + +struct hfi_aspect_ratio { + u32 aspect_width; + u32 aspect_height; +}; + +#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM 0 +#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE 1 +#define HFI_MVC_BUFFER_LAYOUT_SEQ 2 + +struct hfi_mvc_buffer_layout_descp_type { + u32 layout_type; + u32 bright_view_first; + u32 ngap; +}; + +struct hfi_scs_threshold { + u32 threshold_value; +}; + +#define HFI_TEST_SSR_SW_ERR_FATAL 0x1 +#define HFI_TEST_SSR_SW_DIV_BY_ZERO 0x2 +#define HFI_TEST_SSR_HW_WDOG_IRQ 0x3 + +struct hfi_buffer_alloc_mode { + u32 type; + u32 mode; +}; + +struct hfi_index_extradata_config { + u32 enable; + u32 index_extra_data_id; +}; + +struct hfi_extradata_header { + u32 size; + u32 version; + u32 port_index; + u32 type; + u32 data_size; + u8 data[1]; +}; + +struct hfi_batch_info { + u32 input_batch_count; + u32 output_batch_count; +}; + +struct hfi_buffer_count_actual { + u32 type; + u32 count_actual; +}; + +struct hfi_buffer_size_actual { + u32 type; + u32 size; +}; + +struct hfi_buffer_display_hold_count_actual { + u32 type; + u32 hold_count; +}; + +struct hfi_buffer_requirements { + u32 type; + u32 size; + u32 region_size; + u32 hold_count; + u32 count_min; + u32 count_actual; + u32 contiguous; + u32 alignment; +}; + +struct hfi_data_payload { + u32 size; + u8 data[1]; +}; + +struct hfi_enable_picture { + u32 picture_type; +}; + +struct hfi_display_picture_buffer_count { + int enable; + u32 count; +}; + +struct hfi_extra_data_header_config { + u32 type; + u32 buffer_type; + u32 version; + u32 port_index; + u32 client_extra_data_id; +}; + +struct hfi_interlace_format_supported { + u32 buffer_type; + u32 format; +}; + +struct hfi_buffer_alloc_mode_supported { + u32 buffer_type; + u32 num_entries; + u32 data[1]; +}; + +struct hfi_mb_error_map { + u32 error_map_size; + u8 error_map[1]; +}; + +struct hfi_metadata_pass_through { + int enable; + u32 size; +}; + +struct hfi_multi_view_select { + u32 view_index; +}; + +struct hfi_hybrid_hierp { + u32 layers; +}; + +struct hfi_pkt_hdr { + u32 size; + u32 pkt_type; +}; + +struct hfi_session_hdr_pkt { + struct hfi_pkt_hdr hdr; + u32 session_id; +}; + +struct hfi_session_pkt { + struct hfi_session_hdr_pkt shdr; +}; + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c new file mode 100644 index 000000000000..debf80a92797 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#include "core.h" +#include "hfi.h" +#include "hfi_helper.h" +#include "hfi_msgs.h" + +static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, + struct hfi_msg_event_notify_pkt *pkt) +{ + struct hfi_event_data event = {0}; + int num_properties_changed; + struct hfi_framesize *frame_sz; + struct hfi_profile_level *profile_level; + u8 *data_ptr; + u32 ptype; + + inst->error = HFI_ERR_NONE; + + switch (pkt->event_data1) { + case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES: + case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES: + break; + default: + inst->error = HFI_ERR_SESSION_INVALID_PARAMETER; + goto done; + } + + event.event_type = pkt->event_data1; + + num_properties_changed = pkt->event_data2; + if (!num_properties_changed) { + inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; + goto done; + } + + data_ptr = (u8 *)&pkt->ext_event_data[0]; + do { + ptype = *((u32 *)data_ptr); + switch (ptype) { + case HFI_PROPERTY_PARAM_FRAME_SIZE: + data_ptr += sizeof(u32); + frame_sz = (struct hfi_framesize *)data_ptr; + event.width = frame_sz->width; + event.height = frame_sz->height; + data_ptr += sizeof(frame_sz); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + data_ptr += sizeof(u32); + profile_level = (struct hfi_profile_level *)data_ptr; + event.profile = profile_level->profile; + event.level = profile_level->level; + data_ptr += sizeof(profile_level); + break; + default: + break; + } + num_properties_changed--; + } while (num_properties_changed > 0); + +done: + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); +} + +static void event_release_buffer_ref(struct venus_core *core, + struct venus_inst *inst, + struct hfi_msg_event_notify_pkt *pkt) +{ + struct hfi_event_data event = {0}; + struct hfi_msg_event_release_buffer_ref_pkt *data; + + data = (struct hfi_msg_event_release_buffer_ref_pkt *) + pkt->ext_event_data; + + event.event_type = HFI_EVENT_RELEASE_BUFFER_REFERENCE; + event.packet_buffer = data->packet_buffer; + event.extradata_buffer = data->extradata_buffer; + event.tag = data->output_tag; + + inst->error = HFI_ERR_NONE; + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); +} + +static void event_sys_error(struct venus_core *core, u32 event, + struct hfi_msg_event_notify_pkt *pkt) +{ + if (pkt) + dev_dbg(core->dev, + "sys error (session id:%x, data1:%x, data2:%x)\n", + pkt->shdr.session_id, pkt->event_data1, + pkt->event_data2); + + core->core_ops->event_notify(core, event); +} + +static void +event_session_error(struct venus_core *core, struct venus_inst *inst, + struct hfi_msg_event_notify_pkt *pkt) +{ + struct device *dev = core->dev; + + dev_dbg(dev, "session error: event id:%x, session id:%x\n", + pkt->event_data1, pkt->shdr.session_id); + + if (!inst) + return; + + switch (pkt->event_data1) { + /* non fatal session errors */ + case HFI_ERR_SESSION_INVALID_SCALE_FACTOR: + case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE: + case HFI_ERR_SESSION_UNSUPPORTED_SETTING: + case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED: + inst->error = HFI_ERR_NONE; + break; + default: + dev_err(dev, "session error: event id:%x (%x), session id:%x\n", + pkt->event_data1, pkt->event_data2, + pkt->shdr.session_id); + + inst->error = pkt->event_data1; + inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); + break; + } +} + +static void hfi_event_notify(struct venus_core *core, struct venus_inst *inst, + void *packet) +{ + struct hfi_msg_event_notify_pkt *pkt = packet; + + if (!packet) + return; + + switch (pkt->event_id) { + case HFI_EVENT_SYS_ERROR: + event_sys_error(core, EVT_SYS_ERROR, pkt); + break; + case HFI_EVENT_SESSION_ERROR: + event_session_error(core, inst, pkt); + break; + case HFI_EVENT_SESSION_SEQUENCE_CHANGED: + event_seq_changed(core, inst, pkt); + break; + case HFI_EVENT_RELEASE_BUFFER_REFERENCE: + event_release_buffer_ref(core, inst, pkt); + break; + case HFI_EVENT_SESSION_PROPERTY_CHANGED: + break; + default: + break; + } +} + +static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst, + void *packet) +{ + struct hfi_msg_sys_init_done_pkt *pkt = packet; + u32 rem_bytes, read_bytes = 0, num_properties; + u32 error, ptype; + u8 *data; + + error = pkt->error_type; + if (error != HFI_ERR_NONE) + goto err_no_prop; + + num_properties = pkt->num_properties; + + if (!num_properties) { + error = HFI_ERR_SYS_INVALID_PARAMETER; + goto err_no_prop; + } + + rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32); + + if (!rem_bytes) { + /* missing property data */ + error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + goto err_no_prop; + } + + data = (u8 *)&pkt->data[0]; + + if (core->res->hfi_version == HFI_VERSION_3XX) + goto err_no_prop; + + while (num_properties && rem_bytes >= sizeof(u32)) { + ptype = *((u32 *)data); + data += sizeof(u32); + + switch (ptype) { + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: { + struct hfi_codec_supported *prop; + + prop = (struct hfi_codec_supported *)data; + + if (rem_bytes < sizeof(*prop)) { + error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + break; + } + + read_bytes += sizeof(*prop) + sizeof(u32); + core->dec_codecs = prop->dec_codecs; + core->enc_codecs = prop->enc_codecs; + break; + } + case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: { + struct hfi_max_sessions_supported *prop; + + if (rem_bytes < sizeof(*prop)) { + error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + break; + } + + prop = (struct hfi_max_sessions_supported *)data; + read_bytes += sizeof(*prop) + sizeof(u32); + core->max_sessions_supported = prop->max_sessions; + break; + } + default: + error = HFI_ERR_SYS_INVALID_PARAMETER; + break; + } + + if (!error) { + rem_bytes -= read_bytes; + data += read_bytes; + num_properties--; + } + } + +err_no_prop: + core->error = error; + complete(&core->done); +} + +static void +sys_get_prop_image_version(struct device *dev, + struct hfi_msg_sys_property_info_pkt *pkt) +{ + int req_bytes; + + req_bytes = pkt->hdr.size - sizeof(*pkt); + + if (req_bytes < 128 || !pkt->data[1] || pkt->num_properties > 1) + /* bad packet */ + return; + + dev_dbg(dev, "F/W version: %s\n", (u8 *)&pkt->data[1]); +} + +static void hfi_sys_property_info(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_sys_property_info_pkt *pkt = packet; + struct device *dev = core->dev; + + if (!pkt->num_properties) { + dev_dbg(dev, "%s: no properties\n", __func__); + return; + } + + switch (pkt->data[0]) { + case HFI_PROPERTY_SYS_IMAGE_VERSION: + sys_get_prop_image_version(dev, pkt); + break; + default: + dev_dbg(dev, "%s: unknown property data\n", __func__); + break; + } +} + +static void hfi_sys_rel_resource_done(struct venus_core *core, + struct venus_inst *inst, + void *packet) +{ + struct hfi_msg_sys_release_resource_done_pkt *pkt = packet; + + core->error = pkt->error_type; + complete(&core->done); +} + +static void hfi_sys_ping_done(struct venus_core *core, struct venus_inst *inst, + void *packet) +{ + struct hfi_msg_sys_ping_ack_pkt *pkt = packet; + + core->error = HFI_ERR_NONE; + + if (pkt->client_data != 0xbeef) + core->error = HFI_ERR_SYS_FATAL; + + complete(&core->done); +} + +static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst, + void *packet) +{ + dev_dbg(core->dev, "sys idle\n"); +} + +static void hfi_sys_pc_prepare_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet; + + dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type); +} + +static void +hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst) +{ + if (!in || !inst) + return; + + switch (in->capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + inst->cap_width = *in; + break; + case HFI_CAPABILITY_FRAME_HEIGHT: + inst->cap_height = *in; + break; + case HFI_CAPABILITY_MBS_PER_FRAME: + inst->cap_mbs_per_frame = *in; + break; + case HFI_CAPABILITY_MBS_PER_SECOND: + inst->cap_mbs_per_sec = *in; + break; + case HFI_CAPABILITY_FRAMERATE: + inst->cap_framerate = *in; + break; + case HFI_CAPABILITY_SCALE_X: + inst->cap_scale_x = *in; + break; + case HFI_CAPABILITY_SCALE_Y: + inst->cap_scale_y = *in; + break; + case HFI_CAPABILITY_BITRATE: + inst->cap_bitrate = *in; + break; + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + inst->cap_hier_p = *in; + break; + case HFI_CAPABILITY_ENC_LTR_COUNT: + inst->cap_ltr_count = *in; + break; + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + inst->cap_secure_output2_threshold = *in; + break; + default: + break; + } +} + +static unsigned int +session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt, + struct hfi_profile_level *profile_level) +{ + struct hfi_profile_level *hfi; + u32 req_bytes; + + req_bytes = pkt->shdr.hdr.size - sizeof(*pkt); + + if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level)) + /* bad packet */ + return HFI_ERR_SESSION_INVALID_PARAMETER; + + hfi = (struct hfi_profile_level *)&pkt->data[1]; + profile_level->profile = hfi->profile; + profile_level->level = hfi->level; + + return HFI_ERR_NONE; +} + +static unsigned int +session_get_prop_buf_req(struct hfi_msg_session_property_info_pkt *pkt, + struct hfi_buffer_requirements *bufreq) +{ + struct hfi_buffer_requirements *buf_req; + u32 req_bytes; + unsigned int idx = 0; + + req_bytes = pkt->shdr.hdr.size - sizeof(*pkt); + + if (!req_bytes || req_bytes % sizeof(*buf_req) || !pkt->data[1]) + /* bad packet */ + return HFI_ERR_SESSION_INVALID_PARAMETER; + + buf_req = (struct hfi_buffer_requirements *)&pkt->data[1]; + if (!buf_req) + return HFI_ERR_SESSION_INVALID_PARAMETER; + + while (req_bytes) { + memcpy(&bufreq[idx], buf_req, sizeof(*bufreq)); + idx++; + + if (idx > HFI_BUFFER_TYPE_MAX) + return HFI_ERR_SESSION_INVALID_PARAMETER; + + req_bytes -= sizeof(struct hfi_buffer_requirements); + buf_req++; + } + + return HFI_ERR_NONE; +} + +static void hfi_session_prop_info(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_property_info_pkt *pkt = packet; + struct device *dev = core->dev; + union hfi_get_property *hprop = &inst->hprop; + unsigned int error = HFI_ERR_NONE; + + if (!pkt->num_properties) { + error = HFI_ERR_SESSION_INVALID_PARAMETER; + dev_err(dev, "%s: no properties\n", __func__); + goto done; + } + + switch (pkt->data[0]) { + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + memset(hprop->bufreq, 0, sizeof(hprop->bufreq)); + error = session_get_prop_buf_req(pkt, hprop->bufreq); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + memset(&hprop->profile_level, 0, sizeof(hprop->profile_level)); + error = session_get_prop_profile_level(pkt, + &hprop->profile_level); + break; + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + break; + default: + dev_dbg(dev, "%s: unknown property id:%x\n", __func__, + pkt->data[0]); + return; + } + +done: + inst->error = error; + complete(&inst->done); +} + +static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst, + struct hfi_msg_session_init_done_pkt *pkt) +{ + struct device *dev = core->dev; + u32 rem_bytes, num_props, codecs = 0, domain = 0; + u32 ptype, next_offset = 0; + u32 err; + u8 *data; + + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); + if (!rem_bytes) { + dev_err(dev, "%s: missing property info\n", __func__); + return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; + } + + err = pkt->error_type; + if (err) + return err; + + data = (u8 *)&pkt->data[0]; + num_props = pkt->num_properties; + + while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) { + ptype = *((u32 *)data); + next_offset = sizeof(u32); + + switch (ptype) { + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: { + struct hfi_codec_mask_supported *masks = + (struct hfi_codec_mask_supported *) + (data + next_offset); + + codecs = masks->codecs; + domain = masks->video_domains; + next_offset += sizeof(*masks); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: { + struct hfi_capabilities *caps; + struct hfi_capability *cap; + u32 num_caps; + + if ((rem_bytes - next_offset) < sizeof(*cap)) { + err = HFI_ERR_SESSION_INVALID_PARAMETER; + break; + } + + caps = (struct hfi_capabilities *)(data + next_offset); + + num_caps = caps->num_capabilities; + cap = &caps->data[0]; + next_offset += sizeof(u32); + + while (num_caps && + (rem_bytes - next_offset) >= sizeof(u32)) { + hfi_copy_cap_prop(cap, inst); + cap++; + next_offset += sizeof(*cap); + num_caps--; + } + num_props--; + break; + } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: { + struct hfi_uncompressed_format_supported *prop = + (struct hfi_uncompressed_format_supported *) + (data + next_offset); + u32 num_fmt_entries; + u8 *fmt; + struct hfi_uncompressed_plane_info *inf; + + if ((rem_bytes - next_offset) < sizeof(*prop)) { + err = HFI_ERR_SESSION_INVALID_PARAMETER; + break; + } + + num_fmt_entries = prop->format_entries; + next_offset = sizeof(*prop) - sizeof(u32); + fmt = (u8 *)&prop->format_info[0]; + + dev_dbg(dev, "uncomm format support num entries:%u\n", + num_fmt_entries); + + while (num_fmt_entries) { + struct hfi_uncompressed_plane_constraints *cnts; + u32 bytes_to_skip; + + inf = (struct hfi_uncompressed_plane_info *)fmt; + + if ((rem_bytes - next_offset) < sizeof(*inf)) { + err = HFI_ERR_SESSION_INVALID_PARAMETER; + break; + } + + dev_dbg(dev, "plane info: fmt:%x, planes:%x\n", + inf->format, inf->num_planes); + + cnts = &inf->plane_format[0]; + dev_dbg(dev, "%u %u %u %u\n", + cnts->stride_multiples, + cnts->max_stride, + cnts->min_plane_buffer_height_multiple, + cnts->buffer_alignment); + + bytes_to_skip = sizeof(*inf) - sizeof(*cnts) + + inf->num_planes * sizeof(*cnts); + + fmt += bytes_to_skip; + next_offset += bytes_to_skip; + num_fmt_entries--; + } + num_props--; + break; + } + case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: { + struct hfi_properties_supported *prop = + (struct hfi_properties_supported *) + (data + next_offset); + + next_offset += sizeof(*prop) - sizeof(u32) + + prop->num_properties * sizeof(u32); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: { + struct hfi_profile_level_supported *prop = + (struct hfi_profile_level_supported *) + (data + next_offset); + struct hfi_profile_level *pl; + unsigned int prop_count = 0; + unsigned int count = 0; + u8 *ptr; + + ptr = (u8 *)&prop->profile_level[0]; + prop_count = prop->profile_count; + + if (prop_count > HFI_MAX_PROFILE_COUNT) + prop_count = HFI_MAX_PROFILE_COUNT; + + while (prop_count) { + ptr++; + pl = (struct hfi_profile_level *)ptr; + + inst->pl[count].profile = pl->profile; + inst->pl[count].level = pl->level; + prop_count--; + count++; + ptr += sizeof(*pl) / sizeof(u32); + } + + inst->pl_count = count; + next_offset += sizeof(*prop) - sizeof(*pl) + + prop->profile_count * sizeof(*pl); + + num_props--; + break; + } + case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: { + next_offset += + sizeof(struct hfi_interlace_format_supported); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: { + struct hfi_nal_stream_format *nal = + (struct hfi_nal_stream_format *) + (data + next_offset); + dev_dbg(dev, "NAL format: %x\n", nal->format); + next_offset += sizeof(*nal); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: { + next_offset += sizeof(u32); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: { + u32 *max_seq_sz = (u32 *)(data + next_offset); + + dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz); + next_offset += sizeof(u32); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + next_offset += sizeof(struct hfi_intra_refresh); + num_props--; + break; + } + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: { + struct hfi_buffer_alloc_mode_supported *prop = + (struct hfi_buffer_alloc_mode_supported *) + (data + next_offset); + int i; + + for (i = 0; i < prop->num_entries; i++) { + if (prop->buffer_type == HFI_BUFFER_OUTPUT || + prop->buffer_type == HFI_BUFFER_OUTPUT2) { + switch (prop->data[i]) { + case HFI_BUFFER_MODE_STATIC: + inst->cap_bufs_mode_static = 1; + break; + case HFI_BUFFER_MODE_DYNAMIC: + inst->cap_bufs_mode_dynamic = 1; + break; + default: + break; + } + } + } + next_offset += sizeof(*prop) - + sizeof(u32) + prop->num_entries * sizeof(u32); + num_props--; + break; + } + default: + dev_dbg(dev, "%s: default case %#x\n", __func__, ptype); + break; + } + + rem_bytes -= next_offset; + data += next_offset; + } + + return err; +} + +static void hfi_session_init_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_init_done_pkt *pkt = packet; + unsigned int error; + + error = pkt->error_type; + if (error != HFI_ERR_NONE) + goto done; + + if (core->res->hfi_version != HFI_VERSION_1XX) + goto done; + + error = init_done_read_prop(core, inst, pkt); + +done: + inst->error = error; + complete(&inst->done); +} + +static void hfi_session_load_res_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_load_resources_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_flush_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_flush_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_etb_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + inst->ops->buf_done(inst, HFI_BUFFER_INPUT, pkt->input_tag, + pkt->filled_len, pkt->offset, 0, 0, 0); +} + +static void hfi_session_ftb_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + u32 session_type = inst->session_type; + u64 timestamp_us = 0; + u32 timestamp_hi = 0, timestamp_lo = 0; + unsigned int error; + u32 flags = 0, hfi_flags = 0, offset = 0, filled_len = 0; + u32 pic_type = 0, packet_buffer, buffer_type = 0, output_tag = -1; + + if (session_type == VIDC_SESSION_TYPE_ENC) { + struct hfi_msg_session_fbd_compressed_pkt *pkt = packet; + + timestamp_hi = pkt->time_stamp_hi; + timestamp_lo = pkt->time_stamp_lo; + hfi_flags = pkt->flags; + offset = pkt->offset; + filled_len = pkt->filled_len; + pic_type = pkt->picture_type; + packet_buffer = pkt->packet_buffer; + output_tag = pkt->output_tag; + buffer_type = HFI_BUFFER_OUTPUT; + + error = pkt->error_type; + } else if (session_type == VIDC_SESSION_TYPE_DEC) { + struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt = + packet; + + timestamp_hi = pkt->time_stamp_hi; + timestamp_lo = pkt->time_stamp_lo; + hfi_flags = pkt->flags; + offset = pkt->offset; + filled_len = pkt->filled_len; + pic_type = pkt->picture_type; + packet_buffer = pkt->packet_buffer; + output_tag = pkt->output_tag; + + if (pkt->stream_id == 0) + buffer_type = HFI_BUFFER_OUTPUT; + else if (pkt->stream_id == 1) + buffer_type = HFI_BUFFER_OUTPUT2; + + error = pkt->error_type; + } else { + error = HFI_ERR_SESSION_INVALID_PARAMETER; + } + + if (buffer_type != HFI_BUFFER_OUTPUT) + goto done; + + if (hfi_flags & HFI_BUFFERFLAG_EOS) + flags |= V4L2_BUF_FLAG_LAST; + + switch (pic_type) { + case HFI_PICTURE_IDR: + case HFI_PICTURE_I: + flags |= V4L2_BUF_FLAG_KEYFRAME; + break; + case HFI_PICTURE_P: + flags |= V4L2_BUF_FLAG_PFRAME; + break; + case HFI_PICTURE_B: + flags |= V4L2_BUF_FLAG_BFRAME; + break; + case HFI_FRAME_NOTCODED: + case HFI_UNUSED_PICT: + case HFI_FRAME_YUV: + default: + break; + } + + if (!(hfi_flags & HFI_BUFFERFLAG_TIMESTAMPINVALID) && filled_len) { + timestamp_us = timestamp_hi; + timestamp_us = (timestamp_us << 32) | timestamp_lo; + } + +done: + inst->error = error; + inst->ops->buf_done(inst, buffer_type, output_tag, filled_len, + offset, flags, hfi_flags, timestamp_us); +} + +static void hfi_session_start_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_start_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_stop_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_stop_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_rel_res_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_release_resources_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_rel_buf_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_release_buffers_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_end_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_end_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_abort_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_sys_session_abort_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +static void hfi_session_get_seq_hdr_done(struct venus_core *core, + struct venus_inst *inst, void *packet) +{ + struct hfi_msg_session_get_sequence_hdr_done_pkt *pkt = packet; + + inst->error = pkt->error_type; + complete(&inst->done); +} + +struct hfi_done_handler { + u32 pkt; + u32 pkt_sz; + u32 pkt_sz2; + void (*done)(struct venus_core *, struct venus_inst *, void *); + bool is_sys_pkt; +}; + +static const struct hfi_done_handler handlers[] = { + {.pkt = HFI_MSG_EVENT_NOTIFY, + .pkt_sz = sizeof(struct hfi_msg_event_notify_pkt), + .done = hfi_event_notify, + }, + {.pkt = HFI_MSG_SYS_INIT, + .pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt), + .done = hfi_sys_init_done, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_PROPERTY_INFO, + .pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt), + .done = hfi_sys_property_info, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_RELEASE_RESOURCE, + .pkt_sz = sizeof(struct hfi_msg_sys_release_resource_done_pkt), + .done = hfi_sys_rel_resource_done, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_PING_ACK, + .pkt_sz = sizeof(struct hfi_msg_sys_ping_ack_pkt), + .done = hfi_sys_ping_done, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_IDLE, + .pkt_sz = sizeof(struct hfi_msg_sys_idle_pkt), + .done = hfi_sys_idle_done, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_PC_PREP, + .pkt_sz = sizeof(struct hfi_msg_sys_pc_prep_done_pkt), + .done = hfi_sys_pc_prepare_done, + .is_sys_pkt = true, + }, + {.pkt = HFI_MSG_SYS_SESSION_INIT, + .pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt), + .done = hfi_session_init_done, + }, + {.pkt = HFI_MSG_SYS_SESSION_END, + .pkt_sz = sizeof(struct hfi_msg_session_end_done_pkt), + .done = hfi_session_end_done, + }, + {.pkt = HFI_MSG_SESSION_LOAD_RESOURCES, + .pkt_sz = sizeof(struct hfi_msg_session_load_resources_done_pkt), + .done = hfi_session_load_res_done, + }, + {.pkt = HFI_MSG_SESSION_START, + .pkt_sz = sizeof(struct hfi_msg_session_start_done_pkt), + .done = hfi_session_start_done, + }, + {.pkt = HFI_MSG_SESSION_STOP, + .pkt_sz = sizeof(struct hfi_msg_session_stop_done_pkt), + .done = hfi_session_stop_done, + }, + {.pkt = HFI_MSG_SYS_SESSION_ABORT, + .pkt_sz = sizeof(struct hfi_msg_sys_session_abort_done_pkt), + .done = hfi_session_abort_done, + }, + {.pkt = HFI_MSG_SESSION_EMPTY_BUFFER, + .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt), + .done = hfi_session_etb_done, + }, + {.pkt = HFI_MSG_SESSION_FILL_BUFFER, + .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt), + .pkt_sz2 = sizeof(struct hfi_msg_session_fbd_compressed_pkt), + .done = hfi_session_ftb_done, + }, + {.pkt = HFI_MSG_SESSION_FLUSH, + .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt), + .done = hfi_session_flush_done, + }, + {.pkt = HFI_MSG_SESSION_PROPERTY_INFO, + .pkt_sz = sizeof(struct hfi_msg_session_property_info_pkt), + .done = hfi_session_prop_info, + }, + {.pkt = HFI_MSG_SESSION_RELEASE_RESOURCES, + .pkt_sz = sizeof(struct hfi_msg_session_release_resources_done_pkt), + .done = hfi_session_rel_res_done, + }, + {.pkt = HFI_MSG_SESSION_GET_SEQUENCE_HEADER, + .pkt_sz = sizeof(struct hfi_msg_session_get_sequence_hdr_done_pkt), + .done = hfi_session_get_seq_hdr_done, + }, + {.pkt = HFI_MSG_SESSION_RELEASE_BUFFERS, + .pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt), + .done = hfi_session_rel_buf_done, + }, +}; + +void hfi_process_watchdog_timeout(struct venus_core *core) +{ + event_sys_error(core, EVT_SYS_WATCHDOG_TIMEOUT, NULL); +} + +static struct venus_inst *to_instance(struct venus_core *core, u32 session_id) +{ + struct venus_inst *inst; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) + if (hash32_ptr(inst) == session_id) { + mutex_unlock(&core->lock); + return inst; + } + mutex_unlock(&core->lock); + + return NULL; +} + +u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr) +{ + const struct hfi_done_handler *handler; + struct device *dev = core->dev; + struct venus_inst *inst; + bool found = false; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + handler = &handlers[i]; + if (handler->pkt != hdr->pkt_type) + continue; + found = true; + break; + } + + if (!found) + return hdr->pkt_type; + + if (hdr->size && hdr->size < handler->pkt_sz && + hdr->size < handler->pkt_sz2) { + dev_err(dev, "bad packet size (%d should be %d, pkt type:%x)\n", + hdr->size, handler->pkt_sz, hdr->pkt_type); + + return hdr->pkt_type; + } + + if (handler->is_sys_pkt) { + inst = NULL; + } else { + struct hfi_session_pkt *pkt; + + pkt = (struct hfi_session_pkt *)hdr; + inst = to_instance(core, pkt->shdr.session_id); + + if (!inst) + dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n", + pkt->shdr.session_id, + handler ? handler->pkt : 0); + + /* + * Event of type HFI_EVENT_SYS_ERROR will not have any session + * associated with it + */ + if (!inst && hdr->pkt_type != HFI_MSG_EVENT_NOTIFY) { + dev_err(dev, "got invalid session id:%x\n", + pkt->shdr.session_id); + goto invalid_session; + } + } + + handler->done(core, inst, hdr); + +invalid_session: + return hdr->pkt_type; +} diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.h b/drivers/media/platform/qcom/venus/hfi_msgs.h new file mode 100644 index 000000000000..14d9a3979b14 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_msgs.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_HFI_MSGS_H__ +#define __VENUS_HFI_MSGS_H__ + +/* message calls */ +#define HFI_MSG_SYS_INIT 0x20001 +#define HFI_MSG_SYS_PC_PREP 0x20002 +#define HFI_MSG_SYS_RELEASE_RESOURCE 0x20003 +#define HFI_MSG_SYS_DEBUG 0x20004 +#define HFI_MSG_SYS_SESSION_INIT 0x20006 +#define HFI_MSG_SYS_SESSION_END 0x20007 +#define HFI_MSG_SYS_IDLE 0x20008 +#define HFI_MSG_SYS_COV 0x20009 +#define HFI_MSG_SYS_PROPERTY_INFO 0x2000a + +#define HFI_MSG_EVENT_NOTIFY 0x21001 +#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER 0x21002 + +#define HFI_MSG_SYS_PING_ACK 0x220002 +#define HFI_MSG_SYS_SESSION_ABORT 0x220004 + +#define HFI_MSG_SESSION_LOAD_RESOURCES 0x221001 +#define HFI_MSG_SESSION_START 0x221002 +#define HFI_MSG_SESSION_STOP 0x221003 +#define HFI_MSG_SESSION_SUSPEND 0x221004 +#define HFI_MSG_SESSION_RESUME 0x221005 +#define HFI_MSG_SESSION_FLUSH 0x221006 +#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007 +#define HFI_MSG_SESSION_FILL_BUFFER 0x221008 +#define HFI_MSG_SESSION_PROPERTY_INFO 0x221009 +#define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a +#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER 0x22100b +#define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c + +#define HFI_PICTURE_I 0x00000001 +#define HFI_PICTURE_P 0x00000002 +#define HFI_PICTURE_B 0x00000004 +#define HFI_PICTURE_IDR 0x00000008 +#define HFI_FRAME_NOTCODED 0x7f002000 +#define HFI_FRAME_YUV 0x7f004000 +#define HFI_UNUSED_PICT 0x10000000 + +/* message packets */ +struct hfi_msg_event_notify_pkt { + struct hfi_session_hdr_pkt shdr; + u32 event_id; + u32 event_data1; + u32 event_data2; + u32 ext_event_data[1]; +}; + +struct hfi_msg_event_release_buffer_ref_pkt { + u32 packet_buffer; + u32 extradata_buffer; + u32 output_tag; +}; + +struct hfi_msg_sys_init_done_pkt { + struct hfi_pkt_hdr hdr; + u32 error_type; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_msg_sys_pc_prep_done_pkt { + struct hfi_pkt_hdr hdr; + u32 error_type; +}; + +struct hfi_msg_sys_release_resource_done_pkt { + struct hfi_pkt_hdr hdr; + u32 resource_handle; + u32 error_type; +}; + +struct hfi_msg_session_init_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_msg_session_end_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_get_sequence_hdr_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 header_len; + u32 sequence_header; +}; + +struct hfi_msg_sys_session_abort_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_sys_idle_pkt { + struct hfi_pkt_hdr hdr; +}; + +struct hfi_msg_sys_ping_ack_pkt { + struct hfi_pkt_hdr hdr; + u32 client_data; +}; + +struct hfi_msg_sys_property_info_pkt { + struct hfi_pkt_hdr hdr; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_msg_session_load_resources_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_start_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_stop_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_suspend_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_resume_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_flush_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 flush_type; +}; + +struct hfi_msg_session_empty_buffer_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 offset; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[0]; +}; + +struct hfi_msg_session_fbd_compressed_pkt { + struct hfi_session_hdr_pkt shdr; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 error_type; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[0]; +}; + +struct hfi_msg_session_fbd_uncompressed_plane0_pkt { + struct hfi_session_hdr_pkt shdr; + u32 stream_id; + u32 view_id; + u32 error_type; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 frame_width; + u32 frame_height; + u32 start_x_coord; + u32 start_y_coord; + u32 input_tag; + u32 input_tag2; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extradata_buffer; + u32 data[0]; +}; + +struct hfi_msg_session_fbd_uncompressed_plane1_pkt { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 data[0]; +}; + +struct hfi_msg_session_fbd_uncompressed_plane2_pkt { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 data[0]; +}; + +struct hfi_msg_session_parse_sequence_header_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_msg_session_property_info_pkt { + struct hfi_session_hdr_pkt shdr; + u32 num_properties; + u32 data[1]; +}; + +struct hfi_msg_session_release_resources_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; +}; + +struct hfi_msg_session_release_buffers_done_pkt { + struct hfi_session_hdr_pkt shdr; + u32 error_type; + u32 num_buffers; + u32 buffer_info[1]; +}; + +struct hfi_msg_sys_debug_pkt { + struct hfi_pkt_hdr hdr; + u32 msg_type; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 msg_data[1]; +}; + +struct hfi_msg_sys_coverage_pkt { + struct hfi_pkt_hdr hdr; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 msg_data[1]; +}; + +struct venus_core; +struct hfi_pkt_hdr; + +void hfi_process_watchdog_timeout(struct venus_core *core); +u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr); + +#endif From patchwork Fri Apr 28 09:13:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 98318 Delivered-To: patch@linaro.org Received: by 10.182.236.104 with SMTP id ut8csp233662obc; Fri, 28 Apr 2017 02:16:12 -0700 (PDT) X-Received: by 10.84.217.153 with SMTP id p25mr13733999pli.188.1493370971969; Fri, 28 Apr 2017 02:16:11 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k65si5781014pgc.142.2017.04.28.02.16.11; Fri, 28 Apr 2017 02:16:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-media-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1425411AbdD1JQH (ORCPT + 4 others); Fri, 28 Apr 2017 05:16:07 -0400 Received: from mail-wr0-f181.google.com ([209.85.128.181]:35098 "EHLO mail-wr0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1425395AbdD1JPB (ORCPT ); Fri, 28 Apr 2017 05:15:01 -0400 Received: by mail-wr0-f181.google.com with SMTP id z52so30225911wrc.2 for ; Fri, 28 Apr 2017 02:14:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=ydBdhB77Q2t4p2SGkrzKTne49ewvYKBuC0V7nzBQKyI=; b=jrrIRyRpFEzLSe0hWPyqGfkX/Kfh9yuiJTgG/u4QsnWOX64Kgvxm7kbtJGlqoEaGEb Wgt2+UkP6+hh0m9MpcAQj94FLrC9WhcgP/bnNlW4liHhHR7GaCIm1Q5H3ojK16CLYD/I gFVGcFCVAw7nLW2uO1HltyuAAvrqMIeZUzYpI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ydBdhB77Q2t4p2SGkrzKTne49ewvYKBuC0V7nzBQKyI=; b=mMJJ1tmabmCZn+GOQTj3Xl6sJgvzxanghTVygD3zcZpeMnJWH2h/oYxwuCYl7grJbA l2m98sq/K7NWPc4SIc2sNUf8voyqDBO+M8sBLn1U/oazPjkDUHYmq4Ozju3XVW9AkkRK piL2qoocmG40PB6GLWMkGax0a/Bq7QwEc3Wolomp1fTNRHtFADUglthhPLxbfX6ZnhoG +Dz6cfKn2bUsSMhCTnRNh6M5jjyHYgkKn8lzhKjdVgn0aqiFGbZgfEWNhmuNfgmOeEGQ c4W0caCdFyTaD3XBMhAmnpEijWB802lS9jHd5zpUbEiIl1YJoVJqixBanGCxohgqZZyA M2IA== X-Gm-Message-State: AN3rC/7TA7hubj+FQT4qmH0hpLK15v5B7PvgCaZmdMlmkjuAV28RYk8j 4+g9c4p+UyH6zyTv X-Received: by 10.223.136.134 with SMTP id f6mr6369191wrf.187.1493370894100; Fri, 28 Apr 2017 02:14:54 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id p197sm5653070wmb.34.2017.04.28.02.14.52 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 28 Apr 2017 02:14:53 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: Andy Gross , Bjorn Andersson , Stephen Boyd , Srinivas Kandagatla , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v8 09/10] media: venus: hfi: add Venus HFI files Date: Fri, 28 Apr 2017 12:13:56 +0300 Message-Id: <1493370837-19793-10-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> References: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Here is the implementation of Venus video accelerator low-level functionality. It contanins code which setup the registers and startup uthe processor, allocate and manipulates with the shared memory used for sending commands and receiving messages. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/qcom/venus/hfi_venus.c | 1571 ++++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_venus.h | 23 + drivers/media/platform/qcom/venus/hfi_venus_io.h | 113 ++ 3 files changed, 1707 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/hfi_venus.c create mode 100644 drivers/media/platform/qcom/venus/hfi_venus.h create mode 100644 drivers/media/platform/qcom/venus/hfi_venus_io.h -- 2.7.4 diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c new file mode 100644 index 000000000000..4104582dcd9b --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -0,0 +1,1571 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "hfi_cmds.h" +#include "hfi_msgs.h" +#include "hfi_venus.h" +#include "hfi_venus_io.h" + +#define HFI_MASK_QHDR_TX_TYPE 0xff000000 +#define HFI_MASK_QHDR_RX_TYPE 0x00ff0000 +#define HFI_MASK_QHDR_PRI_TYPE 0x0000ff00 +#define HFI_MASK_QHDR_ID_TYPE 0x000000ff + +#define HFI_HOST_TO_CTRL_CMD_Q 0 +#define HFI_CTRL_TO_HOST_MSG_Q 1 +#define HFI_CTRL_TO_HOST_DBG_Q 2 +#define HFI_MASK_QHDR_STATUS 0x000000ff + +#define IFACEQ_NUM 3 +#define IFACEQ_CMD_IDX 0 +#define IFACEQ_MSG_IDX 1 +#define IFACEQ_DBG_IDX 2 +#define IFACEQ_MAX_BUF_COUNT 50 +#define IFACEQ_MAX_PARALLEL_CLNTS 16 +#define IFACEQ_DFLT_QHDR 0x01010000 + +#define POLL_INTERVAL_US 50 + +#define IFACEQ_MAX_PKT_SIZE 1024 +#define IFACEQ_MED_PKT_SIZE 768 +#define IFACEQ_MIN_PKT_SIZE 8 +#define IFACEQ_VAR_SMALL_PKT_SIZE 100 +#define IFACEQ_VAR_LARGE_PKT_SIZE 512 +#define IFACEQ_VAR_HUGE_PKT_SIZE (1024 * 12) + +enum tzbsp_video_state { + TZBSP_VIDEO_STATE_SUSPEND = 0, + TZBSP_VIDEO_STATE_RESUME +}; + +struct hfi_queue_table_header { + u32 version; + u32 size; + u32 qhdr0_offset; + u32 qhdr_size; + u32 num_q; + u32 num_active_q; +}; + +struct hfi_queue_header { + u32 status; + u32 start_addr; + u32 type; + u32 q_size; + u32 pkt_size; + u32 pkt_drop_cnt; + u32 rx_wm; + u32 tx_wm; + u32 rx_req; + u32 tx_req; + u32 rx_irq_status; + u32 tx_irq_status; + u32 read_idx; + u32 write_idx; +}; + +#define IFACEQ_TABLE_SIZE \ + (sizeof(struct hfi_queue_table_header) + \ + sizeof(struct hfi_queue_header) * IFACEQ_NUM) + +#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \ + IFACEQ_MAX_BUF_COUNT * IFACEQ_MAX_PARALLEL_CLNTS) + +#define IFACEQ_GET_QHDR_START_ADDR(ptr, i) \ + (void *)(((ptr) + sizeof(struct hfi_queue_table_header)) + \ + ((i) * sizeof(struct hfi_queue_header))) + +#define QDSS_SIZE SZ_4K +#define SFR_SIZE SZ_4K +#define QUEUE_SIZE \ + (IFACEQ_TABLE_SIZE + (IFACEQ_QUEUE_SIZE * IFACEQ_NUM)) + +#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K) +#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K) +#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K) +#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \ + ALIGNED_QDSS_SIZE, SZ_1M) + +struct mem_desc { + dma_addr_t da; /* device address */ + void *kva; /* kernel virtual address */ + u32 size; + unsigned long attrs; +}; + +struct iface_queue { + struct hfi_queue_header *qhdr; + struct mem_desc qmem; +}; + +enum venus_state { + VENUS_STATE_DEINIT = 1, + VENUS_STATE_INIT, +}; + +struct venus_hfi_device { + struct venus_core *core; + u32 irq_status; + u32 last_packet_type; + bool power_enabled; + bool suspended; + enum venus_state state; + /* serialize read / write to the shared memory */ + struct mutex lock; + struct completion pwr_collapse_prep; + struct completion release_resource; + struct mem_desc ifaceq_table; + struct mem_desc sfr; + struct iface_queue queues[IFACEQ_NUM]; + u8 pkt_buf[IFACEQ_VAR_HUGE_PKT_SIZE]; + u8 dbg_buf[IFACEQ_VAR_HUGE_PKT_SIZE]; +}; + +static bool venus_pkt_debug; +static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL; +static bool venus_sys_idle_indicator; +static bool venus_fw_low_power_mode = true; +static int venus_hw_rsp_timeout = 1000; +static bool venus_fw_coverage; + +static void venus_set_state(struct venus_hfi_device *hdev, + enum venus_state state) +{ + mutex_lock(&hdev->lock); + hdev->state = state; + mutex_unlock(&hdev->lock); +} + +static bool venus_is_valid_state(struct venus_hfi_device *hdev) +{ + return hdev->state != VENUS_STATE_DEINIT; +} + +static void venus_dump_packet(struct venus_hfi_device *hdev, const void *packet) +{ + size_t pkt_size = *(u32 *)packet; + + if (!venus_pkt_debug) + return; + + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, packet, + pkt_size, true); +} + +static int venus_write_queue(struct venus_hfi_device *hdev, + struct iface_queue *queue, + void *packet, u32 *rx_req) +{ + struct hfi_queue_header *qhdr; + u32 dwords, new_wr_idx; + u32 empty_space, rd_idx, wr_idx, qsize; + u32 *wr_ptr; + + if (!queue->qmem.kva) + return -EINVAL; + + qhdr = queue->qhdr; + if (!qhdr) + return -EINVAL; + + venus_dump_packet(hdev, packet); + + dwords = (*(u32 *)packet) >> 2; + if (!dwords) + return -EINVAL; + + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + qsize = qhdr->q_size; + /* ensure rd/wr indices's are read from memory */ + rmb(); + + if (wr_idx >= rd_idx) + empty_space = qsize - (wr_idx - rd_idx); + else + empty_space = rd_idx - wr_idx; + + if (empty_space <= dwords) { + qhdr->tx_req = 1; + /* ensure tx_req is updated in memory */ + wmb(); + return -ENOSPC; + } + + qhdr->tx_req = 0; + /* ensure tx_req is updated in memory */ + wmb(); + + new_wr_idx = wr_idx + dwords; + wr_ptr = (u32 *)(queue->qmem.kva + (wr_idx << 2)); + if (new_wr_idx < qsize) { + memcpy(wr_ptr, packet, dwords << 2); + } else { + size_t len; + + new_wr_idx -= qsize; + len = (dwords - new_wr_idx) << 2; + memcpy(wr_ptr, packet, len); + memcpy(queue->qmem.kva, packet + len, new_wr_idx << 2); + } + + /* make sure packet is written before updating the write index */ + wmb(); + + qhdr->write_idx = new_wr_idx; + *rx_req = qhdr->rx_req ? 1 : 0; + + /* make sure write index is updated before an interrupt is raised */ + mb(); + + return 0; +} + +static int venus_read_queue(struct venus_hfi_device *hdev, + struct iface_queue *queue, void *pkt, u32 *tx_req) +{ + struct hfi_queue_header *qhdr; + u32 dwords, new_rd_idx; + u32 rd_idx, wr_idx, type, qsize; + u32 *rd_ptr; + u32 recv_request = 0; + int ret = 0; + + if (!queue->qmem.kva) + return -EINVAL; + + qhdr = queue->qhdr; + if (!qhdr) + return -EINVAL; + + type = qhdr->type; + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + qsize = qhdr->q_size; + + /* make sure data is valid before using it */ + rmb(); + + /* + * Do not set receive request for debug queue, if set, Venus generates + * interrupt for debug messages even when there is no response message + * available. In general debug queue will not become full as it is being + * emptied out for every interrupt from Venus. Venus will anyway + * generates interrupt if it is full. + */ + if (type & HFI_CTRL_TO_HOST_MSG_Q) + recv_request = 1; + + if (rd_idx == wr_idx) { + qhdr->rx_req = recv_request; + *tx_req = 0; + /* update rx_req field in memory */ + wmb(); + return -ENODATA; + } + + rd_ptr = (u32 *)(queue->qmem.kva + (rd_idx << 2)); + dwords = *rd_ptr >> 2; + if (!dwords) + return -EINVAL; + + new_rd_idx = rd_idx + dwords; + if (((dwords << 2) <= IFACEQ_VAR_HUGE_PKT_SIZE) && rd_idx <= qsize) { + if (new_rd_idx < qsize) { + memcpy(pkt, rd_ptr, dwords << 2); + } else { + size_t len; + + new_rd_idx -= qsize; + len = (dwords - new_rd_idx) << 2; + memcpy(pkt, rd_ptr, len); + memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2); + } + } else { + /* bad packet received, dropping */ + new_rd_idx = qhdr->write_idx; + ret = -EBADMSG; + } + + /* ensure the packet is read before updating read index */ + rmb(); + + qhdr->read_idx = new_rd_idx; + /* ensure updating read index */ + wmb(); + + rd_idx = qhdr->read_idx; + wr_idx = qhdr->write_idx; + /* ensure rd/wr indices are read from memory */ + rmb(); + + if (rd_idx != wr_idx) + qhdr->rx_req = 0; + else + qhdr->rx_req = recv_request; + + *tx_req = qhdr->tx_req ? 1 : 0; + + /* ensure rx_req is stored to memory and tx_req is loaded from memory */ + mb(); + + venus_dump_packet(hdev, pkt); + + return ret; +} + +static int venus_alloc(struct venus_hfi_device *hdev, struct mem_desc *desc, + u32 size) +{ + struct device *dev = hdev->core->dev; + + desc->attrs = DMA_ATTR_WRITE_COMBINE; + desc->size = ALIGN(size, SZ_4K); + + desc->kva = dma_alloc_attrs(dev, size, &desc->da, GFP_KERNEL, + desc->attrs); + if (!desc->kva) + return -ENOMEM; + + return 0; +} + +static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem) +{ + struct device *dev = hdev->core->dev; + + dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs); +} + +static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value) +{ + writel(value, hdev->core->base + reg); +} + +static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg) +{ + return readl(hdev->core->base + reg); +} + +static void venus_set_registers(struct venus_hfi_device *hdev) +{ + const struct venus_resources *res = hdev->core->res; + const struct reg_val *tbl = res->reg_tbl; + unsigned int count = res->reg_tbl_size; + unsigned int i; + + for (i = 0; i < count; i++) + venus_writel(hdev, tbl[i].reg, tbl[i].value); +} + +static void venus_soft_int(struct venus_hfi_device *hdev) +{ + venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT)); +} + +static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct device *dev = hdev->core->dev; + struct hfi_pkt_hdr *cmd_packet; + struct iface_queue *queue; + u32 rx_req; + int ret; + + if (!venus_is_valid_state(hdev)) + return -EINVAL; + + cmd_packet = (struct hfi_pkt_hdr *)pkt; + hdev->last_packet_type = cmd_packet->pkt_type; + + queue = &hdev->queues[IFACEQ_CMD_IDX]; + + ret = venus_write_queue(hdev, queue, pkt, &rx_req); + if (ret) { + dev_err(dev, "write to iface cmd queue failed (%d)\n", ret); + return ret; + } + + if (rx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + mutex_lock(&hdev->lock); + ret = venus_iface_cmdq_write_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_hfi_core_set_resource(struct venus_core *core, u32 id, + u32 size, u32 addr, void *cookie) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_set_resource_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + if (id == VIDC_RESOURCE_NONE) + return 0; + + pkt = (struct hfi_sys_set_resource_pkt *)packet; + + ret = pkt_sys_set_resource(pkt, id, size, addr, cookie); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_boot_core(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + static const unsigned int max_tries = 100; + u32 ctrl_status = 0; + unsigned int count = 0; + int ret = 0; + + venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT)); + venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK); + venus_writel(hdev, CPU_CS_SCIACMDARG3, 1); + + while (!ctrl_status && count < max_tries) { + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) { + dev_err(dev, "invalid setting for UC_REGION\n"); + ret = -EINVAL; + break; + } + + usleep_range(500, 1000); + count++; + } + + if (count >= max_tries) + ret = -ETIMEDOUT; + + return ret; +} + +static u32 venus_hwversion(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION); + u32 major, minor, step; + + major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK; + major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT; + minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK; + minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT; + step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK; + + dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step); + + return major; +} + +static int venus_run(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + int ret; + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + venus_set_registers(hdev); + + venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da); + venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE); + venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da); + venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01); + if (hdev->sfr.da) + venus_writel(hdev, SFR_ADDR, hdev->sfr.da); + + ret = venus_boot_core(hdev); + if (ret) { + dev_err(dev, "failed to reset venus core\n"); + return ret; + } + + venus_hwversion(hdev); + + return 0; +} + +static int venus_halt_axi(struct venus_hfi_device *hdev) +{ + void __iomem *base = hdev->core->base; + struct device *dev = hdev->core->dev; + u32 val; + int ret; + + /* Halt AXI and AXI IMEM VBIF Access */ + val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); + val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; + venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val); + + /* Request for AXI bus port halt */ + ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val, + val & VBIF_AXI_HALT_CTRL1_HALT_ACK, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) { + dev_err(dev, "AXI bus port halt timeout\n"); + return ret; + } + + return 0; +} + +static int venus_power_off(struct venus_hfi_device *hdev) +{ + int ret; + + if (!hdev->power_enabled) + return 0; + + ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0); + if (ret) + return ret; + + ret = venus_halt_axi(hdev); + if (ret) + return ret; + + hdev->power_enabled = false; + + return 0; +} + +static int venus_power_on(struct venus_hfi_device *hdev) +{ + int ret; + + if (hdev->power_enabled) + return 0; + + ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_RESUME, 0); + if (ret) + goto err; + + ret = venus_run(hdev); + if (ret) + goto err_suspend; + + hdev->power_enabled = true; + + return 0; + +err_suspend: + qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0); +err: + hdev->power_enabled = false; + return ret; +} + +static int venus_iface_msgq_read_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct iface_queue *queue; + u32 tx_req; + int ret; + + if (!venus_is_valid_state(hdev)) + return -EINVAL; + + queue = &hdev->queues[IFACEQ_MSG_IDX]; + + ret = venus_read_queue(hdev, queue, pkt, &tx_req); + if (ret) + return ret; + + if (tx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_msgq_read(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + mutex_lock(&hdev->lock); + ret = venus_iface_msgq_read_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_iface_dbgq_read_nolock(struct venus_hfi_device *hdev, + void *pkt) +{ + struct iface_queue *queue; + u32 tx_req; + int ret; + + ret = venus_is_valid_state(hdev); + if (!ret) + return -EINVAL; + + queue = &hdev->queues[IFACEQ_DBG_IDX]; + + ret = venus_read_queue(hdev, queue, pkt, &tx_req); + if (ret) + return ret; + + if (tx_req) + venus_soft_int(hdev); + + return 0; +} + +static int venus_iface_dbgq_read(struct venus_hfi_device *hdev, void *pkt) +{ + int ret; + + if (!pkt) + return -EINVAL; + + mutex_lock(&hdev->lock); + ret = venus_iface_dbgq_read_nolock(hdev, pkt); + mutex_unlock(&hdev->lock); + + return ret; +} + +static void venus_set_qhdr_defaults(struct hfi_queue_header *qhdr) +{ + qhdr->status = 1; + qhdr->type = IFACEQ_DFLT_QHDR; + qhdr->q_size = IFACEQ_QUEUE_SIZE / 4; + qhdr->pkt_size = 0; + qhdr->rx_wm = 1; + qhdr->tx_wm = 1; + qhdr->rx_req = 1; + qhdr->tx_req = 0; + qhdr->rx_irq_status = 0; + qhdr->tx_irq_status = 0; + qhdr->read_idx = 0; + qhdr->write_idx = 0; +} + +static void venus_interface_queues_release(struct venus_hfi_device *hdev) +{ + mutex_lock(&hdev->lock); + + venus_free(hdev, &hdev->ifaceq_table); + venus_free(hdev, &hdev->sfr); + + memset(hdev->queues, 0, sizeof(hdev->queues)); + memset(&hdev->ifaceq_table, 0, sizeof(hdev->ifaceq_table)); + memset(&hdev->sfr, 0, sizeof(hdev->sfr)); + + mutex_unlock(&hdev->lock); +} + +static int venus_interface_queues_init(struct venus_hfi_device *hdev) +{ + struct hfi_queue_table_header *tbl_hdr; + struct iface_queue *queue; + struct hfi_sfr *sfr; + struct mem_desc desc = {0}; + unsigned int offset; + unsigned int i; + int ret; + + ret = venus_alloc(hdev, &desc, ALIGNED_QUEUE_SIZE); + if (ret) + return ret; + + hdev->ifaceq_table.kva = desc.kva; + hdev->ifaceq_table.da = desc.da; + hdev->ifaceq_table.size = IFACEQ_TABLE_SIZE; + offset = hdev->ifaceq_table.size; + + for (i = 0; i < IFACEQ_NUM; i++) { + queue = &hdev->queues[i]; + queue->qmem.da = desc.da + offset; + queue->qmem.kva = desc.kva + offset; + queue->qmem.size = IFACEQ_QUEUE_SIZE; + offset += queue->qmem.size; + queue->qhdr = + IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i); + + venus_set_qhdr_defaults(queue->qhdr); + + queue->qhdr->start_addr = queue->qmem.da; + + if (i == IFACEQ_CMD_IDX) + queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q; + else if (i == IFACEQ_MSG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q; + else if (i == IFACEQ_DBG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q; + } + + tbl_hdr = hdev->ifaceq_table.kva; + tbl_hdr->version = 0; + tbl_hdr->size = IFACEQ_TABLE_SIZE; + tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header); + tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header); + tbl_hdr->num_q = IFACEQ_NUM; + tbl_hdr->num_active_q = IFACEQ_NUM; + + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + queue = &hdev->queues[IFACEQ_DBG_IDX]; + queue->qhdr->rx_req = 0; + + ret = venus_alloc(hdev, &desc, ALIGNED_SFR_SIZE); + if (ret) { + hdev->sfr.da = 0; + } else { + hdev->sfr.da = desc.da; + hdev->sfr.kva = desc.kva; + hdev->sfr.size = ALIGNED_SFR_SIZE; + sfr = hdev->sfr.kva; + sfr->buf_size = ALIGNED_SFR_SIZE; + } + + /* ensure table and queue header structs are settled in memory */ + wmb(); + + return 0; +} + +static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_coverage_config(pkt, mode); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_idle_message(struct venus_hfi_device *hdev, + bool enable) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + if (!enable) + return 0; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_idle_indicator(pkt, enable); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_sys_set_power_control(struct venus_hfi_device *hdev, + bool enable) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_power_control(pkt, enable); + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_get_queue_size(struct venus_hfi_device *hdev, + unsigned int index) +{ + struct hfi_queue_header *qhdr; + + if (index >= IFACEQ_NUM) + return -EINVAL; + + qhdr = hdev->queues[index].qhdr; + if (!qhdr) + return -EINVAL; + + return abs(qhdr->read_idx - qhdr->write_idx); +} + +static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + int ret; + + ret = venus_sys_set_debug(hdev, venus_fw_debug); + if (ret) + dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); + + ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); + if (ret) + dev_warn(dev, "setting idle response ON failed (%d)\n", ret); + + ret = venus_sys_set_power_control(hdev, venus_fw_low_power_mode); + if (ret) + dev_warn(dev, "setting hw power collapse ON failed (%d)\n", + ret); + + return ret; +} + +static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_pkt pkt; + + pkt_session_cmd(&pkt, pkt_type, inst); + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static void venus_flush_debug_queue(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + void *packet = hdev->dbg_buf; + + while (!venus_iface_dbgq_read(hdev, packet)) { + struct hfi_msg_sys_coverage_pkt *pkt = packet; + + if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) { + struct hfi_msg_sys_debug_pkt *pkt = packet; + + dev_dbg(dev, "%s", pkt->msg_data); + } + } +} + +static int venus_prepare_power_collapse(struct venus_hfi_device *hdev, + bool wait) +{ + unsigned long timeout = msecs_to_jiffies(venus_hw_rsp_timeout); + struct hfi_sys_pc_prep_pkt pkt; + int ret; + + init_completion(&hdev->pwr_collapse_prep); + + pkt_sys_pc_prep(&pkt); + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + return ret; + + if (!wait) + return 0; + + ret = wait_for_completion_timeout(&hdev->pwr_collapse_prep, timeout); + if (!ret) { + venus_flush_debug_queue(hdev); + return -ETIMEDOUT; + } + + return 0; +} + +static int venus_are_queues_empty(struct venus_hfi_device *hdev) +{ + int ret1, ret2; + + ret1 = venus_get_queue_size(hdev, IFACEQ_MSG_IDX); + if (ret1 < 0) + return ret1; + + ret2 = venus_get_queue_size(hdev, IFACEQ_CMD_IDX); + if (ret2 < 0) + return ret2; + + if (!ret1 && !ret2) + return 1; + + return 0; +} + +static void venus_sfr_print(struct venus_hfi_device *hdev) +{ + struct device *dev = hdev->core->dev; + struct hfi_sfr *sfr = hdev->sfr.kva; + void *p; + + if (!sfr) + return; + + p = memchr(sfr->data, '\0', sfr->buf_size); + /* + * SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates + * that Venus is in the process of crashing. + */ + if (!p) + sfr->data[sfr->buf_size - 1] = '\0'; + + dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data); +} + +static void venus_process_msg_sys_error(struct venus_hfi_device *hdev, + void *packet) +{ + struct hfi_msg_event_notify_pkt *event_pkt = packet; + + if (event_pkt->event_id != HFI_EVENT_SYS_ERROR) + return; + + venus_set_state(hdev, VENUS_STATE_DEINIT); + + /* + * Once SYS_ERROR received from HW, it is safe to halt the AXI. + * With SYS_ERROR, Venus FW may have crashed and HW might be + * active and causing unnecessary transactions. Hence it is + * safe to stop all AXI transactions from venus subsystem. + */ + venus_halt_axi(hdev); + venus_sfr_print(hdev); +} + +static irqreturn_t venus_isr_thread(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + const struct venus_resources *res = hdev->core->res; + void *pkt; + u32 msg_ret; + + if (!hdev) + return IRQ_NONE; + + pkt = hdev->pkt_buf; + + if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) { + venus_sfr_print(hdev); + hfi_process_watchdog_timeout(core); + } + + while (!venus_iface_msgq_read(hdev, pkt)) { + msg_ret = hfi_process_msg_packet(core, pkt); + switch (msg_ret) { + case HFI_MSG_EVENT_NOTIFY: + venus_process_msg_sys_error(hdev, pkt); + break; + case HFI_MSG_SYS_INIT: + venus_hfi_core_set_resource(core, res->vmem_id, + res->vmem_size, + res->vmem_addr, + hdev); + break; + case HFI_MSG_SYS_RELEASE_RESOURCE: + complete(&hdev->release_resource); + break; + case HFI_MSG_SYS_PC_PREP: + complete(&hdev->pwr_collapse_prep); + break; + default: + break; + } + } + + venus_flush_debug_queue(hdev); + + return IRQ_HANDLED; +} + +static irqreturn_t venus_isr(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + u32 status; + + if (!hdev) + return IRQ_NONE; + + status = venus_readl(hdev, WRAPPER_INTR_STATUS); + + if (status & WRAPPER_INTR_STATUS_A2H_MASK || + status & WRAPPER_INTR_STATUS_A2HWD_MASK || + status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + hdev->irq_status = status; + + venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1); + venus_writel(hdev, WRAPPER_INTR_CLEAR, status); + + return IRQ_WAKE_THREAD; +} + +static int venus_hfi_core_init(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + struct hfi_sys_get_property_pkt version_pkt; + struct hfi_sys_init_pkt pkt; + int ret; + + pkt_sys_init(&pkt, HFI_VIDEO_ARCH_OX); + + venus_set_state(hdev, VENUS_STATE_INIT); + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + return ret; + + pkt_sys_image_version(&version_pkt); + + ret = venus_iface_cmdq_write(hdev, &version_pkt); + if (ret) + dev_warn(dev, "failed to send image version pkt to fw\n"); + + return 0; +} + +static int venus_hfi_core_deinit(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + + venus_set_state(hdev, VENUS_STATE_DEINIT); + hdev->suspended = true; + hdev->power_enabled = false; + + return 0; +} + +static int venus_hfi_core_ping(struct venus_core *core, u32 cookie) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_ping_pkt pkt; + + pkt_sys_ping(&pkt, cookie); + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_hfi_core_trigger_ssr(struct venus_core *core, u32 trigger_type) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_sys_test_ssr_pkt pkt; + int ret; + + ret = pkt_sys_ssr_cmd(&pkt, trigger_type); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_hfi_session_init(struct venus_inst *inst, + u32 session_type, u32 codec) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_init_pkt pkt; + int ret; + + ret = venus_sys_set_default_properties(hdev); + if (ret) + return ret; + + ret = pkt_session_init(&pkt, inst, session_type, codec); + if (ret) + goto err; + + ret = venus_iface_cmdq_write(hdev, &pkt); + if (ret) + goto err; + + return 0; + +err: + venus_flush_debug_queue(hdev); + return ret; +} + +static int venus_hfi_session_end(struct venus_inst *inst) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct device *dev = hdev->core->dev; + + if (venus_fw_coverage) { + if (venus_sys_set_coverage(hdev, venus_fw_coverage)) + dev_warn(dev, "fw coverage msg ON failed\n"); + } + + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END); +} + +static int venus_hfi_session_abort(struct venus_inst *inst) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + + venus_flush_debug_queue(hdev); + + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT); +} + +static int venus_hfi_session_flush(struct venus_inst *inst, u32 flush_mode) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_flush_pkt pkt; + int ret; + + ret = pkt_session_flush(&pkt, inst, flush_mode); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_hfi_session_start(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_START); +} + +static int venus_hfi_session_stop(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_STOP); +} + +static int venus_hfi_session_continue(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE); +} + +static int venus_hfi_session_etb(struct venus_inst *inst, + struct hfi_frame_data *in_frame) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + u32 session_type = inst->session_type; + int ret; + + if (session_type == VIDC_SESSION_TYPE_DEC) { + struct hfi_session_empty_buffer_compressed_pkt pkt; + + ret = pkt_session_etb_decoder(&pkt, inst, in_frame); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, &pkt); + } else if (session_type == VIDC_SESSION_TYPE_ENC) { + struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt; + + ret = pkt_session_etb_encoder(&pkt, inst, in_frame); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, &pkt); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int venus_hfi_session_ftb(struct venus_inst *inst, + struct hfi_frame_data *out_frame) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_fill_buffer_pkt pkt; + int ret; + + ret = pkt_session_ftb(&pkt, inst, out_frame); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_hfi_session_set_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_set_buffers_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + if (bd->buffer_type == HFI_BUFFER_INPUT) + return 0; + + pkt = (struct hfi_session_set_buffers_pkt *)packet; + + ret = pkt_session_set_buffers(pkt, inst, bd); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_hfi_session_unset_buffers(struct venus_inst *inst, + struct hfi_buffer_desc *bd) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_release_buffer_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + if (bd->buffer_type == HFI_BUFFER_INPUT) + return 0; + + pkt = (struct hfi_session_release_buffer_pkt *)packet; + + ret = pkt_session_unset_buffers(pkt, inst, bd); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_hfi_session_load_res(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES); +} + +static int venus_hfi_session_release_res(struct venus_inst *inst) +{ + return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES); +} + +static int venus_hfi_session_parse_seq_hdr(struct venus_inst *inst, + u32 seq_hdr, u32 seq_hdr_len) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_parse_sequence_header_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_parse_sequence_header_pkt *)packet; + + ret = pkt_session_parse_seq_header(pkt, inst, seq_hdr, seq_hdr_len); + if (ret) + return ret; + + ret = venus_iface_cmdq_write(hdev, pkt); + if (ret) + return ret; + + return 0; +} + +static int venus_hfi_session_get_seq_hdr(struct venus_inst *inst, + u32 seq_hdr, u32 seq_hdr_len) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_get_sequence_header_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_get_sequence_header_pkt *)packet; + + ret = pkt_session_get_seq_hdr(pkt, inst, seq_hdr, seq_hdr_len); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_hfi_session_set_property(struct venus_inst *inst, u32 ptype, + void *pdata) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE]; + int ret; + + pkt = (struct hfi_session_set_property_pkt *)packet; + + ret = pkt_session_set_property(pkt, inst, ptype, pdata); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, pkt); +} + +static int venus_hfi_session_get_property(struct venus_inst *inst, u32 ptype) +{ + struct venus_hfi_device *hdev = to_hfi_priv(inst->core); + struct hfi_session_get_property_pkt pkt; + int ret; + + ret = pkt_session_get_property(&pkt, inst, ptype); + if (ret) + return ret; + + return venus_iface_cmdq_write(hdev, &pkt); +} + +static int venus_hfi_resume(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + int ret = 0; + + mutex_lock(&hdev->lock); + + if (!hdev->suspended) + goto unlock; + + ret = venus_power_on(hdev); + +unlock: + if (!ret) + hdev->suspended = false; + + mutex_unlock(&hdev->lock); + + return ret; +} + +static int venus_hfi_suspend_1xx(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + u32 ctrl_status; + int ret; + + if (!hdev->power_enabled || hdev->suspended) + return 0; + + mutex_lock(&hdev->lock); + ret = venus_is_valid_state(hdev); + mutex_unlock(&hdev->lock); + + if (!ret) { + dev_err(dev, "bad state, cannot suspend\n"); + return -EINVAL; + } + + ret = venus_prepare_power_collapse(hdev, true); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", ret); + return ret; + } + + mutex_lock(&hdev->lock); + + if (hdev->last_packet_type != HFI_CMD_SYS_PC_PREP) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ret = venus_are_queues_empty(hdev); + if (ret < 0 || !ret) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { + mutex_unlock(&hdev->lock); + return -EINVAL; + } + + ret = venus_power_off(hdev); + if (ret) { + mutex_unlock(&hdev->lock); + return ret; + } + + hdev->suspended = true; + + mutex_unlock(&hdev->lock); + + return 0; +} + +static int venus_hfi_suspend_3xx(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct device *dev = core->dev; + u32 ctrl_status, wfi_status; + int ret; + int cnt = 100; + + if (!hdev->power_enabled || hdev->suspended) + return 0; + + mutex_lock(&hdev->lock); + ret = venus_is_valid_state(hdev); + mutex_unlock(&hdev->lock); + + if (!ret) { + dev_err(dev, "bad state, cannot suspend\n"); + return -EINVAL; + } + + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { + wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + ret = venus_prepare_power_collapse(hdev, false); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", + ret); + return ret; + } + + cnt = 100; + while (cnt--) { + wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY && + wfi_status & BIT(0)) + break; + usleep_range(1000, 1500); + } + } + + mutex_lock(&hdev->lock); + + ret = venus_power_off(hdev); + if (ret) { + dev_err(dev, "venus_power_off (%d)\n", ret); + mutex_unlock(&hdev->lock); + return ret; + } + + hdev->suspended = true; + + mutex_unlock(&hdev->lock); + + return 0; +} + +static int venus_hfi_suspend(struct venus_core *core) +{ + if (core->res->hfi_version == HFI_VERSION_3XX) + return venus_hfi_suspend_3xx(core); + + return venus_hfi_suspend_1xx(core); +} + +static const struct hfi_ops venus_hfi_ops = { + .core_init = venus_hfi_core_init, + .core_deinit = venus_hfi_core_deinit, + .core_ping = venus_hfi_core_ping, + .core_trigger_ssr = venus_hfi_core_trigger_ssr, + + .session_init = venus_hfi_session_init, + .session_end = venus_hfi_session_end, + .session_abort = venus_hfi_session_abort, + .session_flush = venus_hfi_session_flush, + .session_start = venus_hfi_session_start, + .session_stop = venus_hfi_session_stop, + .session_continue = venus_hfi_session_continue, + .session_etb = venus_hfi_session_etb, + .session_ftb = venus_hfi_session_ftb, + .session_set_buffers = venus_hfi_session_set_buffers, + .session_unset_buffers = venus_hfi_session_unset_buffers, + .session_load_res = venus_hfi_session_load_res, + .session_release_res = venus_hfi_session_release_res, + .session_parse_seq_hdr = venus_hfi_session_parse_seq_hdr, + .session_get_seq_hdr = venus_hfi_session_get_seq_hdr, + .session_set_property = venus_hfi_session_set_property, + .session_get_property = venus_hfi_session_get_property, + + .resume = venus_hfi_resume, + .suspend = venus_hfi_suspend, + + .isr = venus_isr, + .isr_thread = venus_isr_thread, +}; + +void venus_hfi_destroy(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + + venus_interface_queues_release(hdev); + mutex_destroy(&hdev->lock); + kfree(hdev); + core->priv = NULL; + core->ops = NULL; +} + +int venus_hfi_create(struct venus_core *core) +{ + struct venus_hfi_device *hdev; + int ret; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + mutex_init(&hdev->lock); + + hdev->core = core; + hdev->suspended = true; + core->priv = hdev; + core->ops = &venus_hfi_ops; + core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY | + ENC_DEINTERLACE_CAPABILITY | + DEC_MULTI_STREAM_CAPABILITY; + + ret = venus_interface_queues_init(hdev); + if (ret) + goto err_kfree; + + return 0; + +err_kfree: + kfree(hdev); + core->priv = NULL; + core->ops = NULL; + return ret; +} diff --git a/drivers/media/platform/qcom/venus/hfi_venus.h b/drivers/media/platform/qcom/venus/hfi_venus.h new file mode 100644 index 000000000000..885923354033 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_HFI_VENUS_H__ +#define __VENUS_HFI_VENUS_H__ + +struct venus_core; + +void venus_hfi_destroy(struct venus_core *core); +int venus_hfi_create(struct venus_core *core); + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h new file mode 100644 index 000000000000..98cc350113ab --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __VENUS_HFI_VENUS_IO_H__ +#define __VENUS_HFI_VENUS_IO_H__ + +#define VBIF_BASE 0x80000 + +#define VBIF_AXI_HALT_CTRL0 (VBIF_BASE + 0x208) +#define VBIF_AXI_HALT_CTRL1 (VBIF_BASE + 0x20c) + +#define VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0) +#define VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0) +#define VBIF_AXI_HALT_ACK_TIMEOUT_US 500000 + +#define CPU_BASE 0xc0000 +#define CPU_CS_BASE (CPU_BASE + 0x12000) +#define CPU_IC_BASE (CPU_BASE + 0x1f000) + +#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE + 0x1c) + +#define VIDC_CTRL_INIT (CPU_CS_BASE + 0x48) +#define VIDC_CTRL_INIT_RESERVED_BITS31_1_MASK 0xfffffffe +#define VIDC_CTRL_INIT_RESERVED_BITS31_1_SHIFT 1 +#define VIDC_CTRL_INIT_CTRL_MASK 0x1 +#define VIDC_CTRL_INIT_CTRL_SHIFT 0 + +/* HFI control status */ +#define CPU_CS_SCIACMDARG0 (CPU_CS_BASE + 0x4c) +#define CPU_CS_SCIACMDARG0_MASK 0xff +#define CPU_CS_SCIACMDARG0_SHIFT 0x0 +#define CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK 0xfe +#define CPU_CS_SCIACMDARG0_ERROR_STATUS_SHIFT 0x1 +#define CPU_CS_SCIACMDARG0_INIT_STATUS_MASK 0x1 +#define CPU_CS_SCIACMDARG0_INIT_STATUS_SHIFT 0x0 +#define CPU_CS_SCIACMDARG0_PC_READY BIT(8) +#define CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK BIT(30) + +/* HFI queue table info */ +#define CPU_CS_SCIACMDARG1 (CPU_CS_BASE + 0x50) + +/* HFI queue table address */ +#define CPU_CS_SCIACMDARG2 (CPU_CS_BASE + 0x54) + +/* Venus cpu */ +#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE + 0x58) + +#define SFR_ADDR (CPU_CS_BASE + 0x5c) +#define MMAP_ADDR (CPU_CS_BASE + 0x60) +#define UC_REGION_ADDR (CPU_CS_BASE + 0x64) +#define UC_REGION_SIZE (CPU_CS_BASE + 0x68) + +#define CPU_IC_SOFTINT (CPU_IC_BASE + 0x18) +#define CPU_IC_SOFTINT_H2A_MASK 0x8000 +#define CPU_IC_SOFTINT_H2A_SHIFT 0xf + +/* Venus wrapper */ +#define WRAPPER_BASE 0x000e0000 + +#define WRAPPER_HW_VERSION (WRAPPER_BASE + 0x00) +#define WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000 +#define WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28 +#define WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xfff0000 +#define WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16 +#define WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xffff + +#define WRAPPER_CLOCK_CONFIG (WRAPPER_BASE + 0x04) + +#define WRAPPER_INTR_STATUS (WRAPPER_BASE + 0x0c) +#define WRAPPER_INTR_STATUS_A2HWD_MASK 0x10 +#define WRAPPER_INTR_STATUS_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_STATUS_A2H_MASK 0x4 +#define WRAPPER_INTR_STATUS_A2H_SHIFT 0x2 + +#define WRAPPER_INTR_MASK (WRAPPER_BASE + 0x10) +#define WRAPPER_INTR_MASK_A2HWD_BASK 0x10 +#define WRAPPER_INTR_MASK_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_MASK_A2HVCODEC_MASK 0x8 +#define WRAPPER_INTR_MASK_A2HVCODEC_SHIFT 0x3 +#define WRAPPER_INTR_MASK_A2HCPU_MASK 0x4 +#define WRAPPER_INTR_MASK_A2HCPU_SHIFT 0x2 + +#define WRAPPER_INTR_CLEAR (WRAPPER_BASE + 0x14) +#define WRAPPER_INTR_CLEAR_A2HWD_MASK 0x10 +#define WRAPPER_INTR_CLEAR_A2HWD_SHIFT 0x4 +#define WRAPPER_INTR_CLEAR_A2H_MASK 0x4 +#define WRAPPER_INTR_CLEAR_A2H_SHIFT 0x2 + +#define WRAPPER_POWER_STATUS (WRAPPER_BASE + 0x44) +#define WRAPPER_VDEC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x48) +#define WRAPPER_VENC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x4c) +#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET (WRAPPER_BASE + 0x64) + +#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) +#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) + +#define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) +#define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) +#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) + +#endif From patchwork Fri Apr 28 09:13:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanimir Varbanov X-Patchwork-Id: 98319 Delivered-To: patch@linaro.org Received: by 10.182.236.104 with SMTP id ut8csp233683obc; Fri, 28 Apr 2017 02:16:16 -0700 (PDT) X-Received: by 10.99.157.138 with SMTP id i132mr10772985pgd.87.1493370976875; Fri, 28 Apr 2017 02:16:16 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k65si5781014pgc.142.2017.04.28.02.16.16; Fri, 28 Apr 2017 02:16:16 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-media-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-media-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1425426AbdD1JQM (ORCPT + 4 others); Fri, 28 Apr 2017 05:16:12 -0400 Received: from mail-wm0-f54.google.com ([74.125.82.54]:37329 "EHLO mail-wm0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1425398AbdD1JPC (ORCPT ); Fri, 28 Apr 2017 05:15:02 -0400 Received: by mail-wm0-f54.google.com with SMTP id m123so40818608wma.0 for ; Fri, 28 Apr 2017 02:15:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=PaRQT/SpP8rGYqX/FJK8jLEQmfjsJvQavqKsilZc0Bk=; b=gA4cw6YCSEZG3SetkOPuhZXWxkwiO/Y6FCJAHnK2Om/VcMkP1dmt2gydgi0RN2RFBb 7X+my5maln7+rgaWZYyLHapzb5HqkJyW5DBdPG7/i1qtAq3WThlX+bnPUwA04Zxn59lb PgiGkVzyT7SDc3UtmqsAY5kXZgGcaCiFmJ44w= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=PaRQT/SpP8rGYqX/FJK8jLEQmfjsJvQavqKsilZc0Bk=; b=swgEcYa4WxkcIkkd4xnrb4wGbH0uDF6bAgFcWO8tmzcw66kLJM9L2OnGZmaL4m/1J4 Wx3WvTULGaAWAuCWoicPbdgcPpi7z0eUN2KOk2ZKUqpaskSyO5z4pe/7ARuK+dik2jC4 1RBYgVgQizrte9WOSlKyfq2LJ2j0szgX8tHBsCFFSPo+yDO3/5M32hhfmxPa3CvFCeAI VvgXFtjyX0vfszMGfopyCxMLlDMwb03Hjs1J7JKxsSgVmuRSPgYcA8ZTk3lx7i4s9hOM ohjS3vzv0AxsvnOLsB8HMeCDpZkOsvzzL/BmqL7HIJ0SWMIINCWNgv7nceDBm2zXBoV4 2wHA== X-Gm-Message-State: AN3rC/6pd5YOoMxVsflpdyekTtqghOiQ02j2BfD4N9QHg9GIJjMJgX21 RIouqLZ1Rsgyk0pr X-Received: by 10.28.61.198 with SMTP id k189mr4711875wma.104.1493370896411; Fri, 28 Apr 2017 02:14:56 -0700 (PDT) Received: from mms-0440.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.gmail.com with ESMTPSA id p197sm5653070wmb.34.2017.04.28.02.14.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 28 Apr 2017 02:14:55 -0700 (PDT) From: Stanimir Varbanov To: Mauro Carvalho Chehab , Hans Verkuil Cc: Andy Gross , Bjorn Andersson , Stephen Boyd , Srinivas Kandagatla , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Stanimir Varbanov Subject: [PATCH v8 10/10] media: venus: enable building of Venus video driver Date: Fri, 28 Apr 2017 12:13:57 +0300 Message-Id: <1493370837-19793-11-git-send-email-stanimir.varbanov@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> References: <1493370837-19793-1-git-send-email-stanimir.varbanov@linaro.org> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org This adds Venus driver Makefile and changes v4l2 platform Makefile/Kconfig in order to enable building of the driver. Signed-off-by: Stanimir Varbanov --- drivers/media/platform/Kconfig | 13 +++++++++++++ drivers/media/platform/Makefile | 2 ++ drivers/media/platform/qcom/venus/Makefile | 11 +++++++++++ 3 files changed, 26 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/Makefile -- 2.7.4 diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ac026ee1ca07..570cb9ca7703 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -448,6 +448,19 @@ config VIDEO_TI_VPE_DEBUG ---help--- Enable debug messages on VPE driver. +config VIDEO_QCOM_VENUS + tristate "Qualcomm Venus V4L2 encoder/decoder driver" + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select QCOM_MDT_LOADER + select VIDEOBUF2_DMA_SG + select V4L2_MEM2MEM_DEV + ---help--- + This is a V4L2 driver for Qualcomm Venus video accelerator + hardware. It accelerates encoding and decoding operations + on various Qualcomm SoCs. + To compile this driver as a module choose m here. + endif # V4L_MEM2MEM_DRIVERS # TI VIDEO PORT Helper Modules diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 63303d63c64c..c49de824af16 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -77,3 +77,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/ obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/ obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/ + +obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile new file mode 100644 index 000000000000..0fe9afb83697 --- /dev/null +++ b/drivers/media/platform/qcom/venus/Makefile @@ -0,0 +1,11 @@ +# Makefile for Qualcomm Venus driver + +venus-core-objs += core.o helpers.o firmware.o \ + hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o + +venus-dec-objs += vdec.o vdec_ctrls.o +venus-enc-objs += venc.o venc_ctrls.o + +obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-core.o +obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-dec.o +obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-enc.o