root/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
// 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/build_bug.h>
#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_domain.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", &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;
                }

                if (ret)
                        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 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 = {
        .num_clocks = 4,
        .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 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 = {
        .num_clocks = 3,
        .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 struct rkisp1_isr_data imx8mp_isp_isrs[] = {
        { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) },
};

static const char * const imx8mp_isp_pm_domains[] = {
        "isp",
        "csi2",
};

static const struct rkisp1_info imx8mp_isp_info = {
        .num_clocks = 3,
        .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,
        .pm_domains = {
                .names = imx8mp_isp_pm_domains,
                .count = ARRAY_SIZE(imx8mp_isp_pm_domains),
        },
};

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 const char * const rkisp1_clk_names[] = {
        "isp",
        "aclk",
        "hclk",
        "pclk",
};

static int rkisp1_init_clocks(struct rkisp1_device *rkisp1)
{
        const struct rkisp1_info *info = rkisp1->info;
        unsigned int i;
        int ret;

        static_assert(ARRAY_SIZE(rkisp1_clk_names) == ARRAY_SIZE(rkisp1->clks));

        for (i = 0; i < info->num_clocks; i++)
                rkisp1->clks[i].id = rkisp1_clk_names[i];

        ret = devm_clk_bulk_get(rkisp1->dev, info->num_clocks, rkisp1->clks);
        if (ret)
                return ret;

        rkisp1->clk_size = info->num_clocks;

        /*
         * On i.MX8MP the pclk clock is needed to access the HDR stitching
         * registers, but wasn't required by DT bindings. Try to acquire it as
         * an optional clock to avoid breaking backward compatibility.
         */
        if (info->isp_ver == RKISP1_V_IMX8MP) {
                struct clk *clk;

                clk = devm_clk_get_optional(rkisp1->dev, "pclk");
                if (IS_ERR(clk))
                        return dev_err_probe(rkisp1->dev, PTR_ERR(clk),
                                             "Failed to acquire pclk clock\n");

                if (clk)
                        rkisp1->clks[rkisp1->clk_size++].clk = clk;
        }

        return 0;
}

static int rkisp1_init_pm_domains(struct rkisp1_device *rkisp1)
{
        const struct rkisp1_info *info = rkisp1->info;
        struct dev_pm_domain_attach_data pm_domain_data = {
                .pd_names = info->pm_domains.names,
                .num_pd_names = info->pm_domains.count,
        };
        int ret;

        /*
         * Most platforms have a single power domain, which the PM domain core
         * automatically attaches at probe time. When that's the case there's
         * nothing to do here.
         */
        if (rkisp1->dev->pm_domain)
                return 0;

        if (!pm_domain_data.num_pd_names)
                return 0;

        ret = devm_pm_domain_attach_list(rkisp1->dev, &pm_domain_data,
                                         &rkisp1->pm_domains);
        if (ret < 0) {
                dev_err_probe(rkisp1->dev, ret,
                              "Failed to attach power domains\n");
                return ret;
        }

        return 0;
}

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;
                }
        }

        ret = rkisp1_init_clocks(rkisp1);
        if (ret)
                return ret;

        ret = rkisp1_init_pm_domains(rkisp1);
        if (ret)
                return ret;

        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 = rkisp1_remove,
};

module_platform_driver(rkisp1_drv);
MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
MODULE_LICENSE("Dual MIT/GPL");