mirror of https://gitee.com/openkylin/linux.git
media: staging: rkisp1: add Rockchip ISP1 base driver
Add base driver for Rockchip Image Signal Processing v1 Unit, with isp subdevice and sensor biddings. [fixed compilation and run time errors regarding new v4l2 async API] Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com> Signed-off-by: Yichong Zhong <zyc@rock-chips.com> Signed-off-by: Jacob Chen <cc@rock-chips.com> Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com> Signed-off-by: Allon Huang <allon.huang@rock-chips.com> Signed-off-by: Tomasz Figa <tfiga@chromium.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> Signed-off-by: Helen Koike <helen.koike@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
parent
32abcc4491
commit
d65dd85281
|
@ -40,4 +40,6 @@ source "drivers/staging/media/soc_camera/Kconfig"
|
|||
|
||||
source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig"
|
||||
|
||||
source "drivers/staging/media/rkisp1/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -9,3 +9,4 @@ obj-$(CONFIG_VIDEO_HANTRO) += hantro/
|
|||
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
|
||||
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config VIDEO_ROCKCHIP_ISP1
|
||||
tristate "Rockchip Image Signal Processing v1 Unit driver"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select V4L2_FWNODE
|
||||
select PHY_ROCKCHIP_DPHY_RX0
|
||||
default n
|
||||
help
|
||||
Enable this to support the Image Signal Processing (ISP) module
|
||||
present in RK3399 SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called rockchip-isp1.
|
|
@ -0,0 +1,4 @@
|
|||
obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip-isp1.o
|
||||
rockchip-isp1-objs += rkisp1-common.o \
|
||||
rkisp1-dev.o \
|
||||
rkisp1-isp.o
|
|
@ -0,0 +1,37 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Rockchip ISP1 Driver - Common definitions
|
||||
*
|
||||
* Copyright (C) 2019 Collabora, Ltd.
|
||||
*/
|
||||
|
||||
#include <media/v4l2-rect.h>
|
||||
|
||||
#include "rkisp1-common.h"
|
||||
|
||||
static const struct v4l2_rect rkisp1_sd_min_crop = {
|
||||
.width = RKISP1_ISP_MIN_WIDTH,
|
||||
.height = RKISP1_ISP_MIN_HEIGHT,
|
||||
.top = 0,
|
||||
.left = 0,
|
||||
};
|
||||
|
||||
void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
|
||||
const struct v4l2_rect *bounds)
|
||||
{
|
||||
v4l2_rect_set_min_size(crop, &rkisp1_sd_min_crop);
|
||||
v4l2_rect_map_inside(crop, bounds);
|
||||
}
|
||||
|
||||
void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
|
||||
const struct v4l2_mbus_framefmt *bounds)
|
||||
{
|
||||
struct v4l2_rect crop_bounds = {
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.width = bounds->width,
|
||||
.height = bounds->height,
|
||||
};
|
||||
|
||||
rkisp1_sd_adjust_crop_rect(crop, &crop_bounds);
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
||||
/*
|
||||
* Rockchip ISP1 Driver - Common definitions
|
||||
*
|
||||
* Copyright (C) 2019 Collabora, Ltd.
|
||||
*
|
||||
* Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
|
||||
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _RKISP1_COMMON_H
|
||||
#define _RKISP1_COMMON_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <media/media-device.h>
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
|
||||
#include "rkisp1-regs.h"
|
||||
|
||||
#define RKISP1_ISP_MAX_WIDTH 4032
|
||||
#define RKISP1_ISP_MAX_HEIGHT 3024
|
||||
#define RKISP1_ISP_MIN_WIDTH 32
|
||||
#define RKISP1_ISP_MIN_HEIGHT 32
|
||||
|
||||
#define RKISP1_RSZ_MP_SRC_MAX_WIDTH 4416
|
||||
#define RKISP1_RSZ_MP_SRC_MAX_HEIGHT 3312
|
||||
#define RKISP1_RSZ_SP_SRC_MAX_WIDTH 1920
|
||||
#define RKISP1_RSZ_SP_SRC_MAX_HEIGHT 1920
|
||||
#define RKISP1_RSZ_SRC_MIN_WIDTH 32
|
||||
#define RKISP1_RSZ_SRC_MIN_HEIGHT 16
|
||||
|
||||
#define RKISP1_DEFAULT_WIDTH 800
|
||||
#define RKISP1_DEFAULT_HEIGHT 600
|
||||
|
||||
#define RKISP1_DRIVER_NAME "rkisp1"
|
||||
#define RKISP1_BUS_INFO "platform:" RKISP1_DRIVER_NAME
|
||||
|
||||
#define RKISP1_MAX_BUS_CLK 8
|
||||
|
||||
enum rkisp1_fmt_pix_type {
|
||||
RKISP1_FMT_YUV,
|
||||
RKISP1_FMT_RGB,
|
||||
RKISP1_FMT_BAYER,
|
||||
RKISP1_FMT_JPEG,
|
||||
};
|
||||
|
||||
enum rkisp1_fmt_raw_pat_type {
|
||||
RKISP1_RAW_RGGB = 0,
|
||||
RKISP1_RAW_GRBG,
|
||||
RKISP1_RAW_GBRG,
|
||||
RKISP1_RAW_BGGR,
|
||||
};
|
||||
|
||||
enum rkisp1_isp_pad {
|
||||
RKISP1_ISP_PAD_SINK_VIDEO,
|
||||
RKISP1_ISP_PAD_SINK_PARAMS,
|
||||
RKISP1_ISP_PAD_SOURCE_VIDEO,
|
||||
RKISP1_ISP_PAD_SOURCE_STATS,
|
||||
RKISP1_ISP_PAD_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rkisp1_sensor_async - Sensor information
|
||||
* @mbus: media bus configuration
|
||||
*/
|
||||
struct rkisp1_sensor_async {
|
||||
struct v4l2_async_subdev asd;
|
||||
struct v4l2_mbus_config mbus;
|
||||
unsigned int lanes;
|
||||
struct v4l2_subdev *sd;
|
||||
struct v4l2_ctrl *pixel_rate_ctrl;
|
||||
struct phy *dphy;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rkisp1_isp - ISP sub-device
|
||||
*
|
||||
* See Cropping regions of ISP in rkisp1.c for details
|
||||
* @sink_frm: input size, don't have to be equal to sensor size
|
||||
* @sink_fmt: input format
|
||||
* @sink_crop: crop for sink pad
|
||||
* @src_fmt: output format
|
||||
* @src_crop: output size
|
||||
*
|
||||
* @is_dphy_errctrl_disabled : if dphy errctrl is disabled (avoid endless interrupt)
|
||||
* @frame_sequence: used to synchronize frame_id between video devices.
|
||||
* @quantization: output quantization
|
||||
*/
|
||||
struct rkisp1_isp {
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pads[RKISP1_ISP_PAD_MAX];
|
||||
struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX];
|
||||
const struct rkisp1_isp_mbus_info *sink_fmt;
|
||||
const struct rkisp1_isp_mbus_info *src_fmt;
|
||||
bool is_dphy_errctrl_disabled;
|
||||
atomic_t frame_sequence;
|
||||
};
|
||||
|
||||
struct rkisp1_vdev_node {
|
||||
struct vb2_queue buf_queue;
|
||||
struct mutex vlock; /* ioctl serialization mutex */
|
||||
struct video_device vdev;
|
||||
struct media_pad pad;
|
||||
};
|
||||
|
||||
struct rkisp1_buffer {
|
||||
struct vb2_v4l2_buffer vb;
|
||||
struct list_head queue;
|
||||
union {
|
||||
u32 buff_addr[VIDEO_MAX_PLANES];
|
||||
void *vaddr[VIDEO_MAX_PLANES];
|
||||
};
|
||||
};
|
||||
|
||||
struct rkisp1_dummy_buffer {
|
||||
void *vaddr;
|
||||
dma_addr_t dma_addr;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct rkisp1_device;
|
||||
|
||||
struct rkisp1_debug {
|
||||
struct dentry *debugfs_dir;
|
||||
unsigned long data_loss;
|
||||
unsigned long pic_size_error;
|
||||
unsigned long mipi_error;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rkisp1_device - ISP platform device
|
||||
* @base_addr: base register address
|
||||
* @active_sensor: sensor in-use, set when streaming on
|
||||
* @isp: ISP sub-device
|
||||
*/
|
||||
struct rkisp1_device {
|
||||
void __iomem *base_addr;
|
||||
int irq;
|
||||
struct device *dev;
|
||||
unsigned int clk_size;
|
||||
struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK];
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct media_device media_dev;
|
||||
struct v4l2_async_notifier notifier;
|
||||
struct rkisp1_sensor_async *active_sensor;
|
||||
struct rkisp1_isp isp;
|
||||
struct media_pipeline pipe;
|
||||
struct vb2_alloc_ctx *alloc_ctx;
|
||||
struct rkisp1_debug debug;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rkisp1_isp_mbus_info - ISP pad format info
|
||||
*
|
||||
* Translate mbus_code to hardware format values
|
||||
*
|
||||
* @bus_width: used for parallel
|
||||
*/
|
||||
struct rkisp1_isp_mbus_info {
|
||||
u32 mbus_code;
|
||||
enum rkisp1_fmt_pix_type fmt_type;
|
||||
u32 mipi_dt;
|
||||
u32 yuv_seq;
|
||||
u8 bus_width;
|
||||
enum rkisp1_fmt_raw_pat_type bayer_pat;
|
||||
unsigned int direction;
|
||||
};
|
||||
|
||||
static inline void
|
||||
rkisp1_write(struct rkisp1_device *rkisp1, u32 val, unsigned int addr)
|
||||
{
|
||||
writel(val, rkisp1->base_addr + addr);
|
||||
}
|
||||
|
||||
static inline u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr)
|
||||
{
|
||||
return readl(rkisp1->base_addr + addr);
|
||||
}
|
||||
|
||||
void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
|
||||
const struct v4l2_rect *bounds);
|
||||
|
||||
void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
|
||||
const struct v4l2_mbus_framefmt *bounds);
|
||||
|
||||
int rkisp1_isp_register(struct rkisp1_device *rkisp1,
|
||||
struct v4l2_device *v4l2_dev);
|
||||
void rkisp1_isp_unregister(struct rkisp1_device *rkisp1);
|
||||
|
||||
const struct rkisp1_isp_mbus_info *rkisp1_isp_mbus_info_get(u32 mbus_code);
|
||||
|
||||
void rkisp1_isp_isr(struct rkisp1_device *rkisp1);
|
||||
void rkisp1_mipi_isr(struct rkisp1_device *rkisp1);
|
||||
|
||||
#endif /* _RKISP1_COMMON_H */
|
|
@ -0,0 +1,466 @@
|
|||
// 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/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
|
||||
#include "rkisp1-common.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-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
|
||||
* +--------+
|
||||
*
|
||||
*
|
||||
* Media Topology
|
||||
* --------------
|
||||
* +----------+ +----------+
|
||||
* | Sensor 2 | | Sensor X |
|
||||
* ------------ ... ------------
|
||||
* | 0 | | 0 |
|
||||
* +----------+ +----------+
|
||||
* \ |
|
||||
* \ |
|
||||
* +----------+ \ |
|
||||
* | Sensor 1 | v v
|
||||
* ------------ +------+------+
|
||||
* | 0 |----->| 0 | 1 |
|
||||
* +----------+ |------+------|
|
||||
* | ISP |
|
||||
* |------+------|
|
||||
* | 2 | 3 |
|
||||
* +------+------+
|
||||
*/
|
||||
|
||||
struct rkisp1_match_data {
|
||||
const char * const *clks;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Sensor DT bindings
|
||||
*/
|
||||
|
||||
static int rkisp1_create_links(struct rkisp1_device *rkisp1)
|
||||
{
|
||||
unsigned int flags, source_pad;
|
||||
struct v4l2_subdev *sd;
|
||||
int ret;
|
||||
|
||||
/* sensor links */
|
||||
flags = MEDIA_LNK_FL_ENABLED;
|
||||
list_for_each_entry(sd, &rkisp1->v4l2_dev.subdevs, list) {
|
||||
if (sd == &rkisp1->isp.sd)
|
||||
continue;
|
||||
|
||||
ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode,
|
||||
MEDIA_PAD_FL_SOURCE);
|
||||
if (ret) {
|
||||
dev_err(sd->dev, "failed to find src pad for %s\n",
|
||||
sd->name);
|
||||
return ret;
|
||||
}
|
||||
source_pad = ret;
|
||||
|
||||
ret = media_create_pad_link(&sd->entity, source_pad,
|
||||
&rkisp1->isp.sd.entity,
|
||||
RKISP1_ISP_PAD_SINK_VIDEO,
|
||||
flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *sd,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct rkisp1_device *rkisp1 =
|
||||
container_of(notifier, struct rkisp1_device, notifier);
|
||||
struct rkisp1_sensor_async *s_asd =
|
||||
container_of(asd, struct rkisp1_sensor_async, asd);
|
||||
|
||||
s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler,
|
||||
V4L2_CID_PIXEL_RATE);
|
||||
s_asd->sd = sd;
|
||||
s_asd->dphy = devm_phy_get(rkisp1->dev, "dphy");
|
||||
if (IS_ERR(s_asd->dphy)) {
|
||||
if (PTR_ERR(s_asd->dphy) != -EPROBE_DEFER)
|
||||
dev_err(rkisp1->dev, "Couldn't get the MIPI D-PHY\n");
|
||||
return PTR_ERR(s_asd->dphy);
|
||||
}
|
||||
|
||||
phy_init(s_asd->dphy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rkisp1_subdev_notifier_unbind(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *sd,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct rkisp1_sensor_async *s_asd =
|
||||
container_of(asd, struct rkisp1_sensor_async, asd);
|
||||
|
||||
phy_exit(s_asd->dphy);
|
||||
}
|
||||
|
||||
static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
|
||||
{
|
||||
struct rkisp1_device *rkisp1 =
|
||||
container_of(notifier, struct rkisp1_device, notifier);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&rkisp1->media_dev.graph_mutex);
|
||||
ret = rkisp1_create_links(rkisp1);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
dev_dbg(rkisp1->dev, "Async subdev notifier completed\n");
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&rkisp1->media_dev.graph_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkisp1_fwnode_parse(struct device *dev,
|
||||
struct v4l2_fwnode_endpoint *vep,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct rkisp1_sensor_async *s_asd =
|
||||
container_of(asd, struct rkisp1_sensor_async, asd);
|
||||
|
||||
if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
|
||||
dev_err(dev, "Only CSI2 bus type is currently supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (vep->base.port != 0) {
|
||||
dev_err(dev, "The ISP has only port 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s_asd->mbus.type = vep->bus_type;
|
||||
s_asd->mbus.flags = vep->bus.mipi_csi2.flags;
|
||||
s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
|
||||
|
||||
switch (vep->bus.mipi_csi2.num_data_lanes) {
|
||||
case 1:
|
||||
s_asd->mbus.flags |= V4L2_MBUS_CSI2_1_LANE;
|
||||
break;
|
||||
case 2:
|
||||
s_asd->mbus.flags |= V4L2_MBUS_CSI2_2_LANE;
|
||||
break;
|
||||
case 3:
|
||||
s_asd->mbus.flags |= V4L2_MBUS_CSI2_3_LANE;
|
||||
break;
|
||||
case 4:
|
||||
s_asd->mbus.flags |= V4L2_MBUS_CSI2_4_LANE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
|
||||
.bound = rkisp1_subdev_notifier_bound,
|
||||
.unbind = rkisp1_subdev_notifier_unbind,
|
||||
.complete = rkisp1_subdev_notifier_complete,
|
||||
};
|
||||
|
||||
static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
|
||||
{
|
||||
struct v4l2_async_notifier *ntf = &rkisp1->notifier;
|
||||
struct device *dev = rkisp1->dev;
|
||||
int ret;
|
||||
|
||||
v4l2_async_notifier_init(ntf);
|
||||
|
||||
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(dev, ntf,
|
||||
sizeof(struct rkisp1_sensor_async),
|
||||
0, rkisp1_fwnode_parse);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (list_empty(&ntf->asd_list))
|
||||
return -ENODEV;
|
||||
|
||||
ntf->ops = &rkisp1_subdev_notifier_ops;
|
||||
|
||||
return v4l2_async_notifier_register(&rkisp1->v4l2_dev, ntf);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Power
|
||||
*/
|
||||
|
||||
static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
|
||||
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_entities_register(struct rkisp1_device *rkisp1)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rkisp1_isp_register(rkisp1, &rkisp1->v4l2_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rkisp1_subdev_notifier(rkisp1);
|
||||
if (ret) {
|
||||
dev_err(rkisp1->dev,
|
||||
"Failed to register subdev notifier(%d)\n", ret);
|
||||
rkisp1_isp_unregister(rkisp1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rkisp1_isr(int irq, void *ctx)
|
||||
{
|
||||
struct device *dev = ctx;
|
||||
struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
|
||||
|
||||
rkisp1_isp_isr(rkisp1);
|
||||
rkisp1_mipi_isr(rkisp1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const char * const rk3399_isp_clks[] = {
|
||||
"clk_isp",
|
||||
"aclk_isp",
|
||||
"hclk_isp",
|
||||
"aclk_isp_wrap",
|
||||
"hclk_isp_wrap",
|
||||
};
|
||||
|
||||
static const struct rkisp1_match_data rk3399_isp_clk_data = {
|
||||
.clks = rk3399_isp_clks,
|
||||
.size = ARRAY_SIZE(rk3399_isp_clks),
|
||||
};
|
||||
|
||||
static const struct of_device_id rkisp1_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3399-cif-isp",
|
||||
.data = &rk3399_isp_clk_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rkisp1_of_match);
|
||||
|
||||
static void rkisp1_debug_init(struct rkisp1_device *rkisp1)
|
||||
{
|
||||
struct rkisp1_debug *debug = &rkisp1->debug;
|
||||
|
||||
debug->debugfs_dir = debugfs_create_dir(RKISP1_DRIVER_NAME, NULL);
|
||||
if (!debug->debugfs_dir) {
|
||||
dev_dbg(rkisp1->dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir,
|
||||
&debug->data_loss);
|
||||
debugfs_create_ulong("pic_size_error", 0444, debug->debugfs_dir,
|
||||
&debug->pic_size_error);
|
||||
debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir,
|
||||
&debug->mipi_error);
|
||||
}
|
||||
|
||||
static int rkisp1_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct rkisp1_match_data *clk_data;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rkisp1_device *rkisp1;
|
||||
struct v4l2_device *v4l2_dev;
|
||||
unsigned int i;
|
||||
int ret, irq;
|
||||
|
||||
match = of_match_node(rkisp1_of_match, node);
|
||||
rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
|
||||
if (!rkisp1)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, rkisp1);
|
||||
rkisp1->dev = dev;
|
||||
|
||||
rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(rkisp1->base_addr))
|
||||
return PTR_ERR(rkisp1->base_addr);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, rkisp1_isr, IRQF_SHARED,
|
||||
dev_driver_string(dev), dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "request irq failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rkisp1->irq = irq;
|
||||
clk_data = match->data;
|
||||
|
||||
for (i = 0; i < clk_data->size; i++)
|
||||
rkisp1->clks[i].id = clk_data->clks[i];
|
||||
ret = devm_clk_bulk_get(dev, clk_data->size, rkisp1->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
rkisp1->clk_size = clk_data->size;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
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,
|
||||
"platform: " RKISP1_DRIVER_NAME,
|
||||
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)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = rkisp1_entities_register(rkisp1);
|
||||
if (ret)
|
||||
goto err_unreg_media_dev;
|
||||
|
||||
rkisp1_debug_init(rkisp1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unreg_media_dev:
|
||||
media_device_unregister(&rkisp1->media_dev);
|
||||
err_unreg_v4l2_dev:
|
||||
v4l2_device_unregister(&rkisp1->v4l2_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkisp1_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);
|
||||
|
||||
v4l2_async_notifier_unregister(&rkisp1->notifier);
|
||||
v4l2_async_notifier_cleanup(&rkisp1->notifier);
|
||||
|
||||
rkisp1_isp_unregister(rkisp1);
|
||||
|
||||
media_device_unregister(&rkisp1->media_dev);
|
||||
v4l2_device_unregister(&rkisp1->v4l2_dev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
debugfs_remove_recursive(rkisp1->debug.debugfs_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue