Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 48 additions & 170 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/*
* Raspberry Pi HEVC driver
*
* Copyright (C) 2024 Raspberry Pi Ltd
* Copyright (C) 2025 Raspberry Pi Ltd
*
* Based on the Cedrus VPU driver, that is:
*
Expand All @@ -25,6 +25,10 @@
#include "hevc_d_video.h"
#include "hevc_d_hw.h"

int hevc_d_v4l2_debug;
module_param_named(debug, hevc_d_v4l2_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level 0-2");

/*
* Default /dev/videoN node number.
* Deliberately avoid the very low numbers as these are often taken by webcams
Expand All @@ -34,75 +38,41 @@ static int video_nr = 19;
module_param(video_nr, int, 0644);
MODULE_PARM_DESC(video_nr, "decoder video device number");

static const struct hevc_d_control hevc_d_ctrls[] = {
static const struct v4l2_ctrl_config hevc_d_ctrls[] = {
{
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_SPS,
.ops = &hevc_d_hevc_sps_ctrl_ops,
},
.required = false,
.id = V4L2_CID_STATELESS_HEVC_SPS,
.ops = &hevc_d_hevc_sps_ctrl_ops,
}, {
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_PPS,
.ops = &hevc_d_hevc_pps_ctrl_ops,
},
.required = false,
.id = V4L2_CID_STATELESS_HEVC_PPS,
.ops = &hevc_d_hevc_pps_ctrl_ops,
}, {
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
},
.required = false,
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
}, {
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
},
.required = true,
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
}, {
.cfg = {
.name = "Slice param array",
.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
.type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS,
.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY,
.dims = { 0x1000 },
},
.required = true,
.name = "Slice param array",
.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
.type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS,
.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY,
.dims = { 600 },
}, {
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
},
.required = false,
.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
}, {
.cfg = {
.id = V4L2_CID_STATELESS_HEVC_START_CODE,
.min = V4L2_STATELESS_HEVC_START_CODE_NONE,
.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
.def = V4L2_STATELESS_HEVC_START_CODE_NONE,
},
.required = false,
.id = V4L2_CID_STATELESS_HEVC_START_CODE,
.min = V4L2_STATELESS_HEVC_START_CODE_NONE,
.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
.def = V4L2_STATELESS_HEVC_START_CODE_NONE,
},
};

#define HEVC_D_CTRLS_COUNT ARRAY_SIZE(hevc_d_ctrls)

struct v4l2_ctrl *hevc_d_find_ctrl(struct hevc_d_ctx *ctx, u32 id)
{
unsigned int i;

for (i = 0; i < HEVC_D_CTRLS_COUNT; i++)
if (ctx->ctrls[i]->id == id)
return ctx->ctrls[i];

return NULL;
}

void *hevc_d_find_control_data(struct hevc_d_ctx *ctx, u32 id)
{
struct v4l2_ctrl *const ctrl = hevc_d_find_ctrl(ctx, id);
struct v4l2_ctrl *const ctrl = v4l2_ctrl_find(ctx->fh.ctrl_handler, id);

return !ctrl ? NULL : ctrl->p_cur.p;
return ctrl ? ctrl->p_cur.p : NULL;
}

static int hevc_d_init_ctrls(struct hevc_d_dev *dev, struct hevc_d_ctx *ctx)
Expand All @@ -111,31 +81,23 @@ static int hevc_d_init_ctrls(struct hevc_d_dev *dev, struct hevc_d_ctx *ctx)
struct v4l2_ctrl *ctrl;
unsigned int i;

v4l2_ctrl_handler_init(hdl, HEVC_D_CTRLS_COUNT);
v4l2_ctrl_handler_init(hdl, ARRAY_SIZE(hevc_d_ctrls));
if (hdl->error) {
v4l2_err(&dev->v4l2_dev,
"Failed to initialize control handler\n");
return hdl->error;
}

ctx->ctrls = kzalloc(HEVC_D_CTRLS_COUNT * sizeof(ctrl), GFP_KERNEL);
if (!ctx->ctrls)
return -ENOMEM;

for (i = 0; i < HEVC_D_CTRLS_COUNT; i++) {
ctrl = v4l2_ctrl_new_custom(hdl, &hevc_d_ctrls[i].cfg,
ctx);
for (i = 0; i < ARRAY_SIZE(hevc_d_ctrls); i++) {
ctrl = v4l2_ctrl_new_custom(hdl, &hevc_d_ctrls[i], ctx);
if (hdl->error) {
v4l2_err(&dev->v4l2_dev,
"Failed to create new custom control id=%#x\n",
hevc_d_ctrls[i].cfg.id);
hevc_d_ctrls[i].id);

v4l2_ctrl_handler_free(hdl);
kfree(ctx->ctrls);
return hdl->error;
}

ctx->ctrls[i] = ctrl;
}

ctx->fh.ctrl_handler = hdl;
Expand All @@ -144,146 +106,64 @@ static int hevc_d_init_ctrls(struct hevc_d_dev *dev, struct hevc_d_ctx *ctx)
return 0;
}

static int hevc_d_request_validate(struct media_request *req)
{
struct media_request_object *obj;
struct v4l2_ctrl_handler *parent_hdl, *hdl;
struct hevc_d_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 < HEVC_D_CTRLS_COUNT; i++) {
if (!hevc_d_ctrls[i].required)
continue;

ctrl_test =
v4l2_ctrl_request_hdl_ctrl_find(hdl,
hevc_d_ctrls[i].cfg.id);
if (!ctrl_test) {
v4l2_info(&ctx->dev->v4l2_dev,
"Missing required codec control %d: id=%#x\n",
i, hevc_d_ctrls[i].cfg.id);
v4l2_ctrl_request_hdl_put(hdl);
return -ENOENT;
}
}

v4l2_ctrl_request_hdl_put(hdl);

return vb2_request_validate(req);
}

static int hevc_d_open(struct file *file)
{
struct hevc_d_dev *dev = video_drvdata(file);
struct hevc_d_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);
ret = -ENOMEM;
goto err_unlock;
}
if (!ctx)
return -ENOMEM;

mutex_init(&ctx->ctx_mutex);

v4l2_fh_init(&ctx->fh, video_devdata(file));
file->private_data = &ctx->fh;
ctx->dev = dev;

ret = hevc_d_init_ctrls(dev, ctx);
if (ret)
goto err_free;

ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
&hevc_d_queue_init);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
goto err_ctrls;
goto err_free;
}

/* The only bit of format info that we can guess now is H265 src
* Everything else we need more info for
*/
hevc_d_prepare_src_format(&ctx->src_fmt);

v4l2_fh_add(&ctx->fh, file);

mutex_unlock(&dev->dev_mutex);
ret = hevc_d_init_ctrls(dev, ctx);
if (ret)
goto err_ctx;

v4l2_fh_add(&ctx->fh, file);
return 0;

err_ctrls:
v4l2_ctrl_handler_free(&ctx->hdl);
kfree(ctx->ctrls);
err_ctx:
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
err_free:
mutex_destroy(&ctx->ctx_mutex);
kfree(ctx);
err_unlock:
mutex_unlock(&dev->dev_mutex);

return ret;
}

static int hevc_d_release(struct file *file)
{
struct hevc_d_dev *dev = video_drvdata(file);
struct hevc_d_ctx *ctx = container_of(file->private_data,
struct hevc_d_ctx, fh);

mutex_lock(&dev->dev_mutex);

v4l2_fh_del(&ctx->fh, file);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);

v4l2_ctrl_handler_free(&ctx->hdl);
kfree(ctx->ctrls);

v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);

v4l2_fh_exit(&ctx->fh);
mutex_destroy(&ctx->ctx_mutex);

kfree(ctx);

mutex_unlock(&dev->dev_mutex);

return 0;
}

Expand Down Expand Up @@ -317,7 +197,7 @@ static const struct v4l2_m2m_ops hevc_d_m2m_ops = {
};

static const struct media_device_ops hevc_d_m2m_media_ops = {
.req_validate = hevc_d_request_validate,
.req_validate = vb2_request_validate,
.req_queue = hevc_d_media_req_queue,
};

Expand All @@ -335,7 +215,6 @@ static int hevc_d_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->pdev = pdev;

ret = 0;
ret = hevc_d_hw_probe(dev);
if (ret) {
dev_err(&pdev->dev, "Failed to probe hardware - %d\n", ret);
Expand Down Expand Up @@ -425,11 +304,9 @@ static void hevc_d_remove(struct platform_device *pdev)
{
struct hevc_d_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);
}
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);
Expand All @@ -439,7 +316,8 @@ static void hevc_d_remove(struct platform_device *pdev)
}

static const struct of_device_id hevc_d_dt_match[] = {
{ .compatible = "raspberrypi,hevc-dec", },
{ .compatible = "brcm,bcm2711-hevc-dec", },
{ .compatible = "brcm,bcm2712-hevc-dec", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hevc_d_dt_match);
Expand Down
Loading
Loading