264 lines
6.6 KiB
C
264 lines
6.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-fh.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-subdev.h>
|
|
#include "dev.h"
|
|
#include "isp_vpss.h"
|
|
|
|
static int rkisp_sditf_get_set_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_state *sd_state,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct rkisp_sditf_device *sditf = v4l2_get_subdevdata(sd);
|
|
struct rkisp_device *dev = sditf->isp;
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
/* get isp out format */
|
|
fmt->pad = RKISP_ISP_PAD_SOURCE_PATH;
|
|
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
return v4l2_subdev_call(&dev->isp_sdev.sd, pad, get_fmt, NULL, fmt);
|
|
}
|
|
|
|
static int rkisp_sditf_s_stream(struct v4l2_subdev *sd, int on)
|
|
{
|
|
struct rkisp_sditf_device *sditf = v4l2_get_subdevdata(sd);
|
|
struct rkisp_device *dev = sditf->isp;
|
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
|
struct rkisp_isp_subdev *isp_sdev = &dev->isp_sdev;
|
|
struct rkisp_stream *stream = &dev->cap_dev.stream[RKISP_STREAM_LDC];
|
|
int ret = 0;
|
|
|
|
if (stream->linked) {
|
|
v4l2_err(sd, "isp to vpss online no support for ldcpath link\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
v4l2_dbg(1, rkisp_debug, sd, "%s %d\n", __func__, on);
|
|
|
|
mutex_lock(&hw->dev_lock);
|
|
if (on) {
|
|
atomic_inc(&dev->cap_dev.refcnt);
|
|
ret = dev->pipe.open(&dev->pipe, &isp_sdev->sd.entity, true);
|
|
if (ret < 0)
|
|
goto refcnt_dec;
|
|
ret = dev->pipe.set_stream(&dev->pipe, true);
|
|
if (ret < 0)
|
|
goto pipe_close;
|
|
sditf->is_on = true;
|
|
dev->irq_ends_mask |= ISP_FRAME_VPSS;
|
|
goto unlock;
|
|
}
|
|
sditf->is_on = false;
|
|
dev->irq_ends_mask &= ~ISP_FRAME_VPSS;
|
|
dev->pipe.set_stream(&dev->pipe, false);
|
|
pipe_close:
|
|
dev->pipe.close(&dev->pipe);
|
|
refcnt_dec:
|
|
atomic_dec(&dev->cap_dev.refcnt);
|
|
unlock:
|
|
mutex_unlock(&hw->dev_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int rkisp_sditf_s_power(struct v4l2_subdev *sd, int on)
|
|
{
|
|
struct rkisp_sditf_device *sditf = v4l2_get_subdevdata(sd);
|
|
struct rkisp_device *dev = sditf->isp;
|
|
struct rkisp_stream *stream = &dev->cap_dev.stream[0];
|
|
int ret = 0;
|
|
|
|
v4l2_dbg(1, rkisp_debug, sd, "%s %d\n", __func__, on);
|
|
|
|
if (on) {
|
|
sditf->remote_sd = v4l2_get_subdev_hostdata(sd);
|
|
ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity);
|
|
} else {
|
|
v4l2_pipeline_pm_put(&stream->vnode.vdev.entity);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void rkisp_sditf_reset_notify_vpss(struct rkisp_device *dev)
|
|
{
|
|
struct rkisp_sditf_device *sditf = dev->sditf_dev;
|
|
|
|
v4l2_info(&dev->v4l2_dev, "%s\n", __func__);
|
|
v4l2_subdev_call(sditf->remote_sd, core, ioctl, RKISP_VPSS_RESET_NOTIFY_VPSS, NULL);
|
|
}
|
|
|
|
void rkisp_sditf_sof(struct rkisp_device *dev, u32 irq)
|
|
{
|
|
struct rkisp_sditf_device *sditf = dev->sditf_dev;
|
|
struct rkisp_vpss_sof info;
|
|
|
|
if (!sditf || !sditf->is_on || !sditf->remote_sd)
|
|
return;
|
|
info.irq = irq;
|
|
rkisp_dmarx_get_frame(dev, &info.seq, NULL, &info.timestamp, true);
|
|
info.unite_index = dev->unite_index;
|
|
v4l2_subdev_call(sditf->remote_sd, core, ioctl, RKISP_VPSS_CMD_SOF, &info);
|
|
}
|
|
|
|
static long rkisp_sditf_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|
{
|
|
struct rkisp_sditf_device *sditf = v4l2_get_subdevdata(sd);
|
|
long ret = 0;
|
|
|
|
switch (cmd) {
|
|
case RKISP_VPSS_CMD_EOF:
|
|
if (*(int *)arg) {
|
|
sditf->is_on = false;
|
|
sditf->isp->irq_ends_mask &= ~ISP_FRAME_VPSS;
|
|
}
|
|
rkisp_check_idle(sditf->isp, ISP_FRAME_VPSS);
|
|
break;
|
|
case RKISP_VPSS_GET_UNITE_MODE:
|
|
if (sditf->isp->unite_div == ISP_UNITE_DIV2)
|
|
*(unsigned int *)arg = sditf->isp->unite_div;
|
|
else
|
|
*(unsigned int *)arg = 0;
|
|
break;
|
|
default:
|
|
ret = -ENOIOCTLCMD;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const struct v4l2_subdev_pad_ops sditf_pad_ops = {
|
|
.set_fmt = rkisp_sditf_get_set_fmt,
|
|
.get_fmt = rkisp_sditf_get_set_fmt,
|
|
};
|
|
|
|
static const struct v4l2_subdev_video_ops sditf_video_ops = {
|
|
.s_stream = rkisp_sditf_s_stream,
|
|
};
|
|
|
|
static const struct v4l2_subdev_core_ops sditf_core_ops = {
|
|
.s_power = rkisp_sditf_s_power,
|
|
.ioctl = rkisp_sditf_ioctl,
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops sditf_subdev_ops = {
|
|
.core = &sditf_core_ops,
|
|
.video = &sditf_video_ops,
|
|
.pad = &sditf_pad_ops,
|
|
};
|
|
|
|
static int rkisp_sditf_notifier(struct rkisp_sditf_device *sditf)
|
|
{
|
|
struct v4l2_async_notifier *ntf = &sditf->notifier;
|
|
int ret;
|
|
|
|
v4l2_async_nf_init(ntf);
|
|
ret = v4l2_async_subdev_nf_register(&sditf->sd, ntf);
|
|
if (ret) {
|
|
v4l2_async_nf_cleanup(ntf);
|
|
dev_err(sditf->dev, "failed to register async notifier:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return v4l2_async_register_subdev(&sditf->sd);
|
|
}
|
|
|
|
static int rkisp_sditf_attach(struct rkisp_sditf_device *sditf)
|
|
{
|
|
struct rkisp_device *isp;
|
|
struct platform_device *pdev;
|
|
struct device_node *np;
|
|
|
|
np = of_parse_phandle(sditf->dev->of_node, "rockchip,isp", 0);
|
|
if (!np || !of_device_is_available(np)) {
|
|
dev_err(sditf->dev, "failed to get isp node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pdev = of_find_device_by_node(np);
|
|
of_node_put(np);
|
|
if (!pdev) {
|
|
dev_err(sditf->dev, "failed to get isp from node\n");
|
|
return -ENODEV;
|
|
}
|
|
isp = platform_get_drvdata(pdev);
|
|
if (!isp) {
|
|
dev_err(sditf->dev, "failed to attach isp\n");
|
|
return -EINVAL;
|
|
}
|
|
isp->sditf_dev = sditf;
|
|
sditf->isp = isp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rkisp_sditf_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct rkisp_sditf_device *sditf;
|
|
struct v4l2_subdev *sd;
|
|
int ret;
|
|
|
|
sditf = devm_kzalloc(dev, sizeof(*sditf), GFP_KERNEL);
|
|
if (!sditf)
|
|
return -ENOMEM;
|
|
dev_set_drvdata(dev, sditf);
|
|
sditf->dev = dev;
|
|
sd = &sditf->sd;
|
|
v4l2_subdev_init(sd, &sditf_subdev_ops);
|
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
|
|
strscpy(sd->name, dev_name(dev), sizeof(sd->name));
|
|
|
|
strscpy(sd->name, dev_name(dev), sizeof(sd->name));
|
|
v4l2_set_subdevdata(sd, sditf);
|
|
sd->dev = dev;
|
|
|
|
ret = rkisp_sditf_attach(sditf);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
sditf->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
ret = media_entity_pads_init(&sd->entity, 1, &sditf->pad);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return rkisp_sditf_notifier(sditf);
|
|
}
|
|
|
|
static int rkisp_sditf_remove(struct platform_device *pdev)
|
|
{
|
|
struct rkisp_sditf_device *sditf = platform_get_drvdata(pdev);
|
|
struct v4l2_subdev *sd = &sditf->sd;
|
|
|
|
v4l2_async_nf_unregister(&sditf->notifier);
|
|
v4l2_async_nf_cleanup(&sditf->notifier);
|
|
|
|
media_entity_cleanup(&sd->entity);
|
|
v4l2_async_unregister_subdev(sd);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id rkisp_sditf_of_match[] = {
|
|
{
|
|
.compatible = "rockchip,rkisp-sditf",
|
|
},
|
|
{},
|
|
};
|
|
|
|
struct platform_driver rkisp_sditf_drv = {
|
|
.probe = rkisp_sditf_probe,
|
|
.remove = rkisp_sditf_remove,
|
|
.driver = {
|
|
.name = "rkisp_sditf",
|
|
.of_match_table = of_match_ptr(rkisp_sditf_of_match),
|
|
},
|
|
};
|
|
EXPORT_SYMBOL(rkisp_sditf_drv);
|