2020-01-08 19:44:45 +01:00
|
|
|
// 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>
|
2024-02-16 18:54:53 +09:00
|
|
|
#include <linux/mfd/syscon.h>
|
2020-01-08 19:44:45 +01:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_graph.h>
|
2023-07-14 11:47:08 -06:00
|
|
|
#include <linux/platform_device.h>
|
2020-01-08 19:44:45 +01:00
|
|
|
#include <linux/pinctrl/consumer.h>
|
2022-06-14 20:10:50 +01:00
|
|
|
#include <linux/pm_runtime.h>
|
2020-01-08 19:44:45 +01:00
|
|
|
#include <media/v4l2-fwnode.h>
|
2022-06-14 20:11:13 +01:00
|
|
|
#include <media/v4l2-mc.h>
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
#include "rkisp1-common.h"
|
2022-06-14 20:10:50 +01:00
|
|
|
#include "rkisp1-csi.h"
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* -----------------
|
2020-01-08 19:44:46 +01:00
|
|
|
* rkisp1-resizer.c rkisp1-capture.c
|
|
|
|
* |====================| |=======================|
|
2020-01-08 19:44:45 +01:00
|
|
|
* 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
|
|
|
|
* +--------+
|
|
|
|
*
|
2020-01-08 19:44:49 +01:00
|
|
|
* rkisp1-stats.c rkisp1-params.c
|
|
|
|
* |===============| |===============|
|
|
|
|
* +---------------+ +---------------+
|
|
|
|
* | | | |
|
|
|
|
* | ISP | | ISP |
|
|
|
|
* | | | |
|
|
|
|
* +---------------+ +---------------+
|
2020-01-08 19:44:48 +01:00
|
|
|
*
|
2020-01-08 19:44:45 +01:00
|
|
|
*
|
|
|
|
* Media Topology
|
|
|
|
* --------------
|
2022-06-14 20:11:13 +01:00
|
|
|
*
|
|
|
|
* +----------+ +----------+
|
|
|
|
* | Sensor 1 | | Sensor X |
|
|
|
|
* ------------ ... ------------
|
|
|
|
* | 0 | | 0 |
|
|
|
|
* +----------+ +----------+
|
|
|
|
* | |
|
|
|
|
* \----\ /----/
|
|
|
|
* | |
|
|
|
|
* v v
|
|
|
|
* +-------------+
|
|
|
|
* | 0 |
|
|
|
|
* ---------------
|
|
|
|
* | CSI-2 RX |
|
|
|
|
* --------------- +-----------+
|
|
|
|
* | 1 | | params |
|
|
|
|
* +-------------+ | (output) |
|
|
|
|
* | +-----------+
|
|
|
|
* v |
|
|
|
|
* +------+------+ |
|
|
|
|
* | 0 | 1 |<---------+
|
|
|
|
* |------+------|
|
2020-01-08 19:44:45 +01:00
|
|
|
* | ISP |
|
|
|
|
* |------+------|
|
2020-01-08 19:44:48 +01:00
|
|
|
* +-------------| 2 | 3 |----------+
|
|
|
|
* | +------+------+ |
|
|
|
|
* | | |
|
|
|
|
* v v v
|
|
|
|
* +- ---------+ +-----------+ +-----------+
|
|
|
|
* | 0 | | 0 | | stats |
|
|
|
|
* ------------- ------------- | (capture) |
|
|
|
|
* | Resizer | | Resizer | +-----------+
|
2020-01-08 19:44:46 +01:00
|
|
|
* ------------| ------------|
|
|
|
|
* | 1 | | 1 |
|
|
|
|
* +-----------+ +-----------+
|
|
|
|
* | |
|
|
|
|
* v v
|
|
|
|
* +-----------+ +-----------+
|
|
|
|
* | selfpath | | mainpath |
|
|
|
|
* | (capture) | | (capture) |
|
|
|
|
* +-----------+ +-----------+
|
2020-01-08 19:44:45 +01:00
|
|
|
*/
|
|
|
|
|
2021-07-23 09:32:15 +02:00
|
|
|
struct rkisp1_isr_data {
|
|
|
|
const char *name;
|
|
|
|
irqreturn_t (*isr)(int irq, void *ctx);
|
2023-12-07 08:57:47 +01:00
|
|
|
u32 line_mask;
|
2021-07-23 09:32:15 +02:00
|
|
|
};
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
* Sensor DT bindings
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *sd,
|
2023-02-16 14:54:53 +01:00
|
|
|
struct v4l2_async_connection *asc)
|
2020-01-08 19:44:45 +01:00
|
|
|
{
|
|
|
|
struct rkisp1_device *rkisp1 =
|
|
|
|
container_of(notifier, struct rkisp1_device, notifier);
|
|
|
|
struct rkisp1_sensor_async *s_asd =
|
2023-02-16 14:54:53 +01:00
|
|
|
container_of(asc, struct rkisp1_sensor_async, asd);
|
2022-06-14 20:10:45 +01:00
|
|
|
int source_pad;
|
2022-06-14 20:11:16 +01:00
|
|
|
int ret;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
s_asd->sd = sd;
|
|
|
|
|
2022-06-14 20:10:49 +01:00
|
|
|
source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep,
|
2022-06-14 20:10:45 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
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;
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
|
|
|
|
{
|
|
|
|
struct rkisp1_device *rkisp1 =
|
|
|
|
container_of(notifier, struct rkisp1_device, notifier);
|
|
|
|
|
2022-06-14 20:10:46 +01:00
|
|
|
return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
2023-02-16 14:54:53 +01:00
|
|
|
static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc)
|
2022-06-14 20:10:49 +01:00
|
|
|
{
|
|
|
|
struct rkisp1_sensor_async *rk_asd =
|
2023-02-16 14:54:53 +01:00
|
|
|
container_of(asc, struct rkisp1_sensor_async, asd);
|
2022-06-14 20:10:49 +01:00
|
|
|
|
|
|
|
fwnode_handle_put(rk_asd->source_ep);
|
|
|
|
}
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
|
|
|
|
.bound = rkisp1_subdev_notifier_bound,
|
|
|
|
.complete = rkisp1_subdev_notifier_complete,
|
2022-06-14 20:10:49 +01:00
|
|
|
.destroy = rkisp1_subdev_notifier_destroy,
|
2020-01-08 19:44:45 +01:00
|
|
|
};
|
|
|
|
|
2022-06-14 20:10:47 +01:00
|
|
|
static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
|
2020-01-08 19:44:45 +01:00
|
|
|
{
|
|
|
|
struct v4l2_async_notifier *ntf = &rkisp1->notifier;
|
2022-06-14 20:11:14 +01:00
|
|
|
struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev);
|
|
|
|
struct fwnode_handle *ep;
|
2022-06-14 20:10:45 +01:00
|
|
|
unsigned int index = 0;
|
2022-06-14 20:11:14 +01:00
|
|
|
int ret = 0;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2023-02-23 16:24:48 +01:00
|
|
|
v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev);
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2022-06-14 20:11:14 +01:00
|
|
|
ntf->ops = &rkisp1_subdev_notifier_ops;
|
|
|
|
|
|
|
|
fwnode_graph_for_each_endpoint(fwnode, ep) {
|
2022-06-14 20:11:16 +01:00
|
|
|
struct fwnode_handle *port;
|
|
|
|
struct v4l2_fwnode_endpoint vep = { };
|
2021-01-18 02:52:58 +01:00
|
|
|
struct rkisp1_sensor_async *rk_asd;
|
2022-06-14 20:11:14 +01:00
|
|
|
struct fwnode_handle *source;
|
2022-06-14 20:11:16 +01:00
|
|
|
u32 reg = 0;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
/* 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:
|
2022-06-14 20:11:18 +01:00
|
|
|
/* MIPI CSI-2 port */
|
2024-02-16 18:54:47 +09:00
|
|
|
if (!rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
|
2022-06-14 20:11:18 +01:00
|
|
|
dev_err(rkisp1->dev,
|
|
|
|
"internal CSI must be available for port 0\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-11-19 12:56:53 +05:30
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
/* Parse the endpoint and validate the bus type. */
|
2020-03-17 19:07:01 +01:00
|
|
|
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
|
2022-06-14 20:11:14 +01:00
|
|
|
if (ret) {
|
|
|
|
dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n",
|
|
|
|
ep);
|
|
|
|
break;
|
|
|
|
}
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
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. */
|
2022-06-14 20:10:49 +01:00
|
|
|
source = fwnode_graph_get_remote_endpoint(ep);
|
|
|
|
if (!source) {
|
|
|
|
dev_err(rkisp1->dev,
|
|
|
|
"endpoint %pfw has no remote endpoint\n",
|
|
|
|
ep);
|
|
|
|
ret = -ENODEV;
|
2022-06-14 20:11:14 +01:00
|
|
|
break;
|
2022-06-14 20:10:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rk_asd = v4l2_async_nf_add_fwnode(ntf, source,
|
|
|
|
struct rkisp1_sensor_async);
|
2021-01-18 02:52:58 +01:00
|
|
|
if (IS_ERR(rk_asd)) {
|
2022-06-14 20:11:14 +01:00
|
|
|
fwnode_handle_put(source);
|
2021-01-18 02:52:58 +01:00
|
|
|
ret = PTR_ERR(rk_asd);
|
2022-06-14 20:11:14 +01:00
|
|
|
break;
|
2020-03-17 19:07:01 +01:00
|
|
|
}
|
|
|
|
|
2022-06-14 20:10:45 +01:00
|
|
|
rk_asd->index = index++;
|
2022-06-14 20:10:49 +01:00
|
|
|
rk_asd->source_ep = source;
|
2020-03-17 19:07:01 +01:00
|
|
|
rk_asd->mbus_type = vep.bus_type;
|
2022-06-14 20:11:16 +01:00
|
|
|
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;
|
|
|
|
}
|
2020-03-17 19:07:01 +01:00
|
|
|
|
2022-06-14 20:11:16 +01:00
|
|
|
dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n",
|
|
|
|
vep.base.id, rk_asd->mbus_type, rk_asd->lanes);
|
2022-06-14 20:11:14 +01:00
|
|
|
}
|
2020-03-17 19:07:01 +01:00
|
|
|
|
2022-06-14 20:11:14 +01:00
|
|
|
if (ret) {
|
2020-03-17 19:07:01 +01:00
|
|
|
fwnode_handle_put(ep);
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_cleanup(ntf);
|
2020-03-17 19:07:01 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:11:14 +01:00
|
|
|
if (!index)
|
2020-03-17 19:07:01 +01:00
|
|
|
dev_dbg(rkisp1->dev, "no remote subdevice found\n");
|
2022-06-14 20:11:14 +01:00
|
|
|
|
2023-02-23 16:24:48 +01:00
|
|
|
ret = v4l2_async_nf_register(ntf);
|
2020-03-17 19:07:01 +01:00
|
|
|
if (ret) {
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_cleanup(ntf);
|
2020-03-17 19:07:01 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2022-06-14 20:11:14 +01:00
|
|
|
|
2020-03-17 19:07:01 +01:00
|
|
|
return 0;
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
* Power
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
|
|
|
|
|
2023-12-18 08:54:01 +01:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
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;
|
|
|
|
|
2023-12-18 08:54:01 +01:00
|
|
|
rkisp1->irqs_enabled = true;
|
|
|
|
/* Make sure the IRQ handler will see the above */
|
|
|
|
mb();
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2022-06-14 20:10:46 +01:00
|
|
|
static int rkisp1_create_links(struct rkisp1_device *rkisp1)
|
|
|
|
{
|
2024-02-16 18:54:49 +09:00
|
|
|
unsigned int dev_count = rkisp1_path_count(rkisp1);
|
2022-06-14 20:10:46 +01:00
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
|
|
|
|
2024-02-16 18:54:47 +09:00
|
|
|
if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
|
2022-06-14 20:11:18 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
2022-06-14 20:11:13 +01:00
|
|
|
|
2022-06-14 20:10:46 +01:00
|
|
|
/* create ISP->RSZ->CAP links */
|
2024-02-16 18:54:49 +09:00
|
|
|
for (i = 0; i < dev_count; i++) {
|
2022-06-14 20:10:46 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-02-24 14:24:10 +00:00
|
|
|
static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1)
|
|
|
|
{
|
2024-02-16 18:54:47 +09:00
|
|
|
if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
|
2022-06-14 20:11:18 +01:00
|
|
|
rkisp1_csi_unregister(rkisp1);
|
2022-02-24 14:24:10 +00:00
|
|
|
rkisp1_params_unregister(rkisp1);
|
|
|
|
rkisp1_stats_unregister(rkisp1);
|
|
|
|
rkisp1_capture_devs_unregister(rkisp1);
|
|
|
|
rkisp1_resizer_devs_unregister(rkisp1);
|
|
|
|
rkisp1_isp_unregister(rkisp1);
|
|
|
|
}
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
static int rkisp1_entities_register(struct rkisp1_device *rkisp1)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2020-08-06 12:34:14 +02:00
|
|
|
ret = rkisp1_isp_register(rkisp1);
|
2020-01-08 19:44:45 +01:00
|
|
|
if (ret)
|
2022-02-24 14:24:10 +00:00
|
|
|
goto error;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2020-01-08 19:44:46 +01:00
|
|
|
ret = rkisp1_resizer_devs_register(rkisp1);
|
|
|
|
if (ret)
|
2022-02-24 14:24:10 +00:00
|
|
|
goto error;
|
2020-01-08 19:44:46 +01:00
|
|
|
|
|
|
|
ret = rkisp1_capture_devs_register(rkisp1);
|
|
|
|
if (ret)
|
2022-02-24 14:24:10 +00:00
|
|
|
goto error;
|
2020-01-08 19:44:46 +01:00
|
|
|
|
2020-08-06 12:34:14 +02:00
|
|
|
ret = rkisp1_stats_register(rkisp1);
|
2020-01-08 19:44:48 +01:00
|
|
|
if (ret)
|
2022-02-24 14:24:10 +00:00
|
|
|
goto error;
|
2020-01-08 19:44:48 +01:00
|
|
|
|
2020-08-06 12:34:14 +02:00
|
|
|
ret = rkisp1_params_register(rkisp1);
|
2020-01-08 19:44:49 +01:00
|
|
|
if (ret)
|
2022-02-24 14:24:10 +00:00
|
|
|
goto error;
|
2022-06-14 20:11:12 +01:00
|
|
|
|
2024-02-16 18:54:47 +09:00
|
|
|
if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
|
2022-06-14 20:11:18 +01:00
|
|
|
ret = rkisp1_csi_register(rkisp1);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
}
|
2020-01-08 19:44:49 +01:00
|
|
|
|
2022-06-14 20:10:46 +01:00
|
|
|
ret = rkisp1_create_links(rkisp1);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
return 0;
|
2022-02-24 14:24:10 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
rkisp1_entities_unregister(rkisp1);
|
2020-01-08 19:44:46 +01:00
|
|
|
return ret;
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t rkisp1_isr(int irq, void *ctx)
|
|
|
|
{
|
2023-12-07 08:57:46 +01:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
|
2020-01-08 19:44:46 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2023-12-07 08:57:46 +01:00
|
|
|
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;
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
2021-07-23 09:32:21 +02:00
|
|
|
static const char * const px30_isp_clks[] = {
|
|
|
|
"isp",
|
|
|
|
"aclk",
|
|
|
|
"hclk",
|
|
|
|
"pclk",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rkisp1_isr_data px30_isp_isrs[] = {
|
2023-12-07 08:57:47 +01:00
|
|
|
{ "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) },
|
|
|
|
{ "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) },
|
|
|
|
{ "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) },
|
2021-07-23 09:32:21 +02:00
|
|
|
};
|
|
|
|
|
2022-06-14 20:10:38 +01:00
|
|
|
static const struct rkisp1_info px30_isp_info = {
|
2021-07-23 09:32:21 +02:00
|
|
|
.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,
|
2024-02-16 18:54:49 +09:00
|
|
|
.features = RKISP1_FEATURE_MIPI_CSI2
|
2024-02-16 18:54:50 +09:00
|
|
|
| RKISP1_FEATURE_SELF_PATH
|
2024-08-08 22:41:04 +02:00
|
|
|
| RKISP1_FEATURE_DUAL_CROP
|
|
|
|
| RKISP1_FEATURE_BLS,
|
2024-03-16 00:02:41 +01:00
|
|
|
.max_width = 3264,
|
|
|
|
.max_height = 2448,
|
2021-07-23 09:32:21 +02:00
|
|
|
};
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
static const char * const rk3399_isp_clks[] = {
|
media: staging: rkisp1: remove unecessary clocks
aclk_isp_wrap is a child of aclk_isp, and hclk_isp_wrap is a child of
hclk_isp, thus we can remove parents from the list.
Also, for the isp0, we only need the ISP clock, ACLK and HCLK.
In the future we'll need a pixel clock for RK3288 and RK3399, and a JPEG
clock for RK3288.
So with the goal to cleanup the dt-bindings and remove it from staging,
simplify clock names to isp, aclk and hclk.
Assigned clocks are meant to refer to the full path in the clock tree,
i.e. the leaf in the tree.
For instance, in RK3399, the clock responsible for ACLK (ISP AXI CLOCK)
is aclk_isp0_wrapper.
For reference, this is the isp clock topology on RK3399:
xin24m
pll_npll
npll
clk_isp1
clk_isp0
pll_cpll
cpll
aclk_isp1
aclk_isp1_noc
hclk_isp1
aclk_isp1_wrapper
hclk_isp1_noc
aclk_isp0
hclk_isp1_wrapper
aclk_isp0_wrapper
aclk_isp0_noc
hclk_isp0
hclk_isp0_wrapper
hclk_isp0_noc
pclkin_isp1_wrapper
Signed-off-by: Helen Koike <helen.koike@collabora.com>
Reviewed-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-10-20 21:38:46 +02:00
|
|
|
"isp",
|
|
|
|
"aclk",
|
|
|
|
"hclk",
|
2020-01-08 19:44:45 +01:00
|
|
|
};
|
|
|
|
|
2021-07-23 09:32:15 +02:00
|
|
|
static const struct rkisp1_isr_data rk3399_isp_isrs[] = {
|
2023-12-07 08:57:47 +01:00
|
|
|
{ NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) },
|
2021-07-23 09:32:15 +02:00
|
|
|
};
|
|
|
|
|
2022-06-14 20:10:38 +01:00
|
|
|
static const struct rkisp1_info rk3399_isp_info = {
|
2020-01-08 19:44:45 +01:00
|
|
|
.clks = rk3399_isp_clks,
|
2021-07-23 09:32:15 +02:00
|
|
|
.clk_size = ARRAY_SIZE(rk3399_isp_clks),
|
|
|
|
.isrs = rk3399_isp_isrs,
|
|
|
|
.isr_size = ARRAY_SIZE(rk3399_isp_isrs),
|
2021-01-21 15:44:06 +01:00
|
|
|
.isp_ver = RKISP1_V10,
|
2024-02-16 18:54:49 +09:00
|
|
|
.features = RKISP1_FEATURE_MIPI_CSI2
|
2024-02-16 18:54:50 +09:00
|
|
|
| RKISP1_FEATURE_SELF_PATH
|
2024-08-08 22:41:04 +02:00
|
|
|
| RKISP1_FEATURE_DUAL_CROP
|
|
|
|
| RKISP1_FEATURE_BLS,
|
2024-03-16 00:02:41 +01:00
|
|
|
.max_width = 4416,
|
|
|
|
.max_height = 3312,
|
2020-01-08 19:44:45 +01:00
|
|
|
};
|
|
|
|
|
2024-02-16 18:54:58 +09:00
|
|
|
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
|
2024-08-08 22:41:04 +02:00
|
|
|
| RKISP1_FEATURE_DMA_34BIT
|
|
|
|
| RKISP1_FEATURE_COMPAND,
|
2024-03-16 00:02:41 +01:00
|
|
|
.max_width = 4096,
|
|
|
|
.max_height = 3072,
|
2020-01-08 19:44:45 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id rkisp1_of_match[] = {
|
2021-07-23 09:32:21 +02:00
|
|
|
{
|
|
|
|
.compatible = "rockchip,px30-cif-isp",
|
2022-06-14 20:10:38 +01:00
|
|
|
.data = &px30_isp_info,
|
2021-07-23 09:32:21 +02:00
|
|
|
},
|
2020-01-08 19:44:45 +01:00
|
|
|
{
|
|
|
|
.compatible = "rockchip,rk3399-cif-isp",
|
2022-06-14 20:10:38 +01:00
|
|
|
.data = &rk3399_isp_info,
|
2020-01-08 19:44:45 +01:00
|
|
|
},
|
2024-02-16 18:54:58 +09:00
|
|
|
{
|
|
|
|
.compatible = "fsl,imx8mp-isp",
|
|
|
|
.data = &imx8mp_isp_info,
|
|
|
|
},
|
2020-01-08 19:44:45 +01:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, rkisp1_of_match);
|
|
|
|
|
|
|
|
static int rkisp1_probe(struct platform_device *pdev)
|
|
|
|
{
|
2022-06-14 20:10:38 +01:00
|
|
|
const struct rkisp1_info *info;
|
2020-01-08 19:44:45 +01:00
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct rkisp1_device *rkisp1;
|
|
|
|
struct v4l2_device *v4l2_dev;
|
|
|
|
unsigned int i;
|
2024-02-16 18:54:54 +09:00
|
|
|
u64 dma_mask;
|
2020-01-08 19:44:45 +01:00
|
|
|
int ret, irq;
|
2022-06-14 20:10:37 +01:00
|
|
|
u32 cif_id;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
|
|
|
|
if (!rkisp1)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2022-06-14 20:10:39 +01:00
|
|
|
info = of_device_get_match_data(dev);
|
|
|
|
rkisp1->info = info;
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
dev_set_drvdata(dev, rkisp1);
|
|
|
|
rkisp1->dev = dev;
|
|
|
|
|
2024-02-16 18:54:54 +09:00
|
|
|
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;
|
|
|
|
|
2020-03-16 22:00:44 +01:00
|
|
|
mutex_init(&rkisp1->stream_lock);
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
|
|
|
if (IS_ERR(rkisp1->base_addr))
|
|
|
|
return PTR_ERR(rkisp1->base_addr);
|
|
|
|
|
2023-12-07 08:57:47 +01:00
|
|
|
for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il)
|
|
|
|
rkisp1->irqs[il] = -1;
|
|
|
|
|
2022-06-14 20:10:38 +01:00
|
|
|
for (i = 0; i < info->isr_size; i++) {
|
|
|
|
irq = info->isrs[i].name
|
|
|
|
? platform_get_irq_byname(pdev, info->isrs[i].name)
|
2022-02-27 20:56:16 +00:00
|
|
|
: platform_get_irq(pdev, i);
|
2021-07-23 09:32:15 +02:00
|
|
|
if (irq < 0)
|
|
|
|
return irq;
|
|
|
|
|
2023-12-07 08:57:47 +01:00
|
|
|
for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) {
|
|
|
|
if (info->isrs[i].line_mask & BIT(il))
|
|
|
|
rkisp1->irqs[il] = irq;
|
|
|
|
}
|
|
|
|
|
2023-12-18 08:54:00 +01:00
|
|
|
ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED,
|
2021-07-23 09:32:15 +02:00
|
|
|
dev_driver_string(dev), dev);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "request irq failed: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2020-01-08 19:44:45 +01:00
|
|
|
}
|
|
|
|
|
2022-06-14 20:10:38 +01:00
|
|
|
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);
|
2020-01-08 19:44:45 +01:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2022-06-14 20:10:38 +01:00
|
|
|
rkisp1->clk_size = info->clk_size;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2024-02-16 18:54:53 +09:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
|
2022-06-14 20:10:37 +01:00
|
|
|
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);
|
|
|
|
|
2022-06-14 20:10:38 +01:00
|
|
|
rkisp1->media_dev.hw_revision = info->isp_ver;
|
2020-01-08 19:44:45 +01:00
|
|
|
strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME,
|
|
|
|
sizeof(rkisp1->media_dev.model));
|
|
|
|
rkisp1->media_dev.dev = &pdev->dev;
|
2020-02-06 23:07:08 +01:00
|
|
|
strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO,
|
2020-01-08 19:44:45 +01:00
|
|
|
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)
|
2023-11-22 16:50:07 +01:00
|
|
|
goto err_media_dev_cleanup;
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:11:18 +01:00
|
|
|
if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
|
|
|
|
ret = rkisp1_csi_init(rkisp1);
|
|
|
|
if (ret)
|
|
|
|
goto err_unreg_media_dev;
|
|
|
|
}
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2022-06-14 20:10:50 +01:00
|
|
|
ret = rkisp1_entities_register(rkisp1);
|
|
|
|
if (ret)
|
|
|
|
goto err_cleanup_csi;
|
|
|
|
|
2022-06-14 20:11:13 +01:00
|
|
|
ret = rkisp1_subdev_notifier_register(rkisp1);
|
|
|
|
if (ret)
|
|
|
|
goto err_unreg_entities;
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
rkisp1_debug_init(rkisp1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2022-06-14 20:11:13 +01:00
|
|
|
err_unreg_entities:
|
|
|
|
rkisp1_entities_unregister(rkisp1);
|
2022-06-14 20:10:50 +01:00
|
|
|
err_cleanup_csi:
|
2024-02-16 18:54:47 +09:00
|
|
|
if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
|
2022-06-14 20:11:18 +01:00
|
|
|
rkisp1_csi_cleanup(rkisp1);
|
2020-01-08 19:44:45 +01:00
|
|
|
err_unreg_media_dev:
|
|
|
|
media_device_unregister(&rkisp1->media_dev);
|
|
|
|
err_unreg_v4l2_dev:
|
|
|
|
v4l2_device_unregister(&rkisp1->v4l2_dev);
|
2023-11-22 16:50:07 +01:00
|
|
|
err_media_dev_cleanup:
|
|
|
|
media_device_cleanup(&rkisp1->media_dev);
|
2022-06-14 20:10:36 +01:00
|
|
|
err_pm_runtime_disable:
|
2020-01-08 19:44:45 +01:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-03-26 16:31:27 +02:00
|
|
|
static void rkisp1_remove(struct platform_device *pdev)
|
2020-01-08 19:44:45 +01:00
|
|
|
{
|
|
|
|
struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);
|
|
|
|
|
2021-03-05 18:13:12 +01:00
|
|
|
v4l2_async_nf_unregister(&rkisp1->notifier);
|
|
|
|
v4l2_async_nf_cleanup(&rkisp1->notifier);
|
2020-01-08 19:44:45 +01:00
|
|
|
|
2022-02-24 14:24:10 +00:00
|
|
|
rkisp1_entities_unregister(rkisp1);
|
2024-02-16 18:54:47 +09:00
|
|
|
if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
|
2022-06-14 20:11:18 +01:00
|
|
|
rkisp1_csi_cleanup(rkisp1);
|
2022-02-26 07:57:09 +00:00
|
|
|
rkisp1_debug_cleanup(rkisp1);
|
2020-01-08 19:44:45 +01:00
|
|
|
|
|
|
|
media_device_unregister(&rkisp1->media_dev);
|
|
|
|
v4l2_device_unregister(&rkisp1->v4l2_dev);
|
|
|
|
|
2023-11-22 16:50:07 +01:00
|
|
|
media_device_cleanup(&rkisp1->media_dev);
|
|
|
|
|
2020-01-08 19:44:45 +01:00
|
|
|
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,
|
2024-09-25 13:45:47 +02:00
|
|
|
.remove = rkisp1_remove,
|
2020-01-08 19:44:45 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
module_platform_driver(rkisp1_drv);
|
|
|
|
MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
|
|
|
|
MODULE_LICENSE("Dual MIT/GPL");
|