// 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
| RKISP1_FEATURE_BLS,
.max_width = 3264,
.max_height = 2448,
};
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
| RKISP1_FEATURE_BLS,
.max_width = 4416,
.max_height = 3312,
};
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
| RKISP1_FEATURE_COMPAND,
.max_width = 4096,
.max_height = 3072,
};
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");