提交 e58dc5ed 编写于 作者: J John Cox 提交者: Zheng Zengkai

staging: media: Add Raspberry Pi V4L2 H265 decoder

raspberrypi inclusion
category: feature
bugzilla: 50432

--------------------------------

This driver is for the HEVC/H265 decoder block on the Raspberry
Pi 4, and conforms to the V4L2 stateless decoder API.
Signed-off-by: NJohn Cox <jc@kynesim.co.uk>
Signed-off-by: NFang Yafen <yafen@iscas.ac.cn>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 d657bf0b
...@@ -34,6 +34,8 @@ source "drivers/staging/media/omap4iss/Kconfig" ...@@ -34,6 +34,8 @@ source "drivers/staging/media/omap4iss/Kconfig"
source "drivers/staging/media/rkvdec/Kconfig" source "drivers/staging/media/rkvdec/Kconfig"
source "drivers/staging/media/rpivid/Kconfig"
source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/sunxi/Kconfig"
source "drivers/staging/media/tegra-vde/Kconfig" source "drivers/staging/media/tegra-vde/Kconfig"
......
...@@ -5,6 +5,7 @@ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ ...@@ -5,6 +5,7 @@ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/
obj-$(CONFIG_VIDEO_RPIVID) += rpivid/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
......
# SPDX-License-Identifier: GPL-2.0
config VIDEO_RPIVID
tristate "Rpi H265 driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on MEDIA_CONTROLLER
depends on OF
depends on MEDIA_CONTROLLER_REQUEST_API
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Support for the Rpi H265 h/w decoder.
To compile this driver as a module, choose M here: the module
will be called rpivid-hevc.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_VIDEO_RPIVID) += rpivid-hevc.o
rpivid-hevc-y = rpivid.o rpivid_video.o rpivid_dec.o \
rpivid_hw.o rpivid_h265.o
// SPDX-License-Identifier: GPL-2.0
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mem2mem.h>
#include "rpivid.h"
#include "rpivid_video.h"
#include "rpivid_hw.h"
#include "rpivid_dec.h"
/*
* Default /dev/videoN node number.
* Deliberately avoid the very low numbers as these are often taken by webcams
* etc, and simple apps tend to only go for /dev/video0.
*/
static int video_nr = 19;
module_param(video_nr, int, 0644);
MODULE_PARM_DESC(video_nr, "decoder video device number");
static const struct rpivid_control rpivid_ctrls[] = {
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
},
.required = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
},
.required = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
},
.required = false,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
},
.required = true,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE,
.max = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
.def = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
},
.required = false,
},
{
.cfg = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE,
.max = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
.def = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
},
.required = false,
},
};
#define rpivid_ctrls_COUNT ARRAY_SIZE(rpivid_ctrls)
void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id)
{
unsigned int i;
for (i = 0; ctx->ctrls[i]; i++)
if (ctx->ctrls[i]->id == id)
return ctx->ctrls[i]->p_cur.p;
return NULL;
}
static int rpivid_init_ctrls(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
{
struct v4l2_ctrl_handler *hdl = &ctx->hdl;
struct v4l2_ctrl *ctrl;
unsigned int ctrl_size;
unsigned int i;
v4l2_ctrl_handler_init(hdl, rpivid_ctrls_COUNT);
if (hdl->error) {
v4l2_err(&dev->v4l2_dev,
"Failed to initialize control handler\n");
return hdl->error;
}
ctrl_size = sizeof(ctrl) * rpivid_ctrls_COUNT + 1;
ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
if (!ctx->ctrls)
return -ENOMEM;
for (i = 0; i < rpivid_ctrls_COUNT; i++) {
ctrl = v4l2_ctrl_new_custom(hdl, &rpivid_ctrls[i].cfg,
NULL);
if (hdl->error) {
v4l2_err(&dev->v4l2_dev,
"Failed to create new custom control id=%#x\n",
rpivid_ctrls[i].cfg.id);
v4l2_ctrl_handler_free(hdl);
kfree(ctx->ctrls);
return hdl->error;
}
ctx->ctrls[i] = ctrl;
}
ctx->fh.ctrl_handler = hdl;
v4l2_ctrl_handler_setup(hdl);
return 0;
}
static int rpivid_request_validate(struct media_request *req)
{
struct media_request_object *obj;
struct v4l2_ctrl_handler *parent_hdl, *hdl;
struct rpivid_ctx *ctx = NULL;
struct v4l2_ctrl *ctrl_test;
unsigned int count;
unsigned int i;
list_for_each_entry(obj, &req->objects, list) {
struct vb2_buffer *vb;
if (vb2_request_object_is_buffer(obj)) {
vb = container_of(obj, struct vb2_buffer, req_obj);
ctx = vb2_get_drv_priv(vb->vb2_queue);
break;
}
}
if (!ctx)
return -ENOENT;
count = vb2_request_buffer_cnt(req);
if (!count) {
v4l2_info(&ctx->dev->v4l2_dev,
"No buffer was provided with the request\n");
return -ENOENT;
} else if (count > 1) {
v4l2_info(&ctx->dev->v4l2_dev,
"More than one buffer was provided with the request\n");
return -EINVAL;
}
parent_hdl = &ctx->hdl;
hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
if (!hdl) {
v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
return -ENOENT;
}
for (i = 0; i < rpivid_ctrls_COUNT; i++) {
if (!rpivid_ctrls[i].required)
continue;
ctrl_test =
v4l2_ctrl_request_hdl_ctrl_find(hdl,
rpivid_ctrls[i].cfg.id);
if (!ctrl_test) {
v4l2_info(&ctx->dev->v4l2_dev,
"Missing required codec control\n");
return -ENOENT;
}
}
v4l2_ctrl_request_hdl_put(hdl);
return vb2_request_validate(req);
}
static int rpivid_open(struct file *file)
{
struct rpivid_dev *dev = video_drvdata(file);
struct rpivid_ctx *ctx = NULL;
int ret;
if (mutex_lock_interruptible(&dev->dev_mutex))
return -ERESTARTSYS;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
mutex_unlock(&dev->dev_mutex);
return -ENOMEM;
}
v4l2_fh_init(&ctx->fh, video_devdata(file));
file->private_data = &ctx->fh;
ctx->dev = dev;
ret = rpivid_init_ctrls(dev, ctx);
if (ret)
goto err_free;
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
&rpivid_queue_init);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
goto err_ctrls;
}
/* The only bit of format info that we can guess now is H265 src
* Everything else we need more info for
*/
ctx->src_fmt.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
rpivid_prepare_src_format(&ctx->src_fmt);
v4l2_fh_add(&ctx->fh);
mutex_unlock(&dev->dev_mutex);
return 0;
err_ctrls:
v4l2_ctrl_handler_free(&ctx->hdl);
err_free:
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
return ret;
}
static int rpivid_release(struct file *file)
{
struct rpivid_dev *dev = video_drvdata(file);
struct rpivid_ctx *ctx = container_of(file->private_data,
struct rpivid_ctx, fh);
mutex_lock(&dev->dev_mutex);
v4l2_fh_del(&ctx->fh);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->hdl);
kfree(ctx->ctrls);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
return 0;
}
static const struct v4l2_file_operations rpivid_fops = {
.owner = THIS_MODULE,
.open = rpivid_open,
.release = rpivid_release,
.poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = v4l2_m2m_fop_mmap,
};
static const struct video_device rpivid_video_device = {
.name = RPIVID_NAME,
.vfl_dir = VFL_DIR_M2M,
.fops = &rpivid_fops,
.ioctl_ops = &rpivid_ioctl_ops,
.minor = -1,
.release = video_device_release_empty,
.device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
};
static const struct v4l2_m2m_ops rpivid_m2m_ops = {
.device_run = rpivid_device_run,
};
static const struct media_device_ops rpivid_m2m_media_ops = {
.req_validate = rpivid_request_validate,
.req_queue = v4l2_m2m_request_queue,
};
static int rpivid_probe(struct platform_device *pdev)
{
struct rpivid_dev *dev;
struct video_device *vfd;
int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->vfd = rpivid_video_device;
dev->dev = &pdev->dev;
dev->pdev = pdev;
ret = 0;
ret = rpivid_hw_probe(dev);
if (ret) {
dev_err(&pdev->dev, "Failed to probe hardware\n");
return ret;
}
dev->dec_ops = &rpivid_dec_ops_h265;
mutex_init(&dev->dev_mutex);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to register V4L2 device\n");
return ret;
}
vfd = &dev->vfd;
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
video_set_drvdata(vfd, dev);
dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
if (IS_ERR(dev->m2m_dev)) {
v4l2_err(&dev->v4l2_dev,
"Failed to initialize V4L2 M2M device\n");
ret = PTR_ERR(dev->m2m_dev);
goto err_v4l2;
}
dev->mdev.dev = &pdev->dev;
strscpy(dev->mdev.model, RPIVID_NAME, sizeof(dev->mdev.model));
strscpy(dev->mdev.bus_info, "platform:" RPIVID_NAME,
sizeof(dev->mdev.bus_info));
media_device_init(&dev->mdev);
dev->mdev.ops = &rpivid_m2m_media_ops;
dev->v4l2_dev.mdev = &dev->mdev;
ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
goto err_m2m;
}
v4l2_info(&dev->v4l2_dev,
"Device registered as /dev/video%d\n", vfd->num);
ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
MEDIA_ENT_F_PROC_VIDEO_DECODER);
if (ret) {
v4l2_err(&dev->v4l2_dev,
"Failed to initialize V4L2 M2M media controller\n");
goto err_video;
}
ret = media_device_register(&dev->mdev);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
goto err_m2m_mc;
}
platform_set_drvdata(pdev, dev);
return 0;
err_m2m_mc:
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
err_video:
video_unregister_device(&dev->vfd);
err_m2m:
v4l2_m2m_release(dev->m2m_dev);
err_v4l2:
v4l2_device_unregister(&dev->v4l2_dev);
return ret;
}
static int rpivid_remove(struct platform_device *pdev)
{
struct rpivid_dev *dev = platform_get_drvdata(pdev);
if (media_devnode_is_registered(dev->mdev.devnode)) {
media_device_unregister(&dev->mdev);
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
media_device_cleanup(&dev->mdev);
}
v4l2_m2m_release(dev->m2m_dev);
video_unregister_device(&dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
rpivid_hw_remove(dev);
return 0;
}
static const struct of_device_id rpivid_dt_match[] = {
{
.compatible = "raspberrypi,rpivid-vid-decoder",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rpivid_dt_match);
static struct platform_driver rpivid_driver = {
.probe = rpivid_probe,
.remove = rpivid_remove,
.driver = {
.name = RPIVID_NAME,
.of_match_table = of_match_ptr(rpivid_dt_match),
},
};
module_platform_driver(rpivid_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("John Cox <jc@kynesim.co.uk>");
MODULE_DESCRIPTION("Raspberry Pi HEVC V4L2 driver");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#ifndef _RPIVID_H_
#define _RPIVID_H_
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#define OPT_DEBUG_POLL_IRQ 0
#define RPIVID_NAME "rpivid"
#define RPIVID_CAPABILITY_UNTILED BIT(0)
#define RPIVID_CAPABILITY_H265_DEC BIT(1)
#define RPIVID_QUIRK_NO_DMA_OFFSET BIT(0)
#define RPIVID_SRC_PIXELFORMAT_DEFAULT V4L2_PIX_FMT_HEVC_SLICE
enum rpivid_irq_status {
RPIVID_IRQ_NONE,
RPIVID_IRQ_ERROR,
RPIVID_IRQ_OK,
};
struct rpivid_control {
struct v4l2_ctrl_config cfg;
unsigned char required:1;
};
struct rpivid_h265_run {
const struct v4l2_ctrl_hevc_sps *sps;
const struct v4l2_ctrl_hevc_pps *pps;
const struct v4l2_ctrl_hevc_slice_params *slice_params;
const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
};
struct rpivid_run {
struct vb2_v4l2_buffer *src;
struct vb2_v4l2_buffer *dst;
struct rpivid_h265_run h265;
};
struct rpivid_buffer {
struct v4l2_m2m_buffer m2m_buf;
};
struct rpivid_dec_state;
struct rpivid_dec_env;
#define RPIVID_DEC_ENV_COUNT 3
struct rpivid_gptr {
size_t size;
__u8 *ptr;
dma_addr_t addr;
unsigned long attrs;
};
struct rpivid_dev;
typedef void (*rpivid_irq_callback)(struct rpivid_dev *dev, void *ctx);
struct rpivid_q_aux;
#define RPIVID_AUX_ENT_COUNT VB2_MAX_FRAME
#define RPIVID_P2BUF_COUNT 2
struct rpivid_ctx {
struct v4l2_fh fh;
struct rpivid_dev *dev;
struct v4l2_pix_format src_fmt;
struct v4l2_pix_format dst_fmt;
int dst_fmt_set;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl **ctrls;
/* Decode state - stateless decoder my *** */
/* state contains stuff that is only needed in phase0
* it could be held in dec_env but that would be wasteful
*/
struct rpivid_dec_state *state;
struct rpivid_dec_env *dec0;
/* Spinlock protecting dec_free */
spinlock_t dec_lock;
struct rpivid_dec_env *dec_free;
struct rpivid_dec_env *dec_pool;
/* Some of these should be in dev */
struct rpivid_gptr bitbufs[1]; /* Will be 2 */
struct rpivid_gptr cmdbufs[1]; /* Will be 2 */
unsigned int p2idx;
atomic_t p2out;
struct rpivid_gptr pu_bufs[RPIVID_P2BUF_COUNT];
struct rpivid_gptr coeff_bufs[RPIVID_P2BUF_COUNT];
/* Spinlock protecting aux_free */
spinlock_t aux_lock;
struct rpivid_q_aux *aux_free;
struct rpivid_q_aux *aux_ents[RPIVID_AUX_ENT_COUNT];
unsigned int colmv_stride;
unsigned int colmv_picsize;
};
struct rpivid_dec_ops {
void (*setup)(struct rpivid_ctx *ctx, struct rpivid_run *run);
int (*start)(struct rpivid_ctx *ctx);
void (*stop)(struct rpivid_ctx *ctx);
void (*trigger)(struct rpivid_ctx *ctx);
};
struct rpivid_variant {
unsigned int capabilities;
unsigned int quirks;
unsigned int mod_rate;
};
struct rpivid_hw_irq_ent;
struct rpivid_hw_irq_ctrl {
/* Spinlock protecting claim and tail */
spinlock_t lock;
struct rpivid_hw_irq_ent *claim;
struct rpivid_hw_irq_ent *tail;
/* Ent for pending irq - also prevents sched */
struct rpivid_hw_irq_ent *irq;
/* Non-zero => do not start a new job - outer layer sched pending */
int no_sched;
/* Thread CB requested */
bool thread_reqed;
};
struct rpivid_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd;
struct media_device mdev;
struct media_pad pad[2];
struct platform_device *pdev;
struct device *dev;
struct v4l2_m2m_dev *m2m_dev;
struct rpivid_dec_ops *dec_ops;
/* Device file mutex */
struct mutex dev_mutex;
void __iomem *base_irq;
void __iomem *base_h265;
struct clk *clock;
struct rpivid_hw_irq_ctrl ic_active1;
struct rpivid_hw_irq_ctrl ic_active2;
};
extern struct rpivid_dec_ops rpivid_dec_ops_h265;
void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id);
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include "rpivid.h"
#include "rpivid_dec.h"
void rpivid_device_run(void *priv)
{
struct rpivid_ctx *ctx = priv;
struct rpivid_dev *dev = ctx->dev;
struct rpivid_run run = {};
struct media_request *src_req;
run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
if (!run.src || !run.dst) {
v4l2_err(&dev->v4l2_dev, "%s: Missing buffer: src=%p, dst=%p\n",
__func__, run.src, run.dst);
/* We are stuffed - this probably won't dig us out of our
* current situation but it is better than nothing
*/
v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
VB2_BUF_STATE_ERROR);
return;
}
/* Apply request(s) controls if needed. */
src_req = run.src->vb2_buf.req_obj.req;
if (src_req)
v4l2_ctrl_request_setup(src_req, &ctx->hdl);
switch (ctx->src_fmt.pixelformat) {
case V4L2_PIX_FMT_HEVC_SLICE:
run.h265.sps =
rpivid_find_control_data(ctx,
V4L2_CID_MPEG_VIDEO_HEVC_SPS);
run.h265.pps =
rpivid_find_control_data(ctx,
V4L2_CID_MPEG_VIDEO_HEVC_PPS);
run.h265.slice_params =
rpivid_find_control_data(ctx,
V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
run.h265.scaling_matrix =
rpivid_find_control_data(ctx,
V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX);
break;
default:
break;
}
v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
dev->dec_ops->setup(ctx, &run);
/* Complete request(s) controls if needed. */
if (src_req)
v4l2_ctrl_request_complete(src_req, &ctx->hdl);
dev->dec_ops->trigger(ctx);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#ifndef _RPIVID_DEC_H_
#define _RPIVID_DEC_H_
void rpivid_device_run(void *priv);
#endif
此差异已折叠。
// SPDX-License-Identifier: GPL-2.0
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <media/videobuf2-core.h>
#include <media/v4l2-mem2mem.h>
#include "rpivid.h"
#include "rpivid_hw.h"
static void pre_irq(struct rpivid_dev *dev, struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback cb, void *v,
struct rpivid_hw_irq_ctrl *ictl)
{
unsigned long flags;
if (ictl->irq) {
v4l2_err(&dev->v4l2_dev, "Attempt to claim IRQ when already claimed\n");
return;
}
ient->cb = cb;
ient->v = v;
// Not sure this lock is actually required
spin_lock_irqsave(&ictl->lock, flags);
ictl->irq = ient;
spin_unlock_irqrestore(&ictl->lock, flags);
}
static void sched_claim(struct rpivid_dev * const dev,
struct rpivid_hw_irq_ctrl * const ictl)
{
for (;;) {
struct rpivid_hw_irq_ent *ient = NULL;
unsigned long flags;
spin_lock_irqsave(&ictl->lock, flags);
if (--ictl->no_sched <= 0) {
ient = ictl->claim;
if (!ictl->irq && ient) {
ictl->claim = ient->next;
ictl->no_sched = 1;
}
}
spin_unlock_irqrestore(&ictl->lock, flags);
if (!ient)
break;
ient->cb(dev, ient->v);
}
}
/* Should only ever be called from its own IRQ cb so no lock required */
static void pre_thread(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback cb, void *v,
struct rpivid_hw_irq_ctrl *ictl)
{
ient->cb = cb;
ient->v = v;
ictl->irq = ient;
ictl->thread_reqed = true;
ictl->no_sched++;
}
// Called in irq context
static void do_irq(struct rpivid_dev * const dev,
struct rpivid_hw_irq_ctrl * const ictl)
{
struct rpivid_hw_irq_ent *ient;
unsigned long flags;
spin_lock_irqsave(&ictl->lock, flags);
ient = ictl->irq;
if (ient) {
ictl->no_sched++;
ictl->irq = NULL;
}
spin_unlock_irqrestore(&ictl->lock, flags);
if (ient) {
ient->cb(dev, ient->v);
sched_claim(dev, ictl);
}
}
static void do_claim(struct rpivid_dev * const dev,
struct rpivid_hw_irq_ent *ient,
const rpivid_irq_callback cb, void * const v,
struct rpivid_hw_irq_ctrl * const ictl)
{
unsigned long flags;
ient->next = NULL;
ient->cb = cb;
ient->v = v;
spin_lock_irqsave(&ictl->lock, flags);
if (ictl->claim) {
// If we have a Q then add to end
ictl->tail->next = ient;
ictl->tail = ient;
ient = NULL;
} else if (ictl->no_sched || ictl->irq) {
// Empty Q but other activity in progress so Q
ictl->claim = ient;
ictl->tail = ient;
ient = NULL;
} else {
// Nothing else going on - schedule immediately and
// prevent anything else scheduling claims
ictl->no_sched = 1;
}
spin_unlock_irqrestore(&ictl->lock, flags);
if (ient) {
ient->cb(dev, ient->v);
sched_claim(dev, ictl);
}
}
static void ictl_init(struct rpivid_hw_irq_ctrl * const ictl)
{
spin_lock_init(&ictl->lock);
ictl->claim = NULL;
ictl->tail = NULL;
ictl->irq = NULL;
ictl->no_sched = 0;
}
static void ictl_uninit(struct rpivid_hw_irq_ctrl * const ictl)
{
// Nothing to do
}
#if !OPT_DEBUG_POLL_IRQ
static irqreturn_t rpivid_irq_irq(int irq, void *data)
{
struct rpivid_dev * const dev = data;
__u32 ictrl;
ictrl = irq_read(dev, ARG_IC_ICTRL);
if (!(ictrl & ARG_IC_ICTRL_ALL_IRQ_MASK)) {
v4l2_warn(&dev->v4l2_dev, "IRQ but no IRQ bits set\n");
return IRQ_NONE;
}
// Cancel any/all irqs
irq_write(dev, ARG_IC_ICTRL, ictrl & ~ARG_IC_ICTRL_SET_ZERO_MASK);
// Service Active2 before Active1 so Phase 1 can transition to Phase 2
// without delay
if (ictrl & ARG_IC_ICTRL_ACTIVE2_INT_SET)
do_irq(dev, &dev->ic_active2);
if (ictrl & ARG_IC_ICTRL_ACTIVE1_INT_SET)
do_irq(dev, &dev->ic_active1);
return dev->ic_active1.thread_reqed || dev->ic_active2.thread_reqed ?
IRQ_WAKE_THREAD : IRQ_HANDLED;
}
static void do_thread(struct rpivid_dev * const dev,
struct rpivid_hw_irq_ctrl *const ictl)
{
unsigned long flags;
struct rpivid_hw_irq_ent *ient = NULL;
spin_lock_irqsave(&ictl->lock, flags);
if (ictl->thread_reqed) {
ient = ictl->irq;
ictl->thread_reqed = false;
ictl->irq = NULL;
}
spin_unlock_irqrestore(&ictl->lock, flags);
if (ient) {
ient->cb(dev, ient->v);
sched_claim(dev, ictl);
}
}
static irqreturn_t rpivid_irq_thread(int irq, void *data)
{
struct rpivid_dev * const dev = data;
do_thread(dev, &dev->ic_active1);
do_thread(dev, &dev->ic_active2);
return IRQ_HANDLED;
}
#endif
/* May only be called from Active1 CB
* IRQs should not be expected until execution continues in the cb
*/
void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback thread_cb, void *ctx)
{
pre_thread(dev, ient, thread_cb, ctx, &dev->ic_active1);
}
void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback ready_cb, void *ctx)
{
do_claim(dev, ient, ready_cb, ctx, &dev->ic_active1);
}
void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback irq_cb, void *ctx)
{
pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active1);
}
void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback ready_cb, void *ctx)
{
do_claim(dev, ient, ready_cb, ctx, &dev->ic_active2);
}
void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback irq_cb, void *ctx)
{
pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active2);
}
int rpivid_hw_probe(struct rpivid_dev *dev)
{
struct resource *res;
__u32 irq_stat;
int irq_dec;
int ret = 0;
ictl_init(&dev->ic_active1);
ictl_init(&dev->ic_active2);
res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "intc");
if (!res)
return -ENODEV;
dev->base_irq = devm_ioremap(dev->dev, res->start, resource_size(res));
if (IS_ERR(dev->base_irq))
return PTR_ERR(dev->base_irq);
res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "hevc");
if (!res)
return -ENODEV;
dev->base_h265 = devm_ioremap(dev->dev, res->start, resource_size(res));
if (IS_ERR(dev->base_h265))
return PTR_ERR(dev->base_h265);
dev->clock = devm_clk_get(&dev->pdev->dev, "hevc");
if (IS_ERR(dev->clock))
return PTR_ERR(dev->clock);
// Disable IRQs & reset anything pending
irq_write(dev, 0,
ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
irq_stat = irq_read(dev, 0);
irq_write(dev, 0, irq_stat);
#if !OPT_DEBUG_POLL_IRQ
irq_dec = platform_get_irq(dev->pdev, 0);
if (irq_dec <= 0)
return irq_dec;
ret = devm_request_threaded_irq(dev->dev, irq_dec,
rpivid_irq_irq,
rpivid_irq_thread,
0, dev_name(dev->dev), dev);
if (ret) {
dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
return ret;
}
#endif
return ret;
}
void rpivid_hw_remove(struct rpivid_dev *dev)
{
// IRQ auto freed on unload so no need to do it here
ictl_uninit(&dev->ic_active1);
ictl_uninit(&dev->ic_active2);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#ifndef _RPIVID_HW_H_
#define _RPIVID_HW_H_
struct rpivid_hw_irq_ent {
struct rpivid_hw_irq_ent *next;
rpivid_irq_callback cb;
void *v;
};
/* Phase 1 Register offsets */
#define RPI_SPS0 0
#define RPI_SPS1 4
#define RPI_PPS 8
#define RPI_SLICE 12
#define RPI_TILESTART 16
#define RPI_TILEEND 20
#define RPI_SLICESTART 24
#define RPI_MODE 28
#define RPI_LEFT0 32
#define RPI_LEFT1 36
#define RPI_LEFT2 40
#define RPI_LEFT3 44
#define RPI_QP 48
#define RPI_CONTROL 52
#define RPI_STATUS 56
#define RPI_VERSION 60
#define RPI_BFBASE 64
#define RPI_BFNUM 68
#define RPI_BFCONTROL 72
#define RPI_BFSTATUS 76
#define RPI_PUWBASE 80
#define RPI_PUWSTRIDE 84
#define RPI_COEFFWBASE 88
#define RPI_COEFFWSTRIDE 92
#define RPI_SLICECMDS 96
#define RPI_BEGINTILEEND 100
#define RPI_TRANSFER 104
#define RPI_CFBASE 108
#define RPI_CFNUM 112
#define RPI_CFSTATUS 116
/* Phase 2 Register offsets */
#define RPI_PURBASE 0x8000
#define RPI_PURSTRIDE 0x8004
#define RPI_COEFFRBASE 0x8008
#define RPI_COEFFRSTRIDE 0x800C
#define RPI_NUMROWS 0x8010
#define RPI_CONFIG2 0x8014
#define RPI_OUTYBASE 0x8018
#define RPI_OUTYSTRIDE 0x801C
#define RPI_OUTCBASE 0x8020
#define RPI_OUTCSTRIDE 0x8024
#define RPI_STATUS2 0x8028
#define RPI_FRAMESIZE 0x802C
#define RPI_MVBASE 0x8030
#define RPI_MVSTRIDE 0x8034
#define RPI_COLBASE 0x8038
#define RPI_COLSTRIDE 0x803C
#define RPI_CURRPOC 0x8040
/*
* Write a general register value
* Order is unimportant
*/
static inline void apb_write(const struct rpivid_dev * const dev,
const unsigned int offset, const u32 val)
{
writel_relaxed(val, dev->base_h265 + offset);
}
/* Write the final register value that actually starts the phase */
static inline void apb_write_final(const struct rpivid_dev * const dev,
const unsigned int offset, const u32 val)
{
writel(val, dev->base_h265 + offset);
}
static inline u32 apb_read(const struct rpivid_dev * const dev,
const unsigned int offset)
{
return readl(dev->base_h265 + offset);
}
static inline void irq_write(const struct rpivid_dev * const dev,
const unsigned int offset, const u32 val)
{
writel(val, dev->base_irq + offset);
}
static inline u32 irq_read(const struct rpivid_dev * const dev,
const unsigned int offset)
{
return readl(dev->base_irq + offset);
}
static inline void apb_write_vc_addr(const struct rpivid_dev * const dev,
const unsigned int offset,
const dma_addr_t a)
{
apb_write(dev, offset, (u32)(a >> 6));
}
static inline void apb_write_vc_addr_final(const struct rpivid_dev * const dev,
const unsigned int offset,
const dma_addr_t a)
{
apb_write_final(dev, offset, (u32)(a >> 6));
}
static inline void apb_write_vc_len(const struct rpivid_dev * const dev,
const unsigned int offset,
const unsigned int x)
{
apb_write(dev, offset, (x + 63) >> 6);
}
/* *ARG_IC_ICTRL - Interrupt control for ARGON Core*
* Offset (byte space) = 40'h2b10000
* Physical Address (byte space) = 40'h7eb10000
* Verilog Macro Address = `ARG_IC_REG_START + `ARGON_INTCTRL_ICTRL
* Reset Value = 32'b100x100x_100xxxxx_xxxxxxx0_x100x100
* Access = RW (32-bit only)
* Interrupt control logic for ARGON Core.
*/
#define ARG_IC_ICTRL 0
/* acc=LWC ACTIVE1_INT FIELD ACCESS: LWC
*
* Interrupt 1
* This is set and held when an hevc_active1 interrupt edge is detected
* The polarity of the edge is set by the ACTIVE1_EDGE field
* Write a 1 to this bit to clear down the latched interrupt
* The latched interrupt is only enabled out onto the interrupt line if
* ACTIVE1_EN is set
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_ACTIVE1_INT_SET BIT(0)
/* ACTIVE1_EDGE Sets the polarity of the interrupt edge detection logic
* This logic detects edges of the hevc_active1 line from the argon core
* 0 = negedge, 1 = posedge
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_ACTIVE1_EDGE_SET BIT(1)
/* ACTIVE1_EN Enables ACTIVE1_INT out onto the argon interrupt line.
* If this isn't set, the interrupt logic will work but no interrupt will be
* set to the interrupt controller
* Reset value is *1* decimal.
*
* [JC] The above appears to be a lie - if unset then b0 is never set
*/
#define ARG_IC_ICTRL_ACTIVE1_EN_SET BIT(2)
/* acc=RO ACTIVE1_STATUS FIELD ACCESS: RO
*
* The current status of the hevc_active1 signal
*/
#define ARG_IC_ICTRL_ACTIVE1_STATUS_SET BIT(3)
/* acc=LWC ACTIVE2_INT FIELD ACCESS: LWC
*
* Interrupt 2
* This is set and held when an hevc_active2 interrupt edge is detected
* The polarity of the edge is set by the ACTIVE2_EDGE field
* Write a 1 to this bit to clear down the latched interrupt
* The latched interrupt is only enabled out onto the interrupt line if
* ACTIVE2_EN is set
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_ACTIVE2_INT_SET BIT(4)
/* ACTIVE2_EDGE Sets the polarity of the interrupt edge detection logic
* This logic detects edges of the hevc_active2 line from the argon core
* 0 = negedge, 1 = posedge
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_ACTIVE2_EDGE_SET BIT(5)
/* ACTIVE2_EN Enables ACTIVE2_INT out onto the argon interrupt line.
* If this isn't set, the interrupt logic will work but no interrupt will be
* set to the interrupt controller
* Reset value is *1* decimal.
*/
#define ARG_IC_ICTRL_ACTIVE2_EN_SET BIT(6)
/* acc=RO ACTIVE2_STATUS FIELD ACCESS: RO
*
* The current status of the hevc_active2 signal
*/
#define ARG_IC_ICTRL_ACTIVE2_STATUS_SET BIT(7)
/* TEST_INT Forces the argon int high for test purposes.
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_TEST_INT BIT(8)
#define ARG_IC_ICTRL_SPARE BIT(9)
/* acc=RO VP9_INTERRUPT_STATUS FIELD ACCESS: RO
*
* The current status of the vp9_interrupt signal
*/
#define ARG_IC_ICTRL_VP9_INTERRUPT_STATUS BIT(10)
/* AIO_INT_ENABLE 1 = Or the AIO int in with the Argon int so the VPU can see
* it
* 0 = the AIO int is masked. (It should still be connected to the GIC though).
*/
#define ARG_IC_ICTRL_AIO_INT_ENABLE BIT(20)
#define ARG_IC_ICTRL_H264_ACTIVE_INT BIT(21)
#define ARG_IC_ICTRL_H264_ACTIVE_EDGE BIT(22)
#define ARG_IC_ICTRL_H264_ACTIVE_EN BIT(23)
#define ARG_IC_ICTRL_H264_ACTIVE_STATUS BIT(24)
#define ARG_IC_ICTRL_H264_INTERRUPT_INT BIT(25)
#define ARG_IC_ICTRL_H264_INTERRUPT_EDGE BIT(26)
#define ARG_IC_ICTRL_H264_INTERRUPT_EN BIT(27)
/* acc=RO H264_INTERRUPT_STATUS FIELD ACCESS: RO
*
* The current status of the h264_interrupt signal
*/
#define ARG_IC_ICTRL_H264_INTERRUPT_STATUS BIT(28)
/* acc=LWC VP9_INTERRUPT_INT FIELD ACCESS: LWC
*
* Interrupt 1
* This is set and held when an vp9_interrupt interrupt edge is detected
* The polarity of the edge is set by the VP9_INTERRUPT_EDGE field
* Write a 1 to this bit to clear down the latched interrupt
* The latched interrupt is only enabled out onto the interrupt line if
* VP9_INTERRUPT_EN is set
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_VP9_INTERRUPT_INT BIT(29)
/* VP9_INTERRUPT_EDGE Sets the polarity of the interrupt edge detection logic
* This logic detects edges of the vp9_interrupt line from the argon h264 core
* 0 = negedge, 1 = posedge
* Reset value is *0* decimal.
*/
#define ARG_IC_ICTRL_VP9_INTERRUPT_EDGE BIT(30)
/* VP9_INTERRUPT_EN Enables VP9_INTERRUPT_INT out onto the argon interrupt line.
* If this isn't set, the interrupt logic will work but no interrupt will be
* set to the interrupt controller
* Reset value is *1* decimal.
*/
#define ARG_IC_ICTRL_VP9_INTERRUPT_EN BIT(31)
/* Bits 19:12, 11 reserved - read ?, write 0 */
#define ARG_IC_ICTRL_SET_ZERO_MASK ((0xff << 12) | BIT(11))
/* All IRQ bits */
#define ARG_IC_ICTRL_ALL_IRQ_MASK (\
ARG_IC_ICTRL_VP9_INTERRUPT_INT |\
ARG_IC_ICTRL_H264_INTERRUPT_INT |\
ARG_IC_ICTRL_ACTIVE1_INT_SET |\
ARG_IC_ICTRL_ACTIVE2_INT_SET)
/* Auto release once all CBs called */
void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback ready_cb, void *ctx);
/* May only be called in claim cb */
void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback irq_cb, void *ctx);
/* May only be called in irq cb */
void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback thread_cb, void *ctx);
/* Auto release once all CBs called */
void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback ready_cb, void *ctx);
/* May only be called in claim cb */
void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
struct rpivid_hw_irq_ent *ient,
rpivid_irq_callback irq_cb, void *ctx);
int rpivid_hw_probe(struct rpivid_dev *dev);
void rpivid_hw_remove(struct rpivid_dev *dev);
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include "rpivid.h"
#include "rpivid_video.h"
#include "rpivid_dec.h"
#define RPIVID_DECODE_SRC BIT(0)
#define RPIVID_DECODE_DST BIT(1)
#define RPIVID_MIN_WIDTH 16U
#define RPIVID_MIN_HEIGHT 16U
#define RPIVID_MAX_WIDTH 4096U
#define RPIVID_MAX_HEIGHT 4096U
static inline struct rpivid_ctx *rpivid_file2ctx(struct file *file)
{
return container_of(file->private_data, struct rpivid_ctx, fh);
}
/* constrain x to y,y*2 */
static inline unsigned int constrain2x(unsigned int x, unsigned int y)
{
return (x < y) ?
y :
(x > y * 2) ? y : x;
}
int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt)
{
if (pix_fmt->pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
return -EINVAL;
/* Zero bytes per line for encoded source. */
pix_fmt->bytesperline = 0;
/* Choose some minimum size since this can't be 0 */
pix_fmt->sizeimage = max_t(u32, SZ_1K, pix_fmt->sizeimage);
pix_fmt->field = V4L2_FIELD_NONE;
return 0;
}
int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt)
{
unsigned int width = pix_fmt->width;
unsigned int height = pix_fmt->height;
unsigned int sizeimage = pix_fmt->sizeimage;
unsigned int bytesperline = pix_fmt->bytesperline;
switch (pix_fmt->pixelformat) {
/* For column formats set bytesperline to column height (stride2) */
case V4L2_PIX_FMT_NV12_COL128:
/* Width rounds up to columns */
width = ALIGN(min(width, RPIVID_MAX_WIDTH), 128);
/* 16 aligned height - not sure we even need that */
height = ALIGN(height, 16);
/* column height
* Accept suggested shape if at least min & < 2 * min
*/
bytesperline = constrain2x(bytesperline, height * 3 / 2);
/* image size
* Again allow plausible variation in case added padding is
* required
*/
sizeimage = constrain2x(sizeimage, bytesperline * width);
break;
case V4L2_PIX_FMT_NV12_10_COL128:
/* width in pixels (3 pels = 4 bytes) rounded to 128 byte
* columns
*/
width = ALIGN(((min(width, RPIVID_MAX_WIDTH) + 2) / 3), 32) * 3;
/* 16-aligned height. */
height = ALIGN(height, 16);
/* column height
* Accept suggested shape if at least min & < 2 * min
*/
bytesperline = constrain2x(bytesperline, height * 3 / 2);
/* image size
* Again allow plausible variation in case added padding is
* required
*/
sizeimage = constrain2x(sizeimage,
bytesperline * width * 4 / 3);
break;
default:
return -EINVAL;
}
pix_fmt->width = width;
pix_fmt->height = height;
pix_fmt->field = V4L2_FIELD_NONE;
pix_fmt->bytesperline = bytesperline;
pix_fmt->sizeimage = sizeimage;
return 0;
}
static int rpivid_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strscpy(cap->driver, RPIVID_NAME, sizeof(cap->driver));
strscpy(cap->card, RPIVID_NAME, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", RPIVID_NAME);
return 0;
}
static int rpivid_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
// Input formats
// H.265 Slice only currently
if (f->index == 0) {
f->pixelformat = V4L2_PIX_FMT_HEVC_SLICE;
return 0;
}
return -EINVAL;
}
static int rpivid_hevc_validate_sps(const struct v4l2_ctrl_hevc_sps * const sps)
{
const unsigned int ctb_log2_size_y =
sps->log2_min_luma_coding_block_size_minus3 + 3 +
sps->log2_diff_max_min_luma_coding_block_size;
const unsigned int min_tb_log2_size_y =
sps->log2_min_luma_transform_block_size_minus2 + 2;
const unsigned int max_tb_log2_size_y = min_tb_log2_size_y +
sps->log2_diff_max_min_luma_transform_block_size;
/* Local limitations */
if (sps->pic_width_in_luma_samples < 32 ||
sps->pic_width_in_luma_samples > 4096)
return 0;
if (sps->pic_height_in_luma_samples < 32 ||
sps->pic_height_in_luma_samples > 4096)
return 0;
if (!(sps->bit_depth_luma_minus8 == 0 ||
sps->bit_depth_luma_minus8 == 2))
return 0;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
return 0;
if (sps->chroma_format_idc != 1)
return 0;
/* Limits from H.265 7.4.3.2.1 */
if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12)
return 0;
if (sps->sps_max_dec_pic_buffering_minus1 > 15)
return 0;
if (sps->sps_max_num_reorder_pics >
sps->sps_max_dec_pic_buffering_minus1)
return 0;
if (ctb_log2_size_y > 6)
return 0;
if (max_tb_log2_size_y > 5)
return 0;
if (max_tb_log2_size_y > ctb_log2_size_y)
return 0;
if (sps->max_transform_hierarchy_depth_inter >
(ctb_log2_size_y - min_tb_log2_size_y))
return 0;
if (sps->max_transform_hierarchy_depth_intra >
(ctb_log2_size_y - min_tb_log2_size_y))
return 0;
/* Check pcm stuff */
if (sps->num_short_term_ref_pic_sets > 64)
return 0;
if (sps->num_long_term_ref_pics_sps > 32)
return 0;
return 1;
}
static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
{
return sps && sps->pic_width_in_luma_samples != 0;
}
static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps,
const int index)
{
u32 pf = 0;
// Use width 0 as a signifier of unsetness
if (!is_sps_set(sps)) {
/* Treat this as an error? For now return both */
if (index == 0)
pf = V4L2_PIX_FMT_NV12_COL128;
else if (index == 1)
pf = V4L2_PIX_FMT_NV12_10_COL128;
} else if (index == 0 && rpivid_hevc_validate_sps(sps)) {
if (sps->bit_depth_luma_minus8 == 0)
pf = V4L2_PIX_FMT_NV12_COL128;
else if (sps->bit_depth_luma_minus8 == 2)
pf = V4L2_PIX_FMT_NV12_10_COL128;
}
return pf;
}
static struct v4l2_pix_format
rpivid_hevc_default_dst_fmt(struct rpivid_ctx * const ctx)
{
const struct v4l2_ctrl_hevc_sps * const sps =
rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
struct v4l2_pix_format pix_fmt = {
.width = sps->pic_width_in_luma_samples,
.height = sps->pic_height_in_luma_samples,
.pixelformat = pixelformat_from_sps(sps, 0)
};
rpivid_prepare_dst_format(&pix_fmt);
return pix_fmt;
}
static u32 rpivid_hevc_get_dst_pixelformat(struct rpivid_ctx * const ctx,
const int index)
{
const struct v4l2_ctrl_hevc_sps * const sps =
rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
return pixelformat_from_sps(sps, index);
}
static int rpivid_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct rpivid_ctx * const ctx = rpivid_file2ctx(file);
const u32 pf = rpivid_hevc_get_dst_pixelformat(ctx, f->index);
if (pf == 0)
return -EINVAL;
f->pixelformat = pf;
return 0;
}
static int rpivid_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rpivid_ctx *ctx = rpivid_file2ctx(file);
if (!ctx->dst_fmt_set)
ctx->dst_fmt = rpivid_hevc_default_dst_fmt(ctx);
f->fmt.pix = ctx->dst_fmt;
return 0;
}
static int rpivid_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rpivid_ctx *ctx = rpivid_file2ctx(file);
f->fmt.pix = ctx->src_fmt;
return 0;
}
static inline void copy_color(struct v4l2_pix_format *d,
const struct v4l2_pix_format *s)
{
d->colorspace = s->colorspace;
d->xfer_func = s->xfer_func;
d->ycbcr_enc = s->ycbcr_enc;
d->quantization = s->quantization;
}
static int rpivid_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rpivid_ctx *ctx = rpivid_file2ctx(file);
const struct v4l2_ctrl_hevc_sps * const sps =
rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
u32 pixelformat;
int i;
/* Reject format types we don't support */
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
for (i = 0; (pixelformat = pixelformat_from_sps(sps, i)) != 0; i++) {
if (f->fmt.pix.pixelformat == pixelformat)
break;
}
// If we can't use requested fmt then set to default
if (pixelformat == 0) {
pixelformat = pixelformat_from_sps(sps, 0);
// If we don't have a default then give up
if (pixelformat == 0)
return -EINVAL;
}
// We don't have any way of finding out colourspace so believe
// anything we are told - take anything set in src as a default
if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT)
copy_color(&f->fmt.pix, &ctx->src_fmt);
f->fmt.pix.pixelformat = pixelformat;
return rpivid_prepare_dst_format(&f->fmt.pix);
}
static int rpivid_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
if (rpivid_prepare_src_format(&f->fmt.pix)) {
// Set default src format
f->fmt.pix.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
rpivid_prepare_src_format(&f->fmt.pix);
}
return 0;
}
static int rpivid_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rpivid_ctx *ctx = rpivid_file2ctx(file);
struct vb2_queue *vq;
int ret;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (vb2_is_busy(vq))
return -EBUSY;
ret = rpivid_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
ctx->dst_fmt = f->fmt.pix;
ctx->dst_fmt_set = 1;
return 0;
}
static int rpivid_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct rpivid_ctx *ctx = rpivid_file2ctx(file);
struct vb2_queue *vq;
int ret;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (vb2_is_busy(vq))
return -EBUSY;
ret = rpivid_try_fmt_vid_out(file, priv, f);
if (ret)
return ret;
ctx->src_fmt = f->fmt.pix;
ctx->dst_fmt_set = 0; // Setting src invalidates dst
vq->subsystem_flags |=
VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
/* Propagate colorspace information to capture. */
copy_color(&ctx->dst_fmt, &f->fmt.pix);
return 0;
}
const struct v4l2_ioctl_ops rpivid_ioctl_ops = {
.vidioc_querycap = rpivid_querycap,
.vidioc_enum_fmt_vid_cap = rpivid_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = rpivid_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = rpivid_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = rpivid_s_fmt_vid_cap,
.vidioc_enum_fmt_vid_out = rpivid_enum_fmt_vid_out,
.vidioc_g_fmt_vid_out = rpivid_g_fmt_vid_out,
.vidioc_try_fmt_vid_out = rpivid_try_fmt_vid_out,
.vidioc_s_fmt_vid_out = rpivid_s_fmt_vid_out,
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
.vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static int rpivid_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
unsigned int *nplanes, unsigned int sizes[],
struct device *alloc_devs[])
{
struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
struct v4l2_pix_format *pix_fmt;
if (V4L2_TYPE_IS_OUTPUT(vq->type))
pix_fmt = &ctx->src_fmt;
else
pix_fmt = &ctx->dst_fmt;
if (*nplanes) {
if (sizes[0] < pix_fmt->sizeimage)
return -EINVAL;
} else {
sizes[0] = pix_fmt->sizeimage;
*nplanes = 1;
}
return 0;
}
static void rpivid_queue_cleanup(struct vb2_queue *vq, u32 state)
{
struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
struct vb2_v4l2_buffer *vbuf;
for (;;) {
if (V4L2_TYPE_IS_OUTPUT(vq->type))
vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
else
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (!vbuf)
return;
v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
&ctx->hdl);
v4l2_m2m_buf_done(vbuf, state);
}
}
static int rpivid_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 rpivid_buf_prepare(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
struct v4l2_pix_format *pix_fmt;
if (V4L2_TYPE_IS_OUTPUT(vq->type))
pix_fmt = &ctx->src_fmt;
else
pix_fmt = &ctx->dst_fmt;
if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
return -EINVAL;
vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
return 0;
}
static int rpivid_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
struct rpivid_dev *dev = ctx->dev;
int ret = 0;
if (ctx->src_fmt.pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
return -EINVAL;
if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->start)
ret = dev->dec_ops->start(ctx);
ret = clk_set_rate(dev->clock, 500 * 1000 * 1000);
if (ret) {
dev_err(dev->dev, "Failed to set clock rate\n");
goto out;
}
ret = clk_prepare_enable(dev->clock);
if (ret)
dev_err(dev->dev, "Failed to enable clock\n");
out:
if (ret)
rpivid_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
return ret;
}
static void rpivid_stop_streaming(struct vb2_queue *vq)
{
struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
struct rpivid_dev *dev = ctx->dev;
if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->stop)
dev->dec_ops->stop(ctx);
rpivid_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
clk_disable_unprepare(dev->clock);
}
static void rpivid_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
static void rpivid_buf_request_complete(struct vb2_buffer *vb)
{
struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
}
static struct vb2_ops rpivid_qops = {
.queue_setup = rpivid_queue_setup,
.buf_prepare = rpivid_buf_prepare,
.buf_queue = rpivid_buf_queue,
.buf_out_validate = rpivid_buf_out_validate,
.buf_request_complete = rpivid_buf_request_complete,
.start_streaming = rpivid_start_streaming,
.stop_streaming = rpivid_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq)
{
struct rpivid_ctx *ctx = priv;
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct rpivid_buffer);
src_vq->min_buffers_needed = 1;
src_vq->ops = &rpivid_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->dev_mutex;
src_vq->dev = ctx->dev->dev;
src_vq->supports_requests = true;
src_vq->requires_requests = true;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct rpivid_buffer);
dst_vq->min_buffers_needed = 1;
dst_vq->ops = &rpivid_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->dev->dev_mutex;
dst_vq->dev = ctx->dev->dev;
return vb2_queue_init(dst_vq);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2020 Raspberry Pi (Trading) Ltd
*
* Based on the Cedrus VPU driver, that is:
*
* Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
* Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
* Copyright (C) 2018 Bootlin
*/
#ifndef _RPIVID_VIDEO_H_
#define _RPIVID_VIDEO_H_
struct rpivid_format {
u32 pixelformat;
u32 directions;
unsigned int capabilities;
};
extern const struct v4l2_ioctl_ops rpivid_ioctl_ops;
int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt);
int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt);
#endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册