Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_ROCKCHIP_ISP1
tristate "Rockchip Image Signal Processing v1 Unit driver"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV && OF
depends on ARCH_ROCKCHIP || ARCH_MXC || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_FWNODE
select GENERIC_PHY_MIPI_DPHY
select V4L2_ISP
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,14 @@
# SPDX-License-Identifier: GPL-2.0
rockchip-isp1-y := rkisp1-capture.o \
rkisp1-common.o \
rkisp1-csi.o \
rkisp1-dev.o \
rkisp1-isp.o \
rkisp1-resizer.o \
rkisp1-stats.o \
rkisp1-params.o
rockchip-isp1-$(CONFIG_DEBUG_FS) += rkisp1-debug.o
obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip-isp1.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip ISP1 Driver - Common definitions
*
* Copyright (C) 2019 Collabora, Ltd.
*/
#include <media/mipi-csi2.h>
#include <media/v4l2-rect.h>
#include "rkisp1-common.h"
static const struct rkisp1_mbus_info rkisp1_formats[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.pixel_enc = V4L2_PIXEL_ENC_YUV,
.direction = RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW10,
.bayer_pat = RKISP1_RAW_RGGB,
.bus_width = 10,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW10,
.bayer_pat = RKISP1_RAW_BGGR,
.bus_width = 10,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW10,
.bayer_pat = RKISP1_RAW_GBRG,
.bus_width = 10,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW10,
.bayer_pat = RKISP1_RAW_GRBG,
.bus_width = 10,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW12,
.bayer_pat = RKISP1_RAW_RGGB,
.bus_width = 12,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW12,
.bayer_pat = RKISP1_RAW_BGGR,
.bus_width = 12,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW12,
.bayer_pat = RKISP1_RAW_GBRG,
.bus_width = 12,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW12,
.bayer_pat = RKISP1_RAW_GRBG,
.bus_width = 12,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW8,
.bayer_pat = RKISP1_RAW_RGGB,
.bus_width = 8,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW8,
.bayer_pat = RKISP1_RAW_BGGR,
.bus_width = 8,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW8,
.bayer_pat = RKISP1_RAW_GBRG,
.bus_width = 8,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
.pixel_enc = V4L2_PIXEL_ENC_BAYER,
.mipi_dt = MIPI_CSI2_DT_RAW8,
.bayer_pat = RKISP1_RAW_GRBG,
.bus_width = 8,
.direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC,
}, {
.mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
.pixel_enc = V4L2_PIXEL_ENC_YUV,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
.yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR,
.bus_width = 16,
.direction = RKISP1_ISP_SD_SINK,
}, {
.mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
.pixel_enc = V4L2_PIXEL_ENC_YUV,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
.yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB,
.bus_width = 16,
.direction = RKISP1_ISP_SD_SINK,
}, {
.mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
.pixel_enc = V4L2_PIXEL_ENC_YUV,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
.yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY,
.bus_width = 16,
.direction = RKISP1_ISP_SD_SINK,
}, {
.mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
.pixel_enc = V4L2_PIXEL_ENC_YUV,
.mipi_dt = MIPI_CSI2_DT_YUV422_8B,
.yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY,
.bus_width = 16,
.direction = RKISP1_ISP_SD_SINK,
},
};
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index)
{
if (index >= ARRAY_SIZE(rkisp1_formats))
return NULL;
return &rkisp1_formats[index];
}
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rkisp1_formats); i++) {
const struct rkisp1_mbus_info *fmt = &rkisp1_formats[i];
if (fmt->mbus_code == mbus_code)
return fmt;
}
return NULL;
}
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);
}
void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern,
const u32 input[4], u32 output[4])
{
static const unsigned int swap[4][4] = {
[RKISP1_RAW_RGGB] = { 0, 1, 2, 3 },
[RKISP1_RAW_GRBG] = { 1, 0, 3, 2 },
[RKISP1_RAW_GBRG] = { 2, 3, 0, 1 },
[RKISP1_RAW_BGGR] = { 3, 2, 1, 0 },
};
for (unsigned int i = 0; i < 4; ++i)
output[i] = input[swap[pattern][i]];
}
@@ -0,0 +1,700 @@
/* 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/interrupt.h>
#include <linux/mutex.h>
#include <linux/rkisp1-config.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"
struct dentry;
struct dev_pm_domain_list;
struct regmap;
/*
* flags on the 'direction' field in struct rkisp1_mbus_info' that indicate
* on which pad the media bus format is supported
*/
#define RKISP1_ISP_SD_SRC BIT(0)
#define RKISP1_ISP_SD_SINK BIT(1)
/*
* Minimum values for the width and height of entities. The maximum values are
* model-specific and stored in the rkisp1_info structure.
*/
#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
/* the default width and height of all the entities */
#define RKISP1_DEFAULT_WIDTH 800
#define RKISP1_DEFAULT_HEIGHT 600
#define RKISP1_DRIVER_NAME "rkisp1"
#define RKISP1_BUS_INFO "platform:" RKISP1_DRIVER_NAME
/* maximum number of clocks */
#define RKISP1_MAX_BUS_CLK 4
/* a bitmask of the ready stats */
#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \
RKISP1_CIF_ISP_AFM_FIN | \
RKISP1_CIF_ISP_EXP_END | \
RKISP1_CIF_ISP_HIST_MEASURE_RDY)
/* IRQ lines */
enum rkisp1_irq_line {
RKISP1_IRQ_ISP = 0,
RKISP1_IRQ_MI,
RKISP1_IRQ_MIPI,
RKISP1_NUM_IRQS,
};
/* enum for the resizer pads */
enum rkisp1_rsz_pad {
RKISP1_RSZ_PAD_SINK,
RKISP1_RSZ_PAD_SRC,
RKISP1_RSZ_PAD_MAX
};
/* enum for the csi receiver pads */
enum rkisp1_csi_pad {
RKISP1_CSI_PAD_SINK,
RKISP1_CSI_PAD_SRC,
RKISP1_CSI_PAD_NUM
};
/* enum for the capture id */
enum rkisp1_stream_id {
RKISP1_MAINPATH,
RKISP1_SELFPATH,
};
/* bayer patterns */
enum rkisp1_fmt_raw_pat_type {
RKISP1_RAW_RGGB = 0,
RKISP1_RAW_GRBG,
RKISP1_RAW_GBRG,
RKISP1_RAW_BGGR,
};
/* enum for the isp pads */
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
};
/*
* enum rkisp1_feature - ISP features
*
* @RKISP1_FEATURE_MIPI_CSI2: The ISP has an internal MIPI CSI-2 receiver
* @RKISP1_FEATURE_MAIN_STRIDE: The ISP supports configurable stride on the main path
* @RKISP1_FEATURE_SELF_PATH: The ISP has a self path
* @RKISP1_FEATURE_DUAL_CROP: The ISP has the dual crop block at the resizer input
* @RKISP1_FEATURE_DMA_34BIT: The ISP uses 34-bit DMA addresses
* @RKISP1_FEATURE_BLS: The ISP has a dedicated BLS block
* @RKISP1_FEATURE_COMPAND: The ISP has a companding block
*
* The ISP features are stored in a bitmask in &rkisp1_info.features and allow
* the driver to implement support for features present in some ISP versions
* only.
*/
enum rkisp1_feature {
RKISP1_FEATURE_MIPI_CSI2 = BIT(0),
RKISP1_FEATURE_MAIN_STRIDE = BIT(1),
RKISP1_FEATURE_SELF_PATH = BIT(2),
RKISP1_FEATURE_DUAL_CROP = BIT(3),
RKISP1_FEATURE_DMA_34BIT = BIT(4),
RKISP1_FEATURE_BLS = BIT(5),
RKISP1_FEATURE_COMPAND = BIT(6),
};
#define rkisp1_has_feature(rkisp1, feature) \
((rkisp1)->info->features & RKISP1_FEATURE_##feature)
/*
* struct rkisp1_info - Model-specific ISP Information
*
* @num_clocks: number of clocks
* @isrs: array of ISP interrupt descriptors
* @isr_size: number of entries in the @isrs array
* @isp_ver: ISP version
* @features: bitmask of rkisp1_feature features implemented by the ISP
* @max_width: maximum input frame width
* @max_height: maximum input frame height
* @pm_domains.names: name of the power domains
* @pm_domains.count: number of power domains
*
* This structure contains information about the ISP specific to a particular
* ISP model, version, or integration in a particular SoC.
*/
struct rkisp1_info {
unsigned int num_clocks;
const struct rkisp1_isr_data *isrs;
unsigned int isr_size;
enum rkisp1_cif_isp_version isp_ver;
unsigned int features;
unsigned int max_width;
unsigned int max_height;
struct {
const char * const *names;
unsigned int count;
} pm_domains;
};
/*
* struct rkisp1_sensor_async - A container for the v4l2_async_subdev to add to the notifier
* of the v4l2-async API
*
* @asd: async_subdev variable for the sensor
* @index: index of the sensor (counting sensor found in DT)
* @source_ep: fwnode for the sensor source endpoint
* @lanes: number of lanes
* @mbus_type: type of bus (currently only CSI2 is supported)
* @mbus_flags: media bus (V4L2_MBUS_*) flags
* @sd: a pointer to v4l2_subdev struct of the sensor
* @pixel_rate_ctrl: pixel rate of the sensor, used to initialize the phy
* @port: port number (0: MIPI, 1: Parallel)
*/
struct rkisp1_sensor_async {
struct v4l2_async_connection asd;
unsigned int index;
struct fwnode_handle *source_ep;
unsigned int lanes;
enum v4l2_mbus_type mbus_type;
unsigned int mbus_flags;
struct v4l2_subdev *sd;
struct v4l2_ctrl *pixel_rate_ctrl;
unsigned int port;
};
/*
* struct rkisp1_csi - CSI receiver subdev
*
* @rkisp1: pointer to the rkisp1 device
* @dphy: a pointer to the phy
* @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt)
* @sd: v4l2_subdev variable
* @pads: media pads
* @source: source in-use, set when starting streaming
*/
struct rkisp1_csi {
struct rkisp1_device *rkisp1;
struct phy *dphy;
bool is_dphy_errctrl_disabled;
struct v4l2_subdev sd;
struct media_pad pads[RKISP1_CSI_PAD_NUM];
struct v4l2_subdev *source;
};
/*
* struct rkisp1_isp - ISP subdev entity
*
* @sd: v4l2_subdev variable
* @rkisp1: pointer to rkisp1_device
* @pads: media pads
* @sink_fmt: input format
* @frame_sequence: used to synchronize frame_id between video devices.
*/
struct rkisp1_isp {
struct v4l2_subdev sd;
struct rkisp1_device *rkisp1;
struct media_pad pads[RKISP1_ISP_PAD_MAX];
const struct rkisp1_mbus_info *sink_fmt;
__u32 frame_sequence;
bool frame_active;
};
/*
* struct rkisp1_vdev_node - Container for the video nodes: params, stats, mainpath, selfpath
*
* @buf_queue: queue of buffers
* @vlock: lock of the video node
* @vdev: video node
* @pad: media pad
*/
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 - A container for the vb2 buffers used by the video devices:
* stats, mainpath, selfpath
*
* @vb: vb2 buffer
* @queue: entry of the buffer in the queue
* @buff_addr: dma addresses of each plane, used only by the capture devices: selfpath, mainpath
*/
struct rkisp1_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
dma_addr_t buff_addr[VIDEO_MAX_PLANES];
};
/*
* struct rkisp1_params_buffer - A container for the vb2 buffers used by the
* params video device
*
* @vb: vb2 buffer
* @queue: entry of the buffer in the queue
* @cfg: scratch buffer used for caching the ISP configuration parameters
*/
struct rkisp1_params_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
void *cfg;
};
static inline struct rkisp1_params_buffer *
to_rkisp1_params_buffer(struct vb2_v4l2_buffer *vbuf)
{
return container_of(vbuf, struct rkisp1_params_buffer, vb);
}
/*
* struct rkisp1_dummy_buffer - A buffer to write the next frame to in case
* there are no vb2 buffers available.
*
* @vaddr: return value of call to dma_alloc_attrs.
* @dma_addr: dma address of the buffer.
* @size: size of the buffer.
*/
struct rkisp1_dummy_buffer {
void *vaddr;
dma_addr_t dma_addr;
u32 size;
};
struct rkisp1_device;
/*
* struct rkisp1_capture - ISP capture video device
*
* @vnode: video node
* @rkisp1: pointer to rkisp1_device
* @id: id of the capture, one of RKISP1_SELFPATH, RKISP1_MAINPATH
* @ops: list of callbacks to configure the capture device.
* @config: a pointer to the list of registers to configure the capture format.
* @is_streaming: device is streaming
* @is_stopping: stop_streaming callback was called and the device is in the process of
* stopping the streaming.
* @done: when stop_streaming callback is called, the device waits for the next irq
* handler to stop the streaming by waiting on the 'done' wait queue.
* If the irq handler is not called, the stream is stopped by the callback
* after timeout.
* @stride: the line stride for the first plane, in pixel units
* @buf.lock: lock to protect buf.queue
* @buf.queue: queued buffer list
* @buf.dummy: dummy space to store dropped data
*
* rkisp1 uses shadow registers, so it needs two buffers at a time
* @buf.curr: the buffer used for current frame
* @buf.next: the buffer used for next frame
* @pix.cfg: pixel configuration
* @pix.info: a pointer to the v4l2_format_info of the pixel format
* @pix.fmt: buffer format
*/
struct rkisp1_capture {
struct rkisp1_vdev_node vnode;
struct rkisp1_device *rkisp1;
enum rkisp1_stream_id id;
const struct rkisp1_capture_ops *ops;
const struct rkisp1_capture_config *config;
bool is_streaming;
bool is_stopping;
wait_queue_head_t done;
unsigned int stride;
struct {
/* protects queue, curr and next */
spinlock_t lock;
struct list_head queue;
struct rkisp1_dummy_buffer dummy;
struct rkisp1_buffer *curr;
struct rkisp1_buffer *next;
} buf;
struct {
const struct rkisp1_capture_fmt_cfg *cfg;
const struct v4l2_format_info *info;
struct v4l2_pix_format_mplane fmt;
} pix;
};
struct rkisp1_stats;
struct rkisp1_stats_ops {
void (*get_awb_meas)(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf);
void (*get_aec_meas)(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf);
void (*get_hst_meas)(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf);
};
/*
* struct rkisp1_stats - ISP Statistics device
*
* @vnode: video node
* @rkisp1: pointer to the rkisp1 device
* @lock: locks the buffer list 'stat'
* @stat: queue of rkisp1_buffer
* @vdev_fmt: v4l2_format of the metadata format
*/
struct rkisp1_stats {
struct rkisp1_vdev_node vnode;
struct rkisp1_device *rkisp1;
const struct rkisp1_stats_ops *ops;
spinlock_t lock; /* locks the buffers list 'stats' */
struct list_head stat;
struct v4l2_format vdev_fmt;
};
struct rkisp1_params;
struct rkisp1_params_ops {
void (*lsc_matrix_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_lsc_config *pconfig);
void (*goc_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_goc_config *arg);
void (*awb_meas_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_awb_meas_config *arg);
void (*awb_meas_enable)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_awb_meas_config *arg,
bool en);
void (*awb_gain_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_awb_gain_config *arg);
void (*aec_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_aec_config *arg);
void (*hst_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_hst_config *arg);
void (*hst_enable)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_hst_config *arg, bool en);
void (*afm_config)(struct rkisp1_params *params,
const struct rkisp1_cif_isp_afc_config *arg);
};
/*
* struct rkisp1_params - ISP input parameters device
*
* @vnode: video node
* @rkisp1: pointer to the rkisp1 device
* @ops: pointer to the variant-specific operations
* @config_lock: locks the buffer list 'params'
* @params: queue of rkisp1_buffer
* @metafmt the currently enabled metadata format
* @quantization: the quantization configured on the isp's src pad
* @ycbcr_encoding the YCbCr encoding
* @raw_type: the bayer pattern on the isp video sink pad
* @enabled_blocks: bitmask of enabled ISP blocks
*/
struct rkisp1_params {
struct rkisp1_vdev_node vnode;
struct rkisp1_device *rkisp1;
const struct rkisp1_params_ops *ops;
spinlock_t config_lock; /* locks the buffers list 'params' */
struct list_head params;
struct v4l2_ctrl_handler ctrls;
const struct v4l2_meta_format *metafmt;
enum v4l2_quantization quantization;
enum v4l2_ycbcr_encoding ycbcr_encoding;
enum rkisp1_fmt_raw_pat_type raw_type;
u32 enabled_blocks;
};
/*
* struct rkisp1_resizer - Resizer subdev
*
* @sd: v4l2_subdev variable
* @regs_base: base register address offset
* @id: id of the resizer, one of RKISP1_SELFPATH, RKISP1_MAINPATH
* @rkisp1: pointer to the rkisp1 device
* @pads: media pads
* @config: the set of registers to configure the resizer
*/
struct rkisp1_resizer {
struct v4l2_subdev sd;
u32 regs_base;
enum rkisp1_stream_id id;
struct rkisp1_device *rkisp1;
struct media_pad pads[RKISP1_RSZ_PAD_MAX];
const struct rkisp1_rsz_config *config;
};
/*
* struct rkisp1_debug - Values to be exposed on debugfs.
* The parameters are counters of the number of times the
* event occurred since the driver was loaded.
*
* @data_loss: loss of data occurred within a line, processing failure
* @outform_size_error: size error is generated in outmux submodule
* @img_stabilization_size_error: size error is generated in image stabilization submodule
* @inform_size_err: size error is generated in inform submodule
* @mipi_error: mipi error occurred
* @stats_error: writing to the 'Interrupt clear register' did not clear
* it in the register 'Masked interrupt status'
* @stop_timeout: upon stream stop, the capture waits 1 second for the isr to stop
* the stream. This param is incremented in case of timeout.
* @frame_drop: a frame was ready but the buffer queue was empty so the frame
* was not sent to userspace
*/
struct rkisp1_debug {
struct dentry *debugfs_dir;
unsigned long data_loss;
unsigned long outform_size_error;
unsigned long img_stabilization_size_error;
unsigned long inform_size_error;
unsigned long irq_delay;
unsigned long mipi_error;
unsigned long stats_error;
unsigned long stop_timeout[2];
unsigned long frame_drop[2];
unsigned long complete_frames;
};
/*
* struct rkisp1_device - ISP platform device
*
* @base_addr: base register address
* @dev: a pointer to the struct device
* @clk_size: number of clocks
* @clks: array of clocks
* @pm_domains: power domains
* @gasket: the gasket - i.MX8MP only
* @gasket_id: the gasket ID (0 or 1) - i.MX8MP only
* @v4l2_dev: v4l2_device variable
* @media_dev: media_device variable
* @notifier: a notifier to register on the v4l2-async API to be notified on the sensor
* @source: source subdev in-use, set when starting streaming
* @csi: internal CSI-2 receiver
* @isp: ISP sub-device
* @resizer_devs: resizer sub-devices
* @capture_devs: capture devices
* @stats: ISP statistics metadata capture device
* @params: ISP parameters metadata output device
* @pipe: media pipeline
* @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices.
* @debug: debug params to be exposed on debugfs
* @info: version-specific ISP information
* @irqs: IRQ line numbers
* @irqs_enabled: the hardware is enabled and can cause interrupts
*/
struct rkisp1_device {
void __iomem *base_addr;
struct device *dev;
unsigned int clk_size;
struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK];
struct dev_pm_domain_list *pm_domains;
struct regmap *gasket;
unsigned int gasket_id;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source;
struct rkisp1_csi csi;
struct rkisp1_isp isp;
struct rkisp1_resizer resizer_devs[2];
struct rkisp1_capture capture_devs[2];
struct rkisp1_stats stats;
struct rkisp1_params params;
struct media_pipeline pipe;
struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
struct rkisp1_debug debug;
const struct rkisp1_info *info;
int irqs[RKISP1_NUM_IRQS];
bool irqs_enabled;
};
/*
* struct rkisp1_mbus_info - ISP media bus info, Translates media bus code to hardware
* format values
*
* @mbus_code: media bus code
* @pixel_enc: pixel encoding
* @mipi_dt: mipi data type
* @yuv_seq: the order of the Y, Cb, Cr values
* @bus_width: bus width
* @bayer_pat: bayer pattern
* @direction: a bitmask of the flags indicating on which pad the format is supported on
*/
struct rkisp1_mbus_info {
u32 mbus_code;
enum v4l2_pixel_encoding pixel_enc;
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, unsigned int addr, u32 val)
{
writel(val, rkisp1->base_addr + addr);
}
static inline u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr)
{
return readl(rkisp1->base_addr + addr);
}
/*
* rkisp1_cap_enum_mbus_codes - A helper function that return the i'th supported mbus code
* of the capture entity. This is used to enumerate the supported
* mbus codes on the source pad of the resizer.
*
* @cap: the capture entity
* @code: the mbus code, the function reads the code->index and fills the code->code
*/
int rkisp1_cap_enum_mbus_codes(struct rkisp1_capture *cap,
struct v4l2_subdev_mbus_code_enum *code);
/*
* rkisp1_mbus_info_get_by_index - Retrieve the ith supported mbus info
*
* @index: index of the mbus info to fetch
*/
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index);
/*
* rkisp1_path_count - Return the number of paths supported by the device
*
* Some devices only have a main path, while other device have both a main path
* and a self path. This function returns the number of paths that this device
* has, based on the feature flags. It should be used insted of checking
* ARRAY_SIZE of capture_devs/resizer_devs.
*/
static inline unsigned int rkisp1_path_count(struct rkisp1_device *rkisp1)
{
return rkisp1_has_feature(rkisp1, SELF_PATH) ? 2 : 1;
}
/*
* rkisp1_sd_adjust_crop_rect - adjust a rectangle to fit into another rectangle.
*
* @crop: rectangle to adjust.
* @bounds: rectangle used as bounds.
*/
void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
const struct v4l2_rect *bounds);
/*
* rkisp1_sd_adjust_crop - adjust a rectangle to fit into media bus format
*
* @crop: rectangle to adjust.
* @bounds: media bus format used as bounds.
*/
void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
const struct v4l2_mbus_framefmt *bounds);
void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern,
const u32 input[4], u32 output[4]);
/*
* rkisp1_mbus_info_get_by_code - get the isp info of the media bus code
*
* @mbus_code: the media bus code
*/
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code);
/*
* rkisp1_params_pre_configure - Configure the params before stream start
*
* @params: pointer to rkisp1_params
* @bayer_pat: the bayer pattern on the isp video sink pad
* @quantization: the quantization configured on the isp's src pad
* @ycbcr_encoding: the ycbcr_encoding configured on the isp's src pad
*
* This function is called by the ISP entity just before the ISP gets started.
* It applies the initial ISP parameters from the first params buffer, but
* skips LSC as it needs to be configured after the ISP is started.
*/
void rkisp1_params_pre_configure(struct rkisp1_params *params,
enum rkisp1_fmt_raw_pat_type bayer_pat,
enum v4l2_quantization quantization,
enum v4l2_ycbcr_encoding ycbcr_encoding);
/*
* rkisp1_params_post_configure - Configure the params after stream start
*
* @params: pointer to rkisp1_params
*
* This function is called by the ISP entity just after the ISP gets started.
* It applies the initial ISP LSC parameters from the first params buffer.
*/
void rkisp1_params_post_configure(struct rkisp1_params *params);
/* rkisp1_params_disable - disable all parameters.
* This function is called by the isp entity upon stream start
* when capturing bayer format.
*
* @params: pointer to rkisp1_params.
*/
void rkisp1_params_disable(struct rkisp1_params *params);
/* irq handlers */
irqreturn_t rkisp1_isp_isr(int irq, void *ctx);
irqreturn_t rkisp1_csi_isr(int irq, void *ctx);
irqreturn_t rkisp1_capture_isr(int irq, void *ctx);
void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris);
void rkisp1_params_isr(struct rkisp1_device *rkisp1);
/* register/unregisters functions of the entities */
int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1);
void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1);
int rkisp1_isp_register(struct rkisp1_device *rkisp1);
void rkisp1_isp_unregister(struct rkisp1_device *rkisp1);
int rkisp1_resizer_devs_register(struct rkisp1_device *rkisp1);
void rkisp1_resizer_devs_unregister(struct rkisp1_device *rkisp1);
int rkisp1_stats_register(struct rkisp1_device *rkisp1);
void rkisp1_stats_unregister(struct rkisp1_device *rkisp1);
int rkisp1_params_register(struct rkisp1_device *rkisp1);
void rkisp1_params_unregister(struct rkisp1_device *rkisp1);
#if IS_ENABLED(CONFIG_DEBUG_FS)
void rkisp1_debug_init(struct rkisp1_device *rkisp1);
void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1);
#else
static inline void rkisp1_debug_init(struct rkisp1_device *rkisp1)
{
}
static inline void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1)
{
}
#endif
#endif /* _RKISP1_COMMON_H */
@@ -0,0 +1,518 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip ISP1 Driver - CSI-2 Receiver
*
* Copyright (C) 2019 Collabora, Ltd.
* Copyright (C) 2022 Ideas on Board
*
* Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/lockdep.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include "rkisp1-common.h"
#include "rkisp1-csi.h"
#define RKISP1_CSI_DEV_NAME RKISP1_DRIVER_NAME "_csi"
#define RKISP1_CSI_DEF_FMT MEDIA_BUS_FMT_SRGGB10_1X10
static inline struct rkisp1_csi *to_rkisp1_csi(struct v4l2_subdev *sd)
{
return container_of(sd, struct rkisp1_csi, sd);
}
int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd,
struct rkisp1_sensor_async *s_asd,
unsigned int source_pad)
{
struct rkisp1_csi *csi = &rkisp1->csi;
int ret;
s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler,
V4L2_CID_PIXEL_RATE);
if (!s_asd->pixel_rate_ctrl) {
dev_err(rkisp1->dev, "No pixel rate control in subdev %s\n",
sd->name);
return -EINVAL;
}
/* Create the link from the sensor to the CSI receiver. */
ret = media_create_pad_link(&sd->entity, source_pad,
&csi->sd.entity, RKISP1_CSI_PAD_SINK,
!s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
if (ret) {
dev_err(csi->rkisp1->dev, "failed to link src pad of %s\n",
sd->name);
return ret;
}
return 0;
}
static int rkisp1_csi_config(struct rkisp1_csi *csi,
const struct rkisp1_sensor_async *sensor,
const struct rkisp1_mbus_info *format)
{
struct rkisp1_device *rkisp1 = csi->rkisp1;
unsigned int lanes = sensor->lanes;
u32 mipi_ctrl;
if (lanes < 1 || lanes > 4)
return -EINVAL;
mipi_ctrl = RKISP1_CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA;
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, mipi_ctrl);
/* V12 could also use a newer csi2-host, but we don't want that yet */
if (rkisp1->info->isp_ver == RKISP1_V12)
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CSI0_CTRL0, 0);
/* Configure Data Type and Virtual Channel */
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL,
RKISP1_CIF_MIPI_DATA_SEL_DT(format->mipi_dt) |
RKISP1_CIF_MIPI_DATA_SEL_VC(0));
/* Clear MIPI interrupts */
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);
/*
* Disable RKISP1_CIF_MIPI_ERR_DPHY interrupt here temporary for
* isp bus may be dead when switch isp.
*/
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
RKISP1_CIF_MIPI_FRAME_END | RKISP1_CIF_MIPI_ERR_CSI |
RKISP1_CIF_MIPI_ERR_DPHY |
RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(0x03) |
RKISP1_CIF_MIPI_ADD_DATA_OVFLW);
dev_dbg(rkisp1->dev, "\n MIPI_CTRL 0x%08x\n"
" MIPI_IMG_DATA_SEL 0x%08x\n"
" MIPI_STATUS 0x%08x\n"
" MIPI_IMSC 0x%08x\n",
rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL),
rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL),
rkisp1_read(rkisp1, RKISP1_CIF_MIPI_STATUS),
rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC));
return 0;
}
static void rkisp1_csi_enable(struct rkisp1_csi *csi)
{
struct rkisp1_device *rkisp1 = csi->rkisp1;
u32 val;
val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
val | RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA);
}
static void rkisp1_csi_disable(struct rkisp1_csi *csi)
{
struct rkisp1_device *rkisp1 = csi->rkisp1;
u32 val;
/* Mask MIPI interrupts. */
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0);
/* Flush posted writes */
rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
/*
* Wait until the IRQ handler has ended. The IRQ handler may get called
* even after this, but it will return immediately as the MIPI
* interrupts have been masked.
*/
synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MIPI]);
/* Clear MIPI interrupt status */
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);
val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
val & (~RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA));
}
static int rkisp1_csi_start(struct rkisp1_csi *csi,
const struct rkisp1_sensor_async *sensor,
const struct rkisp1_mbus_info *format)
{
struct rkisp1_device *rkisp1 = csi->rkisp1;
union phy_configure_opts opts;
struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
s64 pixel_clock;
int ret;
ret = rkisp1_csi_config(csi, sensor, format);
if (ret)
return ret;
pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl);
if (!pixel_clock) {
dev_err(rkisp1->dev, "Invalid pixel rate value\n");
return -EINVAL;
}
phy_mipi_dphy_get_default_config(pixel_clock, format->bus_width,
sensor->lanes, cfg);
phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY);
phy_configure(csi->dphy, &opts);
phy_power_on(csi->dphy);
rkisp1_csi_enable(csi);
/*
* CIF spec says to wait for sufficient time after enabling
* the MIPI interface and before starting the sensor output.
*/
usleep_range(1000, 1200);
return 0;
}
static void rkisp1_csi_stop(struct rkisp1_csi *csi)
{
rkisp1_csi_disable(csi);
phy_power_off(csi->dphy);
}
irqreturn_t rkisp1_csi_isr(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
u32 val, status;
if (!rkisp1->irqs_enabled)
return IRQ_NONE;
status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS);
if (!status)
return IRQ_NONE;
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, status);
/*
* Disable DPHY errctrl interrupt, because this dphy
* erctrl signal is asserted until the next changes
* of line state. This time is may be too long and cpu
* is hold in this interrupt.
*/
if (status & RKISP1_CIF_MIPI_ERR_CTRL(0x0f)) {
val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
val & ~RKISP1_CIF_MIPI_ERR_CTRL(0x0f));
rkisp1->csi.is_dphy_errctrl_disabled = true;
}
/*
* Enable DPHY errctrl interrupt again, if mipi have receive
* the whole frame without any error.
*/
if (status == RKISP1_CIF_MIPI_FRAME_END) {
/*
* Enable DPHY errctrl interrupt again, if mipi have receive
* the whole frame without any error.
*/
if (rkisp1->csi.is_dphy_errctrl_disabled) {
val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
val |= RKISP1_CIF_MIPI_ERR_CTRL(0x0f);
rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, val);
rkisp1->csi.is_dphy_errctrl_disabled = false;
}
} else {
rkisp1->debug.mipi_error++;
}
return IRQ_HANDLED;
}
/* ----------------------------------------------------------------------------
* Subdev pad operations
*/
static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
unsigned int i;
int pos = 0;
if (code->pad == RKISP1_CSI_PAD_SRC) {
const struct v4l2_mbus_framefmt *sink_fmt;
if (code->index)
return -EINVAL;
sink_fmt = v4l2_subdev_state_get_format(sd_state,
RKISP1_CSI_PAD_SINK);
code->code = sink_fmt->code;
return 0;
}
for (i = 0; ; i++) {
const struct rkisp1_mbus_info *fmt =
rkisp1_mbus_info_get_by_index(i);
if (!fmt)
return -EINVAL;
if (!(fmt->direction & RKISP1_ISP_SD_SINK))
continue;
if (code->index == pos) {
code->code = fmt->mbus_code;
return 0;
}
pos++;
}
return -EINVAL;
}
static int rkisp1_csi_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK);
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC);
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
sink_fmt->field = V4L2_FIELD_NONE;
sink_fmt->code = RKISP1_CSI_DEF_FMT;
*src_fmt = *sink_fmt;
return 0;
}
static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkisp1_csi *csi = to_rkisp1_csi(sd);
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
/* The format on the source pad always matches the sink pad. */
if (fmt->pad == RKISP1_CSI_PAD_SRC)
return v4l2_subdev_get_fmt(sd, sd_state, fmt);
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK);
sink_fmt->code = fmt->format.code;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) {
sink_fmt->code = RKISP1_CSI_DEF_FMT;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
}
sink_fmt->width = clamp_t(u32, fmt->format.width,
RKISP1_ISP_MIN_WIDTH,
csi->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, fmt->format.height,
RKISP1_ISP_MIN_HEIGHT,
csi->rkisp1->info->max_height);
fmt->format = *sink_fmt;
/* Propagate the format to the source pad. */
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SRC);
*src_fmt = *sink_fmt;
return 0;
}
/* ----------------------------------------------------------------------------
* Subdev video operations
*/
static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable)
{
struct rkisp1_csi *csi = to_rkisp1_csi(sd);
struct rkisp1_device *rkisp1 = csi->rkisp1;
const struct v4l2_mbus_framefmt *sink_fmt;
const struct rkisp1_mbus_info *format;
struct rkisp1_sensor_async *source_asd;
struct v4l2_async_connection *asc;
struct v4l2_subdev_state *sd_state;
struct media_pad *source_pad;
struct v4l2_subdev *source;
int ret;
if (!enable) {
v4l2_subdev_call(csi->source, video, s_stream, false);
rkisp1_csi_stop(csi);
return 0;
}
source_pad = media_entity_remote_source_pad_unique(&sd->entity);
if (IS_ERR(source_pad)) {
dev_dbg(rkisp1->dev, "Failed to get source for CSI: %pe\n",
source_pad);
return -EPIPE;
}
source = media_entity_to_v4l2_subdev(source_pad->entity);
if (!source) {
/* This should really not happen, so is not worth a message. */
return -EPIPE;
}
asc = v4l2_async_connection_unique(source);
if (!asc)
return -EPIPE;
source_asd = container_of(asc, struct rkisp1_sensor_async, asd);
if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY)
return -EINVAL;
sd_state = v4l2_subdev_lock_and_get_active_state(sd);
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_CSI_PAD_SINK);
format = rkisp1_mbus_info_get_by_code(sink_fmt->code);
v4l2_subdev_unlock_state(sd_state);
ret = rkisp1_csi_start(csi, source_asd, format);
if (ret)
return ret;
ret = v4l2_subdev_call(source, video, s_stream, true);
if (ret) {
rkisp1_csi_stop(csi);
return ret;
}
csi->source = source;
return 0;
}
/* ----------------------------------------------------------------------------
* Registration
*/
static const struct media_entity_operations rkisp1_csi_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = {
.s_stream = rkisp1_csi_s_stream,
};
static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = {
.enum_mbus_code = rkisp1_csi_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = rkisp1_csi_set_fmt,
};
static const struct v4l2_subdev_ops rkisp1_csi_ops = {
.video = &rkisp1_csi_video_ops,
.pad = &rkisp1_csi_pad_ops,
};
static const struct v4l2_subdev_internal_ops rkisp1_csi_internal_ops = {
.init_state = rkisp1_csi_init_state,
};
int rkisp1_csi_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_csi *csi = &rkisp1->csi;
struct media_pad *pads;
struct v4l2_subdev *sd;
int ret;
csi->rkisp1 = rkisp1;
sd = &csi->sd;
v4l2_subdev_init(sd, &rkisp1_csi_ops);
sd->internal_ops = &rkisp1_csi_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
sd->entity.ops = &rkisp1_csi_media_ops;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
sd->owner = THIS_MODULE;
strscpy(sd->name, RKISP1_CSI_DEV_NAME, sizeof(sd->name));
pads = csi->pads;
pads[RKISP1_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
MEDIA_PAD_FL_MUST_CONNECT;
pads[RKISP1_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE |
MEDIA_PAD_FL_MUST_CONNECT;
ret = media_entity_pads_init(&sd->entity, RKISP1_CSI_PAD_NUM, pads);
if (ret)
goto err_entity_cleanup;
ret = v4l2_subdev_init_finalize(sd);
if (ret)
goto err_entity_cleanup;
ret = v4l2_device_register_subdev(&csi->rkisp1->v4l2_dev, sd);
if (ret) {
dev_err(sd->dev, "Failed to register csi receiver subdev\n");
goto err_subdev_cleanup;
}
return 0;
err_subdev_cleanup:
v4l2_subdev_cleanup(sd);
err_entity_cleanup:
media_entity_cleanup(&sd->entity);
csi->rkisp1 = NULL;
return ret;
}
void rkisp1_csi_unregister(struct rkisp1_device *rkisp1)
{
struct rkisp1_csi *csi = &rkisp1->csi;
if (!csi->rkisp1)
return;
v4l2_device_unregister_subdev(&csi->sd);
v4l2_subdev_cleanup(&csi->sd);
media_entity_cleanup(&csi->sd.entity);
}
int rkisp1_csi_init(struct rkisp1_device *rkisp1)
{
struct rkisp1_csi *csi = &rkisp1->csi;
csi->rkisp1 = rkisp1;
csi->dphy = devm_phy_get(rkisp1->dev, "dphy");
if (IS_ERR(csi->dphy))
return dev_err_probe(rkisp1->dev, PTR_ERR(csi->dphy),
"Couldn't get the MIPI D-PHY\n");
phy_init(csi->dphy);
return 0;
}
void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1)
{
struct rkisp1_csi *csi = &rkisp1->csi;
phy_exit(csi->dphy);
}
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* Rockchip ISP1 Driver - CSI-2 Receiver
*
* Copyright (C) 2019 Collabora, Ltd.
* Copyright (C) 2022 Ideas on Board
*
* Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
#ifndef _RKISP1_CSI_H
#define _RKISP1_CSI_H
struct rkisp1_csi;
struct rkisp1_device;
struct rkisp1_sensor_async;
int rkisp1_csi_init(struct rkisp1_device *rkisp1);
void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1);
int rkisp1_csi_register(struct rkisp1_device *rkisp1);
void rkisp1_csi_unregister(struct rkisp1_device *rkisp1);
int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd,
struct rkisp1_sensor_async *s_asd,
unsigned int source_pad);
#endif /* _RKISP1_CSI_H */
@@ -0,0 +1,249 @@
// 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/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/minmax.h>
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include "rkisp1-common.h"
#include "rkisp1-regs.h"
struct rkisp1_debug_register {
u32 reg;
u32 shd;
const char * const name;
};
#define RKISP1_DEBUG_REG(name) { RKISP1_CIF_##name, 0, #name }
#define RKISP1_DEBUG_SHD_REG(name) { \
RKISP1_CIF_##name, RKISP1_CIF_##name##_SHD, #name \
}
/* Keep this up-to-date when adding new registers. */
#define RKISP1_MAX_REG_LENGTH 21
static int rkisp1_debug_dump_regs(struct rkisp1_device *rkisp1,
struct seq_file *m, unsigned int offset,
const struct rkisp1_debug_register *regs)
{
const int width = RKISP1_MAX_REG_LENGTH;
u32 val, shd;
int ret;
ret = pm_runtime_get_if_in_use(rkisp1->dev);
if (ret <= 0)
return ret ? : -ENODATA;
for (; regs->name; ++regs) {
val = rkisp1_read(rkisp1, offset + regs->reg);
if (regs->shd) {
shd = rkisp1_read(rkisp1, offset + regs->shd);
seq_printf(m, "%*s: 0x%08x/0x%08x\n", width, regs->name,
val, shd);
} else {
seq_printf(m, "%*s: 0x%08x\n", width, regs->name, val);
}
}
pm_runtime_put(rkisp1->dev);
return 0;
}
static int rkisp1_debug_dump_core_regs_show(struct seq_file *m, void *p)
{
static const struct rkisp1_debug_register registers[] = {
RKISP1_DEBUG_REG(VI_CCL),
RKISP1_DEBUG_REG(VI_ICCL),
RKISP1_DEBUG_REG(VI_IRCL),
RKISP1_DEBUG_REG(VI_DPCL),
RKISP1_DEBUG_REG(MI_CTRL),
RKISP1_DEBUG_REG(MI_BYTE_CNT),
RKISP1_DEBUG_REG(MI_CTRL_SHD),
RKISP1_DEBUG_REG(MI_RIS),
RKISP1_DEBUG_REG(MI_STATUS),
RKISP1_DEBUG_REG(MI_DMA_CTRL),
RKISP1_DEBUG_REG(MI_DMA_STATUS),
{ /* Sentinel */ },
};
struct rkisp1_device *rkisp1 = m->private;
return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
}
DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_core_regs);
static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p)
{
static const struct rkisp1_debug_register registers[] = {
RKISP1_DEBUG_REG(ISP_CTRL),
RKISP1_DEBUG_REG(ISP_ACQ_PROP),
RKISP1_DEBUG_REG(ISP_FLAGS_SHD),
RKISP1_DEBUG_REG(ISP_RIS),
RKISP1_DEBUG_REG(ISP_ERR),
RKISP1_DEBUG_SHD_REG(ISP_IS_H_OFFS),
RKISP1_DEBUG_SHD_REG(ISP_IS_V_OFFS),
RKISP1_DEBUG_SHD_REG(ISP_IS_H_SIZE),
RKISP1_DEBUG_SHD_REG(ISP_IS_V_SIZE),
{ /* Sentinel */ },
};
struct rkisp1_device *rkisp1 = m->private;
return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
}
DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_isp_regs);
static int rkisp1_debug_dump_rsz_regs_show(struct seq_file *m, void *p)
{
static const struct rkisp1_debug_register registers[] = {
RKISP1_DEBUG_SHD_REG(RSZ_CTRL),
RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HY),
RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCB),
RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCR),
RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VY),
RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VC),
RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HY),
RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HC),
RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VY),
RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VC),
{ /* Sentinel */ },
};
struct rkisp1_resizer *rsz = m->private;
return rkisp1_debug_dump_regs(rsz->rkisp1, m, rsz->regs_base, registers);
}
DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_rsz_regs);
static int rkisp1_debug_dump_mi_mp_show(struct seq_file *m, void *p)
{
static const struct rkisp1_debug_register registers[] = {
RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT),
RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT2),
RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_SHD),
RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT),
RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT),
RKISP1_DEBUG_REG(MI_MP_Y_SIZE_SHD),
RKISP1_DEBUG_REG(MI_MP_Y_OFFS_CNT_SHD),
{ /* Sentinel */ },
};
struct rkisp1_device *rkisp1 = m->private;
return rkisp1_debug_dump_regs(rkisp1, m, 0, registers);
}
DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_mi_mp);
#define RKISP1_DEBUG_DATA_COUNT_BINS 32
#define RKISP1_DEBUG_DATA_COUNT_STEP (4096 / RKISP1_DEBUG_DATA_COUNT_BINS)
static int rkisp1_debug_input_status_show(struct seq_file *m, void *p)
{
struct rkisp1_device *rkisp1 = m->private;
u16 data_count[RKISP1_DEBUG_DATA_COUNT_BINS] = { };
unsigned int hsync_count = 0;
unsigned int vsync_count = 0;
unsigned int i;
u32 data;
u32 val;
int ret;
ret = pm_runtime_get_if_in_use(rkisp1->dev);
if (ret <= 0)
return ret ? : -ENODATA;
/* Sample the ISP input port status 10000 times with a 1µs interval. */
for (i = 0; i < 10000; ++i) {
val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_FLAGS_SHD);
data = (val & RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK)
>> RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT;
data_count[data / RKISP1_DEBUG_DATA_COUNT_STEP]++;
if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC)
hsync_count++;
if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC)
vsync_count++;
udelay(1);
}
pm_runtime_put(rkisp1->dev);
seq_printf(m, "vsync: %u, hsync: %u\n", vsync_count, hsync_count);
seq_puts(m, "data:\n");
for (i = 0; i < ARRAY_SIZE(data_count); ++i)
seq_printf(m, "- [%04u:%04u]: %u\n",
i * RKISP1_DEBUG_DATA_COUNT_STEP,
(i + 1) * RKISP1_DEBUG_DATA_COUNT_STEP - 1,
data_count[i]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_input_status);
void rkisp1_debug_init(struct rkisp1_device *rkisp1)
{
struct rkisp1_debug *debug = &rkisp1->debug;
struct dentry *regs_dir;
debug->debugfs_dir = debugfs_create_dir(dev_name(rkisp1->dev), NULL);
debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir,
&debug->data_loss);
debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir,
&debug->outform_size_error);
debugfs_create_ulong("img_stabilization_size_error", 0444,
debug->debugfs_dir,
&debug->img_stabilization_size_error);
debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir,
&debug->inform_size_error);
debugfs_create_ulong("irq_delay", 0444, debug->debugfs_dir,
&debug->irq_delay);
debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir,
&debug->mipi_error);
debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir,
&debug->stats_error);
debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir,
&debug->stop_timeout[RKISP1_MAINPATH]);
debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir,
&debug->stop_timeout[RKISP1_SELFPATH]);
debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir,
&debug->frame_drop[RKISP1_MAINPATH]);
debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir,
&debug->frame_drop[RKISP1_SELFPATH]);
debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir,
&debug->complete_frames);
debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1,
&rkisp1_debug_input_status_fops);
regs_dir = debugfs_create_dir("regs", debug->debugfs_dir);
debugfs_create_file("core", 0444, regs_dir, rkisp1,
&rkisp1_debug_dump_core_regs_fops);
debugfs_create_file("isp", 0444, regs_dir, rkisp1,
&rkisp1_debug_dump_isp_regs_fops);
debugfs_create_file("mrsz", 0444, regs_dir,
&rkisp1->resizer_devs[RKISP1_MAINPATH],
&rkisp1_debug_dump_rsz_regs_fops);
debugfs_create_file("srsz", 0444, regs_dir,
&rkisp1->resizer_devs[RKISP1_SELFPATH],
&rkisp1_debug_dump_rsz_regs_fops);
debugfs_create_file("mi_mp", 0444, regs_dir, rkisp1,
&rkisp1_debug_dump_mi_mp_fops);
}
void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1)
{
debugfs_remove_recursive(rkisp1->debug.debugfs_dir);
}
@@ -0,0 +1,828 @@
// 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");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,766 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip ISP1 Driver - V4l resizer device
*
* Copyright (C) 2019 Collabora, Ltd.
*
* Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
#include "rkisp1-common.h"
#define RKISP1_RSZ_SP_DEV_NAME RKISP1_DRIVER_NAME "_resizer_selfpath"
#define RKISP1_RSZ_MP_DEV_NAME RKISP1_DRIVER_NAME "_resizer_mainpath"
#define RKISP1_DEF_FMT MEDIA_BUS_FMT_YUYV8_2X8
#define RKISP1_DEF_PIXEL_ENC V4L2_PIXEL_ENC_YUV
struct rkisp1_rsz_yuv_mbus_info {
u32 mbus_code;
u32 hdiv;
u32 vdiv;
};
static const struct rkisp1_rsz_yuv_mbus_info rkisp1_rsz_yuv_src_formats[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, /* YUV422 */
.hdiv = 2,
.vdiv = 1,
},
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_1_5X8, /* YUV420 */
.hdiv = 2,
.vdiv = 2,
},
};
static const struct rkisp1_rsz_yuv_mbus_info *rkisp1_rsz_get_yuv_mbus_info(u32 mbus_code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rkisp1_rsz_yuv_src_formats); i++) {
if (rkisp1_rsz_yuv_src_formats[i].mbus_code == mbus_code)
return &rkisp1_rsz_yuv_src_formats[i];
}
return NULL;
}
enum rkisp1_shadow_regs_when {
RKISP1_SHADOW_REGS_SYNC,
RKISP1_SHADOW_REGS_ASYNC,
};
struct rkisp1_rsz_config {
/* constrains */
const int max_rsz_width;
const int max_rsz_height;
const int min_rsz_width;
const int min_rsz_height;
/* registers */
struct {
u32 yuvmode_mask;
u32 rawmode_mask;
u32 h_offset;
u32 v_offset;
u32 h_size;
u32 v_size;
} dual_crop;
};
static const struct rkisp1_rsz_config rkisp1_rsz_config_mp = {
/* constraints */
.max_rsz_width = RKISP1_RSZ_MP_SRC_MAX_WIDTH,
.max_rsz_height = RKISP1_RSZ_MP_SRC_MAX_HEIGHT,
.min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH,
.min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT,
/* registers */
.dual_crop = {
.yuvmode_mask = RKISP1_CIF_DUAL_CROP_MP_MODE_YUV,
.rawmode_mask = RKISP1_CIF_DUAL_CROP_MP_MODE_RAW,
.h_offset = RKISP1_CIF_DUAL_CROP_M_H_OFFS,
.v_offset = RKISP1_CIF_DUAL_CROP_M_V_OFFS,
.h_size = RKISP1_CIF_DUAL_CROP_M_H_SIZE,
.v_size = RKISP1_CIF_DUAL_CROP_M_V_SIZE,
},
};
static const struct rkisp1_rsz_config rkisp1_rsz_config_sp = {
/* constraints */
.max_rsz_width = RKISP1_RSZ_SP_SRC_MAX_WIDTH,
.max_rsz_height = RKISP1_RSZ_SP_SRC_MAX_HEIGHT,
.min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH,
.min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT,
/* registers */
.dual_crop = {
.yuvmode_mask = RKISP1_CIF_DUAL_CROP_SP_MODE_YUV,
.rawmode_mask = RKISP1_CIF_DUAL_CROP_SP_MODE_RAW,
.h_offset = RKISP1_CIF_DUAL_CROP_S_H_OFFS,
.v_offset = RKISP1_CIF_DUAL_CROP_S_V_OFFS,
.h_size = RKISP1_CIF_DUAL_CROP_S_H_SIZE,
.v_size = RKISP1_CIF_DUAL_CROP_S_V_SIZE,
},
};
static inline u32 rkisp1_rsz_read(struct rkisp1_resizer *rsz, u32 offset)
{
return rkisp1_read(rsz->rkisp1, rsz->regs_base + offset);
}
static inline void rkisp1_rsz_write(struct rkisp1_resizer *rsz, u32 offset,
u32 value)
{
rkisp1_write(rsz->rkisp1, rsz->regs_base + offset, value);
}
/* ----------------------------------------------------------------------------
* Dual crop hw configs
*/
static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz,
enum rkisp1_shadow_regs_when when)
{
u32 dc_ctrl = rkisp1_read(rsz->rkisp1, RKISP1_CIF_DUAL_CROP_CTRL);
u32 mask = ~(rsz->config->dual_crop.yuvmode_mask |
rsz->config->dual_crop.rawmode_mask);
dc_ctrl &= mask;
if (when == RKISP1_SHADOW_REGS_ASYNC)
dc_ctrl |= RKISP1_CIF_DUAL_CROP_GEN_CFG_UPD;
else
dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD;
rkisp1_write(rsz->rkisp1, RKISP1_CIF_DUAL_CROP_CTRL, dc_ctrl);
}
/* configure dual-crop unit */
static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz,
const struct v4l2_subdev_state *sd_state)
{
struct rkisp1_device *rkisp1 = rsz->rkisp1;
const struct v4l2_mbus_framefmt *sink_fmt;
const struct v4l2_rect *sink_crop;
u32 dc_ctrl;
sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
if (sink_crop->width == sink_fmt->width &&
sink_crop->height == sink_fmt->height &&
sink_crop->left == 0 && sink_crop->top == 0) {
rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_SYNC);
dev_dbg(rkisp1->dev, "capture %d crop disabled\n", rsz->id);
return;
}
dc_ctrl = rkisp1_read(rkisp1, RKISP1_CIF_DUAL_CROP_CTRL);
rkisp1_write(rkisp1, rsz->config->dual_crop.h_offset, sink_crop->left);
rkisp1_write(rkisp1, rsz->config->dual_crop.v_offset, sink_crop->top);
rkisp1_write(rkisp1, rsz->config->dual_crop.h_size, sink_crop->width);
rkisp1_write(rkisp1, rsz->config->dual_crop.v_size, sink_crop->height);
dc_ctrl |= rsz->config->dual_crop.yuvmode_mask;
dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD;
rkisp1_write(rkisp1, RKISP1_CIF_DUAL_CROP_CTRL, dc_ctrl);
dev_dbg(rkisp1->dev, "stream %d crop: %dx%d -> %dx%d\n", rsz->id,
sink_fmt->width, sink_fmt->height,
sink_crop->width, sink_crop->height);
}
/* ----------------------------------------------------------------------------
* Resizer hw configs
*/
static void rkisp1_rsz_update_shadow(struct rkisp1_resizer *rsz,
enum rkisp1_shadow_regs_when when)
{
u32 ctrl_cfg = rkisp1_rsz_read(rsz, RKISP1_CIF_RSZ_CTRL);
if (when == RKISP1_SHADOW_REGS_ASYNC)
ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD_AUTO;
else
ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD;
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, ctrl_cfg);
}
static u32 rkisp1_rsz_calc_ratio(u32 len_sink, u32 len_src)
{
if (len_sink < len_src)
return ((len_sink - 1) * RKISP1_CIF_RSZ_SCALER_FACTOR) /
(len_src - 1);
return ((len_src - 1) * RKISP1_CIF_RSZ_SCALER_FACTOR) /
(len_sink - 1) + 1;
}
static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz,
enum rkisp1_shadow_regs_when when)
{
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, 0);
if (when == RKISP1_SHADOW_REGS_SYNC)
rkisp1_rsz_update_shadow(rsz, when);
}
static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz,
const struct v4l2_rect *sink_y,
const struct v4l2_rect *sink_c,
const struct v4l2_area *src_y,
const struct v4l2_area *src_c,
enum rkisp1_shadow_regs_when when)
{
u32 ratio, rsz_ctrl = 0;
unsigned int i;
/* No phase offset */
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HY, 0);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HC, 0);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VY, 0);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VC, 0);
/* Linear interpolation */
for (i = 0; i < 64; i++) {
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT_ADDR, i);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT, i);
}
if (sink_y->width != src_y->width) {
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HY_ENABLE;
if (sink_y->width < src_y->width)
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HY_UP;
ratio = rkisp1_rsz_calc_ratio(sink_y->width, src_y->width);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HY, ratio);
}
if (sink_c->width != src_c->width) {
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HC_ENABLE;
if (sink_c->width < src_c->width)
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HC_UP;
ratio = rkisp1_rsz_calc_ratio(sink_c->width, src_c->width);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCB, ratio);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCR, ratio);
}
if (sink_y->height != src_y->height) {
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VY_ENABLE;
if (sink_y->height < src_y->height)
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VY_UP;
ratio = rkisp1_rsz_calc_ratio(sink_y->height, src_y->height);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VY, ratio);
}
if (sink_c->height != src_c->height) {
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VC_ENABLE;
if (sink_c->height < src_c->height)
rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VC_UP;
ratio = rkisp1_rsz_calc_ratio(sink_c->height, src_c->height);
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VC, ratio);
}
rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, rsz_ctrl);
rkisp1_rsz_update_shadow(rsz, when);
}
static void rkisp1_rsz_config(struct rkisp1_resizer *rsz,
const struct v4l2_subdev_state *sd_state,
enum rkisp1_shadow_regs_when when)
{
const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info;
const struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
const struct v4l2_rect *sink_y;
struct v4l2_area src_y, src_c;
struct v4l2_rect sink_c;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC);
sink_yuv_info = rkisp1_rsz_get_yuv_mbus_info(sink_fmt->code);
src_yuv_info = rkisp1_rsz_get_yuv_mbus_info(src_fmt->code);
/*
* The resizer only works on yuv formats, so return if it is bayer
* format.
*/
if (!sink_yuv_info) {
rkisp1_rsz_disable(rsz, when);
return;
}
sink_y = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
sink_c.width = sink_y->width / sink_yuv_info->hdiv;
sink_c.height = sink_y->height / sink_yuv_info->vdiv;
src_y.width = src_fmt->width;
src_y.height = src_fmt->height;
src_c.width = src_y.width / src_yuv_info->hdiv;
src_c.height = src_y.height / src_yuv_info->vdiv;
/*
* The resizer is used not only to change the dimensions of the frame
* but also to change the subsampling for YUV formats (for instance
* converting from 4:2:2 to 4:2:0). Check both the luma and chroma
* dimensions to decide whether or not to enable the resizer.
*/
dev_dbg(rsz->rkisp1->dev,
"stream %u rsz/scale: Y %ux%u -> %ux%u, CbCr %ux%u -> %ux%u\n",
rsz->id, sink_y->width, sink_y->height,
src_fmt->width, src_fmt->height,
sink_c.width, sink_c.height, src_c.width, src_c.height);
if (sink_y->width == src_y.width && sink_y->height == src_y.height &&
sink_c.width == src_c.width && sink_c.height == src_c.height) {
rkisp1_rsz_disable(rsz, when);
return;
}
/* Set values in the hardware. */
rkisp1_rsz_config_regs(rsz, sink_y, &sink_c, &src_y, &src_c, when);
}
/* ----------------------------------------------------------------------------
* Subdev pad operations
*/
static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct rkisp1_resizer *rsz =
container_of(sd, struct rkisp1_resizer, sd);
unsigned int index = code->index;
unsigned int i;
if (code->pad == RKISP1_RSZ_PAD_SRC) {
/* supported mbus codes on the src are the same as in the capture */
struct rkisp1_capture *cap = &rsz->rkisp1->capture_devs[rsz->id];
return rkisp1_cap_enum_mbus_codes(cap, code);
}
/*
* The selfpath capture doesn't support bayer formats. Therefore the selfpath resizer
* should support only YUV422 on the sink pad
*/
if (rsz->id == RKISP1_SELFPATH) {
if (code->index > 0)
return -EINVAL;
code->code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
/*
* Supported mbus codes on the sink pad are the same as on the ISP
* source pad.
*/
for (i = 0; ; i++) {
const struct rkisp1_mbus_info *fmt =
rkisp1_mbus_info_get_by_index(i);
if (!fmt)
break;
if (!(fmt->direction & RKISP1_ISP_SD_SRC))
continue;
if (!index) {
code->code = fmt->mbus_code;
return 0;
}
index--;
}
return -EINVAL;
}
static int rkisp1_rsz_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
struct v4l2_rect *sink_crop;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC);
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
sink_fmt->field = V4L2_FIELD_NONE;
sink_fmt->code = RKISP1_DEF_FMT;
sink_fmt->colorspace = V4L2_COLORSPACE_SRGB;
sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
sink_crop->width = RKISP1_DEFAULT_WIDTH;
sink_crop->height = RKISP1_DEFAULT_HEIGHT;
sink_crop->left = 0;
sink_crop->top = 0;
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
*src_fmt = *sink_fmt;
/* NOTE: there is no crop in the source pad, only in the sink */
return 0;
}
static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz,
struct v4l2_subdev_state *sd_state,
struct v4l2_mbus_framefmt *format)
{
const struct rkisp1_mbus_info *sink_mbus_info;
struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC);
sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
/* for YUV formats, userspace can change the mbus code on the src pad if it is supported */
if (sink_mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
rkisp1_rsz_get_yuv_mbus_info(format->code))
src_fmt->code = format->code;
src_fmt->width = clamp_t(u32, format->width,
rsz->config->min_rsz_width,
rsz->config->max_rsz_width);
src_fmt->height = clamp_t(u32, format->height,
rsz->config->min_rsz_height,
rsz->config->max_rsz_height);
*format = *src_fmt;
}
static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz,
struct v4l2_subdev_state *sd_state,
struct v4l2_rect *r)
{
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_rect *sink_crop;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
/* Not crop for MP bayer raw data, or for devices lacking dual crop. */
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
if ((rsz->id == RKISP1_MAINPATH &&
mbus_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) ||
!rkisp1_has_feature(rsz->rkisp1, DUAL_CROP)) {
sink_crop->left = 0;
sink_crop->top = 0;
sink_crop->width = sink_fmt->width;
sink_crop->height = sink_fmt->height;
*r = *sink_crop;
return;
}
sink_crop->left = ALIGN(r->left, 2);
sink_crop->width = ALIGN(r->width, 2);
sink_crop->top = r->top;
sink_crop->height = r->height;
rkisp1_sd_adjust_crop(sink_crop, sink_fmt);
*r = *sink_crop;
}
static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
struct v4l2_subdev_state *sd_state,
struct v4l2_mbus_framefmt *format)
{
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
struct v4l2_rect *sink_crop;
bool is_yuv;
sink_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SINK);
src_fmt = v4l2_subdev_state_get_format(sd_state, RKISP1_RSZ_PAD_SRC);
sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
if (rsz->id == RKISP1_SELFPATH)
sink_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
else
sink_fmt->code = format->code;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) {
sink_fmt->code = RKISP1_DEF_FMT;
mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
}
sink_fmt->width = clamp_t(u32, format->width,
RKISP1_ISP_MIN_WIDTH,
rsz->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, format->height,
RKISP1_ISP_MIN_HEIGHT,
rsz->rkisp1->info->max_height);
/*
* Adjust the color space fields. Accept any color primaries and
* transfer function for both YUV and Bayer. For YUV any YCbCr encoding
* and quantization range is also accepted. For Bayer formats, the YCbCr
* encoding isn't applicable, and the quantization range can only be
* full.
*/
is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
sink_fmt->colorspace = format->colorspace ? :
(is_yuv ? V4L2_COLORSPACE_SRGB :
V4L2_COLORSPACE_RAW);
sink_fmt->xfer_func = format->xfer_func ? :
V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
if (is_yuv) {
sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
sink_fmt->quantization = format->quantization ? :
V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
sink_fmt->ycbcr_enc);
} else {
/*
* The YCbCr encoding isn't applicable for non-YUV formats, but
* V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
* should be ignored by userspace.
*/
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
}
*format = *sink_fmt;
/* Propagate the media bus code and color space to the source pad. */
src_fmt->code = sink_fmt->code;
src_fmt->colorspace = sink_fmt->colorspace;
src_fmt->xfer_func = sink_fmt->xfer_func;
src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
src_fmt->quantization = sink_fmt->quantization;
/* Update sink crop */
rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop);
}
static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkisp1_resizer *rsz =
container_of(sd, struct rkisp1_resizer, sd);
if (fmt->pad == RKISP1_RSZ_PAD_SINK)
rkisp1_rsz_set_sink_fmt(rsz, sd_state, &fmt->format);
else
rkisp1_rsz_set_src_fmt(rsz, sd_state, &fmt->format);
return 0;
}
static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
struct v4l2_mbus_framefmt *mf_sink;
int ret = 0;
if (sel->pad == RKISP1_RSZ_PAD_SRC)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
mf_sink = v4l2_subdev_state_get_format(sd_state,
RKISP1_RSZ_PAD_SINK);
sel->r.height = mf_sink->height;
sel->r.width = mf_sink->width;
sel->r.left = 0;
sel->r.top = 0;
break;
case V4L2_SEL_TGT_CROP:
sel->r = *v4l2_subdev_state_get_crop(sd_state,
RKISP1_RSZ_PAD_SINK);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
struct rkisp1_resizer *rsz =
container_of(sd, struct rkisp1_resizer, sd);
if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC)
return -EINVAL;
dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r);
return 0;
}
static const struct media_entity_operations rkisp1_rsz_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static const struct v4l2_subdev_pad_ops rkisp1_rsz_pad_ops = {
.enum_mbus_code = rkisp1_rsz_enum_mbus_code,
.get_selection = rkisp1_rsz_get_selection,
.set_selection = rkisp1_rsz_set_selection,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = rkisp1_rsz_set_fmt,
.link_validate = v4l2_subdev_link_validate_default,
};
/* ----------------------------------------------------------------------------
* Stream operations
*/
static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable)
{
struct rkisp1_resizer *rsz =
container_of(sd, struct rkisp1_resizer, sd);
struct rkisp1_device *rkisp1 = rsz->rkisp1;
struct rkisp1_capture *other = &rkisp1->capture_devs[rsz->id ^ 1];
enum rkisp1_shadow_regs_when when = RKISP1_SHADOW_REGS_SYNC;
bool has_self_path = rkisp1_has_feature(rkisp1, SELF_PATH);
struct v4l2_subdev_state *sd_state;
if (!enable) {
if (rkisp1_has_feature(rkisp1, DUAL_CROP))
rkisp1_dcrop_disable(rsz, RKISP1_SHADOW_REGS_ASYNC);
rkisp1_rsz_disable(rsz, RKISP1_SHADOW_REGS_ASYNC);
return 0;
}
if (has_self_path && other->is_streaming)
when = RKISP1_SHADOW_REGS_ASYNC;
sd_state = v4l2_subdev_lock_and_get_active_state(sd);
rkisp1_rsz_config(rsz, sd_state, when);
if (rkisp1_has_feature(rkisp1, DUAL_CROP))
rkisp1_dcrop_config(rsz, sd_state);
v4l2_subdev_unlock_state(sd_state);
return 0;
}
static const struct v4l2_subdev_video_ops rkisp1_rsz_video_ops = {
.s_stream = rkisp1_rsz_s_stream,
};
static const struct v4l2_subdev_ops rkisp1_rsz_ops = {
.video = &rkisp1_rsz_video_ops,
.pad = &rkisp1_rsz_pad_ops,
};
static const struct v4l2_subdev_internal_ops rkisp1_rsz_internal_ops = {
.init_state = rkisp1_rsz_init_state,
};
static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz)
{
if (!rsz->rkisp1)
return;
v4l2_device_unregister_subdev(&rsz->sd);
v4l2_subdev_cleanup(&rsz->sd);
media_entity_cleanup(&rsz->sd.entity);
}
static int rkisp1_rsz_register(struct rkisp1_resizer *rsz)
{
static const char * const dev_names[] = {
RKISP1_RSZ_MP_DEV_NAME,
RKISP1_RSZ_SP_DEV_NAME
};
struct media_pad *pads = rsz->pads;
struct v4l2_subdev *sd = &rsz->sd;
int ret;
if (rsz->id == RKISP1_SELFPATH) {
rsz->regs_base = RKISP1_CIF_SRSZ_BASE;
rsz->config = &rkisp1_rsz_config_sp;
} else {
rsz->regs_base = RKISP1_CIF_MRSZ_BASE;
rsz->config = &rkisp1_rsz_config_mp;
}
v4l2_subdev_init(sd, &rkisp1_rsz_ops);
sd->internal_ops = &rkisp1_rsz_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
sd->entity.ops = &rkisp1_rsz_media_ops;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
sd->owner = THIS_MODULE;
strscpy(sd->name, dev_names[rsz->id], sizeof(sd->name));
pads[RKISP1_RSZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
MEDIA_PAD_FL_MUST_CONNECT;
pads[RKISP1_RSZ_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE |
MEDIA_PAD_FL_MUST_CONNECT;
ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads);
if (ret)
goto err_entity_cleanup;
ret = v4l2_subdev_init_finalize(sd);
if (ret)
goto err_entity_cleanup;
ret = v4l2_device_register_subdev(&rsz->rkisp1->v4l2_dev, sd);
if (ret) {
dev_err(sd->dev, "Failed to register resizer subdev\n");
goto err_subdev_cleanup;
}
return 0;
err_subdev_cleanup:
v4l2_subdev_cleanup(sd);
err_entity_cleanup:
media_entity_cleanup(&sd->entity);
return ret;
}
int rkisp1_resizer_devs_register(struct rkisp1_device *rkisp1)
{
unsigned int dev_count = rkisp1_path_count(rkisp1);
unsigned int i;
int ret;
for (i = 0; i < dev_count; i++) {
struct rkisp1_resizer *rsz = &rkisp1->resizer_devs[i];
rsz->rkisp1 = rkisp1;
rsz->id = i;
ret = rkisp1_rsz_register(rsz);
if (ret) {
rsz->rkisp1 = NULL;
rkisp1_resizer_devs_unregister(rkisp1);
return ret;
}
}
return 0;
}
void rkisp1_resizer_devs_unregister(struct rkisp1_device *rkisp1)
{
struct rkisp1_resizer *mp = &rkisp1->resizer_devs[RKISP1_MAINPATH];
struct rkisp1_resizer *sp = &rkisp1->resizer_devs[RKISP1_SELFPATH];
rkisp1_rsz_unregister(mp);
rkisp1_rsz_unregister(sp);
}
@@ -0,0 +1,471 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip ISP1 Driver - Stats subdevice
*
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h> /* for ISP statistics */
#include "rkisp1-common.h"
#define RKISP1_STATS_DEV_NAME RKISP1_DRIVER_NAME "_stats"
#define RKISP1_ISP_STATS_REQ_BUFS_MIN 2
#define RKISP1_ISP_STATS_REQ_BUFS_MAX 8
static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct video_device *video = video_devdata(file);
struct rkisp1_stats *stats = video_get_drvdata(video);
if (f->index > 0 || f->type != video->queue->type)
return -EINVAL;
f->pixelformat = stats->vdev_fmt.fmt.meta.dataformat;
return 0;
}
static int rkisp1_stats_g_fmt_meta_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct video_device *video = video_devdata(file);
struct rkisp1_stats *stats = video_get_drvdata(video);
struct v4l2_meta_format *meta = &f->fmt.meta;
if (f->type != video->queue->type)
return -EINVAL;
memset(meta, 0, sizeof(*meta));
meta->dataformat = stats->vdev_fmt.fmt.meta.dataformat;
meta->buffersize = stats->vdev_fmt.fmt.meta.buffersize;
return 0;
}
static int rkisp1_stats_querycap(struct file *file,
void *priv, struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver));
strscpy(cap->card, vdev->name, sizeof(cap->card));
strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info));
return 0;
}
/* ISP video device IOCTLs */
static const struct v4l2_ioctl_ops rkisp1_stats_ioctl = {
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_fmt_meta_cap = rkisp1_stats_enum_fmt_meta_cap,
.vidioc_g_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
.vidioc_s_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
.vidioc_try_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
.vidioc_querycap = rkisp1_stats_querycap,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct v4l2_file_operations rkisp1_stats_fops = {
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
.poll = vb2_fop_poll,
.open = v4l2_fh_open,
.release = vb2_fop_release
};
static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
struct device *alloc_devs[])
{
*num_planes = 1;
*num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_STATS_REQ_BUFS_MIN,
RKISP1_ISP_STATS_REQ_BUFS_MAX);
sizes[0] = sizeof(struct rkisp1_stat_buffer);
return 0;
}
static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rkisp1_buffer *stats_buf =
container_of(vbuf, struct rkisp1_buffer, vb);
struct vb2_queue *vq = vb->vb2_queue;
struct rkisp1_stats *stats_dev = vq->drv_priv;
spin_lock_irq(&stats_dev->lock);
list_add_tail(&stats_buf->queue, &stats_dev->stat);
spin_unlock_irq(&stats_dev->lock);
}
static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb)
{
if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_stat_buffer))
return -EINVAL;
vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_stat_buffer));
return 0;
}
static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq)
{
struct rkisp1_stats *stats = vq->drv_priv;
struct rkisp1_buffer *buf;
unsigned int i;
spin_lock_irq(&stats->lock);
for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) {
if (list_empty(&stats->stat))
break;
buf = list_first_entry(&stats->stat,
struct rkisp1_buffer, queue);
list_del(&buf->queue);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
spin_unlock_irq(&stats->lock);
}
static const struct vb2_ops rkisp1_stats_vb2_ops = {
.queue_setup = rkisp1_stats_vb2_queue_setup,
.buf_queue = rkisp1_stats_vb2_buf_queue,
.buf_prepare = rkisp1_stats_vb2_buf_prepare,
.stop_streaming = rkisp1_stats_vb2_stop_streaming,
};
static int
rkisp1_stats_init_vb2_queue(struct vb2_queue *q, struct rkisp1_stats *stats)
{
struct rkisp1_vdev_node *node;
node = container_of(q, struct rkisp1_vdev_node, buf_queue);
q->type = V4L2_BUF_TYPE_META_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
q->drv_priv = stats;
q->ops = &rkisp1_stats_vb2_ops;
q->mem_ops = &vb2_vmalloc_memops;
q->buf_struct_size = sizeof(struct rkisp1_buffer);
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->vlock;
return vb2_queue_init(q);
}
static void rkisp1_stats_get_awb_meas_v10(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
/* Protect against concurrent access from ISR? */
struct rkisp1_device *rkisp1 = stats->rkisp1;
u32 reg_val;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AWB;
reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_WHITE_CNT_V10);
pbuf->params.awb.awb_mean[0].cnt =
RKISP1_CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_MEAN_V10);
pbuf->params.awb.awb_mean[0].mean_cr_or_r =
RKISP1_CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
pbuf->params.awb.awb_mean[0].mean_cb_or_b =
RKISP1_CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
pbuf->params.awb.awb_mean[0].mean_y_or_g =
RKISP1_CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
}
static void rkisp1_stats_get_awb_meas_v12(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
/* Protect against concurrent access from ISR? */
struct rkisp1_device *rkisp1 = stats->rkisp1;
u32 reg_val;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AWB;
reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_WHITE_CNT_V12);
pbuf->params.awb.awb_mean[0].cnt =
RKISP1_CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AWB_MEAN_V12);
pbuf->params.awb.awb_mean[0].mean_cr_or_r =
RKISP1_CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
pbuf->params.awb.awb_mean[0].mean_cb_or_b =
RKISP1_CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
pbuf->params.awb.awb_mean[0].mean_y_or_g =
RKISP1_CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
}
static void rkisp1_stats_get_aec_meas_v10(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
unsigned int i;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP;
for (i = 0; i < RKISP1_CIF_ISP_AE_MEAN_MAX_V10; i++)
pbuf->params.ae.exp_mean[i] =
(u8)rkisp1_read(rkisp1,
RKISP1_CIF_ISP_EXP_MEAN_00_V10 + i * 4);
}
static void rkisp1_stats_get_aec_meas_v12(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
u32 value;
int i;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP;
for (i = 0; i < RKISP1_CIF_ISP_AE_MEAN_MAX_V12 / 4; i++) {
value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_EXP_MEAN_V12 + i * 4);
pbuf->params.ae.exp_mean[4 * i + 0] =
RKISP1_CIF_ISP_EXP_GET_MEAN_xy0_V12(value);
pbuf->params.ae.exp_mean[4 * i + 1] =
RKISP1_CIF_ISP_EXP_GET_MEAN_xy1_V12(value);
pbuf->params.ae.exp_mean[4 * i + 2] =
RKISP1_CIF_ISP_EXP_GET_MEAN_xy2_V12(value);
pbuf->params.ae.exp_mean[4 * i + 3] =
RKISP1_CIF_ISP_EXP_GET_MEAN_xy3_V12(value);
}
value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_EXP_MEAN_V12 + i * 4);
pbuf->params.ae.exp_mean[4 * i + 0] = RKISP1_CIF_ISP_EXP_GET_MEAN_xy0_V12(value);
}
static void rkisp1_stats_get_afc_meas(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
struct rkisp1_cif_isp_af_stat *af;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AFM;
af = &pbuf->params.af;
af->window[0].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_A);
af->window[0].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_A);
af->window[1].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_B);
af->window[1].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_B);
af->window[2].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_C);
af->window[2].lum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_LUM_C);
}
static void rkisp1_stats_get_hst_meas_v10(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
unsigned int i;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_HIST;
for (i = 0; i < RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10; i++) {
u32 reg_val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_HIST_BIN_0_V10 + i * 4);
pbuf->params.hist.hist_bins[i] = RKISP1_CIF_ISP_HIST_GET_BIN_V10(reg_val);
}
}
static void rkisp1_stats_get_hst_meas_v12(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
u32 value;
int i;
pbuf->meas_type |= RKISP1_CIF_ISP_STAT_HIST;
for (i = 0; i < RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12 / 2; i++) {
value = rkisp1_read(rkisp1, RKISP1_CIF_ISP_HIST_BIN_V12 + i * 4);
pbuf->params.hist.hist_bins[2 * i] =
RKISP1_CIF_ISP_HIST_GET_BIN0_V12(value);
pbuf->params.hist.hist_bins[2 * i + 1] =
RKISP1_CIF_ISP_HIST_GET_BIN1_V12(value);
}
}
static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
static const u32 regs[] = {
RKISP1_CIF_ISP_BLS_A_MEASURED,
RKISP1_CIF_ISP_BLS_B_MEASURED,
RKISP1_CIF_ISP_BLS_C_MEASURED,
RKISP1_CIF_ISP_BLS_D_MEASURED,
};
struct rkisp1_device *rkisp1 = stats->rkisp1;
const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt;
struct rkisp1_cif_isp_bls_meas_val *bls_val;
u32 swapped[4];
rkisp1_bls_swap_regs(in_fmt->bayer_pat, regs, swapped);
bls_val = &pbuf->params.ae.bls_val;
bls_val->meas_r = rkisp1_read(rkisp1, swapped[0]);
bls_val->meas_gr = rkisp1_read(rkisp1, swapped[1]);
bls_val->meas_gb = rkisp1_read(rkisp1, swapped[2]);
bls_val->meas_b = rkisp1_read(rkisp1, swapped[3]);
}
static const struct rkisp1_stats_ops rkisp1_v10_stats_ops = {
.get_awb_meas = rkisp1_stats_get_awb_meas_v10,
.get_aec_meas = rkisp1_stats_get_aec_meas_v10,
.get_hst_meas = rkisp1_stats_get_hst_meas_v10,
};
static struct rkisp1_stats_ops rkisp1_v12_stats_ops = {
.get_awb_meas = rkisp1_stats_get_awb_meas_v12,
.get_aec_meas = rkisp1_stats_get_aec_meas_v12,
.get_hst_meas = rkisp1_stats_get_hst_meas_v12,
};
static void
rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris)
{
struct rkisp1_stat_buffer *cur_stat_buf;
struct rkisp1_buffer *cur_buf = NULL;
unsigned int frame_sequence = stats->rkisp1->isp.frame_sequence;
u64 timestamp = ktime_get_ns();
/* get one empty buffer */
if (!list_empty(&stats->stat)) {
cur_buf = list_first_entry(&stats->stat,
struct rkisp1_buffer, queue);
list_del(&cur_buf->queue);
}
if (!cur_buf)
return;
cur_stat_buf = (struct rkisp1_stat_buffer *)
vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
if (isp_ris & RKISP1_CIF_ISP_AWB_DONE)
stats->ops->get_awb_meas(stats, cur_stat_buf);
if (isp_ris & RKISP1_CIF_ISP_AFM_FIN)
rkisp1_stats_get_afc_meas(stats, cur_stat_buf);
if (isp_ris & RKISP1_CIF_ISP_EXP_END) {
stats->ops->get_aec_meas(stats, cur_stat_buf);
rkisp1_stats_get_bls_meas(stats, cur_stat_buf);
}
if (isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY)
stats->ops->get_hst_meas(stats, cur_stat_buf);
vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
sizeof(struct rkisp1_stat_buffer));
cur_buf->vb.sequence = frame_sequence;
cur_buf->vb.vb2_buf.timestamp = timestamp;
vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris)
{
struct rkisp1_device *rkisp1 = stats->rkisp1;
unsigned int isp_mis_tmp = 0;
spin_lock(&stats->lock);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, RKISP1_STATS_MEAS_MASK);
isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK)
rkisp1->debug.stats_error++;
if (isp_ris & RKISP1_STATS_MEAS_MASK)
rkisp1_stats_send_measurement(stats, isp_ris);
spin_unlock(&stats->lock);
}
static void rkisp1_init_stats(struct rkisp1_stats *stats)
{
stats->vdev_fmt.fmt.meta.dataformat =
V4L2_META_FMT_RK_ISP1_STAT_3A;
stats->vdev_fmt.fmt.meta.buffersize =
sizeof(struct rkisp1_stat_buffer);
if (stats->rkisp1->info->isp_ver == RKISP1_V12)
stats->ops = &rkisp1_v12_stats_ops;
else
stats->ops = &rkisp1_v10_stats_ops;
}
int rkisp1_stats_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_stats *stats = &rkisp1->stats;
struct rkisp1_vdev_node *node = &stats->vnode;
struct video_device *vdev = &node->vdev;
int ret;
stats->rkisp1 = rkisp1;
mutex_init(&node->vlock);
INIT_LIST_HEAD(&stats->stat);
spin_lock_init(&stats->lock);
strscpy(vdev->name, RKISP1_STATS_DEV_NAME, sizeof(vdev->name));
video_set_drvdata(vdev, stats);
vdev->ioctl_ops = &rkisp1_stats_ioctl;
vdev->fops = &rkisp1_stats_fops;
vdev->release = video_device_release_empty;
vdev->lock = &node->vlock;
vdev->v4l2_dev = &rkisp1->v4l2_dev;
vdev->queue = &node->buf_queue;
vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
vdev->vfl_dir = VFL_DIR_RX;
rkisp1_stats_init_vb2_queue(vdev->queue, stats);
rkisp1_init_stats(stats);
video_set_drvdata(vdev, stats);
node->pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret)
goto error;
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(&vdev->dev,
"failed to register %s, ret=%d\n", vdev->name, ret);
goto error;
}
return 0;
error:
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
stats->rkisp1 = NULL;
return ret;
}
void rkisp1_stats_unregister(struct rkisp1_device *rkisp1)
{
struct rkisp1_stats *stats = &rkisp1->stats;
struct rkisp1_vdev_node *node = &stats->vnode;
struct video_device *vdev = &node->vdev;
if (!stats->rkisp1)
return;
vb2_video_unregister_device(vdev);
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
}