// SPDX-License-Identifier: GPL-2.0 /* * Rockchip Flexbus CIF Driver * * Copyright (C) 2024 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dev.h" #include "procfs.h" #include #include #include #include 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");