Commit 92ec1a5c authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

Merge tag 'br-v5.18r' of git://linuxtv.org/hverkuil/media_tree into media_stage

Tag branch

* tag 'br-v5.18r' of git://linuxtv.org/hverkuil/media_tree:
  media: amphion: add amphion vpu entry in Kconfig and Makefile
  MAINTAINERS: add AMPHION VPU CODEC V4L2 driver entry
  media: amphion: implement malone decoder rpc interface
  media: amphion: implement windsor encoder rpc interface
  media: amphion: add v4l2 m2m vpu decoder stateful driver
  media: amphion: add v4l2 m2m vpu encoder stateful driver
  media: amphion: add vpu v4l2 m2m support
  media: amphion: implement vpu core communication based on mailbox
  media: amphion: add vpu core driver
  media: amphion: add amphion vpu device driver
  media: add nv12m_8l128 and nv12m_10be_8l128 video format.
  dt-bindings: media: amphion: add amphion video codec bindings
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parents 12fdba56 42356ecb
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/amphion,vpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amphion VPU codec IP
maintainers:
- Ming Qian <ming.qian@nxp.com>
- Shijie Qin <shijie.qin@nxp.com>
description: |-
The Amphion MXC video encoder(Windsor) and decoder(Malone) accelerators present
on NXP i.MX8Q SoCs.
properties:
$nodename:
pattern: "^vpu@[0-9a-f]+$"
compatible:
items:
- enum:
- nxp,imx8qm-vpu
- nxp,imx8qxp-vpu
reg:
maxItems: 1
power-domains:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges: true
patternProperties:
"^mailbox@[0-9a-f]+$":
description:
Each vpu encoder or decoder correspond a MU, which used for communication
between driver and firmware. Implement via mailbox on driver.
$ref: ../mailbox/fsl,mu.yaml#
"^vpu_core@[0-9a-f]+$":
description:
Each core correspond a decoder or encoder, need to configure them
separately. NXP i.MX8QM SoC has one decoder and two encoder, i.MX8QXP SoC
has one decoder and one encoder.
type: object
properties:
compatible:
items:
- enum:
- nxp,imx8q-vpu-decoder
- nxp,imx8q-vpu-encoder
reg:
maxItems: 1
power-domains:
maxItems: 1
mbox-names:
items:
- const: tx0
- const: tx1
- const: rx
mboxes:
description:
List of phandle of 2 MU channels for tx, 1 MU channel for rx.
maxItems: 3
memory-region:
description:
Phandle to the reserved memory nodes to be associated with the
remoteproc device. The reserved memory nodes should be carveout nodes,
and should be defined as per the bindings in
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
items:
- description: region reserved for firmware image sections.
- description: region used for RPC shared memory between firmware and
driver.
required:
- compatible
- reg
- power-domains
- mbox-names
- mboxes
- memory-region
additionalProperties: false
required:
- compatible
- reg
- power-domains
additionalProperties: false
examples:
# Device node example for i.MX8QM platform:
- |
#include <dt-bindings/firmware/imx/rsrc.h>
vpu: vpu@2c000000 {
compatible = "nxp,imx8qm-vpu";
ranges = <0x2c000000 0x2c000000 0x2000000>;
reg = <0x2c000000 0x1000000>;
#address-cells = <1>;
#size-cells = <1>;
power-domains = <&pd IMX_SC_R_VPU>;
mu_m0: mailbox@2d000000 {
compatible = "fsl,imx6sx-mu";
reg = <0x2d000000 0x20000>;
interrupts = <0 472 4>;
#mbox-cells = <2>;
power-domains = <&pd IMX_SC_R_VPU_MU_0>;
};
mu1_m0: mailbox@2d020000 {
compatible = "fsl,imx6sx-mu";
reg = <0x2d020000 0x20000>;
interrupts = <0 473 4>;
#mbox-cells = <2>;
power-domains = <&pd IMX_SC_R_VPU_MU_1>;
};
mu2_m0: mailbox@2d040000 {
compatible = "fsl,imx6sx-mu";
reg = <0x2d040000 0x20000>;
interrupts = <0 474 4>;
#mbox-cells = <2>;
power-domains = <&pd IMX_SC_R_VPU_MU_2>;
};
vpu_core0: vpu_core@2d080000 {
compatible = "nxp,imx8q-vpu-decoder";
reg = <0x2d080000 0x10000>;
power-domains = <&pd IMX_SC_R_VPU_DEC_0>;
mbox-names = "tx0", "tx1", "rx";
mboxes = <&mu_m0 0 0>,
<&mu_m0 0 1>,
<&mu_m0 1 0>;
memory-region = <&decoder_boot>, <&decoder_rpc>;
};
vpu_core1: vpu_core@2d090000 {
compatible = "nxp,imx8q-vpu-encoder";
reg = <0x2d090000 0x10000>;
power-domains = <&pd IMX_SC_R_VPU_ENC_0>;
mbox-names = "tx0", "tx1", "rx";
mboxes = <&mu1_m0 0 0>,
<&mu1_m0 0 1>,
<&mu1_m0 1 0>;
memory-region = <&encoder1_boot>, <&encoder1_rpc>;
};
vpu_core2: vpu_core@2d0a0000 {
reg = <0x2d0a0000 0x10000>;
compatible = "nxp,imx8q-vpu-encoder";
power-domains = <&pd IMX_SC_R_VPU_ENC_1>;
mbox-names = "tx0", "tx1", "rx";
mboxes = <&mu2_m0 0 0>,
<&mu2_m0 0 1>,
<&mu2_m0 1 0>;
memory-region = <&encoder2_boot>, <&encoder2_rpc>;
};
};
...
......@@ -257,6 +257,8 @@ of the luma plane.
.. _V4L2-PIX-FMT-NV12-4L4:
.. _V4L2-PIX-FMT-NV12-16L16:
.. _V4L2-PIX-FMT-NV12-32L32:
.. _V4L2_PIX_FMT_NV12M_8L128:
.. _V4L2_PIX_FMT_NV12M_10BE_8L128:
Tiled NV12
----------
......@@ -281,21 +283,41 @@ If the vertical resolution is an odd number of tiles, the last row of
tiles is stored in linear order. The layouts of the luma and chroma
planes are identical.
``V4L2_PIX_FMT_NV12_4L4`` stores pixel in 4x4 tiles, and stores
``V4L2_PIX_FMT_NV12_4L4`` stores pixels in 4x4 tiles, and stores
tiles linearly in memory. The line stride and image height must be
aligned to a multiple of 4. The layouts of the luma and chroma planes are
identical.
``V4L2_PIX_FMT_NV12_16L16`` stores pixel in 16x16 tiles, and stores
``V4L2_PIX_FMT_NV12_16L16`` stores pixels in 16x16 tiles, and stores
tiles linearly in memory. The line stride and image height must be
aligned to a multiple of 16. The layouts of the luma and chroma planes are
identical.
``V4L2_PIX_FMT_NV12_32L32`` stores pixel in 32x32 tiles, and stores
``V4L2_PIX_FMT_NV12_32L32`` stores pixels in 32x32 tiles, and stores
tiles linearly in memory. The line stride and image height must be
aligned to a multiple of 32. The layouts of the luma and chroma planes are
identical.
``V4L2_PIX_FMT_NV12M_8L128`` is similar to ``V4L2_PIX_FMT_NV12M`` but stores
pixels in 2D 8x128 tiles, and stores tiles linearly in memory.
The image height must be aligned to a multiple of 128.
The layouts of the luma and chroma planes are identical.
``V4L2_PIX_FMT_NV12M_10BE_8L128`` is similar to ``V4L2_PIX_FMT_NV12M`` but stores
10 bits pixels in 2D 8x128 tiles, and stores tiles linearly in memory.
the data is arranged in big endian order.
The image height must be aligned to a multiple of 128.
The layouts of the luma and chroma planes are identical.
Note the tile size is 8bytes multiplied by 128 bytes,
it means that the low bits and high bits of one pixel may be in different tiles.
The 10 bit pixels are packed, so 5 bytes contain 4 10-bit pixels layout like
this (for luma):
byte 0: Y0(bits 9-2)
byte 1: Y0(bits 1-0) Y1(bits 9-4)
byte 2: Y1(bits 3-0) Y2(bits 9-6)
byte 3: Y2(bits 5-0) Y3(bits 9-8)
byte 4: Y3(bits 7-0)
``V4L2_PIX_FMT_MM21`` store luma pixel in 16x32 tiles, and chroma pixels
in 16x16 tiles. The line stride must be aligned to a multiple of 16 and the
image height must be aligned to a multiple of 32. The number of luma and chroma
......
......@@ -1030,6 +1030,15 @@ S: Maintained
F: Documentation/hid/amd-sfh*
F: drivers/hid/amd-sfh-hid/
AMPHION VPU CODEC V4L2 DRIVER
M: Ming Qian <ming.qian@nxp.com>
M: Shijie Qin <shijie.qin@nxp.com>
M: Zhou Peng <eagle.zhou@nxp.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/amphion,vpu.yaml
F: drivers/media/platform/amphion/
AMS AS73211 DRIVER
M: Christian Eggers <ceggers@arri.de>
L: linux-iio@vger.kernel.org
......
......@@ -648,6 +648,25 @@ config VIDEO_TEGRA_VDE
Support for the NVIDIA Tegra video decoder unit.
To compile this driver as a module choose m here.
config VIDEO_AMPHION_VPU
tristate "Amphion VPU (Video Processing Unit) Codec IP"
depends on ARCH_MXC || COMPILE_TEST
depends on MEDIA_SUPPORT
depends on VIDEO_DEV
depends on VIDEO_V4L2
select MEDIA_CONTROLLER
select V4L2_MEM2MEM_DEV
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
help
Amphion VPU Codec IP contains two parts: Windsor and Malone.
Windsor is encoder that supports H.264, and Malone is decoder
that supports H.264, HEVC, and other video formats.
This is a V4L2 driver for NXP MXC 8Q video accelerator hardware.
It accelerates encoding and decoding operations on
various NXP SoCs.
To compile this driver as a module choose m here.
endif # V4L_MEM2MEM_DRIVERS
# TI VIDEO PORT Helper Modules
......
......@@ -92,3 +92,5 @@ obj-y += sunxi/
obj-$(CONFIG_VIDEO_MESON_GE2D) += meson/ge2d/
obj-$(CONFIG_VIDEO_TEGRA_VDE) += tegra/vde/
obj-$(CONFIG_VIDEO_AMPHION_VPU) += amphion/
# SPDX-License-Identifier: GPL-2.0
# Makefile for NXP VPU driver
amphion-vpu-objs += vpu_drv.o \
vpu_core.o \
vpu_mbox.o \
vpu_v4l2.o \
vpu_helpers.o \
vpu_cmds.o \
vpu_msgs.o \
vpu_rpc.o \
vpu_imx8q.o \
vpu_windsor.o \
vpu_malone.o \
vpu_color.o \
vdec.o \
venc.o \
vpu_dbg.o
obj-$(CONFIG_VIDEO_AMPHION_VPU) += amphion-vpu.o
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2020-2021 NXP
*/
#ifndef _AMPHION_VPU_H
#define _AMPHION_VPU_H
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mem2mem.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/kfifo.h>
#define VPU_TIMEOUT msecs_to_jiffies(1000)
#define VPU_INST_NULL_ID (-1L)
#define VPU_MSG_BUFFER_SIZE (8192)
enum imx_plat_type {
IMX8QXP = 0,
IMX8QM = 1,
IMX8DM,
IMX8DX,
PLAT_TYPE_RESERVED
};
enum vpu_core_type {
VPU_CORE_TYPE_ENC = 0,
VPU_CORE_TYPE_DEC = 0x10,
};
struct vpu_dev;
struct vpu_resources {
enum imx_plat_type plat_type;
u32 mreg_base;
int (*setup)(struct vpu_dev *vpu);
int (*setup_encoder)(struct vpu_dev *vpu);
int (*setup_decoder)(struct vpu_dev *vpu);
int (*reset)(struct vpu_dev *vpu);
};
struct vpu_buffer {
void *virt;
dma_addr_t phys;
u32 length;
u32 bytesused;
struct device *dev;
};
struct vpu_func {
struct video_device *vfd;
struct v4l2_m2m_dev *m2m_dev;
enum vpu_core_type type;
int function;
};
struct vpu_dev {
void __iomem *base;
struct platform_device *pdev;
struct device *dev;
struct mutex lock; /* protect vpu device */
const struct vpu_resources *res;
struct list_head cores;
struct v4l2_device v4l2_dev;
struct vpu_func encoder;
struct vpu_func decoder;
struct media_device mdev;
struct delayed_work watchdog_work;
void (*get_vpu)(struct vpu_dev *vpu);
void (*put_vpu)(struct vpu_dev *vpu);
void (*get_enc)(struct vpu_dev *vpu);
void (*put_enc)(struct vpu_dev *vpu);
void (*get_dec)(struct vpu_dev *vpu);
void (*put_dec)(struct vpu_dev *vpu);
atomic_t ref_vpu;
atomic_t ref_enc;
atomic_t ref_dec;
struct dentry *debugfs;
};
struct vpu_format {
u32 pixfmt;
unsigned int num_planes;
u32 type;
u32 flags;
u32 width;
u32 height;
u32 sizeimage[VIDEO_MAX_PLANES];
u32 bytesperline[VIDEO_MAX_PLANES];
u32 field;
};
struct vpu_core_resources {
enum vpu_core_type type;
const char *fwname;
u32 stride;
u32 max_width;
u32 min_width;
u32 step_width;
u32 max_height;
u32 min_height;
u32 step_height;
u32 rpc_size;
u32 fwlog_size;
u32 act_size;
};
struct vpu_mbox {
char name[20];
struct mbox_client cl;
struct mbox_chan *ch;
bool block;
};
enum vpu_core_state {
VPU_CORE_DEINIT = 0,
VPU_CORE_ACTIVE,
VPU_CORE_SNAPSHOT,
VPU_CORE_HANG
};
struct vpu_core {
void __iomem *base;
struct platform_device *pdev;
struct device *dev;
struct device *parent;
struct device *pd;
struct device_link *pd_link;
struct mutex lock; /* protect vpu core */
struct mutex cmd_lock; /* Lock vpu command */
struct list_head list;
enum vpu_core_type type;
int id;
const struct vpu_core_resources *res;
unsigned long instance_mask;
u32 supported_instance_count;
unsigned long hang_mask;
u32 request_count;
struct list_head instances;
enum vpu_core_state state;
u32 fw_version;
struct vpu_buffer fw;
struct vpu_buffer rpc;
struct vpu_buffer log;
struct vpu_buffer act;
struct vpu_mbox tx_type;
struct vpu_mbox tx_data;
struct vpu_mbox rx;
unsigned long cmd_seq;
wait_queue_head_t ack_wq;
struct completion cmp;
struct workqueue_struct *workqueue;
struct work_struct msg_work;
struct delayed_work msg_delayed_work;
struct kfifo msg_fifo;
void *msg_buffer;
unsigned int msg_buffer_size;
struct vpu_dev *vpu;
void *iface;
struct dentry *debugfs;
struct dentry *debugfs_fwlog;
};
enum vpu_codec_state {
VPU_CODEC_STATE_DEINIT = 1,
VPU_CODEC_STATE_CONFIGURED,
VPU_CODEC_STATE_START,
VPU_CODEC_STATE_STARTED,
VPU_CODEC_STATE_ACTIVE,
VPU_CODEC_STATE_SEEK,
VPU_CODEC_STATE_STOP,
VPU_CODEC_STATE_DRAIN,
VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE,
};
struct vpu_frame_info {
u32 type;
u32 id;
u32 sequence;
u32 luma;
u32 chroma_u;
u32 chroma_v;
u32 data_offset;
u32 flags;
u32 skipped;
s64 timestamp;
};
struct vpu_inst;
struct vpu_inst_ops {
int (*ctrl_init)(struct vpu_inst *inst);
int (*start)(struct vpu_inst *inst, u32 type);
int (*stop)(struct vpu_inst *inst, u32 type);
int (*abort)(struct vpu_inst *inst);
bool (*check_ready)(struct vpu_inst *inst, unsigned int type);
void (*buf_done)(struct vpu_inst *inst, struct vpu_frame_info *frame);
void (*event_notify)(struct vpu_inst *inst, u32 event, void *data);
void (*release)(struct vpu_inst *inst);
void (*cleanup)(struct vpu_inst *inst);
void (*mem_request)(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);
void (*input_done)(struct vpu_inst *inst);
void (*stop_done)(struct vpu_inst *inst);
int (*process_output)(struct vpu_inst *inst, struct vb2_buffer *vb);
int (*process_capture)(struct vpu_inst *inst, struct vb2_buffer *vb);
int (*get_one_frame)(struct vpu_inst *inst, void *info);
void (*on_queue_empty)(struct vpu_inst *inst, u32 type);
int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i);
void (*wait_prepare)(struct vpu_inst *inst);
void (*wait_finish)(struct vpu_inst *inst);
};
struct vpu_inst {
struct list_head list;
struct mutex lock; /* v4l2 and videobuf2 lock */
struct vpu_dev *vpu;
struct vpu_core *core;
struct device *dev;
int id;
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
atomic_t ref_count;
int (*release)(struct vpu_inst *inst);
enum vpu_codec_state state;
enum vpu_core_type type;
struct workqueue_struct *workqueue;
struct work_struct msg_work;
struct kfifo msg_fifo;
u8 msg_buffer[VPU_MSG_BUFFER_SIZE];
struct vpu_buffer stream_buffer;
bool use_stream_buffer;
struct vpu_buffer act;
struct list_head cmd_q;
void *pending;
struct vpu_inst_ops *ops;
const struct vpu_format *formats;
struct vpu_format out_format;
struct vpu_format cap_format;
u32 min_buffer_cap;
u32 min_buffer_out;
struct v4l2_rect crop;
u32 colorspace;
u8 ycbcr_enc;
u8 quantization;
u8 xfer_func;
u32 sequence;
u32 extra_size;
u32 flows[16];
u32 flow_idx;
pid_t pid;
pid_t tgid;
struct dentry *debugfs;
void *priv;
};
#define call_vop(inst, op, args...) \
((inst)->ops->op ? (inst)->ops->op(inst, ##args) : 0) \
#define call_void_vop(inst, op, args...) \
do { \
if ((inst)->ops->op) \
(inst)->ops->op(inst, ##args); \
} while (0)
enum {
VPU_BUF_STATE_IDLE = 0,
VPU_BUF_STATE_INUSE,
VPU_BUF_STATE_DECODED,
VPU_BUF_STATE_READY,
VPU_BUF_STATE_SKIP,
VPU_BUF_STATE_ERROR
};
struct vpu_vb2_buffer {
struct v4l2_m2m_buffer m2m_buf;
dma_addr_t luma;
dma_addr_t chroma_u;
dma_addr_t chroma_v;
unsigned int state;
u32 tag;
};
void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val);
u32 vpu_readl(struct vpu_dev *vpu, u32 reg);
static inline struct vpu_vb2_buffer *to_vpu_vb2_buffer(struct vb2_v4l2_buffer *vbuf)
{
struct v4l2_m2m_buffer *m2m_buf = container_of(vbuf, struct v4l2_m2m_buffer, vb);
return container_of(m2m_buf, struct vpu_vb2_buffer, m2m_buf);
}
static inline const char *vpu_core_type_desc(enum vpu_core_type type)
{
return type == VPU_CORE_TYPE_ENC ? "encoder" : "decoder";
}
static inline struct vpu_inst *to_inst(struct file *filp)
{
return container_of(filp->private_data, struct vpu_inst, fh);
}
#define ctrl_to_inst(ctrl) \
container_of((ctrl)->handler, struct vpu_inst, ctrl_handler)
const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void);
const struct v4l2_file_operations *venc_get_fops(void);
const struct v4l2_ioctl_ops *vdec_get_ioctl_ops(void);
const struct v4l2_file_operations *vdec_get_fops(void);
int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func);
void vpu_remove_func(struct vpu_func *func);
struct vpu_inst *vpu_inst_get(struct vpu_inst *inst);
void vpu_inst_put(struct vpu_inst *inst);
struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type);
void vpu_release_core(struct vpu_core *core);
int vpu_inst_register(struct vpu_inst *inst);
int vpu_inst_unregister(struct vpu_inst *inst);
const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst);
int vpu_inst_create_dbgfs_file(struct vpu_inst *inst);
int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst);
int vpu_core_create_dbgfs_file(struct vpu_core *core);
int vpu_core_remove_dbgfs_file(struct vpu_core *core);
void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow);
int vpu_core_driver_init(void);
void vpu_core_driver_exit(void);
extern bool debug;
#define vpu_trace(dev, fmt, arg...) \
do { \
if (debug) \
dev_info(dev, "%s: " fmt, __func__, ## arg); \
} while (0)
#endif
// 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/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_cmds.h"
#include "vpu_rpc.h"
#include "vpu_mbox.h"
struct vpu_cmd_request {
u32 request;
u32 response;
u32 handled;
};
struct vpu_cmd_t {
struct list_head list;
u32 id;
struct vpu_cmd_request *request;
struct vpu_rpc_event *pkt;
unsigned long key;
};
static struct vpu_cmd_request vpu_cmd_requests[] = {
{
.request = VPU_CMD_ID_CONFIGURE_CODEC,
.response = VPU_MSG_ID_MEM_REQUEST,
.handled = 1,
},
{
.request = VPU_CMD_ID_START,
.response = VPU_MSG_ID_START_DONE,
.handled = 0,
},
{
.request = VPU_CMD_ID_STOP,
.response = VPU_MSG_ID_STOP_DONE,
.handled = 0,
},
{
.request = VPU_CMD_ID_ABORT,
.response = VPU_MSG_ID_ABORT_DONE,
.handled = 0,
},
{
.request = VPU_CMD_ID_RST_BUF,
.response = VPU_MSG_ID_BUF_RST,
.handled = 1,
},
};
static int vpu_cmd_send(struct vpu_core *core, struct vpu_rpc_event *pkt)
{
int ret = 0;
ret = vpu_iface_send_cmd(core, pkt);
if (ret)
return ret;
/*write cmd data to cmd buffer before trigger a cmd interrupt*/
mb();
vpu_mbox_send_type(core, COMMAND);
return ret;
}
static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data)
{
struct vpu_cmd_t *cmd;
int i;
int ret;
cmd = vzalloc(sizeof(*cmd));
if (!cmd)
return NULL;
cmd->pkt = vzalloc(sizeof(*cmd->pkt));
if (!cmd->pkt) {
vfree(cmd);
return NULL;
}
cmd->id = id;
ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data);
if (ret) {
dev_err(inst->dev, "iface pack cmd(%d) fail\n", id);
vfree(cmd->pkt);
vfree(cmd);
return NULL;
}
for (i = 0; i < ARRAY_SIZE(vpu_cmd_requests); i++) {
if (vpu_cmd_requests[i].request == id) {
cmd->request = &vpu_cmd_requests[i];
break;
}
}
return cmd;
}
static void vpu_free_cmd(struct vpu_cmd_t *cmd)
{
if (!cmd)
return;
if (cmd->pkt)
vfree(cmd->pkt);
vfree(cmd);
}
static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd)
{
int ret;
dev_dbg(inst->dev, "[%d]send cmd(0x%x)\n", inst->id, cmd->id);
vpu_iface_pre_send_cmd(inst);
ret = vpu_cmd_send(inst->core, cmd->pkt);
if (!ret) {
vpu_iface_post_send_cmd(inst);
vpu_inst_record_flow(inst, cmd->id);
} else {
dev_err(inst->dev, "[%d] iface send cmd(0x%x) fail\n", inst->id, cmd->id);
}
return ret;
}
static void vpu_process_cmd_request(struct vpu_inst *inst)
{
struct vpu_cmd_t *cmd;
struct vpu_cmd_t *tmp;
if (!inst || inst->pending)
return;
list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) {
list_del_init(&cmd->list);
if (vpu_session_process_cmd(inst, cmd))
dev_err(inst->dev, "[%d] process cmd(%d) fail\n", inst->id, cmd->id);
if (cmd->request) {
inst->pending = (void *)cmd;
break;
}
vpu_free_cmd(cmd);
}
}
static int vpu_request_cmd(struct vpu_inst *inst, u32 id, void *data,
unsigned long *key, int *sync)
{
struct vpu_core *core;
struct vpu_cmd_t *cmd;
if (!inst || !inst->core)
return -EINVAL;
core = inst->core;
cmd = vpu_alloc_cmd(inst, id, data);
if (!cmd)
return -ENOMEM;
mutex_lock(&core->cmd_lock);
cmd->key = core->cmd_seq++;
if (key)
*key = cmd->key;
if (sync)
*sync = cmd->request ? true : false;
list_add_tail(&cmd->list, &inst->cmd_q);
vpu_process_cmd_request(inst);
mutex_unlock(&core->cmd_lock);
return 0;
}
static void vpu_clear_pending(struct vpu_inst *inst)
{
if (!inst || !inst->pending)
return;
vpu_free_cmd(inst->pending);
wake_up_all(&inst->core->ack_wq);
inst->pending = NULL;
}
static bool vpu_check_response(struct vpu_cmd_t *cmd, u32 response, u32 handled)
{
struct vpu_cmd_request *request;
if (!cmd || !cmd->request)
return false;
request = cmd->request;
if (request->response != response)
return false;
if (request->handled != handled)
return false;
return true;
}
int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled)
{
struct vpu_core *core;
if (!inst || !inst->core)
return -EINVAL;
core = inst->core;
mutex_lock(&core->cmd_lock);
if (vpu_check_response(inst->pending, response, handled))
vpu_clear_pending(inst);
vpu_process_cmd_request(inst);
mutex_unlock(&core->cmd_lock);
return 0;
}
void vpu_clear_request(struct vpu_inst *inst)
{
struct vpu_cmd_t *cmd;
struct vpu_cmd_t *tmp;
mutex_lock(&inst->core->cmd_lock);
if (inst->pending)
vpu_clear_pending(inst);
list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) {
list_del_init(&cmd->list);
vpu_free_cmd(cmd);
}
mutex_unlock(&inst->core->cmd_lock);
}
static bool check_is_responsed(struct vpu_inst *inst, unsigned long key)
{
struct vpu_core *core = inst->core;
struct vpu_cmd_t *cmd;
bool flag = true;
mutex_lock(&core->cmd_lock);
cmd = inst->pending;
if (cmd && key == cmd->key) {
flag = false;
goto exit;
}
list_for_each_entry(cmd, &inst->cmd_q, list) {
if (key == cmd->key) {
flag = false;
break;
}
}
exit:
mutex_unlock(&core->cmd_lock);
return flag;
}
static int sync_session_response(struct vpu_inst *inst, unsigned long key)
{
struct vpu_core *core;
if (!inst || !inst->core)
return -EINVAL;
core = inst->core;
call_void_vop(inst, wait_prepare);
wait_event_timeout(core->ack_wq, check_is_responsed(inst, key), VPU_TIMEOUT);
call_void_vop(inst, wait_finish);
if (!check_is_responsed(inst, key)) {
dev_err(inst->dev, "[%d] sync session timeout\n", inst->id);
set_bit(inst->id, &core->hang_mask);
mutex_lock(&inst->core->cmd_lock);
vpu_clear_pending(inst);
mutex_unlock(&inst->core->cmd_lock);
return -EINVAL;
}
return 0;
}
static int vpu_session_send_cmd(struct vpu_inst *inst, u32 id, void *data)
{
unsigned long key;
int sync = false;
int ret = -EINVAL;
if (inst->id < 0)
return -EINVAL;
ret = vpu_request_cmd(inst, id, data, &key, &sync);
if (!ret && sync)
ret = sync_session_response(inst, key);
if (ret)
dev_err(inst->dev, "[%d] send cmd(0x%x) fail\n", inst->id, id);
return ret;
}
int vpu_session_configure_codec(struct vpu_inst *inst)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_CONFIGURE_CODEC, NULL);
}
int vpu_session_start(struct vpu_inst *inst)
{
vpu_trace(inst->dev, "[%d]\n", inst->id);
return vpu_session_send_cmd(inst, VPU_CMD_ID_START, NULL);
}
int vpu_session_stop(struct vpu_inst *inst)
{
int ret;
vpu_trace(inst->dev, "[%d]\n", inst->id);
ret = vpu_session_send_cmd(inst, VPU_CMD_ID_STOP, NULL);
/* workaround for a firmware bug,
* if the next command is too close after stop cmd,
* the firmware may enter wfi wrongly.
*/
usleep_range(3000, 5000);
return ret;
}
int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_FRAME_ENCODE, &timestamp);
}
int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_ALLOC, fs);
}
int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_RELEASE, fs);
}
int vpu_session_abort(struct vpu_inst *inst)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_ABORT, NULL);
}
int vpu_session_rst_buf(struct vpu_inst *inst)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_RST_BUF, NULL);
}
int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_TIMESTAMP, info);
}
int vpu_session_update_parameters(struct vpu_inst *inst, void *arg)
{
if (inst->type & VPU_CORE_TYPE_DEC)
vpu_iface_set_decode_params(inst, arg, 1);
else
vpu_iface_set_encode_params(inst, arg, 1);
return vpu_session_send_cmd(inst, VPU_CMD_ID_UPDATE_PARAMETER, arg);
}
int vpu_session_debug(struct vpu_inst *inst)
{
return vpu_session_send_cmd(inst, VPU_CMD_ID_DEBUG, NULL);
}
int vpu_core_snapshot(struct vpu_core *core)
{
struct vpu_inst *inst;
int ret;
if (!core || list_empty(&core->instances))
return 0;
inst = list_first_entry(&core->instances, struct vpu_inst, list);
reinit_completion(&core->cmp);
ret = vpu_session_send_cmd(inst, VPU_CMD_ID_SNAPSHOT, NULL);
if (ret)
return ret;
ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
if (!ret) {
dev_err(core->dev, "snapshot timeout\n");
return -EINVAL;
}
return 0;
}
int vpu_core_sw_reset(struct vpu_core *core)
{
struct vpu_rpc_event pkt;
int ret;
memset(&pkt, 0, sizeof(pkt));
vpu_iface_pack_cmd(core, &pkt, 0, VPU_CMD_ID_FIRM_RESET, NULL);
reinit_completion(&core->cmp);
mutex_lock(&core->cmd_lock);
ret = vpu_cmd_send(core, &pkt);
mutex_unlock(&core->cmd_lock);
if (ret)
return ret;
ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
if (!ret) {
dev_err(core->dev, "sw reset timeout\n");
return -EINVAL;
}
return 0;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2020-2021 NXP
*/
#ifndef _AMPHION_VPU_CMDS_H
#define _AMPHION_VPU_CMDS_H
int vpu_session_configure_codec(struct vpu_inst *inst);
int vpu_session_start(struct vpu_inst *inst);
int vpu_session_stop(struct vpu_inst *inst);
int vpu_session_abort(struct vpu_inst *inst);
int vpu_session_rst_buf(struct vpu_inst *inst);
int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp);
int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs);
int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs);
int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info);
int vpu_session_update_parameters(struct vpu_inst *inst, void *arg);
int vpu_core_snapshot(struct vpu_core *core);
int vpu_core_sw_reset(struct vpu_core *core);
int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled);
void vpu_clear_request(struct vpu_inst *inst);
int vpu_session_debug(struct vpu_inst *inst);
#endif
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2020-2021 NXP
*/
#ifndef _AMPHION_VPU_CODEC_H
#define _AMPHION_VPU_CODEC_H
struct vpu_encode_params {
u32 input_format;
u32 codec_format;
u32 profile;
u32 tier;
u32 level;
struct v4l2_fract frame_rate;
u32 src_stride;
u32 src_width;
u32 src_height;
struct v4l2_rect crop;
u32 out_width;
u32 out_height;
u32 gop_length;
u32 bframes;
u32 rc_enable;
u32 rc_mode;
u32 bitrate;
u32 bitrate_min;
u32 bitrate_max;
u32 i_frame_qp;
u32 p_frame_qp;
u32 b_frame_qp;
u32 qp_min;
u32 qp_max;
u32 qp_min_i;
u32 qp_max_i;
struct {
u32 enable;
u32 idc;
u32 width;
u32 height;
} sar;
struct {
u32 primaries;
u32 transfer;
u32 matrix;
u32 full_range;
} color;
};
struct vpu_decode_params {
u32 codec_format;
u32 output_format;
u32 b_dis_reorder;
u32 b_non_frame;
u32 frame_count;
u32 end_flag;
struct {
u32 base;
u32 size;
} udata;
};
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020-2021 NXP
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <media/v4l2-device.h>
#include "vpu.h"
#include "vpu_helpers.h"
static const u8 colorprimaries[] = {
0,
V4L2_COLORSPACE_REC709, /*Rec. ITU-R BT.709-6*/
0,
0,
V4L2_COLORSPACE_470_SYSTEM_M, /*Rec. ITU-R BT.470-6 System M*/
V4L2_COLORSPACE_470_SYSTEM_BG, /*Rec. ITU-R BT.470-6 System B, G*/
V4L2_COLORSPACE_SMPTE170M, /*SMPTE170M*/
V4L2_COLORSPACE_SMPTE240M, /*SMPTE240M*/
0, /*Generic film*/
V4L2_COLORSPACE_BT2020, /*Rec. ITU-R BT.2020-2*/
0, /*SMPTE ST 428-1*/
};
static const u8 colortransfers[] = {
0,
V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.709-6*/
0,
0,
0, /*Rec. ITU-R BT.470-6 System M*/
0, /*Rec. ITU-R BT.470-6 System B, G*/
V4L2_XFER_FUNC_709, /*SMPTE170M*/
V4L2_XFER_FUNC_SMPTE240M, /*SMPTE240M*/
V4L2_XFER_FUNC_NONE, /*Linear transfer characteristics*/
0,
0,
0, /*IEC 61966-2-4*/
0, /*Rec. ITU-R BT.1361-0 extended colour gamut*/
V4L2_XFER_FUNC_SRGB, /*IEC 61966-2-1 sRGB or sYCC*/
V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (10 bit system)*/
V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (12 bit system)*/
V4L2_XFER_FUNC_SMPTE2084, /*SMPTE ST 2084*/
0, /*SMPTE ST 428-1*/
0 /*Rec. ITU-R BT.2100-0 hybrid log-gamma (HLG)*/
};
static const u8 colormatrixcoefs[] = {
0,
V4L2_YCBCR_ENC_709, /*Rec. ITU-R BT.709-6*/
0,
0,
0, /*Title 47 Code of Federal Regulations*/
V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 625*/
V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 525*/
V4L2_YCBCR_ENC_SMPTE240M, /*SMPTE240M*/
0,
V4L2_YCBCR_ENC_BT2020, /*Rec. ITU-R BT.2020-2*/
V4L2_YCBCR_ENC_BT2020_CONST_LUM /*Rec. ITU-R BT.2020-2 constant*/
};
u32 vpu_color_cvrt_primaries_v2i(u32 primaries)
{
return vpu_helper_find_in_array_u8(colorprimaries, ARRAY_SIZE(colorprimaries), primaries);
}
u32 vpu_color_cvrt_primaries_i2v(u32 primaries)
{
return primaries < ARRAY_SIZE(colorprimaries) ? colorprimaries[primaries] : 0;
}
u32 vpu_color_cvrt_transfers_v2i(u32 transfers)
{
return vpu_helper_find_in_array_u8(colortransfers, ARRAY_SIZE(colortransfers), transfers);
}
u32 vpu_color_cvrt_transfers_i2v(u32 transfers)
{
return transfers < ARRAY_SIZE(colortransfers) ? colortransfers[transfers] : 0;
}
u32 vpu_color_cvrt_matrix_v2i(u32 matrix)
{
return vpu_helper_find_in_array_u8(colormatrixcoefs, ARRAY_SIZE(colormatrixcoefs), matrix);
}
u32 vpu_color_cvrt_matrix_i2v(u32 matrix)
{
return matrix < ARRAY_SIZE(colormatrixcoefs) ? colormatrixcoefs[matrix] : 0;
}
u32 vpu_color_cvrt_full_range_v2i(u32 full_range)
{
return (full_range == V4L2_QUANTIZATION_FULL_RANGE);
}
u32 vpu_color_cvrt_full_range_i2v(u32 full_range)
{
if (full_range)
return V4L2_QUANTIZATION_FULL_RANGE;
return V4L2_QUANTIZATION_LIM_RANGE;
}
int vpu_color_check_primaries(u32 primaries)
{
return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL;
}
int vpu_color_check_transfers(u32 transfers)
{
return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL;
}
int vpu_color_check_matrix(u32 matrix)
{
return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL;
}
int vpu_color_check_full_range(u32 full_range)
{
int ret = -EINVAL;
switch (full_range) {
case V4L2_QUANTIZATION_FULL_RANGE:
case V4L2_QUANTIZATION_LIM_RANGE:
ret = 0;
break;
default:
break;
}
return ret;
}
int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range)
{
u32 transfers;
u32 matrix;
u32 full_range;
switch (primaries) {
case V4L2_COLORSPACE_REC709:
transfers = V4L2_XFER_FUNC_709;
matrix = V4L2_YCBCR_ENC_709;
break;
case V4L2_COLORSPACE_470_SYSTEM_M:
case V4L2_COLORSPACE_470_SYSTEM_BG:
case V4L2_COLORSPACE_SMPTE170M:
transfers = V4L2_XFER_FUNC_709;
matrix = V4L2_YCBCR_ENC_601;
break;
case V4L2_COLORSPACE_SMPTE240M:
transfers = V4L2_XFER_FUNC_SMPTE240M;
matrix = V4L2_YCBCR_ENC_SMPTE240M;
break;
case V4L2_COLORSPACE_BT2020:
transfers = V4L2_XFER_FUNC_709;
matrix = V4L2_YCBCR_ENC_BT2020;
break;
default:
transfers = V4L2_XFER_FUNC_DEFAULT;
matrix = V4L2_YCBCR_ENC_DEFAULT;
break;
}
full_range = V4L2_QUANTIZATION_LIM_RANGE;
if (ptransfers)
*ptransfers = transfers;
if (pmatrix)
*pmatrix = matrix;
if (pfull_range)
*pfull_range = full_range;
return 0;
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2020-2021 NXP
*/
#ifndef _AMPHION_VPU_CORE_H
#define _AMPHION_VPU_CORE_H
void csr_writel(struct vpu_core *core, u32 reg, u32 val);
u32 csr_readl(struct vpu_core *core, u32 reg);
int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf);
void vpu_free_dma(struct vpu_buffer *buf);
struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2020-2021 NXP
*/
#ifndef _AMPHION_VPU_HELPERS_H
#define _AMPHION_VPU_HELPERS_H
struct vpu_pair {
u32 src;
u32 dst;
};
#define MAKE_TIMESTAMP(s, ns) (((s32)(s) * NSEC_PER_SEC) + (ns))
#define VPU_INVALID_TIMESTAMP MAKE_TIMESTAMP(-1, 0)
int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x);
bool vpu_helper_check_type(struct vpu_inst *inst, u32 type);
const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt);
const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index);
u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width);
u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height);
u32 vpu_helper_get_plane_size(u32 fmt, u32 width, u32 height, int plane_no,
u32 stride, u32 interlaced, u32 *pbl);
u32 vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer,
u32 *rptr, u32 size, void *dst);
u32 vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer,
u32 *wptr, u32 size, void *src);
u32 vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer,
u32 *wptr, u8 val, u32 size);
u32 vpu_helper_get_free_space(struct vpu_inst *inst);
u32 vpu_helper_get_used_space(struct vpu_inst *inst);
int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
void vpu_helper_get_kmp_next(const u8 *pattern, int *next, int size);
int vpu_helper_kmp_search(u8 *s, int s_len, const u8 *p, int p_len, int *next);
int vpu_helper_kmp_search_in_stream_buffer(struct vpu_buffer *stream_buffer,
u32 offset, int bytesused,
const u8 *p, int p_len, int *next);
int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer,
u32 pixelformat, u32 offset, u32 bytesused);
static inline u32 vpu_helper_step_walk(struct vpu_buffer *stream_buffer, u32 pos, u32 step)
{
pos += step;
if (pos > stream_buffer->phys + stream_buffer->length)
pos -= stream_buffer->length;
return pos;
}
static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos)
{
u8 *pdata = (u8 *)stream_buffer->virt;
return pdata[pos % stream_buffer->length];
}
int vpu_color_check_primaries(u32 primaries);
int vpu_color_check_transfers(u32 transfers);
int vpu_color_check_matrix(u32 matrix);
int vpu_color_check_full_range(u32 full_range);
u32 vpu_color_cvrt_primaries_v2i(u32 primaries);
u32 vpu_color_cvrt_primaries_i2v(u32 primaries);
u32 vpu_color_cvrt_transfers_v2i(u32 transfers);
u32 vpu_color_cvrt_transfers_i2v(u32 transfers);
u32 vpu_color_cvrt_matrix_v2i(u32 matrix);
u32 vpu_color_cvrt_matrix_i2v(u32 matrix);
u32 vpu_color_cvrt_full_range_v2i(u32 full_range);
u32 vpu_color_cvrt_full_range_i2v(u32 full_range);
int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range);
int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src);
int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment