290 lines
6.9 KiB
C
Raw Normal View History

2025-05-10 21:58:58 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip CIF Driver
*
* Copyright (C) 2020 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/reset.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-fwnode.h>
#include "dev.h"
static inline struct sditf_priv *to_sditf_priv(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct sditf_priv, sd);
}
static int sditf_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *fi)
{
struct sditf_priv *priv = to_sditf_priv(sd);
struct rkcif_device *cif_dev = priv->cif_dev;
struct v4l2_subdev *sensor_sd;
if (!cif_dev->terminal_sensor.sd)
rkcif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->terminal_sensor.sd) {
sensor_sd = cif_dev->terminal_sensor.sd;
return v4l2_subdev_call(sensor_sd, video, g_frame_interval, fi);
}
return -EINVAL;
}
static int sditf_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *config)
{
struct sditf_priv *priv = to_sditf_priv(sd);
struct rkcif_device *cif_dev = priv->cif_dev;
struct v4l2_subdev *sensor_sd;
if (!cif_dev->active_sensor)
rkcif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->active_sensor) {
sensor_sd = cif_dev->active_sensor->sd;
return v4l2_subdev_call(sensor_sd, video, g_mbus_config, config);
}
return -EINVAL;
}
static int sditf_get_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct sditf_priv *priv = to_sditf_priv(sd);
struct rkcif_device *cif_dev = priv->cif_dev;
struct v4l2_subdev_selection input_sel;
int ret = -EINVAL;
if (!cif_dev->terminal_sensor.sd)
rkcif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->terminal_sensor.sd) {
ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, fmt);
if (ret) {
v4l2_err(&priv->sd,
"%s: get sensor format failed\n", __func__);
return ret;
}
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
fmt->format.width = input_sel.r.width;
fmt->format.height = input_sel.r.height;
}
}
return 0;
}
static int sditf_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
return -EINVAL;
}
static long sditf_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct sditf_priv *priv = to_sditf_priv(sd);
struct rkcif_device *cif_dev = priv->cif_dev;
struct v4l2_subdev *sensor_sd;
if (!cif_dev->terminal_sensor.sd)
rkcif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->terminal_sensor.sd) {
sensor_sd = cif_dev->terminal_sensor.sd;
return v4l2_subdev_call(sensor_sd, core, ioctl, cmd, arg);
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
static long sditf_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
struct sditf_priv *priv = to_sditf_priv(sd);
struct rkcif_device *cif_dev = priv->cif_dev;
struct v4l2_subdev *sensor_sd;
if (!cif_dev->terminal_sensor.sd)
rkcif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->terminal_sensor.sd) {
sensor_sd = cif_dev->terminal_sensor.sd;
return v4l2_subdev_call(sensor_sd, core, compat_ioctl32, cmd, arg);
}
return -EINVAL;
}
#endif
static const struct v4l2_subdev_pad_ops sditf_subdev_pad_ops = {
.set_fmt = sditf_get_set_fmt,
.get_fmt = sditf_get_set_fmt,
.get_selection = sditf_get_selection,
};
static const struct v4l2_subdev_video_ops sditf_video_ops = {
.g_frame_interval = sditf_g_frame_interval,
.g_mbus_config = sditf_g_mbus_config,
};
static const struct v4l2_subdev_core_ops sditf_core_ops = {
.ioctl = sditf_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = sditf_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_ops sditf_subdev_ops = {
.core = &sditf_core_ops,
.video = &sditf_video_ops,
.pad = &sditf_subdev_pad_ops,
};
static int rkcif_sditf_attach_cifdev(struct sditf_priv *sditf)
{
struct device_node *np;
struct platform_device *pdev;
struct rkcif_device *cif_dev;
np = of_parse_phandle(sditf->dev->of_node, "rockchip,cif", 0);
if (!np || !of_device_is_available(np)) {
dev_err(sditf->dev, "failed to get cif dev node\n");
return -ENODEV;
}
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev) {
dev_err(sditf->dev, "failed to get cif dev from node\n");
return -ENODEV;
}
cif_dev = platform_get_drvdata(pdev);
if (!cif_dev) {
dev_err(sditf->dev, "failed attach cif dev\n");
return -EINVAL;
}
cif_dev->sditf = sditf;
sditf->cif_dev = cif_dev;
return 0;
}
static int rkcif_subdev_media_init(struct sditf_priv *priv)
{
struct rkcif_device *cif_dev = priv->cif_dev;
int ret;
priv->pads.flags = MEDIA_PAD_FL_SOURCE;
priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pads);
if (ret < 0)
return ret;
strncpy(priv->sd.name, dev_name(cif_dev->dev), sizeof(priv->sd.name));
return 0;
}
static int rkcif_subdev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct v4l2_subdev *sd;
struct sditf_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
sd = &priv->sd;
v4l2_subdev_init(sd, &sditf_subdev_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "rockchip-cif-sditf");
sd->dev = dev;
platform_set_drvdata(pdev, &sd->entity);
rkcif_sditf_attach_cifdev(priv);
ret = rkcif_subdev_media_init(priv);
if (ret < 0)
return ret;
pm_runtime_enable(&pdev->dev);
return 0;
}
static int rkcif_subdev_remove(struct platform_device *pdev)
{
struct media_entity *me = platform_get_drvdata(pdev);
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
media_entity_cleanup(&sd->entity);
pm_runtime_disable(&pdev->dev);
return 0;
}
static int sditf_runtime_suspend(struct device *dev)
{
return 0;
}
static int sditf_runtime_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops rkcif_subdev_pm_ops = {
SET_RUNTIME_PM_OPS(sditf_runtime_suspend,
sditf_runtime_resume, NULL)
};
static const struct of_device_id rkcif_subdev_match_id[] = {
{
.compatible = "rockchip,rkcif-sditf",
},
{}
};
MODULE_DEVICE_TABLE(of, rkcif_subdev_match_id);
struct platform_driver rkcif_subdev_driver = {
.probe = rkcif_subdev_probe,
.remove = rkcif_subdev_remove,
.driver = {
.name = "rkcif_sditf",
.pm = &rkcif_subdev_pm_ops,
.of_match_table = rkcif_subdev_match_id,
},
};
EXPORT_SYMBOL(rkcif_subdev_driver);
MODULE_AUTHOR("Rockchip Camera/ISP team");
MODULE_DESCRIPTION("Rockchip CIF platform driver");
MODULE_LICENSE("GPL v2");