aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/platform/amphion/venc.c
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/media/platform/amphion/venc.c
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/media/platform/amphion/venc.c')
-rw-r--r--drivers/media/platform/amphion/venc.c1353
1 files changed, 1353 insertions, 0 deletions
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
new file mode 100644
index 000000000..3cbe8ce63
--- /dev/null
+++ b/drivers/media/platform/amphion/venc.c
@@ -0,0 +1,1353 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/ktime.h>
+#include <linux/rational.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_core.h"
+#include "vpu_helpers.h"
+#include "vpu_v4l2.h"
+#include "vpu_cmds.h"
+#include "vpu_rpc.h"
+
+#define VENC_OUTPUT_ENABLE BIT(0)
+#define VENC_CAPTURE_ENABLE BIT(1)
+#define VENC_ENABLE_MASK (VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE)
+#define VENC_MAX_BUF_CNT 8
+#define VENC_MIN_BUFFER_OUT 6
+#define VENC_MIN_BUFFER_CAP 6
+
+struct venc_t {
+ struct vpu_encode_params params;
+ u32 request_key_frame;
+ u32 input_ready;
+ u32 cpb_size;
+ bool bitrate_change;
+
+ struct vpu_buffer enc[VENC_MAX_BUF_CNT];
+ struct vpu_buffer ref[VENC_MAX_BUF_CNT];
+ struct vpu_buffer act[VENC_MAX_BUF_CNT];
+ struct list_head frames;
+ u32 frame_count;
+ u32 encode_count;
+ u32 ready_count;
+ u32 enable;
+ u32 stopped;
+
+ u32 skipped_count;
+ u32 skipped_bytes;
+
+ wait_queue_head_t wq;
+};
+
+struct venc_frame_t {
+ struct list_head list;
+ struct vpu_enc_pic_info info;
+ u32 bytesused;
+ s64 timestamp;
+};
+
+static const struct vpu_format venc_formats[] = {
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12M,
+ .mem_planes = 2,
+ .comp_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .sibling = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12,
+ .mem_planes = 1,
+ .comp_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .sibling = V4L2_PIX_FMT_NV12M,
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .mem_planes = 1,
+ .comp_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .flags = V4L2_FMT_FLAG_COMPRESSED
+ },
+ {0, 0, 0, 0},
+};
+
+static int venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver));
+ strscpy(cap->card, "amphion vpu encoder", sizeof(cap->card));
+ strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_format *fmt;
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+ fmt = vpu_helper_enum_format(inst, f->type, f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
+ f->flags = fmt->flags;
+
+ return 0;
+}
+
+static int venc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+
+ if (!fsize || fsize->index)
+ return -EINVAL;
+
+ if (!vpu_helper_find_format(inst, 0, fsize->pixel_format))
+ return -EINVAL;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.max_width = res->max_width;
+ fsize->stepwise.max_height = res->max_height;
+ fsize->stepwise.min_width = res->min_width;
+ fsize->stepwise.min_height = res->min_height;
+ fsize->stepwise.step_width = res->step_width;
+ fsize->stepwise.step_height = res->step_height;
+
+ return 0;
+}
+
+static int venc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+
+ if (!fival || fival->index)
+ return -EINVAL;
+
+ if (!vpu_helper_find_format(inst, 0, fival->pixel_format))
+ return -EINVAL;
+
+ if (!fival->width || !fival->height)
+ return -EINVAL;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+ if (fival->width < res->min_width || fival->width > res->max_width ||
+ fival->height < res->min_height || fival->height > res->max_height)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+ fival->stepwise.min.numerator = 1;
+ fival->stepwise.min.denominator = USHRT_MAX;
+ fival->stepwise.max.numerator = USHRT_MAX;
+ fival->stepwise.max.denominator = 1;
+ fival->stepwise.step.numerator = 1;
+ fival->stepwise.step.denominator = 1;
+
+ return 0;
+}
+
+static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct vpu_format *cur_fmt;
+ int i;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+
+ pixmp->pixelformat = cur_fmt->pixfmt;
+ pixmp->num_planes = cur_fmt->mem_planes;
+ pixmp->width = cur_fmt->width;
+ pixmp->height = cur_fmt->height;
+ pixmp->field = cur_fmt->field;
+ pixmp->flags = cur_fmt->flags;
+ for (i = 0; i < pixmp->num_planes; i++) {
+ pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i];
+ pixmp->plane_fmt[i].sizeimage = vpu_get_fmt_plane_size(cur_fmt, i);
+ }
+
+ f->fmt.pix_mp.colorspace = venc->params.color.primaries;
+ f->fmt.pix_mp.xfer_func = venc->params.color.transfer;
+ f->fmt.pix_mp.ycbcr_enc = venc->params.color.matrix;
+ f->fmt.pix_mp.quantization = venc->params.color.full_range;
+
+ return 0;
+}
+
+static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vpu_format fmt;
+
+ vpu_try_fmt_common(inst, f, &fmt);
+
+ return 0;
+}
+
+static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vpu_format fmt;
+ struct vpu_format *cur_fmt;
+ struct vb2_queue *q;
+ struct venc_t *venc = inst->priv;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+
+ q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
+ if (!q)
+ return -EINVAL;
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ if (vpu_try_fmt_common(inst, f, &fmt))
+ return -EINVAL;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+
+ memcpy(cur_fmt, &fmt, sizeof(*cur_fmt));
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ venc->params.input_format = cur_fmt->pixfmt;
+ venc->params.src_stride = cur_fmt->bytesperline[0];
+ venc->params.src_width = cur_fmt->width;
+ venc->params.src_height = cur_fmt->height;
+ venc->params.crop.left = 0;
+ venc->params.crop.top = 0;
+ venc->params.crop.width = cur_fmt->width;
+ venc->params.crop.height = cur_fmt->height;
+ } else {
+ venc->params.codec_format = cur_fmt->pixfmt;
+ venc->params.out_width = cur_fmt->width;
+ venc->params.out_height = cur_fmt->height;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (!vpu_color_check_primaries(pix_mp->colorspace)) {
+ venc->params.color.primaries = pix_mp->colorspace;
+ vpu_color_get_default(venc->params.color.primaries,
+ &venc->params.color.transfer,
+ &venc->params.color.matrix,
+ &venc->params.color.full_range);
+ }
+ if (!vpu_color_check_transfers(pix_mp->xfer_func))
+ venc->params.color.transfer = pix_mp->xfer_func;
+ if (!vpu_color_check_matrix(pix_mp->ycbcr_enc))
+ venc->params.color.matrix = pix_mp->ycbcr_enc;
+ if (!vpu_color_check_full_range(pix_mp->quantization))
+ venc->params.color.full_range = pix_mp->quantization;
+ }
+
+ pix_mp->colorspace = venc->params.color.primaries;
+ pix_mp->xfer_func = venc->params.color.transfer;
+ pix_mp->ycbcr_enc = venc->params.color.matrix;
+ pix_mp->quantization = venc->params.color.full_range;
+
+ return 0;
+}
+
+static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe;
+
+ if (!parm)
+ return -EINVAL;
+
+ if (!V4L2_TYPE_IS_OUTPUT(parm->type))
+ return -EINVAL;
+
+ if (!vpu_helper_check_type(inst, parm->type))
+ return -EINVAL;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.readbuffers = 0;
+ timeperframe->numerator = venc->params.frame_rate.numerator;
+ timeperframe->denominator = venc->params.frame_rate.denominator;
+
+ return 0;
+}
+
+static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe;
+ unsigned long n, d;
+
+ if (!parm)
+ return -EINVAL;
+
+ if (!V4L2_TYPE_IS_OUTPUT(parm->type))
+ return -EINVAL;
+
+ if (!vpu_helper_check_type(inst, parm->type))
+ return -EINVAL;
+
+ if (!timeperframe->numerator)
+ timeperframe->numerator = venc->params.frame_rate.numerator;
+ if (!timeperframe->denominator)
+ timeperframe->denominator = venc->params.frame_rate.denominator;
+
+ venc->params.frame_rate.numerator = timeperframe->numerator;
+ venc->params.frame_rate.denominator = timeperframe->denominator;
+
+ rational_best_approximation(venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator,
+ venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator,
+ &n, &d);
+ venc->params.frame_rate.numerator = n;
+ venc->params.frame_rate.denominator = d;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ memset(parm->parm.capture.reserved, 0, sizeof(parm->parm.capture.reserved));
+
+ return 0;
+}
+
+static int venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->out_format.width;
+ s->r.height = inst->out_format.height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ s->r = venc->params.crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int venc_valid_crop(struct venc_t *venc, const struct vpu_core_resources *res)
+{
+ struct v4l2_rect *rect = NULL;
+ u32 min_width;
+ u32 min_height;
+ u32 src_width;
+ u32 src_height;
+
+ rect = &venc->params.crop;
+ min_width = res->min_width;
+ min_height = res->min_height;
+ src_width = venc->params.src_width;
+ src_height = venc->params.src_height;
+
+ if (rect->width == 0 || rect->height == 0)
+ return -EINVAL;
+ if (rect->left > src_width - min_width || rect->top > src_height - min_height)
+ return -EINVAL;
+
+ rect->width = min(rect->width, src_width - rect->left);
+ rect->width = max_t(u32, rect->width, min_width);
+
+ rect->height = min(rect->height, src_height - rect->top);
+ rect->height = max_t(u32, rect->height, min_height);
+
+ return 0;
+}
+
+static int venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+ struct venc_t *venc = inst->priv;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+ if (s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ venc->params.crop.left = ALIGN(s->r.left, res->step_width);
+ venc->params.crop.top = ALIGN(s->r.top, res->step_height);
+ venc->params.crop.width = ALIGN(s->r.width, res->step_width);
+ venc->params.crop.height = ALIGN(s->r.height, res->step_height);
+ if (venc_valid_crop(venc, res)) {
+ venc->params.crop.left = 0;
+ venc->params.crop.top = 0;
+ venc->params.crop.width = venc->params.src_width;
+ venc->params.crop.height = venc->params.src_height;
+ }
+
+ inst->crop = venc->params.crop;
+
+ return 0;
+}
+
+static int venc_drain(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+ int ret;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+
+ if (inst->state != VPU_CODEC_STATE_DRAIN)
+ return 0;
+
+ if (!vpu_is_source_empty(inst))
+ return 0;
+
+ if (!venc->input_ready)
+ return 0;
+
+ venc->input_ready = false;
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ ret = vpu_session_stop(inst);
+ if (ret)
+ return ret;
+ inst->state = VPU_CODEC_STATE_STOP;
+ wake_up_all(&venc->wq);
+
+ return 0;
+}
+
+static int venc_request_eos(struct vpu_inst *inst)
+{
+ inst->state = VPU_CODEC_STATE_DRAIN;
+ venc_drain(inst);
+
+ return 0;
+}
+
+static int venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
+{
+ struct vpu_inst *inst = to_inst(file);
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
+ if (ret)
+ return ret;
+
+ vpu_inst_lock(inst);
+ if (cmd->cmd == V4L2_ENC_CMD_STOP) {
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ vpu_set_last_buffer_dequeued(inst);
+ else
+ venc_request_eos(inst);
+ }
+ vpu_inst_unlock(inst);
+
+ return 0;
+}
+
+static int venc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops venc_ioctl_ops = {
+ .vidioc_querycap = venc_querycap,
+ .vidioc_enum_fmt_vid_cap = venc_enum_fmt,
+ .vidioc_enum_fmt_vid_out = venc_enum_fmt,
+ .vidioc_enum_framesizes = venc_enum_framesizes,
+ .vidioc_enum_frameintervals = venc_enum_frameintervals,
+ .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_s_fmt_vid_cap_mplane = venc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = venc_s_fmt,
+ .vidioc_g_parm = venc_g_parm,
+ .vidioc_s_parm = venc_s_parm,
+ .vidioc_g_selection = venc_g_selection,
+ .vidioc_s_selection = venc_s_selection,
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = venc_encoder_cmd,
+ .vidioc_subscribe_event = venc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .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,
+};
+
+static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpu_inst *inst = ctrl_to_inst(ctrl);
+ struct venc_t *venc = inst->priv;
+ int ret = 0;
+
+ vpu_inst_lock(inst);
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ venc->params.profile = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ venc->params.level = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ venc->params.rc_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ venc->params.rc_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ if (ctrl->val != venc->params.bitrate)
+ venc->bitrate_change = true;
+ venc->params.bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ venc->params.bitrate_max = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ venc->params.gop_length = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ venc->params.bframes = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ venc->params.i_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ venc->params.p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ venc->params.b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ venc->request_key_frame = 1;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ venc->cpb_size = ctrl->val * 1024;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ venc->params.sar.enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ venc->params.sar.idc = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ venc->params.sar.width = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ venc->params.sar.height = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ vpu_inst_unlock(inst);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops venc_ctrl_ops = {
+ .s_ctrl = venc_op_s_ctrl,
+ .g_volatile_ctrl = vpu_helper_g_volatile_ctrl,
+};
+
+static int venc_ctrl_init(struct vpu_inst *inst)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_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,
+ 0x0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1);
+
+ 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_CBR);
+
+ 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_GOP_SIZE, 1, 8000, 1, 30);
+
+ 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_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_FORCE_KEY_FRAME, 0, 0, 0, 0);
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, 64, 10240, 1, 1024);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+ 0x0,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+ 0, USHRT_MAX, 1, 1);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+ 0, USHRT_MAX, 1, 1);
+ 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_JOINED_WITH_1ST_FRAME);
+
+ if (inst->ctrl_handler.error) {
+ ret = inst->ctrl_handler.error;
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+ }
+
+ ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler);
+ if (ret) {
+ dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool venc_check_ready(struct vpu_inst *inst, unsigned int type)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (vpu_helper_get_free_space(inst) < venc->cpb_size)
+ return false;
+ return venc->input_ready;
+ }
+
+ if (list_empty(&venc->frames))
+ return false;
+ return true;
+}
+
+static u32 venc_get_enable_mask(u32 type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return VENC_OUTPUT_ENABLE;
+ else
+ return VENC_CAPTURE_ENABLE;
+}
+
+static void venc_set_enable(struct venc_t *venc, u32 type, int enable)
+{
+ u32 mask = venc_get_enable_mask(type);
+
+ if (enable)
+ venc->enable |= mask;
+ else
+ venc->enable &= ~mask;
+}
+
+static u32 venc_get_enable(struct venc_t *venc, u32 type)
+{
+ return venc->enable & venc_get_enable_mask(type);
+}
+
+static void venc_input_done(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ vpu_inst_lock(inst);
+ venc->input_ready = true;
+ vpu_process_output_buffer(inst);
+ if (inst->state == VPU_CODEC_STATE_DRAIN)
+ venc_drain(inst);
+ vpu_inst_unlock(inst);
+}
+
+/*
+ * It's hardware limitation, that there may be several bytes
+ * redundant data at the beginning of frame.
+ * For android platform, the redundant data may cause cts test fail
+ * So driver will strip them
+ */
+static int venc_precheck_encoded_frame(struct vpu_inst *inst, struct venc_frame_t *frame)
+{
+ struct venc_t *venc;
+ int skipped;
+
+ if (!frame || !frame->bytesused)
+ return -EINVAL;
+
+ venc = inst->priv;
+ skipped = vpu_helper_find_startcode(&inst->stream_buffer,
+ inst->cap_format.pixfmt,
+ frame->info.wptr - inst->stream_buffer.phys,
+ frame->bytesused);
+ if (skipped > 0) {
+ frame->bytesused -= skipped;
+ frame->info.wptr = vpu_helper_step_walk(&inst->stream_buffer,
+ frame->info.wptr, skipped);
+ venc->skipped_bytes += skipped;
+ venc->skipped_count++;
+ }
+
+ return 0;
+}
+
+static int venc_get_one_encoded_frame(struct vpu_inst *inst,
+ struct venc_frame_t *frame,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ struct venc_t *venc = inst->priv;
+ struct vb2_v4l2_buffer *src_buf;
+
+ if (!vbuf)
+ return -EAGAIN;
+
+ src_buf = vpu_find_buf_by_sequence(inst, inst->out_format.type, frame->info.frame_id);
+ if (src_buf) {
+ v4l2_m2m_buf_copy_metadata(src_buf, vbuf, true);
+ vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE);
+ v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, src_buf);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ } else {
+ vbuf->vb2_buf.timestamp = frame->info.timestamp;
+ }
+ if (!venc_get_enable(inst->priv, vbuf->vb2_buf.type)) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return 0;
+ }
+ if (frame->bytesused > vbuf->vb2_buf.planes[0].length) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return -ENOMEM;
+ }
+
+ venc_precheck_encoded_frame(inst, frame);
+
+ if (frame->bytesused) {
+ u32 rptr = frame->info.wptr;
+ void *dst = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+
+ vpu_helper_copy_from_stream_buffer(&inst->stream_buffer,
+ &rptr, frame->bytesused, dst);
+ vpu_iface_update_stream_buffer(inst, rptr, 0);
+ }
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, frame->bytesused);
+ vbuf->sequence = frame->info.frame_id;
+ vbuf->field = inst->cap_format.field;
+ vbuf->flags |= frame->info.pic_type;
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ venc->ready_count++;
+
+ if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME)
+ dev_dbg(inst->dev, "[%d][%d]key frame\n", inst->id, frame->info.frame_id);
+
+ return 0;
+}
+
+static int venc_get_encoded_frames(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+ struct venc_frame_t *frame;
+ struct venc_frame_t *tmp;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+ venc = inst->priv;
+ list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
+ if (venc_get_one_encoded_frame(inst, frame,
+ v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx)))
+ break;
+ list_del_init(&frame->list);
+ vfree(frame);
+ }
+
+ return 0;
+}
+
+static int venc_frame_encoded(struct vpu_inst *inst, void *arg)
+{
+ struct vpu_enc_pic_info *info = arg;
+ struct venc_frame_t *frame;
+ struct venc_t *venc;
+ int ret = 0;
+
+ if (!info)
+ return -EINVAL;
+ venc = inst->priv;
+ frame = vzalloc(sizeof(*frame));
+ if (!frame)
+ return -ENOMEM;
+
+ memcpy(&frame->info, info, sizeof(frame->info));
+ frame->bytesused = info->frame_size;
+
+ vpu_inst_lock(inst);
+ list_add_tail(&frame->list, &venc->frames);
+ venc->encode_count++;
+ venc_get_encoded_frames(inst);
+ vpu_inst_unlock(inst);
+
+ return ret;
+}
+
+static void venc_set_last_buffer_dequeued(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (venc->stopped && list_empty(&venc->frames))
+ vpu_set_last_buffer_dequeued(inst);
+}
+
+static void venc_stop_done(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ vpu_inst_lock(inst);
+ venc->stopped = true;
+ venc_set_last_buffer_dequeued(inst);
+ vpu_inst_unlock(inst);
+
+ wake_up_all(&venc->wq);
+}
+
+static void venc_event_notify(struct vpu_inst *inst, u32 event, void *data)
+{
+}
+
+static void venc_release(struct vpu_inst *inst)
+{
+}
+
+static void venc_cleanup(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+
+ if (!inst)
+ return;
+
+ venc = inst->priv;
+ vfree(venc);
+ inst->priv = NULL;
+ vfree(inst);
+}
+
+static int venc_start_session(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+ int stream_buffer_size;
+ int ret;
+
+ venc_set_enable(venc, type, 1);
+ if ((venc->enable & VENC_ENABLE_MASK) != VENC_ENABLE_MASK)
+ return 0;
+
+ vpu_iface_init_instance(inst);
+ stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
+ if (stream_buffer_size > 0) {
+ inst->stream_buffer.length = max_t(u32, stream_buffer_size, venc->cpb_size * 3);
+ ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
+ if (ret)
+ goto error;
+
+ inst->use_stream_buffer = true;
+ vpu_iface_config_stream_buffer(inst, &inst->stream_buffer);
+ }
+
+ ret = vpu_iface_set_encode_params(inst, &venc->params, 0);
+ if (ret)
+ goto error;
+ ret = vpu_session_configure_codec(inst);
+ if (ret)
+ goto error;
+
+ inst->state = VPU_CODEC_STATE_CONFIGURED;
+ /*vpu_iface_config_memory_resource*/
+
+ /*config enc expert mode parameter*/
+ ret = vpu_iface_set_encode_params(inst, &venc->params, 1);
+ if (ret)
+ goto error;
+
+ ret = vpu_session_start(inst);
+ if (ret)
+ goto error;
+ inst->state = VPU_CODEC_STATE_STARTED;
+
+ venc->bitrate_change = false;
+ venc->input_ready = true;
+ venc->frame_count = 0;
+ venc->encode_count = 0;
+ venc->ready_count = 0;
+ venc->stopped = false;
+ vpu_process_output_buffer(inst);
+ if (venc->frame_count == 0)
+ dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id);
+
+ return 0;
+error:
+ venc_set_enable(venc, type, 0);
+ inst->state = VPU_CODEC_STATE_DEINIT;
+
+ vpu_free_dma(&inst->stream_buffer);
+ return ret;
+}
+
+static void venc_cleanup_mem_resource(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+ u32 i;
+
+ venc = inst->priv;
+
+ for (i = 0; i < ARRAY_SIZE(venc->enc); i++)
+ vpu_free_dma(&venc->enc[i]);
+ for (i = 0; i < ARRAY_SIZE(venc->ref); i++)
+ vpu_free_dma(&venc->ref[i]);
+}
+
+static void venc_request_mem_resource(struct vpu_inst *inst,
+ u32 enc_frame_size,
+ u32 enc_frame_num,
+ u32 ref_frame_size,
+ u32 ref_frame_num,
+ u32 act_frame_size,
+ u32 act_frame_num)
+{
+ struct venc_t *venc;
+ u32 i;
+ int ret;
+
+ venc = inst->priv;
+ if (enc_frame_num > ARRAY_SIZE(venc->enc)) {
+ dev_err(inst->dev, "[%d] enc num(%d) is out of range\n", inst->id, enc_frame_num);
+ return;
+ }
+ if (ref_frame_num > ARRAY_SIZE(venc->ref)) {
+ dev_err(inst->dev, "[%d] ref num(%d) is out of range\n", inst->id, ref_frame_num);
+ return;
+ }
+ if (act_frame_num > ARRAY_SIZE(venc->act)) {
+ dev_err(inst->dev, "[%d] act num(%d) is out of range\n", inst->id, act_frame_num);
+ return;
+ }
+
+ for (i = 0; i < enc_frame_num; i++) {
+ venc->enc[i].length = enc_frame_size;
+ ret = vpu_alloc_dma(inst->core, &venc->enc[i]);
+ if (ret) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ }
+ for (i = 0; i < ref_frame_num; i++) {
+ venc->ref[i].length = ref_frame_size;
+ ret = vpu_alloc_dma(inst->core, &venc->ref[i]);
+ if (ret) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ }
+ if (act_frame_num != 1 || act_frame_size > inst->act.length) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ venc->act[0].length = act_frame_size;
+ venc->act[0].phys = inst->act.phys;
+ venc->act[0].virt = inst->act.virt;
+
+ for (i = 0; i < enc_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_ENC, i, &venc->enc[i]);
+ for (i = 0; i < ref_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_REF, i, &venc->ref[i]);
+ for (i = 0; i < act_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_ACT, i, &venc->act[i]);
+}
+
+static void venc_cleanup_frames(struct venc_t *venc)
+{
+ struct venc_frame_t *frame;
+ struct venc_frame_t *tmp;
+
+ list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
+ list_del_init(&frame->list);
+ vfree(frame);
+ }
+}
+
+static int venc_stop_session(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+
+ venc_set_enable(venc, type, 0);
+ if (venc->enable & VENC_ENABLE_MASK)
+ return 0;
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return 0;
+
+ if (inst->state != VPU_CODEC_STATE_STOP)
+ venc_request_eos(inst);
+
+ call_void_vop(inst, wait_prepare);
+ if (!wait_event_timeout(venc->wq, venc->stopped, VPU_TIMEOUT)) {
+ set_bit(inst->id, &inst->core->hang_mask);
+ vpu_session_debug(inst);
+ }
+ call_void_vop(inst, wait_finish);
+
+ inst->state = VPU_CODEC_STATE_DEINIT;
+ venc_cleanup_frames(inst->priv);
+ vpu_free_dma(&inst->stream_buffer);
+ venc_cleanup_mem_resource(inst);
+
+ return 0;
+}
+
+static int venc_process_output(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct venc_t *venc = inst->priv;
+ struct vb2_v4l2_buffer *vbuf;
+ u32 flags;
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return -EINVAL;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ if (inst->state == VPU_CODEC_STATE_STARTED)
+ inst->state = VPU_CODEC_STATE_ACTIVE;
+
+ flags = vbuf->flags;
+ if (venc->request_key_frame) {
+ vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ venc->request_key_frame = 0;
+ }
+ if (venc->bitrate_change) {
+ vpu_session_update_parameters(inst, &venc->params);
+ venc->bitrate_change = false;
+ }
+ dev_dbg(inst->dev, "[%d][INPUT TS]%32lld\n", inst->id, vb->timestamp);
+ vpu_iface_input_frame(inst, vb);
+ vbuf->flags = flags;
+ venc->input_ready = false;
+ venc->frame_count++;
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);
+
+ return 0;
+}
+
+static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct venc_t *venc;
+ struct venc_frame_t *frame = NULL;
+ struct vb2_v4l2_buffer *vbuf;
+ int ret;
+
+ venc = inst->priv;
+ if (list_empty(&venc->frames))
+ return -EINVAL;
+
+ frame = list_first_entry(&venc->frames, struct venc_frame_t, list);
+ vbuf = to_vb2_v4l2_buffer(vb);
+ v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ ret = venc_get_one_encoded_frame(inst, frame, vbuf);
+ if (ret)
+ return ret;
+
+ list_del_init(&frame->list);
+ vfree(frame);
+ return 0;
+}
+
+static void venc_on_queue_empty(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return;
+
+ if (venc->stopped)
+ venc_set_last_buffer_dequeued(inst);
+}
+
+static int venc_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+{
+ struct venc_t *venc = inst->priv;
+ int num = -1;
+
+ switch (i) {
+ case 0:
+ num = scnprintf(str, size, "profile = %d\n", venc->params.profile);
+ break;
+ case 1:
+ num = scnprintf(str, size, "level = %d\n", venc->params.level);
+ break;
+ case 2:
+ num = scnprintf(str, size, "fps = %d/%d\n",
+ venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator);
+ break;
+ case 3:
+ num = scnprintf(str, size, "%d x %d -> %d x %d\n",
+ venc->params.src_width,
+ venc->params.src_height,
+ venc->params.out_width,
+ venc->params.out_height);
+ break;
+ case 4:
+ num = scnprintf(str, size, "(%d, %d) %d x %d\n",
+ venc->params.crop.left,
+ venc->params.crop.top,
+ venc->params.crop.width,
+ venc->params.crop.height);
+ break;
+ case 5:
+ num = scnprintf(str, size,
+ "enable = 0x%x, input = %d, encode = %d, ready = %d, stopped = %d\n",
+ venc->enable,
+ venc->frame_count, venc->encode_count,
+ venc->ready_count,
+ venc->stopped);
+ break;
+ case 6:
+ num = scnprintf(str, size, "gop = %d\n", venc->params.gop_length);
+ break;
+ case 7:
+ num = scnprintf(str, size, "bframes = %d\n", venc->params.bframes);
+ break;
+ case 8:
+ num = scnprintf(str, size, "rc: %s, mode = %d, bitrate = %d(%d), qp = %d\n",
+ venc->params.rc_enable ? "enable" : "disable",
+ venc->params.rc_mode,
+ venc->params.bitrate,
+ venc->params.bitrate_max,
+ venc->params.i_frame_qp);
+ break;
+ case 9:
+ num = scnprintf(str, size, "sar: enable = %d, idc = %d, %d x %d\n",
+ venc->params.sar.enable,
+ venc->params.sar.idc,
+ venc->params.sar.width,
+ venc->params.sar.height);
+
+ break;
+ case 10:
+ num = scnprintf(str, size,
+ "colorspace: primaries = %d, transfer = %d, matrix = %d, full_range = %d\n",
+ venc->params.color.primaries,
+ venc->params.color.transfer,
+ venc->params.color.matrix,
+ venc->params.color.full_range);
+ break;
+ case 11:
+ num = scnprintf(str, size, "skipped: count = %d, bytes = %d\n",
+ venc->skipped_count, venc->skipped_bytes);
+ break;
+ default:
+ break;
+ }
+
+ return num;
+}
+
+static struct vpu_inst_ops venc_inst_ops = {
+ .ctrl_init = venc_ctrl_init,
+ .check_ready = venc_check_ready,
+ .input_done = venc_input_done,
+ .get_one_frame = venc_frame_encoded,
+ .stop_done = venc_stop_done,
+ .event_notify = venc_event_notify,
+ .release = venc_release,
+ .cleanup = venc_cleanup,
+ .start = venc_start_session,
+ .mem_request = venc_request_mem_resource,
+ .stop = venc_stop_session,
+ .process_output = venc_process_output,
+ .process_capture = venc_process_capture,
+ .on_queue_empty = venc_on_queue_empty,
+ .get_debug_info = venc_get_debug_info,
+ .wait_prepare = vpu_inst_unlock,
+ .wait_finish = vpu_inst_lock,
+};
+
+static void venc_init(struct file *file)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc;
+ struct v4l2_format f;
+ struct v4l2_streamparm parm;
+
+ venc = inst->priv;
+ venc->params.qp_min = 1;
+ venc->params.qp_max = 51;
+ venc->params.qp_min_i = 1;
+ venc->params.qp_max_i = 51;
+ venc->params.bitrate_min = BITRATE_MIN;
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+ venc_s_fmt(file, &inst->fh, &f);
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ venc_s_fmt(file, &inst->fh, &f);
+
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ parm.parm.capture.timeperframe.numerator = 1;
+ parm.parm.capture.timeperframe.denominator = 30;
+ venc_s_parm(file, &inst->fh, &parm);
+}
+
+static int venc_open(struct file *file)
+{
+ struct vpu_inst *inst;
+ struct venc_t *venc;
+ int ret;
+
+ inst = vzalloc(sizeof(*inst));
+ if (!inst)
+ return -ENOMEM;
+
+ venc = vzalloc(sizeof(*venc));
+ if (!venc) {
+ vfree(inst);
+ return -ENOMEM;
+ }
+
+ inst->ops = &venc_inst_ops;
+ inst->formats = venc_formats;
+ inst->type = VPU_CORE_TYPE_ENC;
+ inst->priv = venc;
+ INIT_LIST_HEAD(&venc->frames);
+ init_waitqueue_head(&venc->wq);
+
+ ret = vpu_v4l2_open(file, inst);
+ if (ret)
+ return ret;
+
+ inst->min_buffer_out = VENC_MIN_BUFFER_OUT;
+ inst->min_buffer_cap = VENC_MIN_BUFFER_CAP;
+ venc_init(file);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations venc_fops = {
+ .owner = THIS_MODULE,
+ .open = venc_open,
+ .release = vpu_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void)
+{
+ return &venc_ioctl_ops;
+}
+
+const struct v4l2_file_operations *venc_get_fops(void)
+{
+ return &venc_fops;
+}