From a45d41dcd5677b876aa907d7b27744a1896984a6 Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Thu, 6 Aug 2020 20:17:53 +0800 Subject: [PATCH] media: rockchip: cif: add subdev as interface for isp Signed-off-by: Hu Kejun Change-Id: Iebf7545485a84a7817c76f9b3c81884aeac5a974 --- drivers/media/platform/rockchip/cif/Makefile | 3 +- drivers/media/platform/rockchip/cif/dev.c | 10 +- drivers/media/platform/rockchip/cif/dev.h | 3 + drivers/media/platform/rockchip/cif/hw.c | 4 +- .../media/platform/rockchip/cif/subdev-itf.c | 268 ++++++++++++++++++ .../media/platform/rockchip/cif/subdev-itf.h | 30 ++ 6 files changed, 308 insertions(+), 10 deletions(-) create mode 100644 drivers/media/platform/rockchip/cif/subdev-itf.c create mode 100644 drivers/media/platform/rockchip/cif/subdev-itf.h diff --git a/drivers/media/platform/rockchip/cif/Makefile b/drivers/media/platform/rockchip/cif/Makefile index 267db8b55f3e..c6e1687939bd 100644 --- a/drivers/media/platform/rockchip/cif/Makefile +++ b/drivers/media/platform/rockchip/cif/Makefile @@ -4,4 +4,5 @@ video_rkcif-objs += dev.o \ capture.o \ mipi-csi2.o \ cif-luma.o \ - hw.o + hw.o \ + subdev-itf.o diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c index 91e23019d897..e950dea16b50 100644 --- a/drivers/media/platform/rockchip/cif/dev.c +++ b/drivers/media/platform/rockchip/cif/dev.c @@ -676,7 +676,6 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int { struct device *dev = cif_dev->dev; struct v4l2_device *v4l2_dev; - char name[V4L2_DEVICE_NAME_SIZE] = {'\0'}; int ret; cif_dev->hdr.mode = NO_HDR; @@ -710,17 +709,12 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int cif_dev->workmode = RKCIF_WORKMODE_PINGPONG; #endif - if (cif_dev->chip_id == CHIP_RV1126_CIF_LITE) - strncpy(name, "rkcif_lite", strlen("rkcif_lite")); - else - strncpy(name, "rkcif", strlen("rkcif")); - - strlcpy(cif_dev->media_dev.model, name, + strlcpy(cif_dev->media_dev.model, dev_name(dev), sizeof(cif_dev->media_dev.model)); cif_dev->media_dev.dev = dev; v4l2_dev = &cif_dev->v4l2_dev; v4l2_dev->mdev = &cif_dev->media_dev; - strlcpy(v4l2_dev->name, name, sizeof(v4l2_dev->name)); + strlcpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name)); ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev); if (ret < 0) diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index 9a7ce89bd087..2f44909ef821 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -21,6 +21,7 @@ #include "cif-luma.h" #include "mipi-csi2.h" #include "hw.h" +#include "subdev-itf.h" #define CIF_DRIVER_NAME "rkcif" #define CIF_VIDEODEVICE_NAME "stream_cif" @@ -370,6 +371,8 @@ struct rkcif_device { struct rkcif_hw *hw_dev; irqreturn_t (*isr_hdl)(int irq, struct rkcif_device *cif_dev); int inf_id; + + struct sditf_priv *sditf; }; extern struct platform_driver rkcif_plat_drv; diff --git a/drivers/media/platform/rockchip/cif/hw.c b/drivers/media/platform/rockchip/cif/hw.c index a701ed900246..2643453029a8 100644 --- a/drivers/media/platform/rockchip/cif/hw.c +++ b/drivers/media/platform/rockchip/cif/hw.c @@ -735,8 +735,10 @@ static int rkcif_plat_probe(struct platform_device *pdev) rkcif_hw_soft_reset(cif_hw, true); if (data->chip_id == CHIP_RK1808_CIF || - data->chip_id == CHIP_RV1126_CIF) + data->chip_id == CHIP_RV1126_CIF) { platform_driver_register(&rkcif_plat_drv); + platform_driver_register(&rkcif_subdev_driver); + } return 0; } diff --git a/drivers/media/platform/rockchip/cif/subdev-itf.c b/drivers/media/platform/rockchip/cif/subdev-itf.c new file mode 100644 index 000000000000..e45040ae6348 --- /dev/null +++ b/drivers/media/platform/rockchip/cif/subdev-itf.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip CIF Driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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->active_sensor) { + sensor_sd = cif_dev->active_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) { + 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 *sensor_sd; + + if (cif_dev->active_sensor) { + sensor_sd = cif_dev->active_sensor->sd; + return v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, fmt); + } + + return -EINVAL; +} + +static int sditf_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + 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) { + sensor_sd = cif_dev->active_sensor->sd; + return v4l2_subdev_call(sensor_sd, pad, get_selection, NULL, 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) { + sensor_sd = cif_dev->terminal_sensor; + 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) { + sensor_sd = cif_dev->terminal_sensor; + 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 v4l2_async_register_subdev(&priv->sd); +} + +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"); diff --git a/drivers/media/platform/rockchip/cif/subdev-itf.h b/drivers/media/platform/rockchip/cif/subdev-itf.h new file mode 100644 index 000000000000..f81b69dc56f6 --- /dev/null +++ b/drivers/media/platform/rockchip/cif/subdev-itf.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip CIF Driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + */ + +#ifndef _RKCIF_SDITF_H +#define _RKCIF_SDITF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hw.h" + +struct sditf_priv { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pads; + struct rkcif_device *cif_dev; +}; + +extern struct platform_driver rkcif_subdev_driver; + +#endif