mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
Tag branch
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRSrcquik9wuZrNjXJrQl33mxwedgUCYiYwEwAKCRBrQl33mxwe dpaMAQC3U8kTkiEs4ejMkd3enkLTg4u7soi6Pw/5Lk+rhfAcFwD+IL28W2QRbutF diW77SktxqTRnSX3MFLfRBr1hG1XVQ8= =6z33 -----END PGP SIGNATURE----- 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: Mauro Carvalho Chehab <mchehab@kernel.org>
This commit is contained in:
commit
92ec1a5c27
180
Documentation/devicetree/bindings/media/amphion,vpu.yaml
Normal file
180
Documentation/devicetree/bindings/media/amphion,vpu.yaml
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
# 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/
|
||||
|
|
|
|||
20
drivers/media/platform/amphion/Makefile
Normal file
20
drivers/media/platform/amphion/Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# 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
|
||||
1691
drivers/media/platform/amphion/vdec.c
Normal file
1691
drivers/media/platform/amphion/vdec.c
Normal file
File diff suppressed because it is too large
Load Diff
1358
drivers/media/platform/amphion/venc.c
Normal file
1358
drivers/media/platform/amphion/venc.c
Normal file
File diff suppressed because it is too large
Load Diff
362
drivers/media/platform/amphion/vpu.h
Normal file
362
drivers/media/platform/amphion/vpu.h
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/* 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
|
||||
433
drivers/media/platform/amphion/vpu_cmds.c
Normal file
433
drivers/media/platform/amphion/vpu_cmds.c
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
// 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, ×tamp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
25
drivers/media/platform/amphion/vpu_cmds.h
Normal file
25
drivers/media/platform/amphion/vpu_cmds.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* 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
|
||||
68
drivers/media/platform/amphion/vpu_codec.h
Normal file
68
drivers/media/platform/amphion/vpu_codec.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* 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
|
||||
183
drivers/media/platform/amphion/vpu_color.c
Normal file
183
drivers/media/platform/amphion/vpu_color.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// 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;
|
||||
}
|
||||
871
drivers/media/platform/amphion/vpu_core.c
Normal file
871
drivers/media/platform/amphion/vpu_core.c
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
// 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/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "vpu.h"
|
||||
#include "vpu_defs.h"
|
||||
#include "vpu_core.h"
|
||||
#include "vpu_mbox.h"
|
||||
#include "vpu_msgs.h"
|
||||
#include "vpu_rpc.h"
|
||||
#include "vpu_cmds.h"
|
||||
|
||||
void csr_writel(struct vpu_core *core, u32 reg, u32 val)
|
||||
{
|
||||
writel(val, core->base + reg);
|
||||
}
|
||||
|
||||
u32 csr_readl(struct vpu_core *core, u32 reg)
|
||||
{
|
||||
return readl(core->base + reg);
|
||||
}
|
||||
|
||||
static int vpu_core_load_firmware(struct vpu_core *core)
|
||||
{
|
||||
const struct firmware *pfw = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!core->fw.virt) {
|
||||
dev_err(core->dev, "firmware buffer is not ready\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = request_firmware(&pfw, core->res->fwname, core->dev);
|
||||
dev_dbg(core->dev, "request_firmware %s : %d\n", core->res->fwname, ret);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "request firmware %s failed, ret = %d\n",
|
||||
core->res->fwname, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (core->fw.length < pfw->size) {
|
||||
dev_err(core->dev, "firmware buffer size want %zu, but %d\n",
|
||||
pfw->size, core->fw.length);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(core->fw.virt, 0, core->fw.length);
|
||||
memcpy(core->fw.virt, pfw->data, pfw->size);
|
||||
core->fw.bytesused = pfw->size;
|
||||
ret = vpu_iface_on_firmware_loaded(core);
|
||||
exit:
|
||||
release_firmware(pfw);
|
||||
pfw = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_core_boot_done(struct vpu_core *core)
|
||||
{
|
||||
u32 fw_version;
|
||||
|
||||
fw_version = vpu_iface_get_version(core);
|
||||
dev_info(core->dev, "%s firmware version : %d.%d.%d\n",
|
||||
vpu_core_type_desc(core->type),
|
||||
(fw_version >> 16) & 0xff,
|
||||
(fw_version >> 8) & 0xff,
|
||||
fw_version & 0xff);
|
||||
core->supported_instance_count = vpu_iface_get_max_instance_count(core);
|
||||
if (core->res->act_size) {
|
||||
u32 count = core->act.length / core->res->act_size;
|
||||
|
||||
core->supported_instance_count = min(core->supported_instance_count, count);
|
||||
}
|
||||
core->fw_version = fw_version;
|
||||
core->state = VPU_CORE_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_core_wait_boot_done(struct vpu_core *core)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
|
||||
if (!ret) {
|
||||
dev_err(core->dev, "boot timeout\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return vpu_core_boot_done(core);
|
||||
}
|
||||
|
||||
static int vpu_core_boot(struct vpu_core *core, bool load)
|
||||
{
|
||||
int ret;
|
||||
|
||||
reinit_completion(&core->cmp);
|
||||
if (load) {
|
||||
ret = vpu_core_load_firmware(core);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
vpu_iface_boot_core(core);
|
||||
return vpu_core_wait_boot_done(core);
|
||||
}
|
||||
|
||||
static int vpu_core_shutdown(struct vpu_core *core)
|
||||
{
|
||||
return vpu_iface_shutdown_core(core);
|
||||
}
|
||||
|
||||
static int vpu_core_restore(struct vpu_core *core)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vpu_core_sw_reset(core);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vpu_core_boot_done(core);
|
||||
return vpu_iface_restore_core(core);
|
||||
}
|
||||
|
||||
static int __vpu_alloc_dma(struct device *dev, struct vpu_buffer *buf)
|
||||
{
|
||||
gfp_t gfp = GFP_KERNEL | GFP_DMA32;
|
||||
|
||||
if (!buf->length)
|
||||
return 0;
|
||||
|
||||
buf->virt = dma_alloc_coherent(dev, buf->length, &buf->phys, gfp);
|
||||
if (!buf->virt)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpu_free_dma(struct vpu_buffer *buf)
|
||||
{
|
||||
if (!buf->virt || !buf->dev)
|
||||
return;
|
||||
|
||||
dma_free_coherent(buf->dev, buf->length, buf->virt, buf->phys);
|
||||
buf->virt = NULL;
|
||||
buf->phys = 0;
|
||||
buf->length = 0;
|
||||
buf->bytesused = 0;
|
||||
buf->dev = NULL;
|
||||
}
|
||||
|
||||
int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf)
|
||||
{
|
||||
return __vpu_alloc_dma(core->dev, buf);
|
||||
}
|
||||
|
||||
static void vpu_core_check_hang(struct vpu_core *core)
|
||||
{
|
||||
if (core->hang_mask)
|
||||
core->state = VPU_CORE_HANG;
|
||||
}
|
||||
|
||||
static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 type)
|
||||
{
|
||||
struct vpu_core *core = NULL;
|
||||
int request_count = INT_MAX;
|
||||
struct vpu_core *c;
|
||||
|
||||
list_for_each_entry(c, &vpu->cores, list) {
|
||||
dev_dbg(c->dev, "instance_mask = 0x%lx, state = %d\n", c->instance_mask, c->state);
|
||||
if (c->type != type)
|
||||
continue;
|
||||
if (c->state == VPU_CORE_DEINIT) {
|
||||
core = c;
|
||||
break;
|
||||
}
|
||||
vpu_core_check_hang(c);
|
||||
if (c->state != VPU_CORE_ACTIVE)
|
||||
continue;
|
||||
if (c->request_count < request_count) {
|
||||
request_count = c->request_count;
|
||||
core = c;
|
||||
}
|
||||
if (!request_count)
|
||||
break;
|
||||
}
|
||||
|
||||
return core;
|
||||
}
|
||||
|
||||
static bool vpu_core_is_exist(struct vpu_dev *vpu, struct vpu_core *core)
|
||||
{
|
||||
struct vpu_core *c;
|
||||
|
||||
list_for_each_entry(c, &vpu->cores, list) {
|
||||
if (c == core)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void vpu_core_get_vpu(struct vpu_core *core)
|
||||
{
|
||||
core->vpu->get_vpu(core->vpu);
|
||||
if (core->type == VPU_CORE_TYPE_ENC)
|
||||
core->vpu->get_enc(core->vpu);
|
||||
if (core->type == VPU_CORE_TYPE_DEC)
|
||||
core->vpu->get_dec(core->vpu);
|
||||
}
|
||||
|
||||
static int vpu_core_register(struct device *dev, struct vpu_core *core)
|
||||
{
|
||||
struct vpu_dev *vpu = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type));
|
||||
if (vpu_core_is_exist(vpu, core))
|
||||
return 0;
|
||||
|
||||
core->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
|
||||
if (!core->workqueue) {
|
||||
dev_err(core->dev, "fail to alloc workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_WORK(&core->msg_work, vpu_msg_run_work);
|
||||
INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work);
|
||||
core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
|
||||
core->msg_buffer = vzalloc(core->msg_buffer_size);
|
||||
if (!core->msg_buffer) {
|
||||
dev_err(core->dev, "failed allocate buffer for fifo\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "failed init kfifo\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
list_add_tail(&core->list, &vpu->cores);
|
||||
|
||||
vpu_core_get_vpu(core);
|
||||
|
||||
if (vpu_iface_get_power_state(core))
|
||||
ret = vpu_core_restore(core);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
if (core->msg_buffer) {
|
||||
vfree(core->msg_buffer);
|
||||
core->msg_buffer = NULL;
|
||||
}
|
||||
if (core->workqueue) {
|
||||
destroy_workqueue(core->workqueue);
|
||||
core->workqueue = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vpu_core_put_vpu(struct vpu_core *core)
|
||||
{
|
||||
if (core->type == VPU_CORE_TYPE_ENC)
|
||||
core->vpu->put_enc(core->vpu);
|
||||
if (core->type == VPU_CORE_TYPE_DEC)
|
||||
core->vpu->put_dec(core->vpu);
|
||||
core->vpu->put_vpu(core->vpu);
|
||||
}
|
||||
|
||||
static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
|
||||
{
|
||||
list_del_init(&core->list);
|
||||
|
||||
vpu_core_put_vpu(core);
|
||||
core->vpu = NULL;
|
||||
vfree(core->msg_buffer);
|
||||
core->msg_buffer = NULL;
|
||||
|
||||
if (core->workqueue) {
|
||||
cancel_work_sync(&core->msg_work);
|
||||
cancel_delayed_work_sync(&core->msg_delayed_work);
|
||||
destroy_workqueue(core->workqueue);
|
||||
core->workqueue = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_core_acquire_instance(struct vpu_core *core)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = ffz(core->instance_mask);
|
||||
if (id >= core->supported_instance_count)
|
||||
return -EINVAL;
|
||||
|
||||
set_bit(id, &core->instance_mask);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void vpu_core_release_instance(struct vpu_core *core, int id)
|
||||
{
|
||||
if (id < 0 || id >= core->supported_instance_count)
|
||||
return;
|
||||
|
||||
clear_bit(id, &core->instance_mask);
|
||||
}
|
||||
|
||||
struct vpu_inst *vpu_inst_get(struct vpu_inst *inst)
|
||||
{
|
||||
if (!inst)
|
||||
return NULL;
|
||||
|
||||
atomic_inc(&inst->ref_count);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void vpu_inst_put(struct vpu_inst *inst)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
if (atomic_dec_and_test(&inst->ref_count)) {
|
||||
if (inst->release)
|
||||
inst->release(inst);
|
||||
}
|
||||
}
|
||||
|
||||
struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type)
|
||||
{
|
||||
struct vpu_core *core = NULL;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&vpu->lock);
|
||||
|
||||
core = vpu_core_find_proper_by_type(vpu, type);
|
||||
if (!core)
|
||||
goto exit;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
pm_runtime_get_sync(core->dev);
|
||||
|
||||
if (core->state == VPU_CORE_DEINIT) {
|
||||
ret = vpu_core_boot(core, true);
|
||||
if (ret) {
|
||||
pm_runtime_put_sync(core->dev);
|
||||
mutex_unlock(&core->lock);
|
||||
core = NULL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
core->request_count++;
|
||||
|
||||
mutex_unlock(&core->lock);
|
||||
exit:
|
||||
mutex_unlock(&vpu->lock);
|
||||
|
||||
return core;
|
||||
}
|
||||
|
||||
void vpu_release_core(struct vpu_core *core)
|
||||
{
|
||||
if (!core)
|
||||
return;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
pm_runtime_put_sync(core->dev);
|
||||
if (core->request_count)
|
||||
core->request_count--;
|
||||
mutex_unlock(&core->lock);
|
||||
}
|
||||
|
||||
int vpu_inst_register(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_dev *vpu;
|
||||
struct vpu_core *core;
|
||||
int ret = 0;
|
||||
|
||||
vpu = inst->vpu;
|
||||
core = inst->core;
|
||||
if (!core) {
|
||||
core = vpu_request_core(vpu, inst->type);
|
||||
if (!core) {
|
||||
dev_err(vpu->dev, "there is no vpu core for %s\n",
|
||||
vpu_core_type_desc(inst->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
inst->core = core;
|
||||
inst->dev = get_device(core->dev);
|
||||
}
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
if (inst->id >= 0 && inst->id < core->supported_instance_count)
|
||||
goto exit;
|
||||
|
||||
ret = vpu_core_acquire_instance(core);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
vpu_trace(inst->dev, "[%d] %p\n", ret, inst);
|
||||
inst->id = ret;
|
||||
list_add_tail(&inst->list, &core->instances);
|
||||
ret = 0;
|
||||
if (core->res->act_size) {
|
||||
inst->act.phys = core->act.phys + core->res->act_size * inst->id;
|
||||
inst->act.virt = core->act.virt + core->res->act_size * inst->id;
|
||||
inst->act.length = core->res->act_size;
|
||||
}
|
||||
vpu_inst_create_dbgfs_file(inst);
|
||||
exit:
|
||||
mutex_unlock(&core->lock);
|
||||
|
||||
if (ret)
|
||||
dev_err(core->dev, "register instance fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vpu_inst_unregister(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_core *core;
|
||||
|
||||
if (!inst->core)
|
||||
return 0;
|
||||
|
||||
core = inst->core;
|
||||
vpu_clear_request(inst);
|
||||
mutex_lock(&core->lock);
|
||||
if (inst->id >= 0 && inst->id < core->supported_instance_count) {
|
||||
vpu_inst_remove_dbgfs_file(inst);
|
||||
list_del_init(&inst->list);
|
||||
vpu_core_release_instance(core, inst->id);
|
||||
inst->id = VPU_INST_NULL_ID;
|
||||
}
|
||||
vpu_core_check_hang(core);
|
||||
if (core->state == VPU_CORE_HANG && !core->instance_mask) {
|
||||
dev_info(core->dev, "reset hang core\n");
|
||||
if (!vpu_core_sw_reset(core)) {
|
||||
core->state = VPU_CORE_ACTIVE;
|
||||
core->hang_mask = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&core->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index)
|
||||
{
|
||||
struct vpu_inst *inst = NULL;
|
||||
struct vpu_inst *tmp;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
if (!test_bit(index, &core->instance_mask))
|
||||
goto exit;
|
||||
list_for_each_entry(tmp, &core->instances, list) {
|
||||
if (tmp->id == index) {
|
||||
inst = vpu_inst_get(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
mutex_unlock(&core->lock);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_dev *vpu;
|
||||
struct vpu_core *core = NULL;
|
||||
const struct vpu_core_resources *res = NULL;
|
||||
|
||||
if (!inst || !inst->vpu)
|
||||
return NULL;
|
||||
|
||||
if (inst->core && inst->core->res)
|
||||
return inst->core->res;
|
||||
|
||||
vpu = inst->vpu;
|
||||
mutex_lock(&vpu->lock);
|
||||
list_for_each_entry(core, &vpu->cores, list) {
|
||||
if (core->type == inst->type) {
|
||||
res = core->res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&vpu->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct resource res;
|
||||
int ret;
|
||||
|
||||
if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) {
|
||||
dev_err(core->dev, "need 2 memory-region for boot and rpc\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
node = of_parse_phandle(np, "memory-region", 0);
|
||||
if (!node) {
|
||||
dev_err(core->dev, "boot-region of_parse_phandle error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (of_address_to_resource(node, 0, &res)) {
|
||||
dev_err(core->dev, "boot-region of_address_to_resource error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
core->fw.phys = res.start;
|
||||
core->fw.length = resource_size(&res);
|
||||
|
||||
node = of_parse_phandle(np, "memory-region", 1);
|
||||
if (!node) {
|
||||
dev_err(core->dev, "rpc-region of_parse_phandle error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (of_address_to_resource(node, 0, &res)) {
|
||||
dev_err(core->dev, "rpc-region of_address_to_resource error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
core->rpc.phys = res.start;
|
||||
core->rpc.length = resource_size(&res);
|
||||
|
||||
if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) {
|
||||
dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n",
|
||||
&core->rpc.phys, core->rpc.length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
core->fw.virt = memremap(core->fw.phys, core->fw.length, MEMREMAP_WC);
|
||||
core->rpc.virt = memremap(core->rpc.phys, core->rpc.length, MEMREMAP_WC);
|
||||
memset(core->rpc.virt, 0, core->rpc.length);
|
||||
|
||||
ret = vpu_iface_check_memory_region(core, core->rpc.phys, core->rpc.length);
|
||||
if (ret != VPU_CORE_MEMORY_UNCACHED) {
|
||||
dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n",
|
||||
&core->rpc.phys, core->rpc.length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
core->log.phys = core->rpc.phys + core->res->rpc_size;
|
||||
core->log.virt = core->rpc.virt + core->res->rpc_size;
|
||||
core->log.length = core->res->fwlog_size;
|
||||
core->act.phys = core->log.phys + core->log.length;
|
||||
core->act.virt = core->log.virt + core->log.length;
|
||||
core->act.length = core->rpc.length - core->res->rpc_size - core->log.length;
|
||||
core->rpc.length = core->res->rpc_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_core_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vpu_core *core;
|
||||
struct vpu_dev *vpu = dev_get_drvdata(dev->parent);
|
||||
struct vpu_shared_addr *iface;
|
||||
u32 iface_data_size;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "probe\n");
|
||||
if (!vpu)
|
||||
return -EINVAL;
|
||||
core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
|
||||
if (!core)
|
||||
return -ENOMEM;
|
||||
|
||||
core->pdev = pdev;
|
||||
core->dev = dev;
|
||||
platform_set_drvdata(pdev, core);
|
||||
core->vpu = vpu;
|
||||
INIT_LIST_HEAD(&core->instances);
|
||||
mutex_init(&core->lock);
|
||||
mutex_init(&core->cmd_lock);
|
||||
init_completion(&core->cmp);
|
||||
init_waitqueue_head(&core->ack_wq);
|
||||
core->state = VPU_CORE_DEINIT;
|
||||
|
||||
core->res = of_device_get_match_data(dev);
|
||||
if (!core->res)
|
||||
return -ENODEV;
|
||||
|
||||
core->type = core->res->type;
|
||||
core->id = of_alias_get_id(dev->of_node, "vpu_core");
|
||||
if (core->id < 0) {
|
||||
dev_err(dev, "can't get vpu core id\n");
|
||||
return core->id;
|
||||
}
|
||||
dev_info(core->dev, "[%d] = %s\n", core->id, vpu_core_type_desc(core->type));
|
||||
ret = vpu_core_parse_dt(core, dev->of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
core->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(core->base))
|
||||
return PTR_ERR(core->base);
|
||||
|
||||
if (!vpu_iface_check_codec(core)) {
|
||||
dev_err(core->dev, "is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = vpu_mbox_init(core);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
|
||||
if (!iface)
|
||||
return -ENOMEM;
|
||||
|
||||
iface_data_size = vpu_iface_get_data_size(core);
|
||||
if (iface_data_size) {
|
||||
iface->priv = devm_kzalloc(dev, iface_data_size, GFP_KERNEL);
|
||||
if (!iface->priv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = vpu_iface_init(core, iface, &core->rpc, core->fw.phys);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "init iface fail, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vpu_iface_config_system(core, vpu->res->mreg_base, vpu->base);
|
||||
vpu_iface_set_log_buf(core, &core->log);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
goto err_runtime_disable;
|
||||
}
|
||||
|
||||
ret = vpu_core_register(dev->parent, core);
|
||||
if (ret)
|
||||
goto err_core_register;
|
||||
core->parent = dev->parent;
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
vpu_core_create_dbgfs_file(core);
|
||||
|
||||
return 0;
|
||||
|
||||
err_core_register:
|
||||
pm_runtime_put_sync(dev);
|
||||
err_runtime_disable:
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_core_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vpu_core *core = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
vpu_core_remove_dbgfs_file(core);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
WARN_ON(ret < 0);
|
||||
|
||||
vpu_core_shutdown(core);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
vpu_core_unregister(core->parent, core);
|
||||
memunmap(core->fw.virt);
|
||||
memunmap(core->rpc.virt);
|
||||
mutex_destroy(&core->lock);
|
||||
mutex_destroy(&core->cmd_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_core_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct vpu_core *core = dev_get_drvdata(dev);
|
||||
|
||||
return vpu_mbox_request(core);
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_core_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct vpu_core *core = dev_get_drvdata(dev);
|
||||
|
||||
vpu_mbox_free(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_core_cancel_work(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_inst *inst = NULL;
|
||||
|
||||
cancel_work_sync(&core->msg_work);
|
||||
cancel_delayed_work_sync(&core->msg_delayed_work);
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
list_for_each_entry(inst, &core->instances, list)
|
||||
cancel_work_sync(&inst->msg_work);
|
||||
mutex_unlock(&core->lock);
|
||||
}
|
||||
|
||||
static void vpu_core_resume_work(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_inst *inst = NULL;
|
||||
unsigned long delay = msecs_to_jiffies(10);
|
||||
|
||||
queue_work(core->workqueue, &core->msg_work);
|
||||
queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay);
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
list_for_each_entry(inst, &core->instances, list)
|
||||
queue_work(inst->workqueue, &inst->msg_work);
|
||||
mutex_unlock(&core->lock);
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_core_resume(struct device *dev)
|
||||
{
|
||||
struct vpu_core *core = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
pm_runtime_get_sync(dev);
|
||||
vpu_core_get_vpu(core);
|
||||
if (core->state != VPU_CORE_SNAPSHOT)
|
||||
goto exit;
|
||||
|
||||
if (!vpu_iface_get_power_state(core)) {
|
||||
if (!list_empty(&core->instances)) {
|
||||
ret = vpu_core_boot(core, false);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "%s boot fail\n", __func__);
|
||||
core->state = VPU_CORE_DEINIT;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
core->state = VPU_CORE_DEINIT;
|
||||
}
|
||||
} else {
|
||||
if (!list_empty(&core->instances)) {
|
||||
ret = vpu_core_sw_reset(core);
|
||||
if (ret) {
|
||||
dev_err(core->dev, "%s sw_reset fail\n", __func__);
|
||||
core->state = VPU_CORE_HANG;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
core->state = VPU_CORE_ACTIVE;
|
||||
}
|
||||
|
||||
exit:
|
||||
pm_runtime_put_sync(dev);
|
||||
mutex_unlock(&core->lock);
|
||||
|
||||
vpu_core_resume_work(core);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_core_suspend(struct device *dev)
|
||||
{
|
||||
struct vpu_core *core = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
if (core->state == VPU_CORE_ACTIVE) {
|
||||
if (!list_empty(&core->instances)) {
|
||||
ret = vpu_core_snapshot(core);
|
||||
if (ret) {
|
||||
mutex_unlock(&core->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
core->state = VPU_CORE_SNAPSHOT;
|
||||
}
|
||||
mutex_unlock(&core->lock);
|
||||
|
||||
vpu_core_cancel_work(core);
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
vpu_core_put_vpu(core);
|
||||
mutex_unlock(&core->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vpu_core_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(vpu_core_runtime_suspend, vpu_core_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(vpu_core_suspend, vpu_core_resume)
|
||||
};
|
||||
|
||||
static struct vpu_core_resources imx8q_enc = {
|
||||
.type = VPU_CORE_TYPE_ENC,
|
||||
.fwname = "vpu/vpu_fw_imx8_enc.bin",
|
||||
.stride = 16,
|
||||
.max_width = 1920,
|
||||
.max_height = 1920,
|
||||
.min_width = 64,
|
||||
.min_height = 48,
|
||||
.step_width = 2,
|
||||
.step_height = 2,
|
||||
.rpc_size = 0x80000,
|
||||
.fwlog_size = 0x80000,
|
||||
.act_size = 0xc0000,
|
||||
};
|
||||
|
||||
static struct vpu_core_resources imx8q_dec = {
|
||||
.type = VPU_CORE_TYPE_DEC,
|
||||
.fwname = "vpu/vpu_fw_imx8_dec.bin",
|
||||
.stride = 256,
|
||||
.max_width = 8188,
|
||||
.max_height = 8188,
|
||||
.min_width = 16,
|
||||
.min_height = 16,
|
||||
.step_width = 1,
|
||||
.step_height = 1,
|
||||
.rpc_size = 0x80000,
|
||||
.fwlog_size = 0x80000,
|
||||
};
|
||||
|
||||
static const struct of_device_id vpu_core_dt_match[] = {
|
||||
{ .compatible = "nxp,imx8q-vpu-encoder", .data = &imx8q_enc },
|
||||
{ .compatible = "nxp,imx8q-vpu-decoder", .data = &imx8q_dec },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vpu_core_dt_match);
|
||||
|
||||
static struct platform_driver amphion_vpu_core_driver = {
|
||||
.probe = vpu_core_probe,
|
||||
.remove = vpu_core_remove,
|
||||
.driver = {
|
||||
.name = "amphion-vpu-core",
|
||||
.of_match_table = vpu_core_dt_match,
|
||||
.pm = &vpu_core_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
int __init vpu_core_driver_init(void)
|
||||
{
|
||||
return platform_driver_register(&hion_vpu_core_driver);
|
||||
}
|
||||
|
||||
void __exit vpu_core_driver_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&hion_vpu_core_driver);
|
||||
}
|
||||
15
drivers/media/platform/amphion/vpu_core.h
Normal file
15
drivers/media/platform/amphion/vpu_core.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* 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
|
||||
494
drivers/media/platform/amphion/vpu_dbg.c
Normal file
494
drivers/media/platform/amphion/vpu_dbg.c
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
// 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/types.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include "vpu.h"
|
||||
#include "vpu_defs.h"
|
||||
#include "vpu_helpers.h"
|
||||
#include "vpu_cmds.h"
|
||||
#include "vpu_rpc.h"
|
||||
#include "vpu_v4l2.h"
|
||||
|
||||
struct print_buf_desc {
|
||||
u32 start_h_phy;
|
||||
u32 start_h_vir;
|
||||
u32 start_m;
|
||||
u32 bytes;
|
||||
u32 read;
|
||||
u32 write;
|
||||
char buffer[0];
|
||||
};
|
||||
|
||||
static char *vb2_stat_name[] = {
|
||||
[VB2_BUF_STATE_DEQUEUED] = "dequeued",
|
||||
[VB2_BUF_STATE_IN_REQUEST] = "in_request",
|
||||
[VB2_BUF_STATE_PREPARING] = "preparing",
|
||||
[VB2_BUF_STATE_QUEUED] = "queued",
|
||||
[VB2_BUF_STATE_ACTIVE] = "active",
|
||||
[VB2_BUF_STATE_DONE] = "done",
|
||||
[VB2_BUF_STATE_ERROR] = "error",
|
||||
};
|
||||
|
||||
static char *vpu_stat_name[] = {
|
||||
[VPU_BUF_STATE_IDLE] = "idle",
|
||||
[VPU_BUF_STATE_INUSE] = "inuse",
|
||||
[VPU_BUF_STATE_DECODED] = "decoded",
|
||||
[VPU_BUF_STATE_READY] = "ready",
|
||||
[VPU_BUF_STATE_SKIP] = "skip",
|
||||
[VPU_BUF_STATE_ERROR] = "error",
|
||||
};
|
||||
|
||||
static int vpu_dbg_instance(struct seq_file *s, void *data)
|
||||
{
|
||||
struct vpu_inst *inst = s->private;
|
||||
char str[128];
|
||||
int num;
|
||||
struct vb2_queue *vq;
|
||||
int i;
|
||||
|
||||
if (!inst->fh.m2m_ctx)
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(inst->type));
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
num = scnprintf(str, sizeof(str), "tgig = %d,pid = %d\n", inst->tgid, inst->pid);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "state = %d\n", inst->state);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"min_buffer_out = %d, min_buffer_cap = %d\n",
|
||||
inst->min_buffer_out, inst->min_buffer_cap);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
|
||||
vb2_is_streaming(vq),
|
||||
vq->num_buffers,
|
||||
inst->out_format.pixfmt,
|
||||
inst->out_format.pixfmt >> 8,
|
||||
inst->out_format.pixfmt >> 16,
|
||||
inst->out_format.pixfmt >> 24,
|
||||
inst->out_format.width,
|
||||
inst->out_format.height,
|
||||
vq->last_buffer_dequeued);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
for (i = 0; i < inst->out_format.num_planes; i++) {
|
||||
num = scnprintf(str, sizeof(str), " %d(%d)",
|
||||
inst->out_format.sizeimage[i],
|
||||
inst->out_format.bytesperline[i]);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
if (seq_write(s, "\n", 1))
|
||||
return 0;
|
||||
|
||||
vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
|
||||
vb2_is_streaming(vq),
|
||||
vq->num_buffers,
|
||||
inst->cap_format.pixfmt,
|
||||
inst->cap_format.pixfmt >> 8,
|
||||
inst->cap_format.pixfmt >> 16,
|
||||
inst->cap_format.pixfmt >> 24,
|
||||
inst->cap_format.width,
|
||||
inst->cap_format.height,
|
||||
vq->last_buffer_dequeued);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
for (i = 0; i < inst->cap_format.num_planes; i++) {
|
||||
num = scnprintf(str, sizeof(str), " %d(%d)",
|
||||
inst->cap_format.sizeimage[i],
|
||||
inst->cap_format.bytesperline[i]);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
if (seq_write(s, "\n", 1))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "crop: (%d, %d) %d x %d\n",
|
||||
inst->crop.left,
|
||||
inst->crop.top,
|
||||
inst->crop.width,
|
||||
inst->crop.height);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
|
||||
for (i = 0; i < vq->num_buffers; i++) {
|
||||
struct vb2_buffer *vb = vq->bufs[i];
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
|
||||
if (vb->state == VB2_BUF_STATE_DEQUEUED)
|
||||
continue;
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"output [%2d] state = %10s, %8s\n",
|
||||
i, vb2_stat_name[vb->state],
|
||||
vpu_stat_name[vpu_get_buffer_state(vbuf)]);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
|
||||
vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
|
||||
for (i = 0; i < vq->num_buffers; i++) {
|
||||
struct vb2_buffer *vb = vq->bufs[i];
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
|
||||
if (vb->state == VB2_BUF_STATE_DEQUEUED)
|
||||
continue;
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"capture[%2d] state = %10s, %8s\n",
|
||||
i, vb2_stat_name[vb->state],
|
||||
vpu_stat_name[vpu_get_buffer_state(vbuf)]);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
if (inst->use_stream_buffer) {
|
||||
num = scnprintf(str, sizeof(str), "stream_buffer = %d / %d, <%pad, 0x%x>\n",
|
||||
vpu_helper_get_used_space(inst),
|
||||
inst->stream_buffer.length,
|
||||
&inst->stream_buffer.phys,
|
||||
inst->stream_buffer.length);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&inst->msg_fifo));
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
num = scnprintf(str, sizeof(str), "flow :\n");
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&inst->core->cmd_lock);
|
||||
for (i = 0; i < ARRAY_SIZE(inst->flows); i++) {
|
||||
u32 idx = (inst->flow_idx + i) % (ARRAY_SIZE(inst->flows));
|
||||
|
||||
if (!inst->flows[idx])
|
||||
continue;
|
||||
num = scnprintf(str, sizeof(str), "\t[%s]0x%x\n",
|
||||
inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C",
|
||||
inst->flows[idx]);
|
||||
if (seq_write(s, str, num)) {
|
||||
mutex_unlock(&inst->core->cmd_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&inst->core->cmd_lock);
|
||||
|
||||
i = 0;
|
||||
while (true) {
|
||||
num = call_vop(inst, get_debug_info, str, sizeof(str), i++);
|
||||
if (num <= 0)
|
||||
break;
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_dbg_core(struct seq_file *s, void *data)
|
||||
{
|
||||
struct vpu_core *core = s->private;
|
||||
struct vpu_shared_addr *iface = core->iface;
|
||||
char str[128];
|
||||
int num;
|
||||
|
||||
num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(core->type));
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
num = scnprintf(str, sizeof(str), "boot_region = <%pad, 0x%x>\n",
|
||||
&core->fw.phys, core->fw.length);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "rpc_region = <%pad, 0x%x> used = 0x%x\n",
|
||||
&core->rpc.phys, core->rpc.length, core->rpc.bytesused);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "fwlog_region = <%pad, 0x%x>\n",
|
||||
&core->log.phys, core->log.length);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
num = scnprintf(str, sizeof(str), "state = %d\n", core->state);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
if (core->state == VPU_CORE_DEINIT)
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "fw version = %d.%d.%d\n",
|
||||
(core->fw_version >> 16) & 0xff,
|
||||
(core->fw_version >> 8) & 0xff,
|
||||
core->fw_version & 0xff);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "instances = %d/%d (0x%02lx), %d\n",
|
||||
hweight32(core->instance_mask),
|
||||
core->supported_instance_count,
|
||||
core->instance_mask,
|
||||
core->request_count);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&core->msg_fifo));
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"cmd_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
|
||||
iface->cmd_desc->start,
|
||||
iface->cmd_desc->end,
|
||||
iface->cmd_desc->wptr,
|
||||
iface->cmd_desc->rptr);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
num = scnprintf(str, sizeof(str),
|
||||
"msg_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
|
||||
iface->msg_desc->start,
|
||||
iface->msg_desc->end,
|
||||
iface->msg_desc->wptr,
|
||||
iface->msg_desc->rptr);
|
||||
if (seq_write(s, str, num))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_dbg_fwlog(struct seq_file *s, void *data)
|
||||
{
|
||||
struct vpu_core *core = s->private;
|
||||
struct print_buf_desc *print_buf;
|
||||
int length;
|
||||
u32 rptr;
|
||||
u32 wptr;
|
||||
int ret = 0;
|
||||
|
||||
if (!core->log.virt || core->state == VPU_CORE_DEINIT)
|
||||
return 0;
|
||||
|
||||
print_buf = core->log.virt;
|
||||
rptr = print_buf->read;
|
||||
wptr = print_buf->write;
|
||||
|
||||
if (rptr == wptr)
|
||||
return 0;
|
||||
else if (rptr < wptr)
|
||||
length = wptr - rptr;
|
||||
else
|
||||
length = print_buf->bytes + wptr - rptr;
|
||||
|
||||
if (s->count + length >= s->size) {
|
||||
s->count = s->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rptr + length >= print_buf->bytes) {
|
||||
int num = print_buf->bytes - rptr;
|
||||
|
||||
if (seq_write(s, print_buf->buffer + rptr, num))
|
||||
ret = -1;
|
||||
length -= num;
|
||||
rptr = 0;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
if (seq_write(s, print_buf->buffer + rptr, length))
|
||||
ret = -1;
|
||||
rptr += length;
|
||||
}
|
||||
if (!ret)
|
||||
print_buf->read = rptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_dbg_inst_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, vpu_dbg_instance, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t vpu_dbg_inst_write(struct file *file,
|
||||
const char __user *user_buf, size_t size, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct vpu_inst *inst = s->private;
|
||||
|
||||
vpu_session_debug(inst);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t vpu_dbg_core_write(struct file *file,
|
||||
const char __user *user_buf, size_t size, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct vpu_core *core = s->private;
|
||||
|
||||
pm_runtime_get_sync(core->dev);
|
||||
mutex_lock(&core->lock);
|
||||
if (core->state != VPU_CORE_DEINIT && !core->instance_mask) {
|
||||
dev_info(core->dev, "reset\n");
|
||||
if (!vpu_core_sw_reset(core)) {
|
||||
core->state = VPU_CORE_ACTIVE;
|
||||
core->hang_mask = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&core->lock);
|
||||
pm_runtime_put_sync(core->dev);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int vpu_dbg_core_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, vpu_dbg_core, inode->i_private);
|
||||
}
|
||||
|
||||
static int vpu_dbg_fwlog_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, vpu_dbg_fwlog, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations vpu_dbg_inst_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vpu_dbg_inst_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
.write = vpu_dbg_inst_write,
|
||||
};
|
||||
|
||||
static const struct file_operations vpu_dbg_core_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vpu_dbg_core_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
.write = vpu_dbg_core_write,
|
||||
};
|
||||
|
||||
static const struct file_operations vpu_dbg_fwlog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vpu_dbg_fwlog_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
};
|
||||
|
||||
int vpu_inst_create_dbgfs_file(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_dev *vpu;
|
||||
char name[64];
|
||||
|
||||
if (!inst || !inst->core || !inst->core->vpu)
|
||||
return -EINVAL;
|
||||
|
||||
vpu = inst->core->vpu;
|
||||
if (!vpu->debugfs)
|
||||
return -EINVAL;
|
||||
|
||||
if (inst->debugfs)
|
||||
return 0;
|
||||
|
||||
scnprintf(name, sizeof(name), "instance.%d.%d", inst->core->id, inst->id);
|
||||
inst->debugfs = debugfs_create_file((const char *)name,
|
||||
VERIFY_OCTAL_PERMISSIONS(0644),
|
||||
vpu->debugfs,
|
||||
inst,
|
||||
&vpu_dbg_inst_fops);
|
||||
if (!inst->debugfs) {
|
||||
dev_err(inst->dev, "vpu create debugfs %s fail\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst)
|
||||
{
|
||||
if (!inst)
|
||||
return 0;
|
||||
|
||||
debugfs_remove(inst->debugfs);
|
||||
inst->debugfs = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_core_create_dbgfs_file(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_dev *vpu;
|
||||
char name[64];
|
||||
|
||||
if (!core || !core->vpu)
|
||||
return -EINVAL;
|
||||
|
||||
vpu = core->vpu;
|
||||
if (!vpu->debugfs)
|
||||
return -EINVAL;
|
||||
|
||||
if (!core->debugfs) {
|
||||
scnprintf(name, sizeof(name), "core.%d", core->id);
|
||||
core->debugfs = debugfs_create_file((const char *)name,
|
||||
VERIFY_OCTAL_PERMISSIONS(0644),
|
||||
vpu->debugfs,
|
||||
core,
|
||||
&vpu_dbg_core_fops);
|
||||
if (!core->debugfs) {
|
||||
dev_err(core->dev, "vpu create debugfs %s fail\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!core->debugfs_fwlog) {
|
||||
scnprintf(name, sizeof(name), "fwlog.%d", core->id);
|
||||
core->debugfs_fwlog = debugfs_create_file((const char *)name,
|
||||
VERIFY_OCTAL_PERMISSIONS(0444),
|
||||
vpu->debugfs,
|
||||
core,
|
||||
&vpu_dbg_fwlog_fops);
|
||||
if (!core->debugfs_fwlog) {
|
||||
dev_err(core->dev, "vpu create debugfs %s fail\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_core_remove_dbgfs_file(struct vpu_core *core)
|
||||
{
|
||||
if (!core)
|
||||
return 0;
|
||||
debugfs_remove(core->debugfs);
|
||||
core->debugfs = NULL;
|
||||
debugfs_remove(core->debugfs_fwlog);
|
||||
core->debugfs_fwlog = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
|
||||
inst->flows[inst->flow_idx] = flow;
|
||||
inst->flow_idx = (inst->flow_idx + 1) % (ARRAY_SIZE(inst->flows));
|
||||
}
|
||||
187
drivers/media/platform/amphion/vpu_defs.h
Normal file
187
drivers/media/platform/amphion/vpu_defs.h
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_DEFS_H
|
||||
#define _AMPHION_VPU_DEFS_H
|
||||
|
||||
enum MSG_TYPE {
|
||||
INIT_DONE = 1,
|
||||
PRC_BUF_OFFSET,
|
||||
BOOT_ADDRESS,
|
||||
COMMAND,
|
||||
EVENT,
|
||||
};
|
||||
|
||||
enum {
|
||||
VPU_IRQ_CODE_BOOT_DONE = 0x55,
|
||||
VPU_IRQ_CODE_SNAPSHOT_DONE = 0xa5,
|
||||
VPU_IRQ_CODE_SYNC = 0xaa,
|
||||
};
|
||||
|
||||
enum {
|
||||
VPU_CMD_ID_NOOP = 0x0,
|
||||
VPU_CMD_ID_CONFIGURE_CODEC,
|
||||
VPU_CMD_ID_START,
|
||||
VPU_CMD_ID_STOP,
|
||||
VPU_CMD_ID_ABORT,
|
||||
VPU_CMD_ID_RST_BUF,
|
||||
VPU_CMD_ID_SNAPSHOT,
|
||||
VPU_CMD_ID_FIRM_RESET,
|
||||
VPU_CMD_ID_UPDATE_PARAMETER,
|
||||
VPU_CMD_ID_FRAME_ENCODE,
|
||||
VPU_CMD_ID_SKIP,
|
||||
VPU_CMD_ID_PARSE_NEXT_SEQ,
|
||||
VPU_CMD_ID_PARSE_NEXT_I,
|
||||
VPU_CMD_ID_PARSE_NEXT_IP,
|
||||
VPU_CMD_ID_PARSE_NEXT_ANY,
|
||||
VPU_CMD_ID_DEC_PIC,
|
||||
VPU_CMD_ID_FS_ALLOC,
|
||||
VPU_CMD_ID_FS_RELEASE,
|
||||
VPU_CMD_ID_TIMESTAMP,
|
||||
VPU_CMD_ID_DEBUG
|
||||
};
|
||||
|
||||
enum {
|
||||
VPU_MSG_ID_NOOP = 0x100,
|
||||
VPU_MSG_ID_RESET_DONE,
|
||||
VPU_MSG_ID_START_DONE,
|
||||
VPU_MSG_ID_STOP_DONE,
|
||||
VPU_MSG_ID_ABORT_DONE,
|
||||
VPU_MSG_ID_BUF_RST,
|
||||
VPU_MSG_ID_MEM_REQUEST,
|
||||
VPU_MSG_ID_PARAM_UPD_DONE,
|
||||
VPU_MSG_ID_FRAME_INPUT_DONE,
|
||||
VPU_MSG_ID_ENC_DONE,
|
||||
VPU_MSG_ID_DEC_DONE,
|
||||
VPU_MSG_ID_FRAME_REQ,
|
||||
VPU_MSG_ID_FRAME_RELEASE,
|
||||
VPU_MSG_ID_SEQ_HDR_FOUND,
|
||||
VPU_MSG_ID_RES_CHANGE,
|
||||
VPU_MSG_ID_PIC_HDR_FOUND,
|
||||
VPU_MSG_ID_PIC_DECODED,
|
||||
VPU_MSG_ID_PIC_EOS,
|
||||
VPU_MSG_ID_FIFO_LOW,
|
||||
VPU_MSG_ID_FIFO_HIGH,
|
||||
VPU_MSG_ID_FIFO_EMPTY,
|
||||
VPU_MSG_ID_FIFO_FULL,
|
||||
VPU_MSG_ID_BS_ERROR,
|
||||
VPU_MSG_ID_UNSUPPORTED,
|
||||
VPU_MSG_ID_TIMESTAMP_INFO,
|
||||
|
||||
VPU_MSG_ID_FIRMWARE_XCPT,
|
||||
};
|
||||
|
||||
enum VPU_ENC_MEMORY_RESOURSE {
|
||||
MEM_RES_ENC,
|
||||
MEM_RES_REF,
|
||||
MEM_RES_ACT
|
||||
};
|
||||
|
||||
enum VPU_DEC_MEMORY_RESOURCE {
|
||||
MEM_RES_FRAME,
|
||||
MEM_RES_MBI,
|
||||
MEM_RES_DCP
|
||||
};
|
||||
|
||||
enum VPU_SCODE_TYPE {
|
||||
SCODE_PADDING_EOS = 1,
|
||||
SCODE_PADDING_BUFFLUSH = 2,
|
||||
SCODE_PADDING_ABORT = 3,
|
||||
SCODE_SEQUENCE = 0x31,
|
||||
SCODE_PICTURE = 0x32,
|
||||
SCODE_SLICE = 0x33
|
||||
};
|
||||
|
||||
struct vpu_pkt_mem_req_data {
|
||||
u32 enc_frame_size;
|
||||
u32 enc_frame_num;
|
||||
u32 ref_frame_size;
|
||||
u32 ref_frame_num;
|
||||
u32 act_buf_size;
|
||||
u32 act_buf_num;
|
||||
};
|
||||
|
||||
struct vpu_enc_pic_info {
|
||||
u32 frame_id;
|
||||
u32 pic_type;
|
||||
u32 skipped_frame;
|
||||
u32 error_flag;
|
||||
u32 psnr;
|
||||
u32 frame_size;
|
||||
u32 wptr;
|
||||
u32 crc;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
struct vpu_dec_codec_info {
|
||||
u32 pixfmt;
|
||||
u32 num_ref_frms;
|
||||
u32 num_dpb_frms;
|
||||
u32 num_dfe_area;
|
||||
u32 color_primaries;
|
||||
u32 transfer_chars;
|
||||
u32 matrix_coeffs;
|
||||
u32 full_range;
|
||||
u32 vui_present;
|
||||
u32 progressive;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 decoded_width;
|
||||
u32 decoded_height;
|
||||
struct v4l2_fract frame_rate;
|
||||
u32 dsp_asp_ratio;
|
||||
u32 level_idc;
|
||||
u32 bit_depth_luma;
|
||||
u32 bit_depth_chroma;
|
||||
u32 chroma_fmt;
|
||||
u32 mvc_num_views;
|
||||
u32 offset_x;
|
||||
u32 offset_y;
|
||||
u32 tag;
|
||||
u32 sizeimage[VIDEO_MAX_PLANES];
|
||||
u32 bytesperline[VIDEO_MAX_PLANES];
|
||||
u32 mbi_size;
|
||||
u32 dcp_size;
|
||||
u32 stride;
|
||||
};
|
||||
|
||||
struct vpu_dec_pic_info {
|
||||
u32 id;
|
||||
u32 luma;
|
||||
u32 start;
|
||||
u32 end;
|
||||
u32 pic_size;
|
||||
u32 stride;
|
||||
u32 skipped;
|
||||
s64 timestamp;
|
||||
u32 consumed_count;
|
||||
};
|
||||
|
||||
struct vpu_fs_info {
|
||||
u32 id;
|
||||
u32 type;
|
||||
u32 tag;
|
||||
u32 luma_addr;
|
||||
u32 luma_size;
|
||||
u32 chroma_addr;
|
||||
u32 chromau_size;
|
||||
u32 chromav_addr;
|
||||
u32 chromav_size;
|
||||
u32 bytesperline;
|
||||
u32 not_displayed;
|
||||
};
|
||||
|
||||
struct vpu_ts_info {
|
||||
s64 timestamp;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
#define BITRATE_STEP (1024)
|
||||
#define BITRATE_MIN (16 * BITRATE_STEP)
|
||||
#define BITRATE_MAX (240 * 1024 * BITRATE_STEP)
|
||||
#define BITRATE_DEFAULT (2 * 1024 * BITRATE_STEP)
|
||||
#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2)
|
||||
|
||||
#endif
|
||||
260
drivers/media/platform/amphion/vpu_drv.c
Normal file
260
drivers/media/platform/amphion/vpu_drv.c
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// 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/dma-map-ops.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/pm_runtime.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/v4l2-mem2mem.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include "vpu.h"
|
||||
#include "vpu_imx8q.h"
|
||||
|
||||
bool debug;
|
||||
module_param(debug, bool, 0644);
|
||||
|
||||
void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val)
|
||||
{
|
||||
writel(val, vpu->base + reg);
|
||||
}
|
||||
|
||||
u32 vpu_readl(struct vpu_dev *vpu, u32 reg)
|
||||
{
|
||||
return readl(vpu->base + reg);
|
||||
}
|
||||
|
||||
static void vpu_dev_get(struct vpu_dev *vpu)
|
||||
{
|
||||
if (atomic_inc_return(&vpu->ref_vpu) == 1 && vpu->res->setup)
|
||||
vpu->res->setup(vpu);
|
||||
}
|
||||
|
||||
static void vpu_dev_put(struct vpu_dev *vpu)
|
||||
{
|
||||
atomic_dec(&vpu->ref_vpu);
|
||||
}
|
||||
|
||||
static void vpu_enc_get(struct vpu_dev *vpu)
|
||||
{
|
||||
if (atomic_inc_return(&vpu->ref_enc) == 1 && vpu->res->setup_encoder)
|
||||
vpu->res->setup_encoder(vpu);
|
||||
}
|
||||
|
||||
static void vpu_enc_put(struct vpu_dev *vpu)
|
||||
{
|
||||
atomic_dec(&vpu->ref_enc);
|
||||
}
|
||||
|
||||
static void vpu_dec_get(struct vpu_dev *vpu)
|
||||
{
|
||||
if (atomic_inc_return(&vpu->ref_dec) == 1 && vpu->res->setup_decoder)
|
||||
vpu->res->setup_decoder(vpu);
|
||||
}
|
||||
|
||||
static void vpu_dec_put(struct vpu_dev *vpu)
|
||||
{
|
||||
atomic_dec(&vpu->ref_dec);
|
||||
}
|
||||
|
||||
static int vpu_init_media_device(struct vpu_dev *vpu)
|
||||
{
|
||||
vpu->mdev.dev = vpu->dev;
|
||||
strscpy(vpu->mdev.model, "amphion-vpu", sizeof(vpu->mdev.model));
|
||||
strscpy(vpu->mdev.bus_info, "platform: amphion-vpu", sizeof(vpu->mdev.bus_info));
|
||||
media_device_init(&vpu->mdev);
|
||||
vpu->v4l2_dev.mdev = &vpu->mdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vpu_dev *vpu;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "probe\n");
|
||||
vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL);
|
||||
if (!vpu)
|
||||
return -ENOMEM;
|
||||
|
||||
vpu->pdev = pdev;
|
||||
vpu->dev = dev;
|
||||
mutex_init(&vpu->lock);
|
||||
INIT_LIST_HEAD(&vpu->cores);
|
||||
platform_set_drvdata(pdev, vpu);
|
||||
atomic_set(&vpu->ref_vpu, 0);
|
||||
atomic_set(&vpu->ref_enc, 0);
|
||||
atomic_set(&vpu->ref_dec, 0);
|
||||
vpu->get_vpu = vpu_dev_get;
|
||||
vpu->put_vpu = vpu_dev_put;
|
||||
vpu->get_enc = vpu_enc_get;
|
||||
vpu->put_enc = vpu_enc_put;
|
||||
vpu->get_dec = vpu_dec_get;
|
||||
vpu->put_dec = vpu_dec_put;
|
||||
|
||||
vpu->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(vpu->base))
|
||||
return PTR_ERR(vpu->base);
|
||||
|
||||
vpu->res = of_device_get_match_data(dev);
|
||||
if (!vpu->res)
|
||||
return -ENODEV;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret)
|
||||
goto err_runtime_disable;
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
ret = v4l2_device_register(dev, &vpu->v4l2_dev);
|
||||
if (ret)
|
||||
goto err_vpu_deinit;
|
||||
|
||||
vpu_init_media_device(vpu);
|
||||
vpu->encoder.type = VPU_CORE_TYPE_ENC;
|
||||
vpu->encoder.function = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
|
||||
vpu->decoder.type = VPU_CORE_TYPE_DEC;
|
||||
vpu->decoder.function = MEDIA_ENT_F_PROC_VIDEO_DECODER;
|
||||
vpu_add_func(vpu, &vpu->decoder);
|
||||
vpu_add_func(vpu, &vpu->encoder);
|
||||
ret = media_device_register(&vpu->mdev);
|
||||
if (ret)
|
||||
goto err_vpu_media;
|
||||
vpu->debugfs = debugfs_create_dir("amphion_vpu", NULL);
|
||||
|
||||
of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_vpu_media:
|
||||
vpu_remove_func(&vpu->encoder);
|
||||
vpu_remove_func(&vpu->decoder);
|
||||
v4l2_device_unregister(&vpu->v4l2_dev);
|
||||
err_vpu_deinit:
|
||||
err_runtime_disable:
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vpu_dev *vpu = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
debugfs_remove_recursive(vpu->debugfs);
|
||||
vpu->debugfs = NULL;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
media_device_unregister(&vpu->mdev);
|
||||
vpu_remove_func(&vpu->decoder);
|
||||
vpu_remove_func(&vpu->encoder);
|
||||
media_device_cleanup(&vpu->mdev);
|
||||
v4l2_device_unregister(&vpu->v4l2_dev);
|
||||
mutex_destroy(&vpu->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_runtime_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused vpu_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vpu_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume)
|
||||
};
|
||||
|
||||
static struct vpu_resources imx8qxp_res = {
|
||||
.plat_type = IMX8QXP,
|
||||
.mreg_base = 0x40000000,
|
||||
.setup = vpu_imx8q_setup,
|
||||
.setup_encoder = vpu_imx8q_setup_enc,
|
||||
.setup_decoder = vpu_imx8q_setup_dec,
|
||||
.reset = vpu_imx8q_reset
|
||||
};
|
||||
|
||||
static struct vpu_resources imx8qm_res = {
|
||||
.plat_type = IMX8QM,
|
||||
.mreg_base = 0x40000000,
|
||||
.setup = vpu_imx8q_setup,
|
||||
.setup_encoder = vpu_imx8q_setup_enc,
|
||||
.setup_decoder = vpu_imx8q_setup_dec,
|
||||
.reset = vpu_imx8q_reset
|
||||
};
|
||||
|
||||
static const struct of_device_id vpu_dt_match[] = {
|
||||
{ .compatible = "nxp,imx8qxp-vpu", .data = &imx8qxp_res },
|
||||
{ .compatible = "nxp,imx8qm-vpu", .data = &imx8qm_res },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vpu_dt_match);
|
||||
|
||||
static struct platform_driver amphion_vpu_driver = {
|
||||
.probe = vpu_probe,
|
||||
.remove = vpu_remove,
|
||||
.driver = {
|
||||
.name = "amphion-vpu",
|
||||
.of_match_table = vpu_dt_match,
|
||||
.pm = &vpu_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init vpu_driver_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&hion_vpu_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vpu_core_driver_init();
|
||||
}
|
||||
|
||||
static void __exit vpu_driver_exit(void)
|
||||
{
|
||||
vpu_core_driver_exit();
|
||||
platform_driver_unregister(&hion_vpu_driver);
|
||||
}
|
||||
module_init(vpu_driver_init);
|
||||
module_exit(vpu_driver_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX8Q");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
413
drivers/media/platform/amphion/vpu_helpers.c
Normal file
413
drivers/media/platform/amphion/vpu_helpers.c
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
// 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/platform_device.h>
|
||||
#include "vpu.h"
|
||||
#include "vpu_core.h"
|
||||
#include "vpu_rpc.h"
|
||||
#include "vpu_helpers.h"
|
||||
|
||||
int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (array[i] == x)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vpu_helper_check_type(struct vpu_inst *inst, u32 type)
|
||||
{
|
||||
const struct vpu_format *pfmt;
|
||||
|
||||
for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
|
||||
if (!vpu_iface_check_format(inst, pfmt->pixfmt))
|
||||
continue;
|
||||
if (pfmt->type == type)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt)
|
||||
{
|
||||
const struct vpu_format *pfmt;
|
||||
|
||||
if (!inst || !inst->formats)
|
||||
return NULL;
|
||||
|
||||
if (!vpu_iface_check_format(inst, pixelfmt))
|
||||
return NULL;
|
||||
|
||||
for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
|
||||
if (pfmt->pixfmt == pixelfmt && (!type || type == pfmt->type))
|
||||
return pfmt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index)
|
||||
{
|
||||
const struct vpu_format *pfmt;
|
||||
int i = 0;
|
||||
|
||||
if (!inst || !inst->formats)
|
||||
return NULL;
|
||||
|
||||
for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
|
||||
if (!vpu_iface_check_format(inst, pfmt->pixfmt))
|
||||
continue;
|
||||
|
||||
if (pfmt->type == type) {
|
||||
if (index == i)
|
||||
return pfmt;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width)
|
||||
{
|
||||
const struct vpu_core_resources *res;
|
||||
|
||||
if (!inst)
|
||||
return width;
|
||||
|
||||
res = vpu_get_resource(inst);
|
||||
if (!res)
|
||||
return width;
|
||||
if (res->max_width)
|
||||
width = clamp(width, res->min_width, res->max_width);
|
||||
if (res->step_width)
|
||||
width = ALIGN(width, res->step_width);
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height)
|
||||
{
|
||||
const struct vpu_core_resources *res;
|
||||
|
||||
if (!inst)
|
||||
return height;
|
||||
|
||||
res = vpu_get_resource(inst);
|
||||
if (!res)
|
||||
return height;
|
||||
if (res->max_height)
|
||||
height = clamp(height, res->min_height, res->max_height);
|
||||
if (res->step_height)
|
||||
height = ALIGN(height, res->step_height);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
static u32 get_nv12_plane_size(u32 width, u32 height, int plane_no,
|
||||
u32 stride, u32 interlaced, u32 *pbl)
|
||||
{
|
||||
u32 bytesperline;
|
||||
u32 size = 0;
|
||||
|
||||
bytesperline = ALIGN(width, stride);
|
||||
if (pbl)
|
||||
bytesperline = max(bytesperline, *pbl);
|
||||
height = ALIGN(height, 2);
|
||||
if (plane_no == 0)
|
||||
size = bytesperline * height;
|
||||
else if (plane_no == 1)
|
||||
size = bytesperline * height >> 1;
|
||||
if (pbl)
|
||||
*pbl = bytesperline;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static u32 get_tiled_8l128_plane_size(u32 fmt, u32 width, u32 height, int plane_no,
|
||||
u32 stride, u32 interlaced, u32 *pbl)
|
||||
{
|
||||
u32 ws = 3;
|
||||
u32 hs = 7;
|
||||
u32 bitdepth = 8;
|
||||
u32 bytesperline;
|
||||
u32 size = 0;
|
||||
|
||||
if (interlaced)
|
||||
hs++;
|
||||
if (fmt == V4L2_PIX_FMT_NV12M_10BE_8L128)
|
||||
bitdepth = 10;
|
||||
bytesperline = DIV_ROUND_UP(width * bitdepth, BITS_PER_BYTE);
|
||||
bytesperline = ALIGN(bytesperline, 1 << ws);
|
||||
bytesperline = ALIGN(bytesperline, stride);
|
||||
if (pbl)
|
||||
bytesperline = max(bytesperline, *pbl);
|
||||
height = ALIGN(height, 1 << hs);
|
||||
if (plane_no == 0)
|
||||
size = bytesperline * height;
|
||||
else if (plane_no == 1)
|
||||
size = (bytesperline * ALIGN(height, 1 << (hs + 1))) >> 1;
|
||||
if (pbl)
|
||||
*pbl = bytesperline;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static u32 get_default_plane_size(u32 width, u32 height, int plane_no,
|
||||
u32 stride, u32 interlaced, u32 *pbl)
|
||||
{
|
||||
u32 bytesperline;
|
||||
u32 size = 0;
|
||||
|
||||
bytesperline = ALIGN(width, stride);
|
||||
if (pbl)
|
||||
bytesperline = max(bytesperline, *pbl);
|
||||
if (plane_no == 0)
|
||||
size = bytesperline * height;
|
||||
if (pbl)
|
||||
*pbl = bytesperline;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u32 vpu_helper_get_plane_size(u32 fmt, u32 w, u32 h, int plane_no,
|
||||
u32 stride, u32 interlaced, u32 *pbl)
|
||||
{
|
||||
switch (fmt) {
|
||||
case V4L2_PIX_FMT_NV12M:
|
||||
return get_nv12_plane_size(w, h, plane_no, stride, interlaced, pbl);
|
||||
case V4L2_PIX_FMT_NV12M_8L128:
|
||||
case V4L2_PIX_FMT_NV12M_10BE_8L128:
|
||||
return get_tiled_8l128_plane_size(fmt, w, h, plane_no, stride, interlaced, pbl);
|
||||
default:
|
||||
return get_default_plane_size(w, h, plane_no, stride, interlaced, pbl);
|
||||
}
|
||||
}
|
||||
|
||||
u32 vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer,
|
||||
u32 *rptr, u32 size, void *dst)
|
||||
{
|
||||
u32 offset;
|
||||
u32 start;
|
||||
u32 end;
|
||||
void *virt;
|
||||
|
||||
if (!stream_buffer || !rptr || !dst)
|
||||
return -EINVAL;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
offset = *rptr;
|
||||
start = stream_buffer->phys;
|
||||
end = start + stream_buffer->length;
|
||||
virt = stream_buffer->virt;
|
||||
|
||||
if (offset < start || offset > end)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset + size <= end) {
|
||||
memcpy(dst, virt + (offset - start), size);
|
||||
} else {
|
||||
memcpy(dst, virt + (offset - start), end - offset);
|
||||
memcpy(dst + end - offset, virt, size + offset - end);
|
||||
}
|
||||
|
||||
*rptr = vpu_helper_step_walk(stream_buffer, offset, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
u32 vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer,
|
||||
u32 *wptr, u32 size, void *src)
|
||||
{
|
||||
u32 offset;
|
||||
u32 start;
|
||||
u32 end;
|
||||
void *virt;
|
||||
|
||||
if (!stream_buffer || !wptr || !src)
|
||||
return -EINVAL;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
offset = *wptr;
|
||||
start = stream_buffer->phys;
|
||||
end = start + stream_buffer->length;
|
||||
virt = stream_buffer->virt;
|
||||
if (offset < start || offset > end)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset + size <= end) {
|
||||
memcpy(virt + (offset - start), src, size);
|
||||
} else {
|
||||
memcpy(virt + (offset - start), src, end - offset);
|
||||
memcpy(virt, src + end - offset, size + offset - end);
|
||||
}
|
||||
|
||||
*wptr = vpu_helper_step_walk(stream_buffer, offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u32 vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer,
|
||||
u32 *wptr, u8 val, u32 size)
|
||||
{
|
||||
u32 offset;
|
||||
u32 start;
|
||||
u32 end;
|
||||
void *virt;
|
||||
|
||||
if (!stream_buffer || !wptr)
|
||||
return -EINVAL;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
offset = *wptr;
|
||||
start = stream_buffer->phys;
|
||||
end = start + stream_buffer->length;
|
||||
virt = stream_buffer->virt;
|
||||
if (offset < start || offset > end)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset + size <= end) {
|
||||
memset(virt + (offset - start), val, size);
|
||||
} else {
|
||||
memset(virt + (offset - start), val, end - offset);
|
||||
memset(virt, val, size + offset - end);
|
||||
}
|
||||
|
||||
offset += size;
|
||||
if (offset >= end)
|
||||
offset -= stream_buffer->length;
|
||||
|
||||
*wptr = offset;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u32 vpu_helper_get_free_space(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_rpc_buffer_desc desc;
|
||||
|
||||
if (vpu_iface_get_stream_buffer_desc(inst, &desc))
|
||||
return 0;
|
||||
|
||||
if (desc.rptr > desc.wptr)
|
||||
return desc.rptr - desc.wptr;
|
||||
else if (desc.rptr < desc.wptr)
|
||||
return (desc.end - desc.start + desc.rptr - desc.wptr);
|
||||
else
|
||||
return desc.end - desc.start;
|
||||
}
|
||||
|
||||
u32 vpu_helper_get_used_space(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_rpc_buffer_desc desc;
|
||||
|
||||
if (vpu_iface_get_stream_buffer_desc(inst, &desc))
|
||||
return 0;
|
||||
|
||||
if (desc.wptr > desc.rptr)
|
||||
return desc.wptr - desc.rptr;
|
||||
else if (desc.wptr < desc.rptr)
|
||||
return (desc.end - desc.start + desc.wptr - desc.rptr);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct vpu_inst *inst = ctrl_to_inst(ctrl);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
|
||||
ctrl->val = inst->min_buffer_cap;
|
||||
break;
|
||||
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
|
||||
ctrl->val = inst->min_buffer_out;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer,
|
||||
u32 pixelformat, u32 offset, u32 bytesused)
|
||||
{
|
||||
u32 start_code;
|
||||
int start_code_size;
|
||||
u32 val = 0;
|
||||
int i;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!stream_buffer || !stream_buffer->virt)
|
||||
return -EINVAL;
|
||||
|
||||
switch (pixelformat) {
|
||||
case V4L2_PIX_FMT_H264:
|
||||
start_code_size = 4;
|
||||
start_code = 0x00000001;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytesused; i++) {
|
||||
val = (val << 8) | vpu_helper_read_byte(stream_buffer, offset + i);
|
||||
if (i < start_code_size - 1)
|
||||
continue;
|
||||
if (val == start_code) {
|
||||
ret = i + 1 - start_code_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if (!pairs || !cnt)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (pairs[i].src == src)
|
||||
return pairs[i].dst;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if (!pairs || !cnt)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (pairs[i].dst == dst)
|
||||
return pairs[i].src;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
74
drivers/media/platform/amphion/vpu_helpers.h
Normal file
74
drivers/media/platform/amphion/vpu_helpers.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* 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
|
||||
271
drivers/media/platform/amphion/vpu_imx8q.c
Normal file
271
drivers/media/platform/amphion/vpu_imx8q.c
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// 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 "vpu.h"
|
||||
#include "vpu_core.h"
|
||||
#include "vpu_imx8q.h"
|
||||
#include "vpu_rpc.h"
|
||||
|
||||
#define IMX8Q_CSR_CM0Px_ADDR_OFFSET 0x00000000
|
||||
#define IMX8Q_CSR_CM0Px_CPUWAIT 0x00000004
|
||||
|
||||
#ifdef CONFIG_IMX_SCU
|
||||
#include <linux/firmware/imx/ipc.h>
|
||||
#include <linux/firmware/imx/svc/misc.h>
|
||||
|
||||
#define VPU_DISABLE_BITS 0x7
|
||||
#define VPU_IMX_DECODER_FUSE_OFFSET 14
|
||||
#define VPU_ENCODER_MASK 0x1
|
||||
#define VPU_DECODER_MASK 0x3UL
|
||||
#define VPU_DECODER_H264_MASK 0x2UL
|
||||
#define VPU_DECODER_HEVC_MASK 0x1UL
|
||||
|
||||
static u32 imx8q_fuse;
|
||||
|
||||
struct vpu_sc_msg_misc {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u32 word;
|
||||
} __packed;
|
||||
#endif
|
||||
|
||||
int vpu_imx8q_setup_dec(struct vpu_dev *vpu)
|
||||
{
|
||||
const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL;
|
||||
|
||||
vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET, 0x1f);
|
||||
vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_SET, 0xffffffff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_setup_enc(struct vpu_dev *vpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_setup(struct vpu_dev *vpu)
|
||||
{
|
||||
const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL;
|
||||
|
||||
vpu_readl(vpu, offset + 0x108);
|
||||
|
||||
vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0x1);
|
||||
vpu_writel(vpu, offset + 0x190, 0xffffffff);
|
||||
vpu_writel(vpu, offset + SCB_BLK_CTRL_XMEM_RESET_SET, 0xffffffff);
|
||||
vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0xE);
|
||||
vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_SET, 0x7);
|
||||
vpu_writel(vpu, XMEM_CONTROL, 0x102);
|
||||
|
||||
vpu_readl(vpu, offset + 0x108);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_imx8q_reset_enc(struct vpu_dev *vpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_imx8q_reset_dec(struct vpu_dev *vpu)
|
||||
{
|
||||
const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL;
|
||||
|
||||
vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_CLR, 0xffffffff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_reset(struct vpu_dev *vpu)
|
||||
{
|
||||
const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL;
|
||||
|
||||
vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_CLR, 0x7);
|
||||
vpu_imx8q_reset_enc(vpu);
|
||||
vpu_imx8q_reset_dec(vpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id)
|
||||
{
|
||||
if (!config)
|
||||
return -EINVAL;
|
||||
|
||||
switch (core_id) {
|
||||
case 0:
|
||||
config->malone_base_addr[0] = regs + DEC_MFD_XREG_SLV_BASE;
|
||||
config->num_malones = 1;
|
||||
config->num_windsors = 0;
|
||||
break;
|
||||
case 1:
|
||||
config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_0_BASE;
|
||||
config->num_windsors = 1;
|
||||
config->num_malones = 0;
|
||||
break;
|
||||
case 2:
|
||||
config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_1_BASE;
|
||||
config->num_windsors = 1;
|
||||
config->num_malones = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (config->num_windsors) {
|
||||
config->windsor_irq_pin[0x0][0x0] = WINDSOR_PAL_IRQ_PIN_L;
|
||||
config->windsor_irq_pin[0x0][0x1] = WINDSOR_PAL_IRQ_PIN_H;
|
||||
}
|
||||
|
||||
config->malone_base_addr[0x1] = 0x0;
|
||||
config->hif_offset[0x0] = MFD_HIF;
|
||||
config->hif_offset[0x1] = 0x0;
|
||||
|
||||
config->dpv_base_addr = 0x0;
|
||||
config->dpv_irq_pin = 0x0;
|
||||
config->pixif_base_addr = regs + DEC_MFD_XREG_SLV_BASE + MFD_PIX_IF;
|
||||
config->cache_base_addr[0] = regs + MC_CACHE_0_BASE;
|
||||
config->cache_base_addr[1] = regs + MC_CACHE_1_BASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_boot_core(struct vpu_core *core)
|
||||
{
|
||||
csr_writel(core, IMX8Q_CSR_CM0Px_ADDR_OFFSET, core->fw.phys);
|
||||
csr_writel(core, IMX8Q_CSR_CM0Px_CPUWAIT, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_imx8q_get_power_state(struct vpu_core *core)
|
||||
{
|
||||
if (csr_readl(core, IMX8Q_CSR_CM0Px_CPUWAIT) == 1)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vpu_imx8q_on_firmware_loaded(struct vpu_core *core)
|
||||
{
|
||||
u8 *p;
|
||||
|
||||
p = core->fw.virt;
|
||||
p[16] = core->vpu->res->plat_type;
|
||||
p[17] = core->id;
|
||||
p[18] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size)
|
||||
{
|
||||
const struct vpu_rpc_region_t imx8q_regions[] = {
|
||||
{0x00000000, 0x08000000, VPU_CORE_MEMORY_CACHED},
|
||||
{0x08000000, 0x10000000, VPU_CORE_MEMORY_UNCACHED},
|
||||
{0x10000000, 0x20000000, VPU_CORE_MEMORY_CACHED},
|
||||
{0x20000000, 0x40000000, VPU_CORE_MEMORY_UNCACHED}
|
||||
};
|
||||
int i;
|
||||
|
||||
if (addr < base)
|
||||
return VPU_CORE_MEMORY_INVALID;
|
||||
|
||||
addr -= base;
|
||||
for (i = 0; i < ARRAY_SIZE(imx8q_regions); i++) {
|
||||
const struct vpu_rpc_region_t *region = &imx8q_regions[i];
|
||||
|
||||
if (addr >= region->start && addr + size < region->end)
|
||||
return region->type;
|
||||
}
|
||||
|
||||
return VPU_CORE_MEMORY_INVALID;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IMX_SCU
|
||||
static u32 vpu_imx8q_get_fuse(void)
|
||||
{
|
||||
static u32 fuse_got;
|
||||
struct imx_sc_ipc *ipc;
|
||||
struct vpu_sc_msg_misc msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
int ret;
|
||||
|
||||
if (fuse_got)
|
||||
return imx8q_fuse;
|
||||
|
||||
ret = imx_scu_get_handle(&ipc);
|
||||
if (ret) {
|
||||
pr_err("error: get sct handle fail: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_MISC;
|
||||
hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ;
|
||||
hdr->size = 2;
|
||||
|
||||
msg.word = VPU_DISABLE_BITS;
|
||||
|
||||
ret = imx_scu_call_rpc(ipc, &msg, true);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
imx8q_fuse = msg.word;
|
||||
fuse_got = 1;
|
||||
return imx8q_fuse;
|
||||
}
|
||||
|
||||
bool vpu_imx8q_check_codec(enum vpu_core_type type)
|
||||
{
|
||||
u32 fuse = vpu_imx8q_get_fuse();
|
||||
|
||||
if (type == VPU_CORE_TYPE_ENC) {
|
||||
if (fuse & VPU_ENCODER_MASK)
|
||||
return false;
|
||||
} else if (type == VPU_CORE_TYPE_DEC) {
|
||||
fuse >>= VPU_IMX_DECODER_FUSE_OFFSET;
|
||||
fuse &= VPU_DECODER_MASK;
|
||||
|
||||
if (fuse == VPU_DECODER_MASK)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt)
|
||||
{
|
||||
u32 fuse = vpu_imx8q_get_fuse();
|
||||
|
||||
if (type == VPU_CORE_TYPE_DEC) {
|
||||
fuse >>= VPU_IMX_DECODER_FUSE_OFFSET;
|
||||
fuse &= VPU_DECODER_MASK;
|
||||
|
||||
if (fuse == VPU_DECODER_HEVC_MASK && pixelfmt == V4L2_PIX_FMT_HEVC)
|
||||
return false;
|
||||
if (fuse == VPU_DECODER_H264_MASK && pixelfmt == V4L2_PIX_FMT_H264)
|
||||
return false;
|
||||
if (fuse == VPU_DECODER_MASK)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool vpu_imx8q_check_codec(enum vpu_core_type type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
115
drivers/media/platform/amphion/vpu_imx8q.h
Normal file
115
drivers/media/platform/amphion/vpu_imx8q.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_IMX8Q_H
|
||||
#define _AMPHION_VPU_IMX8Q_H
|
||||
|
||||
#define SCB_XREG_SLV_BASE 0x00000000
|
||||
#define SCB_SCB_BLK_CTRL 0x00070000
|
||||
#define SCB_BLK_CTRL_XMEM_RESET_SET 0x00000090
|
||||
#define SCB_BLK_CTRL_CACHE_RESET_SET 0x000000A0
|
||||
#define SCB_BLK_CTRL_CACHE_RESET_CLR 0x000000A4
|
||||
#define SCB_BLK_CTRL_SCB_CLK_ENABLE_SET 0x00000100
|
||||
|
||||
#define XMEM_CONTROL 0x00041000
|
||||
|
||||
#define MC_CACHE_0_BASE 0x00060000
|
||||
#define MC_CACHE_1_BASE 0x00068000
|
||||
|
||||
#define DEC_MFD_XREG_SLV_BASE 0x00180000
|
||||
#define ENC_MFD_XREG_SLV_0_BASE 0x00800000
|
||||
#define ENC_MFD_XREG_SLV_1_BASE 0x00A00000
|
||||
|
||||
#define MFD_HIF 0x0001C000
|
||||
#define MFD_HIF_MSD_REG_INTERRUPT_STATUS 0x00000018
|
||||
#define MFD_SIF 0x0001D000
|
||||
#define MFD_SIF_CTRL_STATUS 0x000000F0
|
||||
#define MFD_SIF_INTR_STATUS 0x000000F4
|
||||
#define MFD_MCX 0x00020800
|
||||
#define MFD_MCX_OFF 0x00000020
|
||||
#define MFD_PIX_IF 0x00020000
|
||||
|
||||
#define MFD_BLK_CTRL 0x00030000
|
||||
#define MFD_BLK_CTRL_MFD_SYS_RESET_SET 0x00000000
|
||||
#define MFD_BLK_CTRL_MFD_SYS_RESET_CLR 0x00000004
|
||||
#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET 0x00000100
|
||||
#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_CLR 0x00000104
|
||||
|
||||
#define VID_API_NUM_STREAMS 8
|
||||
#define VID_API_MAX_BUF_PER_STR 3
|
||||
#define VID_API_MAX_NUM_MVC_VIEWS 4
|
||||
#define MEDIAIP_MAX_NUM_MALONES 2
|
||||
#define MEDIAIP_MAX_NUM_MALONE_IRQ_PINS 2
|
||||
#define MEDIAIP_MAX_NUM_WINDSORS 1
|
||||
#define MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS 2
|
||||
#define MEDIAIP_MAX_NUM_CMD_IRQ_PINS 2
|
||||
#define MEDIAIP_MAX_NUM_MSG_IRQ_PINS 1
|
||||
#define MEDIAIP_MAX_NUM_TIMER_IRQ_PINS 4
|
||||
#define MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS 4
|
||||
|
||||
#define WINDSOR_PAL_IRQ_PIN_L 0x4
|
||||
#define WINDSOR_PAL_IRQ_PIN_H 0x5
|
||||
|
||||
struct vpu_rpc_system_config {
|
||||
u32 cfg_cookie;
|
||||
|
||||
u32 num_malones;
|
||||
u32 malone_base_addr[MEDIAIP_MAX_NUM_MALONES];
|
||||
u32 hif_offset[MEDIAIP_MAX_NUM_MALONES];
|
||||
u32 malone_irq_pin[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS];
|
||||
u32 malone_irq_target[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS];
|
||||
|
||||
u32 num_windsors;
|
||||
u32 windsor_base_addr[MEDIAIP_MAX_NUM_WINDSORS];
|
||||
u32 windsor_irq_pin[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS];
|
||||
u32 windsor_irq_target[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS];
|
||||
|
||||
u32 cmd_irq_pin[MEDIAIP_MAX_NUM_CMD_IRQ_PINS];
|
||||
u32 cmd_irq_target[MEDIAIP_MAX_NUM_CMD_IRQ_PINS];
|
||||
|
||||
u32 msg_irq_pin[MEDIAIP_MAX_NUM_MSG_IRQ_PINS];
|
||||
u32 msg_irq_target[MEDIAIP_MAX_NUM_MSG_IRQ_PINS];
|
||||
|
||||
u32 sys_clk_freq;
|
||||
u32 num_timers;
|
||||
u32 timer_base_addr;
|
||||
u32 timer_irq_pin[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS];
|
||||
u32 timer_irq_target[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS];
|
||||
u32 timer_slots[MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS];
|
||||
|
||||
u32 gic_base_addr;
|
||||
u32 uart_base_addr;
|
||||
|
||||
u32 dpv_base_addr;
|
||||
u32 dpv_irq_pin;
|
||||
u32 dpv_irq_target;
|
||||
|
||||
u32 pixif_base_addr;
|
||||
|
||||
u32 pal_trace_level;
|
||||
u32 pal_trace_destination;
|
||||
|
||||
u32 pal_trace_level1;
|
||||
u32 pal_trace_destination1;
|
||||
|
||||
u32 heap_base;
|
||||
u32 heap_size;
|
||||
|
||||
u32 cache_base_addr[2];
|
||||
};
|
||||
|
||||
int vpu_imx8q_setup_dec(struct vpu_dev *vpu);
|
||||
int vpu_imx8q_setup_enc(struct vpu_dev *vpu);
|
||||
int vpu_imx8q_setup(struct vpu_dev *vpu);
|
||||
int vpu_imx8q_reset(struct vpu_dev *vpu);
|
||||
int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id);
|
||||
int vpu_imx8q_boot_core(struct vpu_core *core);
|
||||
int vpu_imx8q_get_power_state(struct vpu_core *core);
|
||||
int vpu_imx8q_on_firmware_loaded(struct vpu_core *core);
|
||||
u32 vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size);
|
||||
bool vpu_imx8q_check_codec(enum vpu_core_type type);
|
||||
bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt);
|
||||
|
||||
#endif
|
||||
1625
drivers/media/platform/amphion/vpu_malone.c
Normal file
1625
drivers/media/platform/amphion/vpu_malone.c
Normal file
File diff suppressed because it is too large
Load Diff
44
drivers/media/platform/amphion/vpu_malone.h
Normal file
44
drivers/media/platform/amphion/vpu_malone.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_MALONE_H
|
||||
#define _AMPHION_VPU_MALONE_H
|
||||
|
||||
u32 vpu_malone_get_data_size(void);
|
||||
void vpu_malone_init_rpc(struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *rpc, dma_addr_t boot_addr);
|
||||
void vpu_malone_set_log_buf(struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *log);
|
||||
void vpu_malone_set_system_cfg(struct vpu_shared_addr *shared,
|
||||
u32 regs_base, void __iomem *regs, u32 core_id);
|
||||
u32 vpu_malone_get_version(struct vpu_shared_addr *shared);
|
||||
int vpu_malone_get_stream_buffer_size(struct vpu_shared_addr *shared);
|
||||
int vpu_malone_config_stream_buffer(struct vpu_shared_addr *shared,
|
||||
u32 instance, struct vpu_buffer *buf);
|
||||
int vpu_malone_get_stream_buffer_desc(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_rpc_buffer_desc *desc);
|
||||
int vpu_malone_update_stream_buffer(struct vpu_shared_addr *shared,
|
||||
u32 instance, u32 ptr, bool write);
|
||||
int vpu_malone_set_decode_params(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_decode_params *params, u32 update);
|
||||
int vpu_malone_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
|
||||
int vpu_malone_convert_msg_id(u32 msg_id);
|
||||
int vpu_malone_unpack_msg_data(struct vpu_rpc_event *pkt, void *data);
|
||||
int vpu_malone_add_scode(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_buffer *stream_buffer,
|
||||
u32 pixelformat,
|
||||
u32 scode_type);
|
||||
int vpu_malone_input_frame(struct vpu_shared_addr *shared,
|
||||
struct vpu_inst *inst, struct vb2_buffer *vb);
|
||||
bool vpu_malone_is_ready(struct vpu_shared_addr *shared, u32 instance);
|
||||
int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance);
|
||||
int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance);
|
||||
int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance);
|
||||
u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared);
|
||||
|
||||
#endif
|
||||
118
drivers/media/platform/amphion/vpu_mbox.c
Normal file
118
drivers/media/platform/amphion/vpu_mbox.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// 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 "vpu.h"
|
||||
#include "vpu_mbox.h"
|
||||
#include "vpu_msgs.h"
|
||||
|
||||
static void vpu_mbox_rx_callback(struct mbox_client *cl, void *msg)
|
||||
{
|
||||
struct vpu_mbox *rx = container_of(cl, struct vpu_mbox, cl);
|
||||
struct vpu_core *core = container_of(rx, struct vpu_core, rx);
|
||||
|
||||
vpu_isr(core, *(u32 *)msg);
|
||||
}
|
||||
|
||||
static int vpu_mbox_request_channel(struct device *dev, struct vpu_mbox *mbox)
|
||||
{
|
||||
struct mbox_chan *ch;
|
||||
struct mbox_client *cl;
|
||||
|
||||
if (!dev || !mbox)
|
||||
return -EINVAL;
|
||||
if (mbox->ch)
|
||||
return 0;
|
||||
|
||||
cl = &mbox->cl;
|
||||
cl->dev = dev;
|
||||
if (mbox->block) {
|
||||
cl->tx_block = true;
|
||||
cl->tx_tout = 1000;
|
||||
} else {
|
||||
cl->tx_block = false;
|
||||
}
|
||||
cl->knows_txdone = false;
|
||||
cl->rx_callback = vpu_mbox_rx_callback;
|
||||
|
||||
ch = mbox_request_channel_byname(cl, mbox->name);
|
||||
if (IS_ERR(ch)) {
|
||||
dev_err(dev, "Failed to request mbox chan %s, ret : %ld\n",
|
||||
mbox->name, PTR_ERR(ch));
|
||||
return PTR_ERR(ch);
|
||||
}
|
||||
|
||||
mbox->ch = ch;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_mbox_init(struct vpu_core *core)
|
||||
{
|
||||
scnprintf(core->tx_type.name, sizeof(core->tx_type.name) - 1, "tx0");
|
||||
core->tx_type.block = true;
|
||||
|
||||
scnprintf(core->tx_data.name, sizeof(core->tx_data.name) - 1, "tx1");
|
||||
core->tx_data.block = false;
|
||||
|
||||
scnprintf(core->rx.name, sizeof(core->rx.name) - 1, "rx");
|
||||
core->rx.block = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_mbox_request(struct vpu_core *core)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vpu_mbox_request_channel(core->dev, &core->tx_type);
|
||||
if (ret)
|
||||
goto error;
|
||||
ret = vpu_mbox_request_channel(core->dev, &core->tx_data);
|
||||
if (ret)
|
||||
goto error;
|
||||
ret = vpu_mbox_request_channel(core->dev, &core->rx);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
dev_dbg(core->dev, "%s request mbox\n", vpu_core_type_desc(core->type));
|
||||
return 0;
|
||||
error:
|
||||
vpu_mbox_free(core);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vpu_mbox_free(struct vpu_core *core)
|
||||
{
|
||||
mbox_free_channel(core->tx_type.ch);
|
||||
mbox_free_channel(core->tx_data.ch);
|
||||
mbox_free_channel(core->rx.ch);
|
||||
core->tx_type.ch = NULL;
|
||||
core->tx_data.ch = NULL;
|
||||
core->rx.ch = NULL;
|
||||
dev_dbg(core->dev, "%s free mbox\n", vpu_core_type_desc(core->type));
|
||||
}
|
||||
|
||||
void vpu_mbox_send_type(struct vpu_core *core, u32 type)
|
||||
{
|
||||
mbox_send_message(core->tx_type.ch, &type);
|
||||
}
|
||||
|
||||
void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data)
|
||||
{
|
||||
mbox_send_message(core->tx_data.ch, &data);
|
||||
mbox_send_message(core->tx_type.ch, &type);
|
||||
}
|
||||
|
||||
void vpu_mbox_enable_rx(struct vpu_dev *dev)
|
||||
{
|
||||
}
|
||||
16
drivers/media/platform/amphion/vpu_mbox.h
Normal file
16
drivers/media/platform/amphion/vpu_mbox.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_MBOX_H
|
||||
#define _AMPHION_VPU_MBOX_H
|
||||
|
||||
int vpu_mbox_init(struct vpu_core *core);
|
||||
int vpu_mbox_request(struct vpu_core *core);
|
||||
void vpu_mbox_free(struct vpu_core *core);
|
||||
void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data);
|
||||
void vpu_mbox_send_type(struct vpu_core *core, u32 type);
|
||||
void vpu_mbox_enable_rx(struct vpu_dev *dev);
|
||||
|
||||
#endif
|
||||
385
drivers/media/platform/amphion/vpu_msgs.c
Normal file
385
drivers/media/platform/amphion/vpu_msgs.c
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
// 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 "vpu.h"
|
||||
#include "vpu_core.h"
|
||||
#include "vpu_rpc.h"
|
||||
#include "vpu_mbox.h"
|
||||
#include "vpu_defs.h"
|
||||
#include "vpu_cmds.h"
|
||||
#include "vpu_msgs.h"
|
||||
#include "vpu_v4l2.h"
|
||||
|
||||
#define VPU_PKT_HEADER_LENGTH 3
|
||||
|
||||
struct vpu_msg_handler {
|
||||
u32 id;
|
||||
void (*done)(struct vpu_inst *inst, struct vpu_rpc_event *pkt);
|
||||
};
|
||||
|
||||
static void vpu_session_handle_start_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
vpu_trace(inst->dev, "[%d]\n", inst->id);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_mem_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_pkt_mem_req_data req_data;
|
||||
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&req_data);
|
||||
vpu_trace(inst->dev, "[%d] %d:%d %d:%d %d:%d\n",
|
||||
inst->id,
|
||||
req_data.enc_frame_size,
|
||||
req_data.enc_frame_num,
|
||||
req_data.ref_frame_size,
|
||||
req_data.ref_frame_num,
|
||||
req_data.act_buf_size,
|
||||
req_data.act_buf_num);
|
||||
call_void_vop(inst, mem_request,
|
||||
req_data.enc_frame_size,
|
||||
req_data.enc_frame_num,
|
||||
req_data.ref_frame_size,
|
||||
req_data.ref_frame_num,
|
||||
req_data.act_buf_size,
|
||||
req_data.act_buf_num);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_stop_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
vpu_trace(inst->dev, "[%d]\n", inst->id);
|
||||
|
||||
call_void_vop(inst, stop_done);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_seq_hdr(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_dec_codec_info info;
|
||||
const struct vpu_core_resources *res;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
res = vpu_get_resource(inst);
|
||||
info.stride = res ? res->stride : 1;
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_SEQ_HDR_FOUND, &info);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_resolution_change(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_RES_CHANGE, NULL);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_enc_pic_info info;
|
||||
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
|
||||
dev_dbg(inst->dev, "[%d] frame id = %d, wptr = 0x%x, size = %d\n",
|
||||
inst->id, info.frame_id, info.wptr, info.frame_size);
|
||||
call_void_vop(inst, get_one_frame, &info);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_frame_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_fs_info fs;
|
||||
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, &fs);
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_REQ, &fs);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_frame_release(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
if (inst->core->type == VPU_CORE_TYPE_ENC) {
|
||||
struct vpu_frame_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info.sequence);
|
||||
dev_dbg(inst->dev, "[%d] %d\n", inst->id, info.sequence);
|
||||
info.type = inst->out_format.type;
|
||||
call_void_vop(inst, buf_done, &info);
|
||||
} else if (inst->core->type == VPU_CORE_TYPE_DEC) {
|
||||
struct vpu_fs_info fs;
|
||||
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, &fs);
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_RELEASE, &fs);
|
||||
}
|
||||
}
|
||||
|
||||
static void vpu_session_handle_input_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
dev_dbg(inst->dev, "[%d]\n", inst->id);
|
||||
call_void_vop(inst, input_done);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_dec_pic_info info;
|
||||
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
|
||||
call_void_vop(inst, get_one_frame, &info);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_pic_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
struct vpu_dec_pic_info info;
|
||||
struct vpu_frame_info frame;
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
|
||||
if (inst->core->type == VPU_CORE_TYPE_DEC)
|
||||
frame.type = inst->cap_format.type;
|
||||
frame.id = info.id;
|
||||
frame.luma = info.luma;
|
||||
frame.skipped = info.skipped;
|
||||
frame.timestamp = info.timestamp;
|
||||
|
||||
call_void_vop(inst, buf_done, &frame);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_eos(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_PIC_EOS, NULL);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_error(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
dev_err(inst->dev, "unsupported stream\n");
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_UNSUPPORTED, NULL);
|
||||
vpu_v4l2_set_error(inst);
|
||||
}
|
||||
|
||||
static void vpu_session_handle_firmware_xcpt(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
char *str = (char *)pkt->data;
|
||||
|
||||
dev_err(inst->dev, "%s firmware xcpt: %s\n",
|
||||
vpu_core_type_desc(inst->core->type), str);
|
||||
call_void_vop(inst, event_notify, VPU_MSG_ID_FIRMWARE_XCPT, NULL);
|
||||
set_bit(inst->id, &inst->core->hang_mask);
|
||||
vpu_v4l2_set_error(inst);
|
||||
}
|
||||
|
||||
static struct vpu_msg_handler handlers[] = {
|
||||
{VPU_MSG_ID_START_DONE, vpu_session_handle_start_done},
|
||||
{VPU_MSG_ID_STOP_DONE, vpu_session_handle_stop_done},
|
||||
{VPU_MSG_ID_MEM_REQUEST, vpu_session_handle_mem_request},
|
||||
{VPU_MSG_ID_SEQ_HDR_FOUND, vpu_session_handle_seq_hdr},
|
||||
{VPU_MSG_ID_RES_CHANGE, vpu_session_handle_resolution_change},
|
||||
{VPU_MSG_ID_FRAME_INPUT_DONE, vpu_session_handle_input_done},
|
||||
{VPU_MSG_ID_FRAME_REQ, vpu_session_handle_frame_request},
|
||||
{VPU_MSG_ID_FRAME_RELEASE, vpu_session_handle_frame_release},
|
||||
{VPU_MSG_ID_ENC_DONE, vpu_session_handle_enc_frame_done},
|
||||
{VPU_MSG_ID_PIC_DECODED, vpu_session_handle_pic_decoded},
|
||||
{VPU_MSG_ID_DEC_DONE, vpu_session_handle_pic_done},
|
||||
{VPU_MSG_ID_PIC_EOS, vpu_session_handle_eos},
|
||||
{VPU_MSG_ID_UNSUPPORTED, vpu_session_handle_error},
|
||||
{VPU_MSG_ID_FIRMWARE_XCPT, vpu_session_handle_firmware_xcpt},
|
||||
};
|
||||
|
||||
static int vpu_session_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *msg)
|
||||
{
|
||||
int ret;
|
||||
u32 msg_id;
|
||||
struct vpu_msg_handler *handler = NULL;
|
||||
unsigned int i;
|
||||
|
||||
ret = vpu_iface_convert_msg_id(inst->core, msg->hdr.id);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg_id = ret;
|
||||
dev_dbg(inst->dev, "[%d] receive event(0x%x)\n", inst->id, msg_id);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
|
||||
if (handlers[i].id == msg_id) {
|
||||
handler = &handlers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler && handler->done)
|
||||
handler->done(inst, msg);
|
||||
|
||||
vpu_response_cmd(inst, msg_id, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool vpu_inst_receive_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
u32 bytes = sizeof(struct vpu_rpc_event_header);
|
||||
u32 ret;
|
||||
|
||||
memset(pkt, 0, sizeof(*pkt));
|
||||
if (kfifo_len(&inst->msg_fifo) < bytes)
|
||||
return false;
|
||||
|
||||
ret = kfifo_out(&inst->msg_fifo, pkt, bytes);
|
||||
if (ret != bytes)
|
||||
return false;
|
||||
|
||||
if (pkt->hdr.num > 0) {
|
||||
bytes = pkt->hdr.num * sizeof(u32);
|
||||
ret = kfifo_out(&inst->msg_fifo, pkt->data, bytes);
|
||||
if (ret != bytes)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vpu_inst_run_work(struct work_struct *work)
|
||||
{
|
||||
struct vpu_inst *inst = container_of(work, struct vpu_inst, msg_work);
|
||||
struct vpu_rpc_event pkt;
|
||||
|
||||
while (vpu_inst_receive_msg(inst, &pkt))
|
||||
vpu_session_handle_msg(inst, &pkt);
|
||||
}
|
||||
|
||||
static void vpu_inst_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
|
||||
{
|
||||
u32 bytes;
|
||||
u32 id = pkt->hdr.id;
|
||||
int ret;
|
||||
|
||||
if (!inst->workqueue)
|
||||
return;
|
||||
|
||||
bytes = sizeof(pkt->hdr) + pkt->hdr.num * sizeof(u32);
|
||||
ret = kfifo_in(&inst->msg_fifo, pkt, bytes);
|
||||
if (ret != bytes)
|
||||
dev_err(inst->dev, "[%d:%d]overflow: %d\n", inst->core->id, inst->id, id);
|
||||
queue_work(inst->workqueue, &inst->msg_work);
|
||||
}
|
||||
|
||||
static int vpu_handle_msg(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_rpc_event pkt;
|
||||
struct vpu_inst *inst;
|
||||
int ret;
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
while (!vpu_iface_receive_msg(core, &pkt)) {
|
||||
dev_dbg(core->dev, "event index = %d, id = %d, num = %d\n",
|
||||
pkt.hdr.index, pkt.hdr.id, pkt.hdr.num);
|
||||
|
||||
ret = vpu_iface_convert_msg_id(core, pkt.hdr.id);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
inst = vpu_core_find_instance(core, pkt.hdr.index);
|
||||
if (inst) {
|
||||
vpu_response_cmd(inst, ret, 0);
|
||||
mutex_lock(&core->cmd_lock);
|
||||
vpu_inst_record_flow(inst, ret);
|
||||
mutex_unlock(&core->cmd_lock);
|
||||
|
||||
vpu_inst_handle_msg(inst, &pkt);
|
||||
vpu_inst_put(inst);
|
||||
}
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_isr_thread(struct vpu_core *core, u32 irq_code)
|
||||
{
|
||||
dev_dbg(core->dev, "irq code = 0x%x\n", irq_code);
|
||||
switch (irq_code) {
|
||||
case VPU_IRQ_CODE_SYNC:
|
||||
vpu_mbox_send_msg(core, PRC_BUF_OFFSET, core->rpc.phys - core->fw.phys);
|
||||
vpu_mbox_send_msg(core, BOOT_ADDRESS, core->fw.phys);
|
||||
vpu_mbox_send_msg(core, INIT_DONE, 2);
|
||||
break;
|
||||
case VPU_IRQ_CODE_BOOT_DONE:
|
||||
break;
|
||||
case VPU_IRQ_CODE_SNAPSHOT_DONE:
|
||||
break;
|
||||
default:
|
||||
vpu_handle_msg(core);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_core_run_msg_work(struct vpu_core *core)
|
||||
{
|
||||
const unsigned int SIZE = sizeof(u32);
|
||||
|
||||
while (kfifo_len(&core->msg_fifo) >= SIZE) {
|
||||
u32 data = 0;
|
||||
|
||||
if (kfifo_out(&core->msg_fifo, &data, SIZE) == SIZE)
|
||||
vpu_isr_thread(core, data);
|
||||
}
|
||||
}
|
||||
|
||||
void vpu_msg_run_work(struct work_struct *work)
|
||||
{
|
||||
struct vpu_core *core = container_of(work, struct vpu_core, msg_work);
|
||||
unsigned long delay = msecs_to_jiffies(10);
|
||||
|
||||
vpu_core_run_msg_work(core);
|
||||
queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay);
|
||||
}
|
||||
|
||||
void vpu_msg_delayed_work(struct work_struct *work)
|
||||
{
|
||||
struct vpu_core *core;
|
||||
struct delayed_work *dwork;
|
||||
u32 bytes = sizeof(bytes);
|
||||
u32 i;
|
||||
|
||||
if (!work)
|
||||
return;
|
||||
|
||||
dwork = to_delayed_work(work);
|
||||
core = container_of(dwork, struct vpu_core, msg_delayed_work);
|
||||
if (kfifo_len(&core->msg_fifo) >= bytes)
|
||||
vpu_core_run_msg_work(core);
|
||||
|
||||
bytes = sizeof(struct vpu_rpc_event_header);
|
||||
for (i = 0; i < core->supported_instance_count; i++) {
|
||||
struct vpu_inst *inst = vpu_core_find_instance(core, i);
|
||||
|
||||
if (!inst)
|
||||
continue;
|
||||
|
||||
if (inst->workqueue && kfifo_len(&inst->msg_fifo) >= bytes)
|
||||
queue_work(inst->workqueue, &inst->msg_work);
|
||||
|
||||
vpu_inst_put(inst);
|
||||
}
|
||||
}
|
||||
|
||||
int vpu_isr(struct vpu_core *core, u32 irq)
|
||||
{
|
||||
switch (irq) {
|
||||
case VPU_IRQ_CODE_SYNC:
|
||||
break;
|
||||
case VPU_IRQ_CODE_BOOT_DONE:
|
||||
complete(&core->cmp);
|
||||
break;
|
||||
case VPU_IRQ_CODE_SNAPSHOT_DONE:
|
||||
complete(&core->cmp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (kfifo_in(&core->msg_fifo, &irq, sizeof(irq)) != sizeof(irq))
|
||||
dev_err(core->dev, "[%d]overflow: %d\n", core->id, irq);
|
||||
queue_work(core->workqueue, &core->msg_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
drivers/media/platform/amphion/vpu_msgs.h
Normal file
14
drivers/media/platform/amphion/vpu_msgs.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_MSGS_H
|
||||
#define _AMPHION_VPU_MSGS_H
|
||||
|
||||
int vpu_isr(struct vpu_core *core, u32 irq);
|
||||
void vpu_inst_run_work(struct work_struct *work);
|
||||
void vpu_msg_run_work(struct work_struct *work);
|
||||
void vpu_msg_delayed_work(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
257
drivers/media/platform/amphion/vpu_rpc.c
Normal file
257
drivers/media/platform/amphion/vpu_rpc.c
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
// 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/firmware/imx/ipc.h>
|
||||
#include <linux/firmware/imx/svc/misc.h>
|
||||
#include "vpu.h"
|
||||
#include "vpu_rpc.h"
|
||||
#include "vpu_imx8q.h"
|
||||
#include "vpu_windsor.h"
|
||||
#include "vpu_malone.h"
|
||||
|
||||
u32 vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->check_memory_region)
|
||||
return VPU_CORE_MEMORY_INVALID;
|
||||
|
||||
return ops->check_memory_region(core->fw.phys, addr, size);
|
||||
}
|
||||
|
||||
static u32 vpu_rpc_check_buffer_space(struct vpu_rpc_buffer_desc *desc, bool write)
|
||||
{
|
||||
u32 ptr1;
|
||||
u32 ptr2;
|
||||
u32 size;
|
||||
|
||||
size = desc->end - desc->start;
|
||||
if (write) {
|
||||
ptr1 = desc->wptr;
|
||||
ptr2 = desc->rptr;
|
||||
} else {
|
||||
ptr1 = desc->rptr;
|
||||
ptr2 = desc->wptr;
|
||||
}
|
||||
|
||||
if (ptr1 == ptr2) {
|
||||
if (!write)
|
||||
return 0;
|
||||
else
|
||||
return size;
|
||||
}
|
||||
|
||||
return (ptr2 + size - ptr1) % size;
|
||||
}
|
||||
|
||||
static int vpu_rpc_send_cmd_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *cmd)
|
||||
{
|
||||
struct vpu_rpc_buffer_desc *desc;
|
||||
u32 space = 0;
|
||||
u32 *data;
|
||||
u32 wptr;
|
||||
u32 i;
|
||||
|
||||
desc = shared->cmd_desc;
|
||||
space = vpu_rpc_check_buffer_space(desc, true);
|
||||
if (space < (((cmd->hdr.num + 1) << 2) + 16))
|
||||
return -EINVAL;
|
||||
wptr = desc->wptr;
|
||||
data = (u32 *)(shared->cmd_mem_vir + desc->wptr - desc->start);
|
||||
*data = 0;
|
||||
*data |= ((cmd->hdr.index & 0xff) << 24);
|
||||
*data |= ((cmd->hdr.num & 0xff) << 16);
|
||||
*data |= (cmd->hdr.id & 0x3fff);
|
||||
wptr += 4;
|
||||
data++;
|
||||
if (wptr >= desc->end) {
|
||||
wptr = desc->start;
|
||||
data = shared->cmd_mem_vir;
|
||||
}
|
||||
|
||||
for (i = 0; i < cmd->hdr.num; i++) {
|
||||
*data = cmd->data[i];
|
||||
wptr += 4;
|
||||
data++;
|
||||
if (wptr >= desc->end) {
|
||||
wptr = desc->start;
|
||||
data = shared->cmd_mem_vir;
|
||||
}
|
||||
}
|
||||
|
||||
/*update wptr after data is written*/
|
||||
mb();
|
||||
desc->wptr = wptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool vpu_rpc_check_msg(struct vpu_shared_addr *shared)
|
||||
{
|
||||
struct vpu_rpc_buffer_desc *desc;
|
||||
u32 space = 0;
|
||||
u32 msgword;
|
||||
u32 msgnum;
|
||||
|
||||
desc = shared->msg_desc;
|
||||
space = vpu_rpc_check_buffer_space(desc, 0);
|
||||
space = (space >> 2);
|
||||
|
||||
if (space) {
|
||||
msgword = *(u32 *)(shared->msg_mem_vir + desc->rptr - desc->start);
|
||||
msgnum = (msgword & 0xff0000) >> 16;
|
||||
if (msgnum <= space)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int vpu_rpc_receive_msg_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *msg)
|
||||
{
|
||||
struct vpu_rpc_buffer_desc *desc;
|
||||
u32 *data;
|
||||
u32 msgword;
|
||||
u32 rptr;
|
||||
u32 i;
|
||||
|
||||
if (!vpu_rpc_check_msg(shared))
|
||||
return -EINVAL;
|
||||
|
||||
desc = shared->msg_desc;
|
||||
data = (u32 *)(shared->msg_mem_vir + desc->rptr - desc->start);
|
||||
rptr = desc->rptr;
|
||||
msgword = *data;
|
||||
data++;
|
||||
rptr += 4;
|
||||
if (rptr >= desc->end) {
|
||||
rptr = desc->start;
|
||||
data = shared->msg_mem_vir;
|
||||
}
|
||||
|
||||
msg->hdr.index = (msgword >> 24) & 0xff;
|
||||
msg->hdr.num = (msgword >> 16) & 0xff;
|
||||
msg->hdr.id = msgword & 0x3fff;
|
||||
|
||||
if (msg->hdr.num > ARRAY_SIZE(msg->data))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < msg->hdr.num; i++) {
|
||||
msg->data[i] = *data;
|
||||
data++;
|
||||
rptr += 4;
|
||||
if (rptr >= desc->end) {
|
||||
rptr = desc->start;
|
||||
data = shared->msg_mem_vir;
|
||||
}
|
||||
}
|
||||
|
||||
/*update rptr after data is read*/
|
||||
mb();
|
||||
desc->rptr = rptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vpu_iface_ops imx8q_rpc_ops[] = {
|
||||
[VPU_CORE_TYPE_ENC] = {
|
||||
.check_codec = vpu_imx8q_check_codec,
|
||||
.check_fmt = vpu_imx8q_check_fmt,
|
||||
.boot_core = vpu_imx8q_boot_core,
|
||||
.get_power_state = vpu_imx8q_get_power_state,
|
||||
.on_firmware_loaded = vpu_imx8q_on_firmware_loaded,
|
||||
.get_data_size = vpu_windsor_get_data_size,
|
||||
.check_memory_region = vpu_imx8q_check_memory_region,
|
||||
.init_rpc = vpu_windsor_init_rpc,
|
||||
.set_log_buf = vpu_windsor_set_log_buf,
|
||||
.set_system_cfg = vpu_windsor_set_system_cfg,
|
||||
.get_version = vpu_windsor_get_version,
|
||||
.send_cmd_buf = vpu_rpc_send_cmd_buf,
|
||||
.receive_msg_buf = vpu_rpc_receive_msg_buf,
|
||||
.pack_cmd = vpu_windsor_pack_cmd,
|
||||
.convert_msg_id = vpu_windsor_convert_msg_id,
|
||||
.unpack_msg_data = vpu_windsor_unpack_msg_data,
|
||||
.config_memory_resource = vpu_windsor_config_memory_resource,
|
||||
.get_stream_buffer_size = vpu_windsor_get_stream_buffer_size,
|
||||
.config_stream_buffer = vpu_windsor_config_stream_buffer,
|
||||
.get_stream_buffer_desc = vpu_windsor_get_stream_buffer_desc,
|
||||
.update_stream_buffer = vpu_windsor_update_stream_buffer,
|
||||
.set_encode_params = vpu_windsor_set_encode_params,
|
||||
.input_frame = vpu_windsor_input_frame,
|
||||
.get_max_instance_count = vpu_windsor_get_max_instance_count,
|
||||
},
|
||||
[VPU_CORE_TYPE_DEC] = {
|
||||
.check_codec = vpu_imx8q_check_codec,
|
||||
.check_fmt = vpu_imx8q_check_fmt,
|
||||
.boot_core = vpu_imx8q_boot_core,
|
||||
.get_power_state = vpu_imx8q_get_power_state,
|
||||
.on_firmware_loaded = vpu_imx8q_on_firmware_loaded,
|
||||
.get_data_size = vpu_malone_get_data_size,
|
||||
.check_memory_region = vpu_imx8q_check_memory_region,
|
||||
.init_rpc = vpu_malone_init_rpc,
|
||||
.set_log_buf = vpu_malone_set_log_buf,
|
||||
.set_system_cfg = vpu_malone_set_system_cfg,
|
||||
.get_version = vpu_malone_get_version,
|
||||
.send_cmd_buf = vpu_rpc_send_cmd_buf,
|
||||
.receive_msg_buf = vpu_rpc_receive_msg_buf,
|
||||
.get_stream_buffer_size = vpu_malone_get_stream_buffer_size,
|
||||
.config_stream_buffer = vpu_malone_config_stream_buffer,
|
||||
.set_decode_params = vpu_malone_set_decode_params,
|
||||
.pack_cmd = vpu_malone_pack_cmd,
|
||||
.convert_msg_id = vpu_malone_convert_msg_id,
|
||||
.unpack_msg_data = vpu_malone_unpack_msg_data,
|
||||
.get_stream_buffer_desc = vpu_malone_get_stream_buffer_desc,
|
||||
.update_stream_buffer = vpu_malone_update_stream_buffer,
|
||||
.add_scode = vpu_malone_add_scode,
|
||||
.input_frame = vpu_malone_input_frame,
|
||||
.pre_send_cmd = vpu_malone_pre_cmd,
|
||||
.post_send_cmd = vpu_malone_post_cmd,
|
||||
.init_instance = vpu_malone_init_instance,
|
||||
.get_max_instance_count = vpu_malone_get_max_instance_count,
|
||||
},
|
||||
};
|
||||
|
||||
static struct vpu_iface_ops *vpu_get_iface(struct vpu_dev *vpu, enum vpu_core_type type)
|
||||
{
|
||||
struct vpu_iface_ops *rpc_ops = NULL;
|
||||
u32 size = 0;
|
||||
|
||||
switch (vpu->res->plat_type) {
|
||||
case IMX8QXP:
|
||||
case IMX8QM:
|
||||
rpc_ops = imx8q_rpc_ops;
|
||||
size = ARRAY_SIZE(imx8q_rpc_ops);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type >= size)
|
||||
return NULL;
|
||||
|
||||
return &rpc_ops[type];
|
||||
}
|
||||
|
||||
struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core)
|
||||
{
|
||||
return vpu_get_iface(core->vpu, core->type);
|
||||
}
|
||||
|
||||
struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst)
|
||||
{
|
||||
if (inst->core)
|
||||
return vpu_core_get_iface(inst->core);
|
||||
|
||||
return vpu_get_iface(inst->vpu, inst->type);
|
||||
}
|
||||
456
drivers/media/platform/amphion/vpu_rpc.h
Normal file
456
drivers/media/platform/amphion/vpu_rpc.h
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_RPC_H
|
||||
#define _AMPHION_VPU_RPC_H
|
||||
|
||||
#include <media/videobuf2-core.h>
|
||||
#include "vpu_codec.h"
|
||||
|
||||
struct vpu_rpc_buffer_desc {
|
||||
u32 wptr;
|
||||
u32 rptr;
|
||||
u32 start;
|
||||
u32 end;
|
||||
};
|
||||
|
||||
struct vpu_shared_addr {
|
||||
void *iface;
|
||||
struct vpu_rpc_buffer_desc *cmd_desc;
|
||||
void *cmd_mem_vir;
|
||||
struct vpu_rpc_buffer_desc *msg_desc;
|
||||
void *msg_mem_vir;
|
||||
|
||||
unsigned long boot_addr;
|
||||
struct vpu_core *core;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct vpu_rpc_event_header {
|
||||
u32 index;
|
||||
u32 id;
|
||||
u32 num;
|
||||
};
|
||||
|
||||
struct vpu_rpc_event {
|
||||
struct vpu_rpc_event_header hdr;
|
||||
u32 data[128];
|
||||
};
|
||||
|
||||
struct vpu_iface_ops {
|
||||
bool (*check_codec)(enum vpu_core_type type);
|
||||
bool (*check_fmt)(enum vpu_core_type type, u32 pixelfmt);
|
||||
u32 (*get_data_size)(void);
|
||||
u32 (*check_memory_region)(dma_addr_t base, dma_addr_t addr, u32 size);
|
||||
int (*boot_core)(struct vpu_core *core);
|
||||
int (*shutdown_core)(struct vpu_core *core);
|
||||
int (*restore_core)(struct vpu_core *core);
|
||||
int (*get_power_state)(struct vpu_core *core);
|
||||
int (*on_firmware_loaded)(struct vpu_core *core);
|
||||
void (*init_rpc)(struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *rpc, dma_addr_t boot_addr);
|
||||
void (*set_log_buf)(struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *log);
|
||||
void (*set_system_cfg)(struct vpu_shared_addr *shared,
|
||||
u32 regs_base, void __iomem *regs, u32 index);
|
||||
void (*set_stream_cfg)(struct vpu_shared_addr *shared, u32 index);
|
||||
u32 (*get_version)(struct vpu_shared_addr *shared);
|
||||
u32 (*get_max_instance_count)(struct vpu_shared_addr *shared);
|
||||
int (*get_stream_buffer_size)(struct vpu_shared_addr *shared);
|
||||
int (*send_cmd_buf)(struct vpu_shared_addr *shared,
|
||||
struct vpu_rpc_event *cmd);
|
||||
int (*receive_msg_buf)(struct vpu_shared_addr *shared,
|
||||
struct vpu_rpc_event *msg);
|
||||
int (*pack_cmd)(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
|
||||
int (*convert_msg_id)(u32 msg_id);
|
||||
int (*unpack_msg_data)(struct vpu_rpc_event *pkt, void *data);
|
||||
int (*input_frame)(struct vpu_shared_addr *shared,
|
||||
struct vpu_inst *inst, struct vb2_buffer *vb);
|
||||
int (*config_memory_resource)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
u32 type,
|
||||
u32 index,
|
||||
struct vpu_buffer *buf);
|
||||
int (*config_stream_buffer)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_buffer *buf);
|
||||
int (*update_stream_buffer)(struct vpu_shared_addr *shared,
|
||||
u32 instance, u32 ptr, bool write);
|
||||
int (*get_stream_buffer_desc)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_rpc_buffer_desc *desc);
|
||||
int (*set_encode_params)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_encode_params *params,
|
||||
u32 update);
|
||||
int (*set_decode_params)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_decode_params *params,
|
||||
u32 update);
|
||||
int (*add_scode)(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_buffer *stream_buffer,
|
||||
u32 pixelformat,
|
||||
u32 scode_type);
|
||||
int (*pre_send_cmd)(struct vpu_shared_addr *shared, u32 instance);
|
||||
int (*post_send_cmd)(struct vpu_shared_addr *shared, u32 instance);
|
||||
int (*init_instance)(struct vpu_shared_addr *shared, u32 instance);
|
||||
};
|
||||
|
||||
enum {
|
||||
VPU_CORE_MEMORY_INVALID = 0,
|
||||
VPU_CORE_MEMORY_CACHED,
|
||||
VPU_CORE_MEMORY_UNCACHED
|
||||
};
|
||||
|
||||
struct vpu_rpc_region_t {
|
||||
dma_addr_t start;
|
||||
dma_addr_t end;
|
||||
dma_addr_t type;
|
||||
};
|
||||
|
||||
struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core);
|
||||
struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst);
|
||||
u32 vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size);
|
||||
|
||||
static inline bool vpu_iface_check_codec(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->check_codec)
|
||||
return ops->check_codec(core->type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool vpu_iface_check_format(struct vpu_inst *inst, u32 pixelfmt)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_inst_get_iface(inst);
|
||||
|
||||
if (ops && ops->check_fmt)
|
||||
return ops->check_fmt(inst->type, pixelfmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_boot_core(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->boot_core)
|
||||
return ops->boot_core(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_get_power_state(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->get_power_state)
|
||||
return ops->get_power_state(core);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_shutdown_core(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->shutdown_core)
|
||||
return ops->shutdown_core(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_restore_core(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->restore_core)
|
||||
return ops->restore_core(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_on_firmware_loaded(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (ops && ops->on_firmware_loaded)
|
||||
return ops->on_firmware_loaded(core);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 vpu_iface_get_data_size(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->get_data_size)
|
||||
return 0;
|
||||
|
||||
return ops->get_data_size();
|
||||
}
|
||||
|
||||
static inline int vpu_iface_init(struct vpu_core *core,
|
||||
struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *rpc,
|
||||
dma_addr_t boot_addr)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->init_rpc)
|
||||
return -EINVAL;
|
||||
|
||||
ops->init_rpc(shared, rpc, boot_addr);
|
||||
core->iface = shared;
|
||||
shared->core = core;
|
||||
if (rpc->bytesused > rpc->length)
|
||||
return -ENOSPC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_set_log_buf(struct vpu_core *core,
|
||||
struct vpu_buffer *log)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops)
|
||||
return -EINVAL;
|
||||
|
||||
if (ops->set_log_buf)
|
||||
ops->set_log_buf(core->iface, log);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_config_system(struct vpu_core *core, u32 regs_base, void __iomem *regs)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops)
|
||||
return -EINVAL;
|
||||
if (ops->set_system_cfg)
|
||||
ops->set_system_cfg(core->iface, regs_base, regs, core->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_get_stream_buffer_size(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->get_stream_buffer_size)
|
||||
return 0;
|
||||
|
||||
return ops->get_stream_buffer_size(core->iface);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_config_stream(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || inst->id < 0)
|
||||
return -EINVAL;
|
||||
if (ops->set_stream_cfg)
|
||||
ops->set_stream_cfg(inst->core->iface, inst->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_send_cmd(struct vpu_core *core, struct vpu_rpc_event *cmd)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->send_cmd_buf)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->send_cmd_buf(core->iface, cmd);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_receive_msg(struct vpu_core *core, struct vpu_rpc_event *msg)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->receive_msg_buf)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->receive_msg_buf(core->iface, msg);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_pack_cmd(struct vpu_core *core,
|
||||
struct vpu_rpc_event *pkt,
|
||||
u32 index, u32 id, void *data)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->pack_cmd)
|
||||
return -EINVAL;
|
||||
return ops->pack_cmd(pkt, index, id, data);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_convert_msg_id(struct vpu_core *core, u32 msg_id)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->convert_msg_id)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->convert_msg_id(msg_id);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_unpack_msg_data(struct vpu_core *core,
|
||||
struct vpu_rpc_event *pkt, void *data)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->unpack_msg_data)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->unpack_msg_data(pkt, data);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_input_frame(struct vpu_inst *inst,
|
||||
struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->input_frame)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->input_frame(inst->core->iface, inst, vb);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_config_memory_resource(struct vpu_inst *inst,
|
||||
u32 type,
|
||||
u32 index,
|
||||
struct vpu_buffer *buf)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->config_memory_resource || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->config_memory_resource(inst->core->iface,
|
||||
inst->id,
|
||||
type, index, buf);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_config_stream_buffer(struct vpu_inst *inst,
|
||||
struct vpu_buffer *buf)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->config_stream_buffer || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->config_stream_buffer(inst->core->iface, inst->id, buf);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_update_stream_buffer(struct vpu_inst *inst,
|
||||
u32 ptr, bool write)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->update_stream_buffer || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->update_stream_buffer(inst->core->iface, inst->id, ptr, write);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_get_stream_buffer_desc(struct vpu_inst *inst,
|
||||
struct vpu_rpc_buffer_desc *desc)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->get_stream_buffer_desc || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!desc)
|
||||
return 0;
|
||||
|
||||
return ops->get_stream_buffer_desc(inst->core->iface, inst->id, desc);
|
||||
}
|
||||
|
||||
static inline u32 vpu_iface_get_version(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->get_version)
|
||||
return 0;
|
||||
|
||||
return ops->get_version(core->iface);
|
||||
}
|
||||
|
||||
static inline u32 vpu_iface_get_max_instance_count(struct vpu_core *core)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(core);
|
||||
|
||||
if (!ops || !ops->get_max_instance_count)
|
||||
return 0;
|
||||
|
||||
return ops->get_max_instance_count(core->iface);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_set_encode_params(struct vpu_inst *inst,
|
||||
struct vpu_encode_params *params, u32 update)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->set_encode_params || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->set_encode_params(inst->core->iface, inst->id, params, update);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_set_decode_params(struct vpu_inst *inst,
|
||||
struct vpu_decode_params *params, u32 update)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->set_decode_params || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->set_decode_params(inst->core->iface, inst->id, params, update);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_add_scode(struct vpu_inst *inst, u32 scode_type)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (!ops || !ops->add_scode || inst->id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->add_scode(inst->core->iface, inst->id,
|
||||
&inst->stream_buffer,
|
||||
inst->out_format.pixfmt,
|
||||
scode_type);
|
||||
}
|
||||
|
||||
static inline int vpu_iface_pre_send_cmd(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (ops && ops->pre_send_cmd && inst->id >= 0)
|
||||
return ops->pre_send_cmd(inst->core->iface, inst->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_post_send_cmd(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (ops && ops->post_send_cmd && inst->id >= 0)
|
||||
return ops->post_send_cmd(inst->core->iface, inst->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vpu_iface_init_instance(struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
|
||||
|
||||
if (ops && ops->init_instance && inst->id >= 0)
|
||||
return ops->init_instance(inst->core->iface, inst->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
712
drivers/media/platform/amphion/vpu_v4l2.c
Normal file
712
drivers/media/platform/amphion/vpu_v4l2.c
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
// 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/pm_runtime.h>
|
||||
#include <linux/videodev2.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_core.h"
|
||||
#include "vpu_v4l2.h"
|
||||
#include "vpu_msgs.h"
|
||||
#include "vpu_helpers.h"
|
||||
|
||||
void vpu_inst_lock(struct vpu_inst *inst)
|
||||
{
|
||||
mutex_lock(&inst->lock);
|
||||
}
|
||||
|
||||
void vpu_inst_unlock(struct vpu_inst *inst)
|
||||
{
|
||||
mutex_unlock(&inst->lock);
|
||||
}
|
||||
|
||||
dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no)
|
||||
{
|
||||
if (plane_no >= vb->num_planes)
|
||||
return 0;
|
||||
return vb2_dma_contig_plane_dma_addr(vb, plane_no) +
|
||||
vb->planes[plane_no].data_offset;
|
||||
}
|
||||
|
||||
unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no)
|
||||
{
|
||||
if (plane_no >= vb->num_planes)
|
||||
return 0;
|
||||
return vb2_plane_size(vb, plane_no) - vb->planes[plane_no].data_offset;
|
||||
}
|
||||
|
||||
void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state)
|
||||
{
|
||||
struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
|
||||
|
||||
vpu_buf->state = state;
|
||||
}
|
||||
|
||||
unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf)
|
||||
{
|
||||
struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
|
||||
|
||||
return vpu_buf->state;
|
||||
}
|
||||
|
||||
void vpu_v4l2_set_error(struct vpu_inst *inst)
|
||||
{
|
||||
struct vb2_queue *src_q;
|
||||
struct vb2_queue *dst_q;
|
||||
|
||||
vpu_inst_lock(inst);
|
||||
dev_err(inst->dev, "some error occurs in codec\n");
|
||||
if (inst->fh.m2m_ctx) {
|
||||
src_q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
|
||||
dst_q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
|
||||
if (src_q)
|
||||
src_q->error = 1;
|
||||
if (dst_q)
|
||||
dst_q->error = 1;
|
||||
}
|
||||
vpu_inst_unlock(inst);
|
||||
}
|
||||
|
||||
int vpu_notify_eos(struct vpu_inst *inst)
|
||||
{
|
||||
static const struct v4l2_event ev = {
|
||||
.id = 0,
|
||||
.type = V4L2_EVENT_EOS
|
||||
};
|
||||
|
||||
vpu_trace(inst->dev, "[%d]\n", inst->id);
|
||||
v4l2_event_queue_fh(&inst->fh, &ev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_notify_source_change(struct vpu_inst *inst)
|
||||
{
|
||||
static const struct v4l2_event ev = {
|
||||
.id = 0,
|
||||
.type = V4L2_EVENT_SOURCE_CHANGE,
|
||||
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION
|
||||
};
|
||||
|
||||
vpu_trace(inst->dev, "[%d]\n", inst->id);
|
||||
v4l2_event_queue_fh(&inst->fh, &ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_set_last_buffer_dequeued(struct vpu_inst *inst)
|
||||
{
|
||||
struct vb2_queue *q;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return -EINVAL;
|
||||
|
||||
q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
|
||||
if (!list_empty(&q->done_list))
|
||||
return -EINVAL;
|
||||
|
||||
if (q->last_buffer_dequeued)
|
||||
return 0;
|
||||
vpu_trace(inst->dev, "last buffer dequeued\n");
|
||||
q->last_buffer_dequeued = true;
|
||||
wake_up(&q->done_wq);
|
||||
vpu_notify_eos(inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f)
|
||||
{
|
||||
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
|
||||
u32 type = f->type;
|
||||
u32 stride = 1;
|
||||
u32 bytesperline;
|
||||
u32 sizeimage;
|
||||
const struct vpu_format *fmt;
|
||||
const struct vpu_core_resources *res;
|
||||
int i;
|
||||
|
||||
fmt = vpu_helper_find_format(inst, type, pixmp->pixelformat);
|
||||
if (!fmt) {
|
||||
fmt = vpu_helper_enum_format(inst, type, 0);
|
||||
if (!fmt)
|
||||
return NULL;
|
||||
pixmp->pixelformat = fmt->pixfmt;
|
||||
}
|
||||
|
||||
res = vpu_get_resource(inst);
|
||||
if (res)
|
||||
stride = res->stride;
|
||||
if (pixmp->width)
|
||||
pixmp->width = vpu_helper_valid_frame_width(inst, pixmp->width);
|
||||
if (pixmp->height)
|
||||
pixmp->height = vpu_helper_valid_frame_height(inst, pixmp->height);
|
||||
pixmp->flags = fmt->flags;
|
||||
pixmp->num_planes = fmt->num_planes;
|
||||
if (pixmp->field == V4L2_FIELD_ANY)
|
||||
pixmp->field = V4L2_FIELD_NONE;
|
||||
for (i = 0; i < pixmp->num_planes; i++) {
|
||||
bytesperline = max_t(s32, pixmp->plane_fmt[i].bytesperline, 0);
|
||||
sizeimage = vpu_helper_get_plane_size(pixmp->pixelformat,
|
||||
pixmp->width,
|
||||
pixmp->height,
|
||||
i,
|
||||
stride,
|
||||
pixmp->field > V4L2_FIELD_NONE ? 1 : 0,
|
||||
&bytesperline);
|
||||
sizeimage = max_t(s32, pixmp->plane_fmt[i].sizeimage, sizeimage);
|
||||
pixmp->plane_fmt[i].bytesperline = bytesperline;
|
||||
pixmp->plane_fmt[i].sizeimage = sizeimage;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
static bool vpu_check_ready(struct vpu_inst *inst, u32 type)
|
||||
{
|
||||
if (!inst)
|
||||
return false;
|
||||
if (inst->state == VPU_CODEC_STATE_DEINIT || inst->id < 0)
|
||||
return false;
|
||||
if (!inst->ops->check_ready)
|
||||
return true;
|
||||
return call_vop(inst, check_ready, type);
|
||||
}
|
||||
|
||||
int vpu_process_output_buffer(struct vpu_inst *inst)
|
||||
{
|
||||
struct v4l2_m2m_buffer *buf = NULL;
|
||||
struct vb2_v4l2_buffer *vbuf = NULL;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return -EINVAL;
|
||||
|
||||
if (!vpu_check_ready(inst, inst->out_format.type))
|
||||
return -EINVAL;
|
||||
|
||||
v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
|
||||
if (!vbuf)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(inst->dev, "[%d]frame id = %d / %d\n",
|
||||
inst->id, vbuf->sequence, inst->sequence);
|
||||
return call_vop(inst, process_output, &vbuf->vb2_buf);
|
||||
}
|
||||
|
||||
int vpu_process_capture_buffer(struct vpu_inst *inst)
|
||||
{
|
||||
struct v4l2_m2m_buffer *buf = NULL;
|
||||
struct vb2_v4l2_buffer *vbuf = NULL;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return -EINVAL;
|
||||
|
||||
if (!vpu_check_ready(inst, inst->cap_format.type))
|
||||
return -EINVAL;
|
||||
|
||||
v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
if (!vbuf)
|
||||
return -EINVAL;
|
||||
|
||||
return call_vop(inst, process_capture, &vbuf->vb2_buf);
|
||||
}
|
||||
|
||||
struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence)
|
||||
{
|
||||
struct v4l2_m2m_buffer *buf = NULL;
|
||||
struct vb2_v4l2_buffer *vbuf = NULL;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return NULL;
|
||||
|
||||
if (V4L2_TYPE_IS_OUTPUT(type)) {
|
||||
v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vbuf->sequence == sequence)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
} else {
|
||||
v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vbuf->sequence == sequence)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return vbuf;
|
||||
}
|
||||
|
||||
struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx)
|
||||
{
|
||||
struct v4l2_m2m_buffer *buf = NULL;
|
||||
struct vb2_v4l2_buffer *vbuf = NULL;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return NULL;
|
||||
|
||||
if (V4L2_TYPE_IS_OUTPUT(type)) {
|
||||
v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vbuf->vb2_buf.index == idx)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
} else {
|
||||
v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
|
||||
vbuf = &buf->vb;
|
||||
if (vbuf->vb2_buf.index == idx)
|
||||
break;
|
||||
vbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return vbuf;
|
||||
}
|
||||
|
||||
int vpu_get_num_buffers(struct vpu_inst *inst, u32 type)
|
||||
{
|
||||
struct vb2_queue *q;
|
||||
|
||||
if (!inst || !inst->fh.m2m_ctx)
|
||||
return -EINVAL;
|
||||
|
||||
if (V4L2_TYPE_IS_OUTPUT(type))
|
||||
q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
|
||||
else
|
||||
q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
|
||||
|
||||
return q->num_buffers;
|
||||
}
|
||||
|
||||
static void vpu_m2m_device_run(void *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static void vpu_m2m_job_abort(void *priv)
|
||||
{
|
||||
struct vpu_inst *inst = priv;
|
||||
struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx;
|
||||
|
||||
v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx);
|
||||
}
|
||||
|
||||
static const struct v4l2_m2m_ops vpu_m2m_ops = {
|
||||
.device_run = vpu_m2m_device_run,
|
||||
.job_abort = vpu_m2m_job_abort
|
||||
};
|
||||
|
||||
static int vpu_vb2_queue_setup(struct vb2_queue *vq,
|
||||
unsigned int *buf_count,
|
||||
unsigned int *plane_count,
|
||||
unsigned int psize[],
|
||||
struct device *allocators[])
|
||||
{
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(vq);
|
||||
struct vpu_format *cur_fmt;
|
||||
int i;
|
||||
|
||||
cur_fmt = vpu_get_format(inst, vq->type);
|
||||
|
||||
if (*plane_count) {
|
||||
if (*plane_count != cur_fmt->num_planes)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < cur_fmt->num_planes; i++) {
|
||||
if (psize[i] < cur_fmt->sizeimage[i])
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*plane_count = cur_fmt->num_planes;
|
||||
for (i = 0; i < cur_fmt->num_planes; i++)
|
||||
psize[i] = cur_fmt->sizeimage[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_vb2_buf_init(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
|
||||
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_vb2_buf_out_validate(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
|
||||
vbuf->field = V4L2_FIELD_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_vb2_buf_prepare(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
struct vpu_format *cur_fmt;
|
||||
u32 i;
|
||||
|
||||
cur_fmt = vpu_get_format(inst, vb->type);
|
||||
for (i = 0; i < cur_fmt->num_planes; i++) {
|
||||
if (vpu_get_vb_length(vb, i) < cur_fmt->sizeimage[i]) {
|
||||
dev_dbg(inst->dev, "[%d] %s buf[%d] is invalid\n",
|
||||
inst->id, vpu_type_name(vb->type), vb->index);
|
||||
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_vb2_buf_finish(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vb2_queue *q = vb->vb2_queue;
|
||||
|
||||
if (vbuf->flags & V4L2_BUF_FLAG_LAST)
|
||||
vpu_notify_eos(inst);
|
||||
|
||||
if (list_empty(&q->done_list))
|
||||
call_void_vop(inst, on_queue_empty, q->type);
|
||||
}
|
||||
|
||||
void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state)
|
||||
{
|
||||
struct vb2_v4l2_buffer *buf;
|
||||
|
||||
if (V4L2_TYPE_IS_OUTPUT(type)) {
|
||||
while ((buf = v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx)))
|
||||
v4l2_m2m_buf_done(buf, state);
|
||||
} else {
|
||||
while ((buf = v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx)))
|
||||
v4l2_m2m_buf_done(buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
|
||||
{
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(q);
|
||||
struct vpu_format *fmt = vpu_get_format(inst, q->type);
|
||||
int ret;
|
||||
|
||||
vpu_inst_unlock(inst);
|
||||
ret = vpu_inst_register(inst);
|
||||
vpu_inst_lock(inst);
|
||||
if (ret) {
|
||||
vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_QUEUED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vpu_trace(inst->dev, "[%d] %s %c%c%c%c %dx%d %u(%u) %u(%u) %u(%u) %d\n",
|
||||
inst->id, vpu_type_name(q->type),
|
||||
fmt->pixfmt,
|
||||
fmt->pixfmt >> 8,
|
||||
fmt->pixfmt >> 16,
|
||||
fmt->pixfmt >> 24,
|
||||
fmt->width, fmt->height,
|
||||
fmt->sizeimage[0], fmt->bytesperline[0],
|
||||
fmt->sizeimage[1], fmt->bytesperline[1],
|
||||
fmt->sizeimage[2], fmt->bytesperline[2],
|
||||
q->num_buffers);
|
||||
call_void_vop(inst, start, q->type);
|
||||
vb2_clear_last_buffer_dequeued(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_vb2_stop_streaming(struct vb2_queue *q)
|
||||
{
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(q);
|
||||
|
||||
vpu_trace(inst->dev, "[%d] %s\n", inst->id, vpu_type_name(q->type));
|
||||
|
||||
call_void_vop(inst, stop, q->type);
|
||||
vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_ERROR);
|
||||
if (V4L2_TYPE_IS_OUTPUT(q->type))
|
||||
inst->sequence = 0;
|
||||
}
|
||||
|
||||
static void vpu_vb2_buf_queue(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
|
||||
|
||||
if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
|
||||
vbuf->sequence = inst->sequence++;
|
||||
if ((s64)vb->timestamp < 0)
|
||||
vb->timestamp = VPU_INVALID_TIMESTAMP;
|
||||
}
|
||||
|
||||
v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf);
|
||||
vpu_process_output_buffer(inst);
|
||||
vpu_process_capture_buffer(inst);
|
||||
}
|
||||
|
||||
static const struct vb2_ops vpu_vb2_ops = {
|
||||
.queue_setup = vpu_vb2_queue_setup,
|
||||
.buf_init = vpu_vb2_buf_init,
|
||||
.buf_out_validate = vpu_vb2_buf_out_validate,
|
||||
.buf_prepare = vpu_vb2_buf_prepare,
|
||||
.buf_finish = vpu_vb2_buf_finish,
|
||||
.start_streaming = vpu_vb2_start_streaming,
|
||||
.stop_streaming = vpu_vb2_stop_streaming,
|
||||
.buf_queue = vpu_vb2_buf_queue,
|
||||
.wait_prepare = vb2_ops_wait_prepare,
|
||||
.wait_finish = vb2_ops_wait_finish,
|
||||
};
|
||||
|
||||
static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
|
||||
{
|
||||
struct vpu_inst *inst = priv;
|
||||
int ret;
|
||||
|
||||
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
inst->out_format.type = src_vq->type;
|
||||
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
||||
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
||||
src_vq->ops = &vpu_vb2_ops;
|
||||
src_vq->mem_ops = &vb2_dma_contig_memops;
|
||||
if (inst->type == VPU_CORE_TYPE_DEC && inst->use_stream_buffer)
|
||||
src_vq->mem_ops = &vb2_vmalloc_memops;
|
||||
src_vq->drv_priv = inst;
|
||||
src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
|
||||
src_vq->min_buffers_needed = 1;
|
||||
src_vq->dev = inst->vpu->dev;
|
||||
src_vq->lock = &inst->lock;
|
||||
ret = vb2_queue_init(src_vq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
inst->cap_format.type = dst_vq->type;
|
||||
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
||||
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
||||
dst_vq->ops = &vpu_vb2_ops;
|
||||
dst_vq->mem_ops = &vb2_dma_contig_memops;
|
||||
if (inst->type == VPU_CORE_TYPE_ENC && inst->use_stream_buffer)
|
||||
dst_vq->mem_ops = &vb2_vmalloc_memops;
|
||||
dst_vq->drv_priv = inst;
|
||||
dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
|
||||
dst_vq->min_buffers_needed = 1;
|
||||
dst_vq->dev = inst->vpu->dev;
|
||||
dst_vq->lock = &inst->lock;
|
||||
ret = vb2_queue_init(dst_vq);
|
||||
if (ret) {
|
||||
vb2_queue_release(src_vq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_v4l2_release(struct vpu_inst *inst)
|
||||
{
|
||||
vpu_trace(inst->vpu->dev, "%p\n", inst);
|
||||
|
||||
vpu_release_core(inst->core);
|
||||
put_device(inst->dev);
|
||||
|
||||
if (inst->workqueue) {
|
||||
cancel_work_sync(&inst->msg_work);
|
||||
destroy_workqueue(inst->workqueue);
|
||||
inst->workqueue = NULL;
|
||||
}
|
||||
|
||||
v4l2_ctrl_handler_free(&inst->ctrl_handler);
|
||||
mutex_destroy(&inst->lock);
|
||||
v4l2_fh_del(&inst->fh);
|
||||
v4l2_fh_exit(&inst->fh);
|
||||
|
||||
call_void_vop(inst, cleanup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
|
||||
{
|
||||
struct vpu_dev *vpu = video_drvdata(file);
|
||||
struct vpu_func *func;
|
||||
int ret = 0;
|
||||
|
||||
if (!inst || !inst->ops)
|
||||
return -EINVAL;
|
||||
|
||||
if (inst->type == VPU_CORE_TYPE_ENC)
|
||||
func = &vpu->encoder;
|
||||
else
|
||||
func = &vpu->decoder;
|
||||
|
||||
atomic_set(&inst->ref_count, 0);
|
||||
vpu_inst_get(inst);
|
||||
inst->vpu = vpu;
|
||||
inst->core = vpu_request_core(vpu, inst->type);
|
||||
if (inst->core)
|
||||
inst->dev = get_device(inst->core->dev);
|
||||
mutex_init(&inst->lock);
|
||||
INIT_LIST_HEAD(&inst->cmd_q);
|
||||
inst->id = VPU_INST_NULL_ID;
|
||||
inst->release = vpu_v4l2_release;
|
||||
inst->pid = current->pid;
|
||||
inst->tgid = current->tgid;
|
||||
inst->min_buffer_cap = 2;
|
||||
inst->min_buffer_out = 2;
|
||||
v4l2_fh_init(&inst->fh, func->vfd);
|
||||
v4l2_fh_add(&inst->fh);
|
||||
|
||||
ret = call_vop(inst, ctrl_init);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
inst->fh.m2m_ctx = v4l2_m2m_ctx_init(func->m2m_dev, inst, vpu_m2m_queue_init);
|
||||
if (IS_ERR(inst->fh.m2m_ctx)) {
|
||||
dev_err(vpu->dev, "v4l2_m2m_ctx_init fail\n");
|
||||
ret = PTR_ERR(inst->fh.m2m_ctx);
|
||||
goto error;
|
||||
}
|
||||
|
||||
inst->fh.ctrl_handler = &inst->ctrl_handler;
|
||||
file->private_data = &inst->fh;
|
||||
inst->state = VPU_CODEC_STATE_DEINIT;
|
||||
inst->workqueue = alloc_workqueue("vpu_inst", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
|
||||
if (inst->workqueue) {
|
||||
INIT_WORK(&inst->msg_work, vpu_inst_run_work);
|
||||
ret = kfifo_init(&inst->msg_fifo,
|
||||
inst->msg_buffer,
|
||||
rounddown_pow_of_two(sizeof(inst->msg_buffer)));
|
||||
if (ret) {
|
||||
destroy_workqueue(inst->workqueue);
|
||||
inst->workqueue = NULL;
|
||||
}
|
||||
}
|
||||
vpu_trace(vpu->dev, "tgid = %d, pid = %d, type = %s, inst = %p\n",
|
||||
inst->tgid, inst->pid, vpu_core_type_desc(inst->type), inst);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
vpu_inst_put(inst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vpu_v4l2_close(struct file *file)
|
||||
{
|
||||
struct vpu_dev *vpu = video_drvdata(file);
|
||||
struct vpu_inst *inst = to_inst(file);
|
||||
|
||||
vpu_trace(vpu->dev, "tgid = %d, pid = %d, inst = %p\n", inst->tgid, inst->pid, inst);
|
||||
|
||||
vpu_inst_lock(inst);
|
||||
if (inst->fh.m2m_ctx) {
|
||||
v4l2_m2m_ctx_release(inst->fh.m2m_ctx);
|
||||
inst->fh.m2m_ctx = NULL;
|
||||
}
|
||||
vpu_inst_unlock(inst);
|
||||
|
||||
call_void_vop(inst, release);
|
||||
vpu_inst_unregister(inst);
|
||||
vpu_inst_put(inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func)
|
||||
{
|
||||
struct video_device *vfd;
|
||||
int ret;
|
||||
|
||||
if (!vpu || !func)
|
||||
return -EINVAL;
|
||||
|
||||
if (func->vfd)
|
||||
return 0;
|
||||
|
||||
func->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops);
|
||||
if (IS_ERR(func->m2m_dev)) {
|
||||
dev_err(vpu->dev, "v4l2_m2m_init fail\n");
|
||||
func->vfd = NULL;
|
||||
return PTR_ERR(func->m2m_dev);
|
||||
}
|
||||
|
||||
vfd = video_device_alloc();
|
||||
if (!vfd) {
|
||||
v4l2_m2m_release(func->m2m_dev);
|
||||
dev_err(vpu->dev, "alloc vpu decoder video device fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
vfd->release = video_device_release;
|
||||
vfd->vfl_dir = VFL_DIR_M2M;
|
||||
vfd->v4l2_dev = &vpu->v4l2_dev;
|
||||
vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
|
||||
if (func->type == VPU_CORE_TYPE_ENC) {
|
||||
strscpy(vfd->name, "amphion-vpu-encoder", sizeof(vfd->name));
|
||||
vfd->fops = venc_get_fops();
|
||||
vfd->ioctl_ops = venc_get_ioctl_ops();
|
||||
} else {
|
||||
strscpy(vfd->name, "amphion-vpu-decoder", sizeof(vfd->name));
|
||||
vfd->fops = vdec_get_fops();
|
||||
vfd->ioctl_ops = vdec_get_ioctl_ops();
|
||||
}
|
||||
|
||||
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
|
||||
if (ret) {
|
||||
video_device_release(vfd);
|
||||
v4l2_m2m_release(func->m2m_dev);
|
||||
return ret;
|
||||
}
|
||||
video_set_drvdata(vfd, vpu);
|
||||
func->vfd = vfd;
|
||||
|
||||
ret = v4l2_m2m_register_media_controller(func->m2m_dev, func->vfd, func->function);
|
||||
if (ret) {
|
||||
v4l2_m2m_release(func->m2m_dev);
|
||||
func->m2m_dev = NULL;
|
||||
video_unregister_device(func->vfd);
|
||||
func->vfd = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpu_remove_func(struct vpu_func *func)
|
||||
{
|
||||
if (!func)
|
||||
return;
|
||||
|
||||
if (func->m2m_dev) {
|
||||
v4l2_m2m_unregister_media_controller(func->m2m_dev);
|
||||
v4l2_m2m_release(func->m2m_dev);
|
||||
func->m2m_dev = NULL;
|
||||
}
|
||||
if (func->vfd) {
|
||||
video_unregister_device(func->vfd);
|
||||
func->vfd = NULL;
|
||||
}
|
||||
}
|
||||
55
drivers/media/platform/amphion/vpu_v4l2.h
Normal file
55
drivers/media/platform/amphion/vpu_v4l2.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_V4L2_H
|
||||
#define _AMPHION_VPU_V4L2_H
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
void vpu_inst_lock(struct vpu_inst *inst);
|
||||
void vpu_inst_unlock(struct vpu_inst *inst);
|
||||
void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state);
|
||||
unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf);
|
||||
|
||||
int vpu_v4l2_open(struct file *file, struct vpu_inst *inst);
|
||||
int vpu_v4l2_close(struct file *file);
|
||||
|
||||
const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f);
|
||||
int vpu_process_output_buffer(struct vpu_inst *inst);
|
||||
int vpu_process_capture_buffer(struct vpu_inst *inst);
|
||||
struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence);
|
||||
struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx);
|
||||
void vpu_v4l2_set_error(struct vpu_inst *inst);
|
||||
int vpu_notify_eos(struct vpu_inst *inst);
|
||||
int vpu_notify_source_change(struct vpu_inst *inst);
|
||||
int vpu_set_last_buffer_dequeued(struct vpu_inst *inst);
|
||||
void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state);
|
||||
int vpu_get_num_buffers(struct vpu_inst *inst, u32 type);
|
||||
|
||||
dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no);
|
||||
unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no);
|
||||
static inline struct vpu_format *vpu_get_format(struct vpu_inst *inst, u32 type)
|
||||
{
|
||||
if (V4L2_TYPE_IS_OUTPUT(type))
|
||||
return &inst->out_format;
|
||||
else
|
||||
return &inst->cap_format;
|
||||
}
|
||||
|
||||
static inline char *vpu_type_name(u32 type)
|
||||
{
|
||||
return V4L2_TYPE_IS_OUTPUT(type) ? "output" : "capture";
|
||||
}
|
||||
|
||||
static inline int vpu_vb_is_codecconfig(struct vb2_v4l2_buffer *vbuf)
|
||||
{
|
||||
#ifdef V4L2_BUF_FLAG_CODECCONFIG
|
||||
return (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG) ? 1 : 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
1169
drivers/media/platform/amphion/vpu_windsor.c
Normal file
1169
drivers/media/platform/amphion/vpu_windsor.c
Normal file
File diff suppressed because it is too large
Load Diff
37
drivers/media/platform/amphion/vpu_windsor.h
Normal file
37
drivers/media/platform/amphion/vpu_windsor.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020-2021 NXP
|
||||
*/
|
||||
|
||||
#ifndef _AMPHION_VPU_WINDSOR_H
|
||||
#define _AMPHION_VPU_WINDSOR_H
|
||||
|
||||
u32 vpu_windsor_get_data_size(void);
|
||||
void vpu_windsor_init_rpc(struct vpu_shared_addr *shared,
|
||||
struct vpu_buffer *rpc, dma_addr_t boot_addr);
|
||||
void vpu_windsor_set_log_buf(struct vpu_shared_addr *shared, struct vpu_buffer *log);
|
||||
void vpu_windsor_set_system_cfg(struct vpu_shared_addr *shared,
|
||||
u32 regs_base, void __iomem *regs, u32 core_id);
|
||||
int vpu_windsor_get_stream_buffer_size(struct vpu_shared_addr *shared);
|
||||
int vpu_windsor_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
|
||||
int vpu_windsor_convert_msg_id(u32 msg_id);
|
||||
int vpu_windsor_unpack_msg_data(struct vpu_rpc_event *pkt, void *data);
|
||||
int vpu_windsor_config_memory_resource(struct vpu_shared_addr *shared,
|
||||
u32 instance, u32 type, u32 index,
|
||||
struct vpu_buffer *buf);
|
||||
int vpu_windsor_config_stream_buffer(struct vpu_shared_addr *shared,
|
||||
u32 instance, struct vpu_buffer *buf);
|
||||
int vpu_windsor_update_stream_buffer(struct vpu_shared_addr *shared,
|
||||
u32 instance, u32 ptr, bool write);
|
||||
int vpu_windsor_get_stream_buffer_desc(struct vpu_shared_addr *shared,
|
||||
u32 instance, struct vpu_rpc_buffer_desc *desc);
|
||||
u32 vpu_windsor_get_version(struct vpu_shared_addr *shared);
|
||||
int vpu_windsor_set_encode_params(struct vpu_shared_addr *shared,
|
||||
u32 instance,
|
||||
struct vpu_encode_params *params,
|
||||
u32 update);
|
||||
int vpu_windsor_input_frame(struct vpu_shared_addr *shared,
|
||||
struct vpu_inst *inst, struct vb2_buffer *vb);
|
||||
u32 vpu_windsor_get_max_instance_count(struct vpu_shared_addr *shared);
|
||||
|
||||
#endif
|
||||
|
|
@ -1390,6 +1390,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
|
|||
case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break;
|
||||
case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break;
|
||||
case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break;
|
||||
case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break;
|
||||
case V4L2_PIX_FMT_NV12M_10BE_8L128: descr = "10-bit NV12M (8x128 Linear, BE)"; break;
|
||||
|
||||
default:
|
||||
/* Compressed formats */
|
||||
|
|
|
|||
|
|
@ -632,6 +632,8 @@ struct v4l2_pix_format {
|
|||
/* Tiled YUV formats, non contiguous planes */
|
||||
#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 tiles */
|
||||
#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 tiles */
|
||||
#define V4L2_PIX_FMT_NV12M_8L128 v4l2_fourcc('N', 'A', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
|
||||
#define V4L2_PIX_FMT_NV12M_10BE_8L128 v4l2_fourcc_be('N', 'T', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
|
||||
|
||||
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
|
||||
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user