Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Helen Mae Koike Fornazier | 1349 | 47.35% | 6 | 11.32% |
Paul Elder | 440 | 15.44% | 9 | 16.98% |
Laurent Pinchart | 434 | 15.23% | 18 | 33.96% |
Tomi Valkeinen | 230 | 8.07% | 5 | 9.43% |
Heiko Stübner | 191 | 6.70% | 3 | 5.66% |
Dafna Hirschfeld | 119 | 4.18% | 4 | 7.55% |
Jacob Chen | 58 | 2.04% | 2 | 3.77% |
Sakari Ailus | 17 | 0.60% | 3 | 5.66% |
Ezequiel García | 8 | 0.28% | 1 | 1.89% |
Uwe Kleine-König | 2 | 0.07% | 1 | 1.89% |
Rob Herring | 1 | 0.04% | 1 | 1.89% |
Total | 2849 | 53 |
// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Rockchip ISP1 Driver - Base driver * * Copyright (C) 2019 Collabora, Ltd. * * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. * Copyright (C) 2017 Rockchip Electronics Co., Ltd. */ #include <linux/clk.h> #include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-mc.h> #include "rkisp1-common.h" #include "rkisp1-csi.h" /* * ISP Details * ----------- * * ISP Comprises with: * MIPI serial camera interface * Image Signal Processing * Many Image Enhancement Blocks * Crop * Resizer * RBG display ready image * Image Rotation * * ISP Block Diagram * ----------------- * rkisp1-resizer.c rkisp1-capture.c * |====================| |=======================| * rkisp1-isp.c Main Picture Path * |==========================| |===============================================| * +-----------+ +--+--+--+--+ +--------+ +--------+ +-----------+ * | | | | | | | | | | | | | * +--------+ |\ | | | | | | | -->| Crop |->| RSZ |------------->| | * | MIPI |--->| \ | | | | | | | | | | | | | | * +--------+ | | | | |IE|IE|IE|IE| | +--------+ +--------+ | Memory | * |MUX|--->| ISP |->|0 |1 |2 |3 |---+ | Interface | * +--------+ | | | | | | | | | | +--------+ +--------+ +--------+ | | * |Parallel|--->| / | | | | | | | | | | | | | | | | * +--------+ |/ | | | | | | | -->| Crop |->| RSZ |->| RGB |->| | * | | | | | | | | | | | | Rotate | | | * +-----------+ +--+--+--+--+ +--------+ +--------+ +--------+ +-----------+ * ^ * +--------+ | |===============================================| * | DMA |------------------------------------+ Self Picture Path * +--------+ * * rkisp1-stats.c rkisp1-params.c * |===============| |===============| * +---------------+ +---------------+ * | | | | * | ISP | | ISP | * | | | | * +---------------+ +---------------+ * * * Media Topology * -------------- * * +----------+ +----------+ * | Sensor 1 | | Sensor X | * ------------ ... ------------ * | 0 | | 0 | * +----------+ +----------+ * | | * \----\ /----/ * | | * v v * +-------------+ * | 0 | * --------------- * | CSI-2 RX | * --------------- +-----------+ * | 1 | | params | * +-------------+ | (output) | * | +-----------+ * v | * +------+------+ | * | 0 | 1 |<---------+ * |------+------| * | ISP | * |------+------| * +-------------| 2 | 3 |----------+ * | +------+------+ | * | | | * v v v * +- ---------+ +-----------+ +-----------+ * | 0 | | 0 | | stats | * ------------- ------------- | (capture) | * | Resizer | | Resizer | +-----------+ * ------------| ------------| * | 1 | | 1 | * +-----------+ +-----------+ * | | * v v * +-----------+ +-----------+ * | selfpath | | mainpath | * | (capture) | | (capture) | * +-----------+ +-----------+ */ struct rkisp1_isr_data { const char *name; irqreturn_t (*isr)(int irq, void *ctx); u32 line_mask; }; /* ---------------------------------------------------------------------------- * Sensor DT bindings */ static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asc) { struct rkisp1_device *rkisp1 = container_of(notifier, struct rkisp1_device, notifier); struct rkisp1_sensor_async *s_asd = container_of(asc, struct rkisp1_sensor_async, asd); int source_pad; int ret; s_asd->sd = sd; source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep, MEDIA_PAD_FL_SOURCE); if (source_pad < 0) { dev_err(rkisp1->dev, "failed to find source pad for %s\n", sd->name); return source_pad; } if (s_asd->port == 0) return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad); ret = media_create_pad_link(&sd->entity, source_pad, &rkisp1->isp.sd.entity, RKISP1_ISP_PAD_SINK_VIDEO, !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0); if (ret) { dev_err(rkisp1->dev, "failed to link source pad of %s\n", sd->name); return ret; } return 0; } static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) { struct rkisp1_device *rkisp1 = container_of(notifier, struct rkisp1_device, notifier); return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); } static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc) { struct rkisp1_sensor_async *rk_asd = container_of(asc, struct rkisp1_sensor_async, asd); fwnode_handle_put(rk_asd->source_ep); } static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = { .bound = rkisp1_subdev_notifier_bound, .complete = rkisp1_subdev_notifier_complete, .destroy = rkisp1_subdev_notifier_destroy, }; static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) { struct v4l2_async_notifier *ntf = &rkisp1->notifier; struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev); struct fwnode_handle *ep; unsigned int index = 0; int ret = 0; v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev); ntf->ops = &rkisp1_subdev_notifier_ops; fwnode_graph_for_each_endpoint(fwnode, ep) { struct fwnode_handle *port; struct v4l2_fwnode_endpoint vep = { }; struct rkisp1_sensor_async *rk_asd; struct fwnode_handle *source; u32 reg = 0; /* Select the bus type based on the port. */ port = fwnode_get_parent(ep); fwnode_property_read_u32(port, "reg", ®); fwnode_handle_put(port); switch (reg) { case 0: /* MIPI CSI-2 port */ if (!rkisp1_has_feature(rkisp1, MIPI_CSI2)) { dev_err(rkisp1->dev, "internal CSI must be available for port 0\n"); ret = -EINVAL; break; } vep.bus_type = V4L2_MBUS_CSI2_DPHY; break; case 1: /* * Parallel port. The bus-type property in DT is * mandatory for port 1, it will be used to determine if * it's PARALLEL or BT656. */ vep.bus_type = V4L2_MBUS_UNKNOWN; break; } /* Parse the endpoint and validate the bus type. */ ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) { dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n", ep); break; } if (vep.base.port == 1) { if (vep.bus_type != V4L2_MBUS_PARALLEL && vep.bus_type != V4L2_MBUS_BT656) { dev_err(rkisp1->dev, "port 1 must be parallel or BT656\n"); ret = -EINVAL; break; } } /* Add the async subdev to the notifier. */ source = fwnode_graph_get_remote_endpoint(ep); if (!source) { dev_err(rkisp1->dev, "endpoint %pfw has no remote endpoint\n", ep); ret = -ENODEV; break; } rk_asd = v4l2_async_nf_add_fwnode(ntf, source, struct rkisp1_sensor_async); if (IS_ERR(rk_asd)) { fwnode_handle_put(source); ret = PTR_ERR(rk_asd); break; } rk_asd->index = index++; rk_asd->source_ep = source; rk_asd->mbus_type = vep.bus_type; rk_asd->port = vep.base.port; if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) { rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; } else { rk_asd->mbus_flags = vep.bus.parallel.flags; } dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n", vep.base.id, rk_asd->mbus_type, rk_asd->lanes); } if (ret) { fwnode_handle_put(ep); v4l2_async_nf_cleanup(ntf); return ret; } if (!index) dev_dbg(rkisp1->dev, "no remote subdevice found\n"); ret = v4l2_async_nf_register(ntf); if (ret) { v4l2_async_nf_cleanup(ntf); return ret; } return 0; } /* ---------------------------------------------------------------------------- * Power */ static int __maybe_unused rkisp1_runtime_suspend(struct device *dev) { struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); rkisp1->irqs_enabled = false; /* Make sure the IRQ handler will see the above */ mb(); /* * Wait until any running IRQ handler has returned. The IRQ handler * may get called even after this (as it's a shared interrupt line) * but the 'irqs_enabled' flag will make the handler return immediately. */ for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) { if (rkisp1->irqs[il] == -1) continue; /* Skip if the irq line is the same as previous */ if (il == 0 || rkisp1->irqs[il - 1] != rkisp1->irqs[il]) synchronize_irq(rkisp1->irqs[il]); } clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks); return pinctrl_pm_select_sleep_state(dev); } static int __maybe_unused rkisp1_runtime_resume(struct device *dev) { struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); int ret; ret = pinctrl_pm_select_default_state(dev); if (ret) return ret; ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks); if (ret) return ret; rkisp1->irqs_enabled = true; /* Make sure the IRQ handler will see the above */ mb(); return 0; } static const struct dev_pm_ops rkisp1_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL) }; /* ---------------------------------------------------------------------------- * Core */ static int rkisp1_create_links(struct rkisp1_device *rkisp1) { unsigned int dev_count = rkisp1_path_count(rkisp1); unsigned int i; int ret; if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) { /* Link the CSI receiver to the ISP. */ ret = media_create_pad_link(&rkisp1->csi.sd.entity, RKISP1_CSI_PAD_SRC, &rkisp1->isp.sd.entity, RKISP1_ISP_PAD_SINK_VIDEO, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } /* create ISP->RSZ->CAP links */ for (i = 0; i < dev_count; i++) { struct media_entity *resizer = &rkisp1->resizer_devs[i].sd.entity; struct media_entity *capture = &rkisp1->capture_devs[i].vnode.vdev.entity; ret = media_create_pad_link(&rkisp1->isp.sd.entity, RKISP1_ISP_PAD_SOURCE_VIDEO, resizer, RKISP1_RSZ_PAD_SINK, MEDIA_LNK_FL_ENABLED); if (ret) return ret; ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC, capture, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); if (ret) return ret; } /* params links */ ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0, &rkisp1->isp.sd.entity, RKISP1_ISP_PAD_SINK_PARAMS, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); if (ret) return ret; /* 3A stats links */ return media_create_pad_link(&rkisp1->isp.sd.entity, RKISP1_ISP_PAD_SOURCE_STATS, &rkisp1->stats.vnode.vdev.entity, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); } static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1) { if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) rkisp1_csi_unregister(rkisp1); rkisp1_params_unregister(rkisp1); rkisp1_stats_unregister(rkisp1); rkisp1_capture_devs_unregister(rkisp1); rkisp1_resizer_devs_unregister(rkisp1); rkisp1_isp_unregister(rkisp1); } static int rkisp1_entities_register(struct rkisp1_device *rkisp1) { int ret; ret = rkisp1_isp_register(rkisp1); if (ret) goto error; ret = rkisp1_resizer_devs_register(rkisp1); if (ret) goto error; ret = rkisp1_capture_devs_register(rkisp1); if (ret) goto error; ret = rkisp1_stats_register(rkisp1); if (ret) goto error; ret = rkisp1_params_register(rkisp1); if (ret) goto error; if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) { ret = rkisp1_csi_register(rkisp1); if (ret) goto error; } ret = rkisp1_create_links(rkisp1); if (ret) goto error; return 0; error: rkisp1_entities_unregister(rkisp1); return ret; } static irqreturn_t rkisp1_isr(int irq, void *ctx) { irqreturn_t ret = IRQ_NONE; /* * Call rkisp1_capture_isr() first to handle the frame that * potentially completed using the current frame_sequence number before * it is potentially incremented by rkisp1_isp_isr() in the vertical * sync. */ if (rkisp1_capture_isr(irq, ctx) == IRQ_HANDLED) ret = IRQ_HANDLED; if (rkisp1_isp_isr(irq, ctx) == IRQ_HANDLED) ret = IRQ_HANDLED; if (rkisp1_csi_isr(irq, ctx) == IRQ_HANDLED) ret = IRQ_HANDLED; return ret; } static const char * const px30_isp_clks[] = { "isp", "aclk", "hclk", "pclk", }; static const struct rkisp1_isr_data px30_isp_isrs[] = { { "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) }, { "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) }, { "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) }, }; static const struct rkisp1_info px30_isp_info = { .clks = px30_isp_clks, .clk_size = ARRAY_SIZE(px30_isp_clks), .isrs = px30_isp_isrs, .isr_size = ARRAY_SIZE(px30_isp_isrs), .isp_ver = RKISP1_V12, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH | RKISP1_FEATURE_DUAL_CROP, }; static const char * const rk3399_isp_clks[] = { "isp", "aclk", "hclk", }; static const struct rkisp1_isr_data rk3399_isp_isrs[] = { { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) }, }; static const struct rkisp1_info rk3399_isp_info = { .clks = rk3399_isp_clks, .clk_size = ARRAY_SIZE(rk3399_isp_clks), .isrs = rk3399_isp_isrs, .isr_size = ARRAY_SIZE(rk3399_isp_isrs), .isp_ver = RKISP1_V10, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH | RKISP1_FEATURE_DUAL_CROP, }; static const char * const imx8mp_isp_clks[] = { "isp", "hclk", "aclk", }; static const struct rkisp1_isr_data imx8mp_isp_isrs[] = { { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) }, }; static const struct rkisp1_info imx8mp_isp_info = { .clks = imx8mp_isp_clks, .clk_size = ARRAY_SIZE(imx8mp_isp_clks), .isrs = imx8mp_isp_isrs, .isr_size = ARRAY_SIZE(imx8mp_isp_isrs), .isp_ver = RKISP1_V_IMX8MP, .features = RKISP1_FEATURE_MAIN_STRIDE | RKISP1_FEATURE_DMA_34BIT, }; static const struct of_device_id rkisp1_of_match[] = { { .compatible = "rockchip,px30-cif-isp", .data = &px30_isp_info, }, { .compatible = "rockchip,rk3399-cif-isp", .data = &rk3399_isp_info, }, { .compatible = "fsl,imx8mp-isp", .data = &imx8mp_isp_info, }, {}, }; MODULE_DEVICE_TABLE(of, rkisp1_of_match); static int rkisp1_probe(struct platform_device *pdev) { const struct rkisp1_info *info; struct device *dev = &pdev->dev; struct rkisp1_device *rkisp1; struct v4l2_device *v4l2_dev; unsigned int i; u64 dma_mask; int ret, irq; u32 cif_id; rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL); if (!rkisp1) return -ENOMEM; info = of_device_get_match_data(dev); rkisp1->info = info; dev_set_drvdata(dev, rkisp1); rkisp1->dev = dev; dma_mask = rkisp1_has_feature(rkisp1, DMA_34BIT) ? DMA_BIT_MASK(34) : DMA_BIT_MASK(32); ret = dma_set_mask_and_coherent(dev, dma_mask); if (ret) return ret; mutex_init(&rkisp1->stream_lock); rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rkisp1->base_addr)) return PTR_ERR(rkisp1->base_addr); for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) rkisp1->irqs[il] = -1; for (i = 0; i < info->isr_size; i++) { irq = info->isrs[i].name ? platform_get_irq_byname(pdev, info->isrs[i].name) : platform_get_irq(pdev, i); if (irq < 0) return irq; for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) { if (info->isrs[i].line_mask & BIT(il)) rkisp1->irqs[il] = irq; } ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED, dev_driver_string(dev), dev); if (ret) { dev_err(dev, "request irq failed: %d\n", ret); return ret; } } for (i = 0; i < info->clk_size; i++) rkisp1->clks[i].id = info->clks[i]; ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks); if (ret) return ret; rkisp1->clk_size = info->clk_size; if (info->isp_ver == RKISP1_V_IMX8MP) { unsigned int id; rkisp1->gasket = syscon_regmap_lookup_by_phandle_args(dev->of_node, "fsl,blk-ctrl", 1, &id); if (IS_ERR(rkisp1->gasket)) { ret = PTR_ERR(rkisp1->gasket); dev_err(dev, "failed to get gasket: %d\n", ret); return ret; } rkisp1->gasket_id = id; } pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret) goto err_pm_runtime_disable; cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID); dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id); pm_runtime_put(&pdev->dev); rkisp1->media_dev.hw_revision = info->isp_ver; strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, sizeof(rkisp1->media_dev.model)); rkisp1->media_dev.dev = &pdev->dev; strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO, sizeof(rkisp1->media_dev.bus_info)); media_device_init(&rkisp1->media_dev); v4l2_dev = &rkisp1->v4l2_dev; v4l2_dev->mdev = &rkisp1->media_dev; strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name)); ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev); if (ret) goto err_media_dev_cleanup; ret = media_device_register(&rkisp1->media_dev); if (ret) { dev_err(dev, "Failed to register media device: %d\n", ret); goto err_unreg_v4l2_dev; } if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) { ret = rkisp1_csi_init(rkisp1); if (ret) goto err_unreg_media_dev; } ret = rkisp1_entities_register(rkisp1); if (ret) goto err_cleanup_csi; ret = rkisp1_subdev_notifier_register(rkisp1); if (ret) goto err_unreg_entities; rkisp1_debug_init(rkisp1); return 0; err_unreg_entities: rkisp1_entities_unregister(rkisp1); err_cleanup_csi: if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) rkisp1_csi_cleanup(rkisp1); err_unreg_media_dev: media_device_unregister(&rkisp1->media_dev); err_unreg_v4l2_dev: v4l2_device_unregister(&rkisp1->v4l2_dev); err_media_dev_cleanup: media_device_cleanup(&rkisp1->media_dev); err_pm_runtime_disable: pm_runtime_disable(&pdev->dev); return ret; } static void rkisp1_remove(struct platform_device *pdev) { struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev); v4l2_async_nf_unregister(&rkisp1->notifier); v4l2_async_nf_cleanup(&rkisp1->notifier); rkisp1_entities_unregister(rkisp1); if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) rkisp1_csi_cleanup(rkisp1); rkisp1_debug_cleanup(rkisp1); media_device_unregister(&rkisp1->media_dev); v4l2_device_unregister(&rkisp1->v4l2_dev); media_device_cleanup(&rkisp1->media_dev); pm_runtime_disable(&pdev->dev); } static struct platform_driver rkisp1_drv = { .driver = { .name = RKISP1_DRIVER_NAME, .of_match_table = of_match_ptr(rkisp1_of_match), .pm = &rkisp1_pm_ops, }, .probe = rkisp1_probe, .remove_new = rkisp1_remove, }; module_platform_driver(rkisp1_drv); MODULE_DESCRIPTION("Rockchip ISP1 platform driver"); MODULE_LICENSE("Dual MIT/GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1