media: rockchip/vpu: Open-code media controller register
In preparation to support decoders, using a single memory-to-memory device, we need to roll our own media controller entities registration. To do that, we define a rockchip_vpu_func object that embeds the video_device object plus all the elements that are needed to attach this vdev to the media device. Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
parent
0a4f091c12
commit
b1c6cc64dd
@ -71,12 +71,47 @@ enum rockchip_vpu_codec_mode {
|
|||||||
RK_VPU_MODE_JPEG_ENC,
|
RK_VPU_MODE_JPEG_ENC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct rockchip_vpu_func - rockchip VPU functionality
|
||||||
|
*
|
||||||
|
* @id: processing functionality ID (can be
|
||||||
|
* %MEDIA_ENT_F_PROC_VIDEO_ENCODER or
|
||||||
|
* %MEDIA_ENT_F_PROC_VIDEO_DECODER)
|
||||||
|
* @vdev: &struct video_device that exposes the encoder or
|
||||||
|
* decoder functionality
|
||||||
|
* @source_pad: &struct media_pad with the source pad.
|
||||||
|
* @sink: &struct media_entity pointer with the sink entity
|
||||||
|
* @sink_pad: &struct media_pad with the sink pad.
|
||||||
|
* @proc: &struct media_entity pointer with the M2M device itself.
|
||||||
|
* @proc_pads: &struct media_pad with the @proc pads.
|
||||||
|
* @intf_devnode: &struct media_intf devnode pointer with the interface
|
||||||
|
* with controls the M2M device.
|
||||||
|
*
|
||||||
|
* Contains everything needed to attach the video device to the media device.
|
||||||
|
*/
|
||||||
|
struct rockchip_vpu_func {
|
||||||
|
unsigned int id;
|
||||||
|
struct video_device vdev;
|
||||||
|
struct media_pad source_pad;
|
||||||
|
struct media_entity sink;
|
||||||
|
struct media_pad sink_pad;
|
||||||
|
struct media_entity proc;
|
||||||
|
struct media_pad proc_pads[2];
|
||||||
|
struct media_intf_devnode *intf_devnode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct rockchip_vpu_func *
|
||||||
|
rockchip_vpu_vdev_to_func(struct video_device *vdev)
|
||||||
|
{
|
||||||
|
return container_of(vdev, struct rockchip_vpu_func, vdev);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct rockchip_vpu_dev - driver data
|
* struct rockchip_vpu_dev - driver data
|
||||||
* @v4l2_dev: V4L2 device to register video devices for.
|
* @v4l2_dev: V4L2 device to register video devices for.
|
||||||
* @m2m_dev: mem2mem device associated to this device.
|
* @m2m_dev: mem2mem device associated to this device.
|
||||||
* @mdev: media device associated to this device.
|
* @mdev: media device associated to this device.
|
||||||
* @vfd_enc: Video device for encoder.
|
* @encoder: encoder functionality.
|
||||||
* @pdev: Pointer to VPU platform device.
|
* @pdev: Pointer to VPU platform device.
|
||||||
* @dev: Pointer to device for convenient logging using
|
* @dev: Pointer to device for convenient logging using
|
||||||
* dev_ macros.
|
* dev_ macros.
|
||||||
@ -93,7 +128,7 @@ struct rockchip_vpu_dev {
|
|||||||
struct v4l2_device v4l2_dev;
|
struct v4l2_device v4l2_dev;
|
||||||
struct v4l2_m2m_dev *m2m_dev;
|
struct v4l2_m2m_dev *m2m_dev;
|
||||||
struct media_device mdev;
|
struct media_device mdev;
|
||||||
struct video_device *vfd_enc;
|
struct rockchip_vpu_func *encoder;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct clk_bulk_data clocks[ROCKCHIP_VPU_MAX_CLOCKS];
|
struct clk_bulk_data clocks[ROCKCHIP_VPU_MAX_CLOCKS];
|
||||||
|
@ -239,6 +239,7 @@ static int rockchip_vpu_open(struct file *filp)
|
|||||||
{
|
{
|
||||||
struct rockchip_vpu_dev *vpu = video_drvdata(filp);
|
struct rockchip_vpu_dev *vpu = video_drvdata(filp);
|
||||||
struct video_device *vdev = video_devdata(filp);
|
struct video_device *vdev = video_devdata(filp);
|
||||||
|
struct rockchip_vpu_func *func = rockchip_vpu_vdev_to_func(vdev);
|
||||||
struct rockchip_vpu_ctx *ctx;
|
struct rockchip_vpu_ctx *ctx;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -256,7 +257,7 @@ static int rockchip_vpu_open(struct file *filp)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ctx->dev = vpu;
|
ctx->dev = vpu;
|
||||||
if (vdev == vpu->vfd_enc)
|
if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER)
|
||||||
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx,
|
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx,
|
||||||
&enc_queue_init);
|
&enc_queue_init);
|
||||||
else
|
else
|
||||||
@ -324,52 +325,206 @@ static const struct of_device_id of_rockchip_vpu_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, of_rockchip_vpu_match);
|
MODULE_DEVICE_TABLE(of, of_rockchip_vpu_match);
|
||||||
|
|
||||||
static int rockchip_vpu_video_device_register(struct rockchip_vpu_dev *vpu)
|
static int rockchip_vpu_register_entity(struct media_device *mdev,
|
||||||
|
struct media_entity *entity,
|
||||||
|
const char *entity_name,
|
||||||
|
struct media_pad *pads, int num_pads,
|
||||||
|
int function,
|
||||||
|
struct video_device *vdev)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
entity->obj_type = MEDIA_ENTITY_TYPE_BASE;
|
||||||
|
if (function == MEDIA_ENT_F_IO_V4L) {
|
||||||
|
entity->info.dev.major = VIDEO_MAJOR;
|
||||||
|
entity->info.dev.minor = vdev->minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = devm_kasprintf(mdev->dev, GFP_KERNEL, "%s-%s", vdev->name,
|
||||||
|
entity_name);
|
||||||
|
if (!name)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
entity->name = name;
|
||||||
|
entity->function = function;
|
||||||
|
|
||||||
|
ret = media_entity_pads_init(entity, num_pads, pads);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = media_device_register_entity(mdev, entity);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_attach_func(struct rockchip_vpu_dev *vpu,
|
||||||
|
struct rockchip_vpu_func *func)
|
||||||
|
{
|
||||||
|
struct media_device *mdev = &vpu->mdev;
|
||||||
|
struct media_link *link;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Create the three encoder entities with their pads */
|
||||||
|
func->source_pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
|
ret = rockchip_vpu_register_entity(mdev, &func->vdev.entity,
|
||||||
|
"source", &func->source_pad, 1,
|
||||||
|
MEDIA_ENT_F_IO_V4L, &func->vdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
func->proc_pads[0].flags = MEDIA_PAD_FL_SINK;
|
||||||
|
func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE;
|
||||||
|
ret = rockchip_vpu_register_entity(mdev, &func->proc, "proc",
|
||||||
|
func->proc_pads, 2, func->id,
|
||||||
|
&func->vdev);
|
||||||
|
if (ret)
|
||||||
|
goto err_rel_entity0;
|
||||||
|
|
||||||
|
func->sink_pad.flags = MEDIA_PAD_FL_SINK;
|
||||||
|
ret = rockchip_vpu_register_entity(mdev, &func->sink, "sink",
|
||||||
|
&func->sink_pad, 1,
|
||||||
|
MEDIA_ENT_F_IO_V4L, &func->vdev);
|
||||||
|
if (ret)
|
||||||
|
goto err_rel_entity1;
|
||||||
|
|
||||||
|
/* Connect the three entities */
|
||||||
|
ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 1,
|
||||||
|
MEDIA_LNK_FL_IMMUTABLE |
|
||||||
|
MEDIA_LNK_FL_ENABLED);
|
||||||
|
if (ret)
|
||||||
|
goto err_rel_entity2;
|
||||||
|
|
||||||
|
ret = media_create_pad_link(&func->proc, 0, &func->sink, 0,
|
||||||
|
MEDIA_LNK_FL_IMMUTABLE |
|
||||||
|
MEDIA_LNK_FL_ENABLED);
|
||||||
|
if (ret)
|
||||||
|
goto err_rm_links0;
|
||||||
|
|
||||||
|
/* Create video interface */
|
||||||
|
func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO,
|
||||||
|
0, VIDEO_MAJOR,
|
||||||
|
func->vdev.minor);
|
||||||
|
if (!func->intf_devnode) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_rm_links1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect the two DMA engines to the interface */
|
||||||
|
link = media_create_intf_link(&func->vdev.entity,
|
||||||
|
&func->intf_devnode->intf,
|
||||||
|
MEDIA_LNK_FL_IMMUTABLE |
|
||||||
|
MEDIA_LNK_FL_ENABLED);
|
||||||
|
if (!link) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_rm_devnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
link = media_create_intf_link(&func->sink, &func->intf_devnode->intf,
|
||||||
|
MEDIA_LNK_FL_IMMUTABLE |
|
||||||
|
MEDIA_LNK_FL_ENABLED);
|
||||||
|
if (!link) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_rm_devnode;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_rm_devnode:
|
||||||
|
media_devnode_remove(func->intf_devnode);
|
||||||
|
|
||||||
|
err_rm_links1:
|
||||||
|
media_entity_remove_links(&func->sink);
|
||||||
|
|
||||||
|
err_rm_links0:
|
||||||
|
media_entity_remove_links(&func->proc);
|
||||||
|
media_entity_remove_links(&func->vdev.entity);
|
||||||
|
|
||||||
|
err_rel_entity2:
|
||||||
|
media_device_unregister_entity(&func->sink);
|
||||||
|
|
||||||
|
err_rel_entity1:
|
||||||
|
media_device_unregister_entity(&func->proc);
|
||||||
|
|
||||||
|
err_rel_entity0:
|
||||||
|
media_device_unregister_entity(&func->vdev.entity);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_detach_func(struct rockchip_vpu_func *func)
|
||||||
|
{
|
||||||
|
media_devnode_remove(func->intf_devnode);
|
||||||
|
media_entity_remove_links(&func->sink);
|
||||||
|
media_entity_remove_links(&func->proc);
|
||||||
|
media_entity_remove_links(&func->vdev.entity);
|
||||||
|
media_device_unregister_entity(&func->sink);
|
||||||
|
media_device_unregister_entity(&func->proc);
|
||||||
|
media_device_unregister_entity(&func->vdev.entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_vpu_add_enc_func(struct rockchip_vpu_dev *vpu)
|
||||||
{
|
{
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
struct rockchip_vpu_func *func;
|
||||||
struct video_device *vfd;
|
struct video_device *vfd;
|
||||||
int function, ret;
|
int ret;
|
||||||
|
|
||||||
match = of_match_node(of_rockchip_vpu_match, vpu->dev->of_node);
|
match = of_match_node(of_rockchip_vpu_match, vpu->dev->of_node);
|
||||||
vfd = video_device_alloc();
|
func = devm_kzalloc(vpu->dev, sizeof(*func), GFP_KERNEL);
|
||||||
if (!vfd) {
|
if (!func) {
|
||||||
v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
|
v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func->id = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
|
||||||
|
|
||||||
|
vfd = &func->vdev;
|
||||||
vfd->fops = &rockchip_vpu_fops;
|
vfd->fops = &rockchip_vpu_fops;
|
||||||
vfd->release = video_device_release;
|
vfd->release = video_device_release_empty;
|
||||||
vfd->lock = &vpu->vpu_mutex;
|
vfd->lock = &vpu->vpu_mutex;
|
||||||
vfd->v4l2_dev = &vpu->v4l2_dev;
|
vfd->v4l2_dev = &vpu->v4l2_dev;
|
||||||
vfd->vfl_dir = VFL_DIR_M2M;
|
vfd->vfl_dir = VFL_DIR_M2M;
|
||||||
vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
|
vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
|
||||||
vfd->ioctl_ops = &rockchip_vpu_enc_ioctl_ops;
|
vfd->ioctl_ops = &rockchip_vpu_enc_ioctl_ops;
|
||||||
snprintf(vfd->name, sizeof(vfd->name), "%s-enc", match->compatible);
|
snprintf(vfd->name, sizeof(vfd->name), "%s-enc", match->compatible);
|
||||||
vpu->vfd_enc = vfd;
|
|
||||||
|
vpu->encoder = func;
|
||||||
video_set_drvdata(vfd, vpu);
|
video_set_drvdata(vfd, vpu);
|
||||||
|
|
||||||
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
|
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
|
v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
|
||||||
goto err_free_dev;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = rockchip_attach_func(vpu, func);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(&vpu->v4l2_dev,
|
||||||
|
"Failed to attach functionality to the media device\n");
|
||||||
|
goto err_unreg_dev;
|
||||||
|
}
|
||||||
|
|
||||||
v4l2_info(&vpu->v4l2_dev, "registered as /dev/video%d\n", vfd->num);
|
v4l2_info(&vpu->v4l2_dev, "registered as /dev/video%d\n", vfd->num);
|
||||||
|
|
||||||
function = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
|
|
||||||
ret = v4l2_m2m_register_media_controller(vpu->m2m_dev, vfd, function);
|
|
||||||
if (ret) {
|
|
||||||
v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem media controller\n");
|
|
||||||
goto err_unreg_video;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_unreg_video:
|
err_unreg_dev:
|
||||||
video_unregister_device(vfd);
|
video_unregister_device(vfd);
|
||||||
err_free_dev:
|
|
||||||
video_device_release(vfd);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rockchip_vpu_remove_enc_func(struct rockchip_vpu_dev *vpu)
|
||||||
|
{
|
||||||
|
struct rockchip_vpu_func *func = vpu->encoder;
|
||||||
|
|
||||||
|
if (!func)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rockchip_detach_func(func);
|
||||||
|
video_unregister_device(&func->vdev);
|
||||||
|
}
|
||||||
|
|
||||||
static int rockchip_vpu_probe(struct platform_device *pdev)
|
static int rockchip_vpu_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
@ -464,7 +619,7 @@ static int rockchip_vpu_probe(struct platform_device *pdev)
|
|||||||
media_device_init(&vpu->mdev);
|
media_device_init(&vpu->mdev);
|
||||||
vpu->v4l2_dev.mdev = &vpu->mdev;
|
vpu->v4l2_dev.mdev = &vpu->mdev;
|
||||||
|
|
||||||
ret = rockchip_vpu_video_device_register(vpu);
|
ret = rockchip_vpu_add_enc_func(vpu);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to register encoder\n");
|
dev_err(&pdev->dev, "Failed to register encoder\n");
|
||||||
goto err_m2m_rel;
|
goto err_m2m_rel;
|
||||||
@ -473,15 +628,13 @@ static int rockchip_vpu_probe(struct platform_device *pdev)
|
|||||||
ret = media_device_register(&vpu->mdev);
|
ret = media_device_register(&vpu->mdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n");
|
v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n");
|
||||||
goto err_video_dev_unreg;
|
goto err_rm_enc_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err_video_dev_unreg:
|
|
||||||
if (vpu->vfd_enc) {
|
err_rm_enc_func:
|
||||||
v4l2_m2m_unregister_media_controller(vpu->m2m_dev);
|
rockchip_vpu_remove_enc_func(vpu);
|
||||||
video_unregister_device(vpu->vfd_enc);
|
|
||||||
video_device_release(vpu->vfd_enc);
|
|
||||||
}
|
|
||||||
err_m2m_rel:
|
err_m2m_rel:
|
||||||
media_device_cleanup(&vpu->mdev);
|
media_device_cleanup(&vpu->mdev);
|
||||||
v4l2_m2m_release(vpu->m2m_dev);
|
v4l2_m2m_release(vpu->m2m_dev);
|
||||||
@ -501,11 +654,7 @@ static int rockchip_vpu_remove(struct platform_device *pdev)
|
|||||||
v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name);
|
v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name);
|
||||||
|
|
||||||
media_device_unregister(&vpu->mdev);
|
media_device_unregister(&vpu->mdev);
|
||||||
if (vpu->vfd_enc) {
|
rockchip_vpu_remove_enc_func(vpu);
|
||||||
v4l2_m2m_unregister_media_controller(vpu->m2m_dev);
|
|
||||||
video_unregister_device(vpu->vfd_enc);
|
|
||||||
video_device_release(vpu->vfd_enc);
|
|
||||||
}
|
|
||||||
media_device_cleanup(&vpu->mdev);
|
media_device_cleanup(&vpu->mdev);
|
||||||
v4l2_m2m_release(vpu->m2m_dev);
|
v4l2_m2m_release(vpu->m2m_dev);
|
||||||
v4l2_device_unregister(&vpu->v4l2_dev);
|
v4l2_device_unregister(&vpu->v4l2_dev);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user