682 lines
17 KiB
C
682 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Rockchip Flexbus CIF Driver
|
|
*
|
|
* Copyright (C) 2024 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 <linux/iommu.h>
|
|
#include <dt-bindings/soc/rockchip-system-status.h>
|
|
#include <soc/rockchip/rockchip-system-status.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include "dev.h"
|
|
#include "procfs.h"
|
|
#include <linux/kthread.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/of_address.h>
|
|
#include <dt-bindings/mfd/rockchip-flexbus.h>
|
|
|
|
int flexbus_cif_debug;
|
|
module_param_named(debug, flexbus_cif_debug, int, 0644);
|
|
MODULE_PARM_DESC(debug, "Debug level (0-1)");
|
|
|
|
static DEFINE_MUTEX(flexbus_cif_mutex);
|
|
static LIST_HEAD(flexbus_cif_device_list);
|
|
|
|
void flexbus_cif_write_register(struct flexbus_cif_device *dev,
|
|
u32 offset, u32 val)
|
|
{
|
|
rockchip_flexbus_writel(dev->fb_dev, offset, val);
|
|
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
|
|
"write reg[0x%x]:0x%x!!!\n",
|
|
offset, val);
|
|
|
|
}
|
|
|
|
void flexbus_cif_write_register_or(struct flexbus_cif_device *dev,
|
|
u32 offset, u32 val)
|
|
{
|
|
unsigned int reg_val = 0x0;
|
|
|
|
reg_val = rockchip_flexbus_readl(dev->fb_dev, offset);
|
|
reg_val |= val;
|
|
rockchip_flexbus_writel(dev->fb_dev, offset, reg_val);
|
|
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
|
|
"write or reg[0x%x]:0x%x!!!\n",
|
|
offset, val);
|
|
}
|
|
|
|
void flexbus_cif_write_register_and(struct flexbus_cif_device *dev,
|
|
u32 offset, u32 val)
|
|
{
|
|
unsigned int reg_val = 0x0;
|
|
|
|
reg_val = rockchip_flexbus_readl(dev->fb_dev, offset);
|
|
reg_val &= val;
|
|
rockchip_flexbus_writel(dev->fb_dev, offset, reg_val);
|
|
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
|
|
"write and reg[0x%x]:0x%x!!!\n",
|
|
offset, val);
|
|
}
|
|
|
|
unsigned int flexbus_cif_read_register(struct flexbus_cif_device *dev, u32 offset)
|
|
{
|
|
unsigned int val = 0x0;
|
|
|
|
val = rockchip_flexbus_readl(dev->fb_dev, offset);
|
|
|
|
return val;
|
|
}
|
|
|
|
/**************************** pipeline operations *****************************/
|
|
static int __cif_pipeline_prepare(struct flexbus_cif_pipeline *p,
|
|
struct media_entity *me)
|
|
{
|
|
struct v4l2_subdev *sd;
|
|
int i;
|
|
|
|
p->num_subdevs = 0;
|
|
memset(p->subdevs, 0, sizeof(p->subdevs));
|
|
|
|
while (1) {
|
|
struct media_pad *pad = NULL;
|
|
|
|
/* Find remote source pad */
|
|
for (i = 0; i < me->num_pads; i++) {
|
|
struct media_pad *spad = &me->pads[i];
|
|
|
|
if (!(spad->flags & MEDIA_PAD_FL_SINK))
|
|
continue;
|
|
pad = media_pad_remote_pad_first(spad);
|
|
if (pad)
|
|
break;
|
|
}
|
|
|
|
if (!pad)
|
|
break;
|
|
|
|
sd = media_entity_to_v4l2_subdev(pad->entity);
|
|
p->subdevs[p->num_subdevs++] = sd;
|
|
me = &sd->entity;
|
|
if (me->num_pads == 1)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __cif_pipeline_s_cif_clk(struct flexbus_cif_pipeline *p)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int flexbus_cif_pipeline_open(struct flexbus_cif_pipeline *p,
|
|
struct media_entity *me,
|
|
bool prepare)
|
|
{
|
|
int ret;
|
|
|
|
if (WARN_ON(!p || !me))
|
|
return -EINVAL;
|
|
if (atomic_inc_return(&p->power_cnt) > 1)
|
|
return 0;
|
|
|
|
/* go through media graphic and get subdevs */
|
|
if (prepare)
|
|
__cif_pipeline_prepare(p, me);
|
|
|
|
if (!p->num_subdevs)
|
|
return -EINVAL;
|
|
|
|
ret = __cif_pipeline_s_cif_clk(p);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flexbus_cif_pipeline_close(struct flexbus_cif_pipeline *p)
|
|
{
|
|
atomic_dec_return(&p->power_cnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* stream-on order: isp_subdev, mipi dphy, sensor
|
|
* stream-off order: mipi dphy, sensor, isp_subdev
|
|
*/
|
|
static int flexbus_cif_pipeline_set_stream(struct flexbus_cif_pipeline *p, bool on)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
if ((on && atomic_inc_return(&p->stream_cnt) > 1) ||
|
|
(!on && atomic_dec_return(&p->stream_cnt) > 0))
|
|
return 0;
|
|
|
|
/* phy -> sensor */
|
|
for (i = 0; i < p->num_subdevs; i++) {
|
|
ret = v4l2_subdev_call(p->subdevs[i], video, s_stream, on);
|
|
if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
|
|
goto err_stream_off;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_stream_off:
|
|
for (--i; i >= 0; --i)
|
|
v4l2_subdev_call(p->subdevs[i], video, s_stream, false);
|
|
return ret;
|
|
}
|
|
|
|
static int flexbus_cif_create_link(struct flexbus_cif_device *dev,
|
|
struct flexbus_cif_sensor_info *sensor)
|
|
{
|
|
struct flexbus_cif_sensor_info linked_sensor;
|
|
struct media_entity *source_entity, *sink_entity;
|
|
int ret = 0;
|
|
u32 pad;
|
|
|
|
linked_sensor.sd = sensor->sd;
|
|
|
|
memcpy(&linked_sensor.mbus, &sensor->mbus,
|
|
sizeof(struct v4l2_mbus_config));
|
|
|
|
for (pad = 0; pad < linked_sensor.sd->entity.num_pads; pad++) {
|
|
if (linked_sensor.sd->entity.pads[pad].flags &
|
|
MEDIA_PAD_FL_SOURCE) {
|
|
if (pad == linked_sensor.sd->entity.num_pads) {
|
|
dev_err(dev->dev,
|
|
"failed to find src pad for %s\n",
|
|
linked_sensor.sd->name);
|
|
|
|
break;
|
|
}
|
|
|
|
if (linked_sensor.mbus.type == V4L2_MBUS_BT656 ||
|
|
linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) {
|
|
source_entity = &linked_sensor.sd->entity;
|
|
sink_entity = &dev->stream[FLEXBUS_CIF_STREAM_CIF].vnode.vdev.entity;
|
|
|
|
ret = media_create_pad_link(source_entity,
|
|
pad,
|
|
sink_entity,
|
|
0,
|
|
MEDIA_LNK_FL_ENABLED);
|
|
if (ret)
|
|
dev_err(dev->dev, "failed to create link for %s\n",
|
|
linked_sensor.sd->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************** media controller *******************************/
|
|
static int flexbus_cif_create_links(struct flexbus_cif_device *dev)
|
|
{
|
|
u32 s = 0;
|
|
|
|
/* sensor links(or mipi-phy) */
|
|
for (s = 0; s < dev->num_sensors; ++s) {
|
|
struct flexbus_cif_sensor_info *sensor = &dev->sensors[s];
|
|
|
|
flexbus_cif_create_link(dev, sensor);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _set_pipeline_default_fmt(struct flexbus_cif_device *dev)
|
|
{
|
|
flexbus_cif_set_default_fmt(dev);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
|
|
{
|
|
struct flexbus_cif_device *dev;
|
|
struct flexbus_cif_sensor_info *sensor;
|
|
struct v4l2_subdev *sd;
|
|
struct v4l2_device *v4l2_dev = NULL;
|
|
int ret, index;
|
|
|
|
dev = container_of(notifier, struct flexbus_cif_device, notifier);
|
|
|
|
v4l2_dev = &dev->v4l2_dev;
|
|
|
|
for (index = 0; index < dev->num_sensors; index++) {
|
|
sensor = &dev->sensors[index];
|
|
|
|
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
|
|
if (sd->ops) {
|
|
if (sd == sensor->sd) {
|
|
ret = v4l2_subdev_call(sd,
|
|
pad,
|
|
get_mbus_config,
|
|
0,
|
|
&sensor->mbus);
|
|
if (ret)
|
|
v4l2_err(v4l2_dev,
|
|
"get mbus config failed for linking\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sensor->mbus.type == V4L2_MBUS_PARALLEL ||
|
|
sensor->mbus.type == V4L2_MBUS_BT656) {
|
|
ret = flexbus_cif_register_cif_sof_subdev(dev);
|
|
if (ret < 0) {
|
|
v4l2_err(&dev->v4l2_dev,
|
|
"Err: register cif sof subdev failed!!!\n");
|
|
goto notifier_end;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = flexbus_cif_create_links(dev);
|
|
if (ret < 0)
|
|
goto unregister_cif;
|
|
|
|
ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
|
|
if (ret < 0)
|
|
goto unregister_cif;
|
|
|
|
ret = _set_pipeline_default_fmt(dev);
|
|
if (ret < 0)
|
|
goto unregister_cif;
|
|
|
|
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
|
|
|
|
return ret;
|
|
|
|
unregister_cif:
|
|
flexbus_cif_unregister_cif_sof_subdev(dev);
|
|
notifier_end:
|
|
return ret;
|
|
}
|
|
|
|
struct flexbus_cif_async_subdev {
|
|
struct v4l2_async_subdev asd;
|
|
struct v4l2_mbus_config mbus;
|
|
int lanes;
|
|
};
|
|
|
|
static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
|
|
struct v4l2_subdev *subdev,
|
|
struct v4l2_async_subdev *asd)
|
|
{
|
|
struct flexbus_cif_device *cif_dev = container_of(notifier,
|
|
struct flexbus_cif_device, notifier);
|
|
struct flexbus_cif_async_subdev *s_asd = container_of(asd,
|
|
struct flexbus_cif_async_subdev, asd);
|
|
|
|
if (cif_dev->num_sensors == ARRAY_SIZE(cif_dev->sensors)) {
|
|
v4l2_err(&cif_dev->v4l2_dev,
|
|
"%s: the num of subdev is beyond %d\n",
|
|
__func__, cif_dev->num_sensors);
|
|
return -EBUSY;
|
|
}
|
|
|
|
cif_dev->sensors[cif_dev->num_sensors].lanes = s_asd->lanes;
|
|
cif_dev->sensors[cif_dev->num_sensors].mbus = s_asd->mbus;
|
|
cif_dev->sensors[cif_dev->num_sensors].sd = subdev;
|
|
++cif_dev->num_sensors;
|
|
|
|
v4l2_err(subdev, "Async registered subdev\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flexbus_cif_fwnode_parse(struct device *dev,
|
|
struct v4l2_fwnode_endpoint *vep,
|
|
struct v4l2_async_subdev *asd)
|
|
{
|
|
struct flexbus_cif_async_subdev *rk_asd =
|
|
container_of(asd, struct flexbus_cif_async_subdev, asd);
|
|
|
|
if (vep->bus_type != V4L2_MBUS_BT656 &&
|
|
vep->bus_type != V4L2_MBUS_PARALLEL)
|
|
return 0;
|
|
|
|
rk_asd->mbus.type = vep->bus_type;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
|
|
.bound = subdev_notifier_bound,
|
|
.complete = subdev_notifier_complete,
|
|
};
|
|
|
|
static int cif_subdev_notifier(struct flexbus_cif_device *cif_dev)
|
|
{
|
|
struct v4l2_async_notifier *ntf = &cif_dev->notifier;
|
|
struct device *dev = cif_dev->dev;
|
|
int ret;
|
|
|
|
v4l2_async_nf_init(ntf);
|
|
|
|
ret = v4l2_async_nf_parse_fwnode_endpoints(
|
|
dev, ntf, sizeof(struct flexbus_cif_async_subdev), flexbus_cif_fwnode_parse);
|
|
|
|
if (ret < 0) {
|
|
v4l2_err(&cif_dev->v4l2_dev,
|
|
"%s: parse fwnode failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ntf->ops = &subdev_notifier_ops;
|
|
|
|
ret = v4l2_async_nf_register(&cif_dev->v4l2_dev, ntf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************** platform deive *******************************/
|
|
|
|
static int flexbus_cif_register_platform_subdevs(struct flexbus_cif_device *cif_dev)
|
|
{
|
|
int stream_num = 0, ret;
|
|
|
|
stream_num = FLEXBUS_CIF_SINGLE_STREAM;
|
|
ret = flexbus_cif_register_stream_vdevs(cif_dev, stream_num,
|
|
false);
|
|
|
|
if (ret < 0) {
|
|
dev_err(cif_dev->dev, "cif register stream[%d] failed!\n", stream_num);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = cif_subdev_notifier(cif_dev);
|
|
if (ret < 0) {
|
|
v4l2_err(&cif_dev->v4l2_dev,
|
|
"Failed to register subdev notifier(%d)\n", ret);
|
|
goto err_unreg_stream_vdev;
|
|
}
|
|
|
|
return 0;
|
|
err_unreg_stream_vdev:
|
|
flexbus_cif_unregister_stream_vdevs(cif_dev, stream_num);
|
|
return ret;
|
|
}
|
|
|
|
static void flexbus_cif_irq_handler(struct rockchip_flexbus *fb_dev, u32 isr)
|
|
{
|
|
struct flexbus_cif_device *cif_dev = (struct flexbus_cif_device *)fb_dev->fb1_data;
|
|
|
|
flexbus_cif_irq_pingpong(cif_dev, isr);
|
|
flexbus_cif_write_register(cif_dev, FLEXBUS_ICR, isr);
|
|
if (cif_dev->err_state &&
|
|
(!work_busy(&cif_dev->err_state_work.work))) {
|
|
cif_dev->err_state_work.err_state = cif_dev->err_state;
|
|
cif_dev->err_state = 0;
|
|
schedule_work(&cif_dev->err_state_work.work);
|
|
}
|
|
}
|
|
|
|
static int flexbus_cif_plat_init(struct flexbus_cif_device *cif_dev, struct device_node *node)
|
|
{
|
|
struct device *dev = cif_dev->dev;
|
|
struct v4l2_device *v4l2_dev;
|
|
int ret;
|
|
|
|
mutex_init(&cif_dev->stream_lock);
|
|
atomic_set(&cif_dev->pipe.power_cnt, 0);
|
|
atomic_set(&cif_dev->pipe.stream_cnt, 0);
|
|
atomic_set(&cif_dev->power_cnt, 0);
|
|
cif_dev->pipe.open = flexbus_cif_pipeline_open;
|
|
cif_dev->pipe.close = flexbus_cif_pipeline_close;
|
|
cif_dev->pipe.set_stream = flexbus_cif_pipeline_set_stream;
|
|
cif_dev->id_use_cnt = 0;
|
|
INIT_WORK(&cif_dev->err_state_work.work, flexbus_cif_err_print_work);
|
|
|
|
flexbus_cif_stream_init(cif_dev, FLEXBUS_CIF_STREAM_CIF);
|
|
cif_dev->is_dma_sg_ops = true;
|
|
|
|
#if defined(CONFIG_ROCKCHIP_FLEXBUS_CIF_USE_DUMMY_BUF)
|
|
cif_dev->is_use_dummybuf = true;
|
|
#else
|
|
cif_dev->is_use_dummybuf = false;
|
|
#endif
|
|
|
|
strscpy(cif_dev->media_dev.model, "flexbus-cif",
|
|
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;
|
|
strscpy(v4l2_dev->name, "flexbus-cif", sizeof(v4l2_dev->name));
|
|
|
|
ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
media_device_init(&cif_dev->media_dev);
|
|
ret = media_device_register(&cif_dev->media_dev);
|
|
if (ret < 0) {
|
|
v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
|
|
ret);
|
|
goto err_unreg_v4l2_dev;
|
|
}
|
|
|
|
/* create & register platefom subdev (from of_node) */
|
|
ret = flexbus_cif_register_platform_subdevs(cif_dev);
|
|
if (ret < 0)
|
|
goto err_unreg_media_dev;
|
|
mutex_lock(&flexbus_cif_mutex);
|
|
list_add_tail(&cif_dev->list, &flexbus_cif_device_list);
|
|
mutex_unlock(&flexbus_cif_mutex);
|
|
|
|
return 0;
|
|
|
|
err_unreg_media_dev:
|
|
media_device_unregister(&cif_dev->media_dev);
|
|
err_unreg_v4l2_dev:
|
|
v4l2_device_unregister(&cif_dev->v4l2_dev);
|
|
return ret;
|
|
}
|
|
|
|
static int flexbus_cif_plat_uninit(struct flexbus_cif_device *cif_dev)
|
|
{
|
|
int stream_num = 0;
|
|
|
|
if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_BT656 ||
|
|
cif_dev->active_sensor->mbus.type == V4L2_MBUS_PARALLEL)
|
|
flexbus_cif_unregister_cif_sof_subdev(cif_dev);
|
|
|
|
media_device_unregister(&cif_dev->media_dev);
|
|
v4l2_device_unregister(&cif_dev->v4l2_dev);
|
|
|
|
stream_num = FLEXBUS_CIF_SINGLE_STREAM;
|
|
|
|
flexbus_cif_unregister_stream_vdevs(cif_dev, stream_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct flexbus_cif_match_data rk3576_cif_match_data = {
|
|
.chip_id = RK_FLEXBUS_CIF_RK3576,
|
|
};
|
|
|
|
static const struct flexbus_cif_match_data rk3506_cif_match_data = {
|
|
.chip_id = RK_FLEXBUS_CIF_RK3506,
|
|
};
|
|
|
|
static const struct of_device_id flexbus_cif_plat_of_match[] = {
|
|
{
|
|
.compatible = "rockchip,flexbus-cif-rk3576",
|
|
.data = &rk3576_cif_match_data,
|
|
},
|
|
{
|
|
.compatible = "rockchip,flexbus-cif-rk3506",
|
|
.data = &rk3506_cif_match_data,
|
|
},
|
|
{},
|
|
};
|
|
|
|
static int flexbus_cif_plat_probe(struct platform_device *pdev)
|
|
{
|
|
struct rockchip_flexbus *rkfb = dev_get_drvdata(pdev->dev.parent);
|
|
const struct of_device_id *match;
|
|
struct device_node *node = pdev->dev.of_node;
|
|
struct device *dev = &pdev->dev;
|
|
struct flexbus_cif_device *cif_dev;
|
|
const struct flexbus_cif_match_data *data = NULL;
|
|
int ret;
|
|
|
|
dev_info(dev, "czf flexbus_cif driver probe\n");
|
|
|
|
if (rkfb->opmode1 != ROCKCHIP_FLEXBUS1_OPMODE_CIF) {
|
|
dev_err(&pdev->dev, "flexbus1 opmode mismatch!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
match = of_match_node(flexbus_cif_plat_of_match, node);
|
|
if (IS_ERR(match))
|
|
return PTR_ERR(match);
|
|
|
|
cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL);
|
|
if (!cif_dev)
|
|
return -ENOMEM;
|
|
|
|
dev_set_drvdata(dev, cif_dev);
|
|
cif_dev->dev = dev;
|
|
data = match->data;
|
|
cif_dev->chip_id = data->chip_id;
|
|
|
|
ret = flexbus_cif_plat_init(cif_dev, node);
|
|
if (ret)
|
|
return ret;
|
|
|
|
cif_dev->fb_dev = rkfb;
|
|
rockchip_flexbus_set_fb1(rkfb, cif_dev, flexbus_cif_irq_handler);
|
|
|
|
if (flexbus_cif_proc_init(cif_dev))
|
|
dev_warn(dev, "dev:%s create proc failed\n", dev_name(dev));
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flexbus_cif_plat_remove(struct platform_device *pdev)
|
|
{
|
|
struct flexbus_cif_device *cif_dev = platform_get_drvdata(pdev);
|
|
|
|
flexbus_cif_plat_uninit(cif_dev);
|
|
flexbus_cif_proc_cleanup(cif_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused flexbus_cif_runtime_suspend(struct device *dev)
|
|
{
|
|
struct flexbus_cif_device *cif_dev = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
if (atomic_dec_return(&cif_dev->power_cnt))
|
|
return 0;
|
|
|
|
return (ret > 0) ? 0 : ret;
|
|
}
|
|
|
|
static int __maybe_unused flexbus_cif_runtime_resume(struct device *dev)
|
|
{
|
|
struct flexbus_cif_device *cif_dev = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
if (atomic_inc_return(&cif_dev->power_cnt) > 1)
|
|
return 0;
|
|
|
|
return (ret > 0) ? 0 : ret;
|
|
}
|
|
|
|
static int __maybe_unused __flexbus_cif_clr_unready_dev(void)
|
|
{
|
|
struct flexbus_cif_device *cif_dev;
|
|
|
|
mutex_lock(&flexbus_cif_mutex);
|
|
|
|
list_for_each_entry(cif_dev, &flexbus_cif_device_list, list) {
|
|
v4l2_async_notifier_clr_unready_dev(&cif_dev->notifier);
|
|
}
|
|
|
|
mutex_unlock(&flexbus_cif_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flexbus_cif_clr_unready_dev_param_set(const char *val, const struct kernel_param *kp)
|
|
{
|
|
#ifdef MODULE
|
|
__flexbus_cif_clr_unready_dev();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
module_param_call(flexbus_cif_clr_unready_dev, flexbus_cif_clr_unready_dev_param_set, NULL, NULL, 0200);
|
|
MODULE_PARM_DESC(flexbus_cif_clr_unready_dev, "clear unready devices");
|
|
|
|
#ifndef MODULE
|
|
int flexbus_cif_clr_unready_dev(void)
|
|
{
|
|
__flexbus_cif_clr_unready_dev();
|
|
|
|
return 0;
|
|
}
|
|
late_initcall(flexbus_cif_clr_unready_dev);
|
|
#endif
|
|
|
|
static const struct dev_pm_ops flexbus_cif_plat_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
pm_runtime_force_resume)
|
|
SET_RUNTIME_PM_OPS(flexbus_cif_runtime_suspend, flexbus_cif_runtime_resume, NULL)
|
|
};
|
|
|
|
struct platform_driver flexbus_cif_plat_drv = {
|
|
.driver = {
|
|
.name = CIF_DRIVER_NAME,
|
|
.of_match_table = of_match_ptr(flexbus_cif_plat_of_match),
|
|
.pm = &flexbus_cif_plat_pm_ops,
|
|
},
|
|
.probe = flexbus_cif_plat_probe,
|
|
.remove = flexbus_cif_plat_remove,
|
|
};
|
|
|
|
static int flexbus_cif_plat_drv_init(void)
|
|
{
|
|
return platform_driver_register(&flexbus_cif_plat_drv);
|
|
}
|
|
|
|
static void __exit flexbus_cif_plat_drv_exit(void)
|
|
{
|
|
platform_driver_unregister(&flexbus_cif_plat_drv);
|
|
}
|
|
|
|
module_init(flexbus_cif_plat_drv_init);
|
|
module_exit(flexbus_cif_plat_drv_exit);
|
|
|
|
MODULE_AUTHOR("Rockchip Camera/ISP team");
|
|
MODULE_DESCRIPTION("Rockchip flexbus cif platform driver");
|
|
MODULE_LICENSE("GPL");
|