media: starfive: Add vin driver support

Add vin driver support.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
This commit is contained in:
Changhuang Liang
2023-06-05 13:54:16 +08:00
committed by Hal Feng
parent 01326bebd9
commit 7f3fb7fc3c
37 changed files with 23803 additions and 0 deletions

View File

@@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/Kconfig"
source "drivers/media/platform/rockchip/Kconfig"
source "drivers/media/platform/samsung/Kconfig"
source "drivers/media/platform/st/Kconfig"
source "drivers/media/platform/starfive/Kconfig"
source "drivers/media/platform/sunxi/Kconfig"
source "drivers/media/platform/ti/Kconfig"
source "drivers/media/platform/verisilicon/Kconfig"

View File

@@ -23,6 +23,7 @@ obj-y += renesas/
obj-y += rockchip/
obj-y += samsung/
obj-y += st/
obj-y += starfive/
obj-y += sunxi/
obj-y += ti/
obj-y += verisilicon/

View File

@@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0-only
comment "Starfive media platform drivers"
config VIN_SENSOR_OV5640
tristate "VIN SENSOR support OV5640"
depends on VIDEO_STF_VIN
select V4L2_FWNODE
default n
help
Say Y here if you want to have support for VIN sensor OV5640
config VIN_SENSOR_SC2235
tristate "VIN SENSOR support SC2235"
depends on VIDEO_STF_VIN
select V4L2_FWNODE
default n
help
Say Y here if you want to have support for VIN sensor SC2235
config VIN_SENSOR_OV4689
tristate "VIN SENSOR support OV4689"
depends on VIDEO_STF_VIN
select V4L2_FWNODE
default n
help
Say Y here if you want to have support for VIN sensor OV4689
config VIN_SENSOR_OV13850
bool "VIN SENSOR support OV13850"
depends on VIDEO_STF_VIN
select V4L2_FWNODE
default n
help
Say Y here if you want to have support for VIN sensor OV13850
config VIN_SENSOR_IMX219
tristate "VIN SENSOR support IMX219"
depends on VIDEO_STF_VIN
select V4L2_FWNODE
default n
help
Say Y here if you want to have support for VIN sensor IMX219
config VIDEO_STF_VIN
tristate "starfive VIC video in support"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
select MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
help
To compile this driver as a module, choose M here: the module
will be called stf-vin.

View File

@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_VIN_SENSOR_OV5640) += v4l2_driver/ov5640.o
obj-$(CONFIG_VIN_SENSOR_SC2235) += v4l2_driver/sc2235.o
obj-$(CONFIG_VIN_SENSOR_OV4689) += v4l2_driver/ov4689_mipi.o
obj-$(CONFIG_VIN_SENSOR_OV13850) += v4l2_driver/ov13850_mipi.o
obj-$(CONFIG_VIN_SENSOR_IMX219) += v4l2_driver/imx219_mipi.o
starfivecamss-objs += v4l2_driver/stfcamss.o \
v4l2_driver/stf_event.o \
v4l2_driver/stf_dvp.o \
v4l2_driver/stf_csi.o \
v4l2_driver/stf_csiphy.o \
v4l2_driver/stf_isp.o \
v4l2_driver/stf_video.o \
v4l2_driver/stf_vin.o \
v4l2_driver/stf_vin_hw_ops.o \
v4l2_driver/stf_csi_hw_ops.o \
v4l2_driver/stf_csiphy_hw_ops.o \
v4l2_driver/stf_isp_hw_ops.o \
v4l2_driver/stf_dvp_hw_ops.o \
v4l2_driver/stf_dmabuf.o
obj-$(CONFIG_VIDEO_STF_VIN) += starfivecamss.o \

View File

@@ -0,0 +1,11 @@
/dev/video0: Output the camera data directly.
/dev/video1: Output the data of the camera converted by isp.
ensure linux/arch/riscv/configs/starfive_jh7110_defconfig:
CONFIG_VIDEO_STF_VIN=y
CONFIG_VIN_SENSOR_SC2235=y
CONFIG_VIN_SENSOR_OV4689=y
Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4
as data lane.

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_COMMON_H
#define STF_COMMON_H
#include <linux/kern_levels.h>
// #define STF_DEBUG
// #define USE_CSIDPHY_ONE_CLK_MODE 1
enum {
ST_DVP = 0x0001,
ST_CSIPHY = 0x0002,
ST_CSI = 0x0004,
ST_ISP = 0x0008,
ST_VIN = 0x0010,
ST_VIDEO = 0x0020,
ST_CAMSS = 0x0040,
ST_SENSOR = 0x0080,
};
enum {
ST_NONE = 0x00,
ST_ERR = 0x01,
ST_WARN = 0x02,
ST_INFO = 0x03,
ST_DEBUG = 0x04,
};
extern unsigned int stdbg_level;
extern unsigned int stdbg_mask;
#define ST_MODULE2STRING(__module) ({ \
char *__str; \
\
switch (__module) { \
case ST_DVP: \
__str = "st_dvp"; \
break; \
case ST_CSIPHY: \
__str = "st_csiphy"; \
break; \
case ST_CSI: \
__str = "st_csi"; \
break; \
case ST_ISP: \
__str = "st_isp"; \
break; \
case ST_VIN: \
__str = "st_vin"; \
break; \
case ST_VIDEO: \
__str = "st_video"; \
break; \
case ST_CAMSS: \
__str = "st_camss"; \
break; \
case ST_SENSOR: \
__str = "st_sensor"; \
break; \
default: \
__str = "unknow"; \
break; \
} \
\
__str; \
})
#define st_debug(module, __fmt, arg...) \
do { \
if (stdbg_level > ST_INFO) { \
if (stdbg_mask & module) \
pr_err("[%s] debug: " __fmt, \
ST_MODULE2STRING(module), \
## arg); \
} \
} while (0)
#define st_info(module, __fmt, arg...) \
do { \
if (stdbg_level > ST_WARN) { \
if (stdbg_mask & module) \
pr_err("[%s] info: " __fmt, \
ST_MODULE2STRING(module), \
## arg); \
} \
} while (0)
#define st_warn(module, __fmt, arg...) \
do { \
if (stdbg_level > ST_ERR) { \
if (stdbg_mask & module) \
pr_err("[%s] warn: " __fmt, \
ST_MODULE2STRING(module), \
## arg); \
} \
} while (0)
#define st_err(module, __fmt, arg...) \
do { \
if (stdbg_level > ST_NONE) { \
if (stdbg_mask & module) \
pr_err("[%s] error: " __fmt, \
ST_MODULE2STRING(module), \
## arg); \
} \
} while (0)
#define st_err_ratelimited(module, fmt, ...) \
do { \
static DEFINE_RATELIMIT_STATE(_rs, \
DEFAULT_RATELIMIT_INTERVAL, \
DEFAULT_RATELIMIT_BURST); \
if (__ratelimit(&_rs) && (stdbg_level > ST_NONE)) { \
if (stdbg_mask & module) \
pr_err("[%s] error: " fmt, \
ST_MODULE2STRING(module), \
##__VA_ARGS__); \
} \
} while (0)
#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b)))
static inline u32 reg_read(void __iomem *base, u32 reg)
{
return ioread32(base + reg);
}
static inline void reg_write(void __iomem *base, u32 reg, u32 val)
{
iowrite32(val, base + reg);
}
static inline void reg_set_bit(void __iomem *base, u32 reg, u32 mask, u32 val)
{
u32 value;
value = ioread32(base + reg) & ~mask;
val &= mask;
val |= value;
iowrite32(val, base + reg);
}
static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
{
iowrite32(ioread32(base + reg) | mask, base + reg);
}
static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
{
iowrite32(ioread32(base + reg) & ~mask, base + reg);
}
static inline void reg_set_highest_bit(void __iomem *base, u32 reg)
{
u32 val;
val = ioread32(base + reg);
val &= ~(0x1 << 31);
val |= (0x1 & 0x1) << 31;
iowrite32(val, base + reg);
}
static inline void reg_clr_highest_bit(void __iomem *base, u32 reg)
{
u32 val;
val = ioread32(base + reg);
val &= ~(0x1 << 31);
val |= (0x0 & 0x1) << 31;
iowrite32(val, base + reg);
}
static inline void print_reg(unsigned int module, void __iomem *base, u32 reg)
{
//st_debug(module, "REG 0x%x = 0x%x\n",
// base + reg, ioread32(base + reg));
}
#endif /* STF_COMMON_H */

View File

@@ -0,0 +1,465 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
static const struct csi_format csi_formats_sink[] = {
{ MEDIA_BUS_FMT_UYVY8_2X8, 16},
{ MEDIA_BUS_FMT_SRGGB10_1X10, 10},
{ MEDIA_BUS_FMT_SGRBG10_1X10, 10},
{ MEDIA_BUS_FMT_SGBRG10_1X10, 10},
{ MEDIA_BUS_FMT_SBGGR10_1X10, 10},
};
/* this bpp need see csi controllor */
static const struct csi_format csi_formats_src[] = {
{ MEDIA_BUS_FMT_AYUV8_1X32, 32},
{ MEDIA_BUS_FMT_SRGGB10_1X10, 16},
{ MEDIA_BUS_FMT_SGRBG10_1X10, 16},
{ MEDIA_BUS_FMT_SGBRG10_1X10, 16},
{ MEDIA_BUS_FMT_SBGGR10_1X10, 16},
};
static int csi_find_format(u32 code,
const struct csi_format *formats,
unsigned int nformats)
{
int i;
for (i = 0; i < nformats; i++)
if (formats[i].code == code)
return i;
return -EINVAL;
}
int stf_csi_subdev_init(struct stfcamss *stfcamss)
{
struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
csi_dev->s_type = SENSOR_VIN;
csi_dev->hw_ops = &csi_ops;
csi_dev->stfcamss = stfcamss;
csi_dev->formats_sink = csi_formats_sink;
csi_dev->nformats_sink = ARRAY_SIZE(csi_formats_sink);
csi_dev->formats_src = csi_formats_src;
csi_dev->nformats_src = ARRAY_SIZE(csi_formats_src);
mutex_init(&csi_dev->stream_lock);
return 0;
}
static int csi_set_power(struct v4l2_subdev *sd, int on)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
csi_dev->hw_ops->csi_power_on(csi_dev, (u8)on);
return 0;
}
static struct v4l2_mbus_framefmt *
__csi_get_format(struct stf_csi_dev *csi_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(&csi_dev->subdev, state, pad);
return &csi_dev->fmt[pad];
}
static u32 code_to_data_type(int code)
{
switch (code) {
case MEDIA_BUS_FMT_SRGGB10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SBGGR10_1X10:
return 0x2b;
case MEDIA_BUS_FMT_UYVY8_2X8:
return 0x1E;
default:
return 0x2b;
}
}
static int csi_set_stream(struct v4l2_subdev *sd, int enable)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
int ret = 0;
u32 code, width, dt;
u8 bpp;
format = __csi_get_format(csi_dev, NULL, STF_CSI_PAD_SINK,
V4L2_SUBDEV_FORMAT_ACTIVE);
if (format == NULL)
return -EINVAL;
width = format->width;
ret = csi_find_format(format->code,
csi_dev->formats_sink,
csi_dev->nformats_sink);
if (ret < 0)
return ret;
code = csi_dev->formats_sink[ret].code;
bpp = csi_dev->formats_src[ret].bpp;
dt = code_to_data_type(code);
mutex_lock(&csi_dev->stream_lock);
if (enable) {
if (csi_dev->stream_count == 0) {
csi_dev->hw_ops->csi_clk_enable(csi_dev);
csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
}
csi_dev->stream_count++;
} else {
if (csi_dev->stream_count == 0)
goto exit;
if (csi_dev->stream_count == 1) {
csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
csi_dev->hw_ops->csi_clk_disable(csi_dev);
}
csi_dev->stream_count--;
}
exit:
mutex_unlock(&csi_dev->stream_lock);
return 0;
}
static void csi_try_format(struct stf_csi_dev *csi_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
unsigned int i;
switch (pad) {
case STF_CSI_PAD_SINK:
/* Set format on sink pad */
for (i = 0; i < csi_dev->nformats_sink; i++)
if (fmt->code == csi_dev->formats_sink[i].code)
break;
if (i >= csi_dev->nformats_sink)
fmt->code = csi_dev->formats_sink[0].code;
fmt->width = clamp_t(u32,
fmt->width,
STFCAMSS_FRAME_MIN_WIDTH,
STFCAMSS_FRAME_MAX_WIDTH);
fmt->height = clamp_t(u32,
fmt->height,
STFCAMSS_FRAME_MIN_HEIGHT,
STFCAMSS_FRAME_MAX_HEIGHT);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->flags = 0;
break;
case STF_CSI_PAD_SRC:
/* Set format on src pad */
for (i = 0; i < csi_dev->nformats_src; i++)
if (fmt->code == csi_dev->formats_src[i].code)
break;
if (i >= csi_dev->nformats_src)
fmt->code = csi_dev->formats_src[0].code;
fmt->width = clamp_t(u32,
fmt->width,
STFCAMSS_FRAME_MIN_WIDTH,
STFCAMSS_FRAME_MAX_WIDTH);
fmt->height = clamp_t(u32,
fmt->height,
STFCAMSS_FRAME_MIN_HEIGHT,
STFCAMSS_FRAME_MAX_HEIGHT);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->flags = 0;
break;
}
}
static int csi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
if (code->index >= csi_dev->nformats_sink)
return -EINVAL;
if (code->pad == STF_CSI_PAD_SINK) {
code->code = csi_dev->formats_sink[code->index].code;
} else {
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = __csi_get_format(csi_dev, state, STF_CSI_PAD_SINK,
code->which);
code->code = sink_fmt->code;
if (!code->code)
return -EINVAL;
}
code->flags = 0;
return 0;
}
static int csi_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
if (fse->index != 0)
return -EINVAL;
format.code = fse->code;
format.width = 1;
format.height = 1;
csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
fse->min_width = format.width;
fse->min_height = format.height;
if (format.code != fse->code)
return -EINVAL;
format.code = fse->code;
format.width = -1;
format.height = -1;
csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
fse->max_width = format.width;
fse->max_height = format.height;
return 0;
}
static int csi_get_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
fmt->format = *format;
return 0;
}
static int csi_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
struct v4l2_mbus_framefmt *format_src;
int ret;
format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
mutex_lock(&csi_dev->stream_lock);
if (csi_dev->stream_count) {
fmt->format = *format;
mutex_unlock(&csi_dev->stream_lock);
goto out;
} else {
csi_try_format(csi_dev, state, fmt->pad, &fmt->format, fmt->which);
*format = fmt->format;
}
mutex_unlock(&csi_dev->stream_lock);
if (fmt->pad == STF_CSI_PAD_SINK) {
format_src = __csi_get_format(csi_dev, state, STF_DVP_PAD_SRC,
fmt->which);
ret = csi_find_format(format->code, csi_dev->formats_sink,
csi_dev->nformats_sink);
if (ret < 0)
return ret;
format_src->code = csi_dev->formats_src[ret].code;
csi_try_format(csi_dev, state, STF_DVP_PAD_SRC, format_src,
fmt->which);
}
out:
return 0;
}
static int csi_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct v4l2_subdev_format format = {
.pad = STF_CSI_PAD_SINK,
.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
V4L2_SUBDEV_FORMAT_ACTIVE,
.format = {
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.width = 1920,
.height = 1080
}
};
return csi_set_format(sd, fh ? fh->state : NULL, &format);
}
static int csi_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
(flags & MEDIA_LNK_FL_ENABLED)) {
struct v4l2_subdev *sd;
struct stf_csi_dev *csi_dev;
struct vin_line *line;
if (media_pad_remote_pad_first(local))
return -EBUSY;
sd = media_entity_to_v4l2_subdev(entity);
csi_dev = v4l2_get_subdevdata(sd);
sd = media_entity_to_v4l2_subdev(remote->entity);
line = v4l2_get_subdevdata(sd);
if (line->sdev_type == VIN_DEV_TYPE)
csi_dev->s_type = SENSOR_VIN;
if (line->sdev_type == ISP_DEV_TYPE)
csi_dev->s_type = SENSOR_ISP;
st_info(ST_CSI, "CSI device sensor type: %d\n", csi_dev->s_type);
}
if ((local->flags & MEDIA_PAD_FL_SINK) &&
(flags & MEDIA_LNK_FL_ENABLED)) {
struct v4l2_subdev *sd;
struct stf_csi_dev *csi_dev;
struct stf_csiphy_dev *csiphy_dev;
if (media_pad_remote_pad_first(local))
return -EBUSY;
sd = media_entity_to_v4l2_subdev(entity);
csi_dev = v4l2_get_subdevdata(sd);
sd = media_entity_to_v4l2_subdev(remote->entity);
csiphy_dev = v4l2_get_subdevdata(sd);
st_info(ST_CSI, "CSI0 link to csiphy0\n");
}
return 0;
}
static const struct v4l2_subdev_core_ops csi_core_ops = {
.s_power = csi_set_power,
};
static const struct v4l2_subdev_video_ops csi_video_ops = {
.s_stream = csi_set_stream,
};
static const struct v4l2_subdev_pad_ops csi_pad_ops = {
.enum_mbus_code = csi_enum_mbus_code,
.enum_frame_size = csi_enum_frame_size,
.get_fmt = csi_get_format,
.set_fmt = csi_set_format,
};
static const struct v4l2_subdev_ops csi_v4l2_ops = {
.core = &csi_core_ops,
.video = &csi_video_ops,
.pad = &csi_pad_ops,
};
static const struct v4l2_subdev_internal_ops csi_v4l2_internal_ops = {
.open = csi_init_formats,
};
static const struct media_entity_operations csi_media_ops = {
.link_setup = csi_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
int stf_csi_register(struct stf_csi_dev *csi_dev, struct v4l2_device *v4l2_dev)
{
struct v4l2_subdev *sd = &csi_dev->subdev;
struct device *dev = csi_dev->stfcamss->dev;
struct media_pad *pads = csi_dev->pads;
int ret;
csi_dev->mipirx_1p8 = devm_regulator_get(dev, "mipirx_1p8");
if (IS_ERR(csi_dev->mipirx_1p8))
return PTR_ERR(csi_dev->mipirx_1p8);
csi_dev->mipirx_0p9 = devm_regulator_get(dev, "mipirx_0p9");
if (IS_ERR(csi_dev->mipirx_0p9))
return PTR_ERR(csi_dev->mipirx_0p9);
v4l2_subdev_init(sd, &csi_v4l2_ops);
sd->internal_ops = &csi_v4l2_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
STF_CSI_NAME, 0);
v4l2_set_subdevdata(sd, csi_dev);
ret = csi_init_formats(sd, NULL);
if (ret < 0) {
dev_err(dev, "Failed to init format: %d\n", ret);
return ret;
}
pads[STF_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[STF_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &csi_media_ops;
ret = media_entity_pads_init(&sd->entity, STF_CSI_PADS_NUM, pads);
if (ret < 0) {
dev_err(dev, "Failed to init media entity: %d\n", ret);
return ret;
}
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0) {
dev_err(dev, "Failed to register subdev: %d\n", ret);
goto err_sreg;
}
return 0;
err_sreg:
media_entity_cleanup(&sd->entity);
return ret;
}
int stf_csi_unregister(struct stf_csi_dev *csi_dev)
{
v4l2_device_unregister_subdev(&csi_dev->subdev);
media_entity_cleanup(&csi_dev->subdev.entity);
mutex_destroy(&csi_dev->stream_lock);
return 0;
}

View File

@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_CSI_H
#define STF_CSI_H
#include <linux/regulator/consumer.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
#include <media/media-entity.h>
#include <video/stf-vin.h>
#define STF_CSI_NAME "stf_csi"
#define STF_CSI_PAD_SINK 0
#define STF_CSI_PAD_SRC 1
#define STF_CSI_PADS_NUM 2
struct csi_format {
u32 code;
u8 bpp;
};
struct stf_csi_dev;
struct csi_hw_ops {
int (*csi_power_on)(struct stf_csi_dev *csi_dev, u8 on);
int (*csi_clk_enable)(struct stf_csi_dev *csi_dev);
int (*csi_clk_disable)(struct stf_csi_dev *csi_dev);
int (*csi_stream_set)(struct stf_csi_dev *csi_dev, int on,
u32 dt, u32 width, u8 bpp);
};
struct stf_csi_dev {
struct stfcamss *stfcamss;
enum sensor_type s_type;
struct v4l2_subdev subdev;
struct media_pad pads[STF_CSI_PADS_NUM];
struct v4l2_mbus_framefmt fmt[STF_CSI_PADS_NUM];
const struct csi_format *formats_sink;
unsigned int nformats_sink;
const struct csi_format *formats_src;
unsigned int nformats_src;
struct csi_hw_ops *hw_ops;
struct mutex stream_lock;
int stream_count;
struct regulator *mipirx_1p8;
struct regulator *mipirx_0p9;
};
extern int stf_csi_subdev_init(struct stfcamss *stfcamss);
extern int stf_csi_register(struct stf_csi_dev *csi_dev,
struct v4l2_device *v4l2_dev);
extern int stf_csi_unregister(struct stf_csi_dev *csi_dev);
extern struct csi_hw_ops csi_ops;
extern void dump_csi_reg(void *__iomem csibase);
#endif /* STF_CSI_H */

View File

@@ -0,0 +1,310 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <linux/regmap.h>
#define CSI2RX_DEVICE_CFG_REG 0x000
#define CSI2RX_SOFT_RESET_REG 0x004
#define CSI2RX_SOFT_RESET_PROTOCOL BIT(1)
#define CSI2RX_SOFT_RESET_FRONT BIT(0)
#define CSI2RX_DPHY_LANE_CONTROL 0x040
#define CSI2RX_STATIC_CFG_REG 0x008
#define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) \
((plane) << (16 + (llane) * 4))
#define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8)
#define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100)
#define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000)
#define CSI2RX_STREAM_CTRL_START BIT(0)
#define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008)
#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31)
#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 BIT(7)
#define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16)
#define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c)
#define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8)
#define CSI2RX_LANES_MAX 4
#define CSI2RX_STREAMS_MAX 4
static int stf_csi_power_on(struct stf_csi_dev *csi_dev, u8 on)
{
struct stfcamss *stfcamss = csi_dev->stfcamss;
int ret;
if (on) {
ret = regulator_enable(csi_dev->mipirx_1p8);
if (ret) {
st_err(ST_CSI, "Cannot enable mipirx_1p8 regulator\n");
goto err_1p8;
}
ret = regulator_enable(csi_dev->mipirx_0p9);
if (ret) {
st_err(ST_CSI, "Cannot enable mipirx_0p9 regulator\n");
goto err_0p9;
}
} else {
regulator_disable(csi_dev->mipirx_1p8);
regulator_disable(csi_dev->mipirx_0p9);
}
regmap_update_bits(stfcamss->stf_aon_syscon, stfcamss->aon_gp_reg,
BIT(31), BIT(31));
return 0;
err_0p9:
regulator_disable(csi_dev->mipirx_1p8);
err_1p8:
return ret;
}
static int stf_csi_clk_enable(struct stf_csi_dev *csi_dev)
{
struct stfcamss *stfcamss = csi_dev->stfcamss;
clk_set_rate(stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk, 198000000);
clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
switch (csi_dev->s_type) {
case SENSOR_VIN:
reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
break;
case SENSOR_ISP:
clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
break;
}
return 0;
}
static int stf_csi_clk_disable(struct stf_csi_dev *csi_dev)
{
struct stfcamss *stfcamss = csi_dev->stfcamss;
switch (csi_dev->s_type) {
case SENSOR_VIN:
reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
break;
case SENSOR_ISP:
break;
}
reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
return 0;
}
static void csi2rx_reset(void *reg_base)
{
writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
reg_base + CSI2RX_SOFT_RESET_REG);
udelay(10);
writel(0, reg_base + CSI2RX_SOFT_RESET_REG);
}
static int csi2rx_start(struct stf_csi_dev *csi_dev, void *reg_base, u32 dt)
{
struct stfcamss *stfcamss = csi_dev->stfcamss;
struct csi2phy_cfg *csiphy =
stfcamss->csiphy_dev->csiphy;
unsigned int i;
unsigned long lanes_used = 0;
u32 reg;
if (!csiphy) {
st_err(ST_CSI, "csiphy0 config not exist\n");
return -EINVAL;
}
csi2rx_reset(reg_base);
reg = csiphy->num_data_lanes << 8;
for (i = 0; i < csiphy->num_data_lanes; i++) {
#ifndef USE_CSIDPHY_ONE_CLK_MODE
reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csiphy->data_lanes[i]);
set_bit(csiphy->data_lanes[i] - 1, &lanes_used);
#else
reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
set_bit(i, &lanes_used);
#endif
}
/*
* Even the unused lanes need to be mapped. In order to avoid
* to map twice to the same physical lane, keep the lanes used
* in the previous loop, and only map unused physical lanes to
* the rest of our logical lanes.
*/
for (i = csiphy->num_data_lanes; i < CSI2RX_LANES_MAX; i++) {
unsigned int idx = find_first_zero_bit(&lanes_used,
CSI2RX_LANES_MAX);
set_bit(idx, &lanes_used);
reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, idx + 1);
}
writel(reg, reg_base + CSI2RX_STATIC_CFG_REG);
// 0x40 DPHY_LANE_CONTROL
reg = 0;
#ifndef USE_CSIDPHY_ONE_CLK_MODE
for (i = 0; i < csiphy->num_data_lanes; i++)
reg |= 1 << (csiphy->data_lanes[i] - 1)
| 1 << (csiphy->data_lanes[i] + 11);
#else
for (i = 0; i < csiphy->num_data_lanes; i++)
reg |= 1 << i | 1 << (i + 12); //data_clane
#endif
reg |= 1 << 4 | 1 << 16; //clk_lane
writel(reg, reg_base + CSI2RX_DPHY_LANE_CONTROL);
/*
* Create a static mapping between the CSI virtual channels
* and the output stream.
*
* This should be enhanced, but v4l2 lacks the support for
* changing that mapping dynamically.
*
* We also cannot enable and disable independent streams here,
* hence the reference counting.
*/
for (i = 0; i < CSI2RX_STREAMS_MAX; i++) {
writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
reg_base + CSI2RX_STREAM_CFG_REG(i));
writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT |
CSI2RX_STREAM_DATA_CFG_VC_SELECT(i) |
CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt,
reg_base + CSI2RX_STREAM_DATA_CFG_REG(i));
writel(CSI2RX_STREAM_CTRL_START,
reg_base + CSI2RX_STREAM_CTRL_REG(i));
}
return 0;
}
static void csi2rx_stop(struct stf_csi_dev *csi_dev, void *reg_base)
{
unsigned int i;
for (i = 0; i < CSI2RX_STREAMS_MAX; i++)
writel(0, reg_base + CSI2RX_STREAM_CTRL_REG(i));
}
static void csi_set_vin_axiwr_pix(struct stf_csi_dev *csi_dev, u32 width, u8 bpp)
{
struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
u32 value = 0;
int cnfg_axiwr_pix_ct = 64 / bpp;
if (cnfg_axiwr_pix_ct == 2)
value = 0;
else if (cnfg_axiwr_pix_ct == 4)
value = 1;
else if (cnfg_axiwr_pix_ct == 8)
value = 2;
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
BIT(14)|BIT(13), value << 13); //u0_vin_cnfg_axiwr0_pix_ct
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2),
(width / cnfg_axiwr_pix_ct - 1)<<2); //u0_vin_cnfg_axiwr0_pix_cnt_end
}
static int stf_csi_stream_set(struct stf_csi_dev *csi_dev,
int on, u32 dt, u32 width, u8 bpp)
{
struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
void __iomem *reg_base = vin->csi2rx_base;
switch (csi_dev->s_type) {
case SENSOR_VIN:
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20,
BIT(3)|BIT(2)|BIT(1)|BIT(0),
0<<0); //u0_vin_cnfg_axiwr0_channel_sel
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
BIT(16)|BIT(15),
0<<15); //u0_vin_cnfg_axiwr0_pixel_high_bit_sel
csi_set_vin_axiwr_pix(csi_dev, width, bpp);
break;
case SENSOR_ISP:
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
BIT(7)|BIT(6),
0<<6); //u0_vin_cnfg_mipi_byte_en_isp
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
BIT(11)|BIT(10)|BIT(9)|BIT(8),
0<<8); //u0_vin_cnfg_mipi_channel_sel0
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
BIT(16)|BIT(15)|BIT(14)|BIT(13),
0<<13); //u0_vin_cnfg_pix_num
if (dt == 0x2b)
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
BIT(12),
1<<12); //u0_vin_cnfg_p_i_mipi_header_en0
break;
}
if (on)
csi2rx_start(csi_dev, reg_base, dt);
else
csi2rx_stop(csi_dev, reg_base);
return 0;
}
void dump_csi_reg(void *__iomem csibase)
{
st_info(ST_CSI, "DUMP CSI register:\n");
print_reg(ST_CSI, csibase, 0x00);
print_reg(ST_CSI, csibase, 0x04);
print_reg(ST_CSI, csibase, 0x08);
print_reg(ST_CSI, csibase, 0x10);
print_reg(ST_CSI, csibase, 0x40);
print_reg(ST_CSI, csibase, 0x48);
print_reg(ST_CSI, csibase, 0x4c);
print_reg(ST_CSI, csibase, 0x50);
}
struct csi_hw_ops csi_ops = {
.csi_power_on = stf_csi_power_on,
.csi_clk_enable = stf_csi_clk_enable,
.csi_clk_disable = stf_csi_clk_disable,
.csi_stream_set = stf_csi_stream_set,
};

View File

@@ -0,0 +1,357 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
static const struct csiphy_format csiphy_formats_st7110[] = {
{ MEDIA_BUS_FMT_UYVY8_2X8, 16},
{ MEDIA_BUS_FMT_SRGGB10_1X10, 10},
{ MEDIA_BUS_FMT_SGRBG10_1X10, 10},
{ MEDIA_BUS_FMT_SGBRG10_1X10, 10},
{ MEDIA_BUS_FMT_SBGGR10_1X10, 10},
};
int stf_csiphy_subdev_init(struct stfcamss *stfcamss)
{
struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
csiphy_dev->hw_ops = &csiphy_ops;
csiphy_dev->stfcamss = stfcamss;
csiphy_dev->formats = csiphy_formats_st7110;
csiphy_dev->nformats = ARRAY_SIZE(csiphy_formats_st7110);
mutex_init(&csiphy_dev->stream_lock);
return 0;
}
static int csiphy_set_power(struct v4l2_subdev *sd, int on)
{
return 0;
}
static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
{
struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
mutex_lock(&csiphy_dev->stream_lock);
if (enable) {
if (csiphy_dev->stream_count == 0) {
csiphy_dev->hw_ops->csiphy_clk_enable(csiphy_dev);
csiphy_dev->hw_ops->csiphy_config_set(csiphy_dev);
csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 1);
}
csiphy_dev->stream_count++;
} else {
if (csiphy_dev->stream_count == 0)
goto exit;
if (csiphy_dev->stream_count == 1) {
csiphy_dev->hw_ops->csiphy_clk_disable(csiphy_dev);
csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 0);
}
csiphy_dev->stream_count--;
}
exit:
mutex_unlock(&csiphy_dev->stream_lock);
return 0;
}
static struct v4l2_mbus_framefmt *
__csiphy_get_format(struct stf_csiphy_dev *csiphy_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(
&csiphy_dev->subdev,
state,
pad);
return &csiphy_dev->fmt[pad];
}
static void csiphy_try_format(struct stf_csiphy_dev *csiphy_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
unsigned int i;
switch (pad) {
case STF_CSIPHY_PAD_SINK:
/* Set format on sink pad */
for (i = 0; i < csiphy_dev->nformats; i++)
if (fmt->code == csiphy_dev->formats[i].code)
break;
if (i >= csiphy_dev->nformats)
fmt->code = csiphy_dev->formats[0].code;
fmt->width = clamp_t(u32,
fmt->width,
STFCAMSS_FRAME_MIN_WIDTH,
STFCAMSS_FRAME_MAX_WIDTH);
fmt->height = clamp_t(u32,
fmt->height,
STFCAMSS_FRAME_MIN_HEIGHT,
STFCAMSS_FRAME_MAX_HEIGHT);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->flags = 0;
break;
case STF_CSIPHY_PAD_SRC:
*fmt = *__csiphy_get_format(csiphy_dev,
state,
STF_CSIPHY_PAD_SINK, which);
break;
}
}
static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
if (code->index >= csiphy_dev->nformats)
return -EINVAL;
if (code->pad == STF_CSIPHY_PAD_SINK) {
code->code = csiphy_dev->formats[code->index].code;
} else {
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = __csiphy_get_format(csiphy_dev, state,
STF_CSIPHY_PAD_SINK,
code->which);
code->code = sink_fmt->code;
if (!code->code)
return -EINVAL;
}
code->flags = 0;
return 0;
}
static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
if (fse->index != 0)
return -EINVAL;
format.code = fse->code;
format.width = 1;
format.height = 1;
csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
fse->min_width = format.width;
fse->min_height = format.height;
if (format.code != fse->code)
return -EINVAL;
format.code = fse->code;
format.width = -1;
format.height = -1;
csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
fse->max_width = format.width;
fse->max_height = format.height;
return 0;
}
static int csiphy_get_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
fmt->format = *format;
return 0;
}
static int csiphy_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
mutex_lock(&csiphy_dev->stream_lock);
if (csiphy_dev->stream_count) {
fmt->format = *format;
mutex_unlock(&csiphy_dev->stream_lock);
goto out;
} else {
csiphy_try_format(csiphy_dev, state, fmt->pad, &fmt->format, fmt->which);
*format = fmt->format;
}
mutex_unlock(&csiphy_dev->stream_lock);
/* Propagate the format from sink to source */
if (fmt->pad == STF_CSIPHY_PAD_SINK) {
format = __csiphy_get_format(csiphy_dev,
state,
STF_CSIPHY_PAD_SRC,
fmt->which);
*format = fmt->format;
csiphy_try_format(csiphy_dev, state, STF_CSIPHY_PAD_SRC, format,
fmt->which);
}
out:
return 0;
}
static int csiphy_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct v4l2_subdev_format format = {
.pad = STF_CSIPHY_PAD_SINK,
.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
V4L2_SUBDEV_FORMAT_ACTIVE,
.format = {
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.width = 1920,
.height = 1080
}
};
return csiphy_set_format(sd, fh ? fh->state : NULL, &format);
}
static int csiphy_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
(flags & MEDIA_LNK_FL_ENABLED)) {
struct v4l2_subdev *sd;
struct stf_csiphy_dev *csiphy_dev;
struct stf_csi_dev *csi_dev;
if (media_pad_remote_pad_first(local))
return -EBUSY;
sd = media_entity_to_v4l2_subdev(entity);
csiphy_dev = v4l2_get_subdevdata(sd);
sd = media_entity_to_v4l2_subdev(remote->entity);
csi_dev = v4l2_get_subdevdata(sd);
st_info(ST_CSIPHY, "CSIPHY0 link to CSI0\n");
}
return 0;
}
static const struct v4l2_subdev_core_ops csiphy_core_ops = {
.s_power = csiphy_set_power,
};
static const struct v4l2_subdev_video_ops csiphy_video_ops = {
.s_stream = csiphy_set_stream,
};
static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
.enum_mbus_code = csiphy_enum_mbus_code,
.enum_frame_size = csiphy_enum_frame_size,
.get_fmt = csiphy_get_format,
.set_fmt = csiphy_set_format,
};
static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
.core = &csiphy_core_ops,
.video = &csiphy_video_ops,
.pad = &csiphy_pad_ops,
};
static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
.open = csiphy_init_formats,
};
static const struct media_entity_operations csiphy_media_ops = {
.link_setup = csiphy_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
struct v4l2_device *v4l2_dev)
{
struct v4l2_subdev *sd = &csiphy_dev->subdev;
struct device *dev = csiphy_dev->stfcamss->dev;
struct media_pad *pads = csiphy_dev->pads;
int ret;
v4l2_subdev_init(sd, &csiphy_v4l2_ops);
sd->internal_ops = &csiphy_v4l2_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
STF_CSIPHY_NAME, 0);
v4l2_set_subdevdata(sd, csiphy_dev);
ret = csiphy_init_formats(sd, NULL);
if (ret < 0) {
dev_err(dev, "Failed to init format: %d\n", ret);
return ret;
}
pads[STF_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[STF_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &csiphy_media_ops;
ret = media_entity_pads_init(&sd->entity, STF_CSIPHY_PADS_NUM, pads);
if (ret < 0) {
dev_err(dev, "Failed to init media entity: %d\n", ret);
return ret;
}
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0) {
dev_err(dev, "Failed to register subdev: %d\n", ret);
goto err_sreg;
}
return 0;
err_sreg:
media_entity_cleanup(&sd->entity);
return ret;
}
int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev)
{
v4l2_device_unregister_subdev(&csiphy_dev->subdev);
media_entity_cleanup(&csiphy_dev->subdev.entity);
mutex_destroy(&csiphy_dev->stream_lock);
return 0;
}

View File

@@ -0,0 +1,188 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_CSIPHY_H
#define STF_CSIPHY_H
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
#include <media/media-entity.h>
#include <video/stf-vin.h>
#define STF_CSIPHY_NAME "stf_csiphy"
#define STF_CSIPHY_PAD_SINK 0
#define STF_CSIPHY_PAD_SRC 1
#define STF_CSIPHY_PADS_NUM 2
#define STF_CSI2_MAX_DATA_LANES 4
union static_config {
u32 raw;
struct {
u32 sel : 2;
u32 rsvd_6 : 2;
u32 v2p0_support_enable : 1;
u32 rsvd_5 : 3;
u32 lane_nb : 3;
u32 rsvd_4 : 5;
u32 dl0_map : 3;
u32 rsvd_3 : 1;
u32 dl1_map : 3;
u32 rsvd_2 : 1;
u32 dl2_map : 3;
u32 rsvd_1 : 1;
u32 dl3_map : 3;
u32 rsvd_0 : 1;
} bits;
};
union error_bypass_cfg {
u32 value;
struct {
u32 crc : 1;
u32 ecc : 1;
u32 data_id : 1;
u32 rsvd_0 : 29;
};
};
union stream_monitor_ctrl {
u32 value;
struct {
u32 lb_vc : 4;
u32 lb_en : 1;
u32 timer_vc : 4;
u32 timer_en : 1;
u32 timer_eof : 1;
u32 frame_mon_vc : 4;
u32 frame_mon_en : 1;
u32 frame_length : 16;
};
};
union stream_cfg {
u32 value;
struct {
u32 interface_mode : 1;
u32 ls_le_mode : 1;
u32 rsvd_3 : 2;
u32 num_pixels : 2;
u32 rsvd_2 : 2;
u32 fifo_mode : 2;
u32 rsvd_1 : 2;
u32 bpp_bypass : 3;
u32 rsvd_0 : 1;
u32 fifo_fill : 16;
};
};
union dphy_lane_ctrl {
u32 raw;
struct {
u32 dl0_en : 1;
u32 dl1_en : 1;
u32 dl2_en : 1;
u32 dl3_en : 1;
u32 cl_en : 1;
u32 rsvd_1 : 7;
u32 dl0_reset : 1;
u32 dl1_reset : 1;
u32 dl2_reset : 1;
u32 dl3_reset : 1;
u32 cl_reset : 1;
u32 rsvd_0 : 15;
} bits;
};
union dphy_lane_swap {
u32 raw;
struct {
u32 rx_1c2c_sel : 1;
u32 lane_swap_clk : 3;
u32 lane_swap_clk1 : 3;
u32 lane_swap_lan0 : 3;
u32 lane_swap_lan1 : 3;
u32 lane_swap_lan2 : 3;
u32 lane_swap_lan3 : 3;
u32 dpdn_swap_clk : 1;
u32 dpdn_swap_clk1 : 1;
u32 dpdn_swap_lan0 : 1;
u32 dpdn_swap_lan1 : 1;
u32 dpdn_swap_lan2 : 1;
u32 dpdn_swap_lan3 : 1;
u32 hs_freq_chang_clk0 : 1;
u32 hs_freq_chang_clk1 : 1;
u32 reserved : 5;
} bits;
};
union dphy_lane_en {
u32 raw;
struct {
u32 gpio_en : 6;
u32 mp_test_mode_sel : 5;
u32 mp_test_en : 1;
u32 dphy_enable_lan0 : 1;
u32 dphy_enable_lan1 : 1;
u32 dphy_enable_lan2 : 1;
u32 dphy_enable_lan3 : 1;
u32 rsvd_0 : 16;
} bits;
};
struct csiphy_format {
u32 code;
u8 bpp;
};
struct csi2phy_cfg {
unsigned int flags;
unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
unsigned char clock_lane;
unsigned char num_data_lanes;
bool lane_polarities[1 + STF_CSI2_MAX_DATA_LANES];
};
struct csi2phy_cfg2 {
unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
unsigned char num_data_lanes;
unsigned char num_clks;
unsigned char clock_lane;
unsigned char clock1_lane;
bool lane_polarities[2 + STF_CSI2_MAX_DATA_LANES];
};
struct stf_csiphy_dev;
struct csiphy_hw_ops {
int (*csiphy_clk_enable)(struct stf_csiphy_dev *csiphy_dev);
int (*csiphy_clk_disable)(struct stf_csiphy_dev *csiphy_dev);
int (*csiphy_config_set)(struct stf_csiphy_dev *csiphy_dev);
int (*csiphy_stream_set)(struct stf_csiphy_dev *csiphy_dev, int on);
};
struct stf_csiphy_dev {
struct stfcamss *stfcamss;
struct csi2phy_cfg *csiphy;
struct v4l2_subdev subdev;
struct media_pad pads[STF_CSIPHY_PADS_NUM];
struct v4l2_mbus_framefmt fmt[STF_CSIPHY_PADS_NUM];
const struct csiphy_format *formats;
unsigned int nformats;
struct csiphy_hw_ops *hw_ops;
struct mutex stream_lock;
int stream_count;
};
extern int stf_csiphy_subdev_init(struct stfcamss *stfcamss);
extern int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
struct v4l2_device *v4l2_dev);
extern int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev);
extern struct csiphy_hw_ops csiphy_ops;
#endif /* STF_CSIPHY_H */

View File

@@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <linux/sort.h>
static int stf_csiphy_clk_set(struct stf_csiphy_dev *csiphy_dev, int on)
{
struct stfcamss *stfcamss = csiphy_dev->stfcamss;
static int init_flag;
static struct mutex count_lock;
static int count;
if (!init_flag) {
init_flag = 1;
mutex_init(&count_lock);
}
mutex_lock(&count_lock);
if (on) {
clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_CFGCLK_IN].clk,
99000000);
clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_REFCLK_IN].clk,
49500000);
clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_TXCLKESC_LAN0].clk,
19800000);
reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
count++;
} else {
if (count == 0)
goto exit;
if (count == 1) {
reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
}
count--;
}
exit:
mutex_unlock(&count_lock);
return 0;
}
static int stf_csiphy_clk_enable(struct stf_csiphy_dev *csiphy_dev)
{
return stf_csiphy_clk_set(csiphy_dev, 1);
}
static int stf_csiphy_clk_disable(struct stf_csiphy_dev *csiphy_dev)
{
return stf_csiphy_clk_set(csiphy_dev, 0);
}
#ifndef USE_CSIDPHY_ONE_CLK_MODE
static int cmp_func(const void *x1, const void *x2)
{
return *((unsigned char *)x1) - *((unsigned char *)x2);
}
#endif
int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
struct csi2phy_cfg *cfg1)
{
int i = 0;
cfg->clock_lane = 0;
cfg->clock1_lane = 5;
cfg->data_lanes[0] = 1;
cfg->data_lanes[1] = 2;
cfg->data_lanes[2] = 3;
cfg->data_lanes[3] = 4;
if (cfg0 && cfg1) {
st_debug(ST_CSIPHY, "CSIPHY use 2 clk mode\n");
cfg->num_clks = 2;
cfg->num_data_lanes =
cfg1->num_data_lanes + cfg0->num_data_lanes;
if (cfg->num_data_lanes > STF_CSI2_MAX_DATA_LANES)
return -EINVAL;
cfg->clock_lane = cfg0->clock_lane;
cfg->lane_polarities[0] = cfg0->lane_polarities[0];
cfg->clock1_lane = cfg1->clock_lane;
cfg->lane_polarities[1] = cfg1->lane_polarities[0];
for (i = 0; i < cfg0->num_data_lanes; i++) {
cfg->data_lanes[i] = cfg0->data_lanes[i];
cfg->lane_polarities[i + 2] =
cfg0->lane_polarities[i + 1];
}
for (i = cfg0->num_data_lanes; i < cfg->num_data_lanes; i++) {
cfg->data_lanes[i] =
cfg1->data_lanes[i - cfg0->num_data_lanes];
cfg->lane_polarities[i + 2] =
cfg1->lane_polarities[i - cfg0->num_data_lanes + 1];
}
} else if (cfg0 && !cfg1) {
st_debug(ST_CSIPHY, "CSIPHY cfg0 use 1 clk mode\n");
cfg->num_clks = 1;
cfg->num_data_lanes = cfg0->num_data_lanes;
cfg->clock_lane = cfg->clock1_lane = cfg0->clock_lane;
cfg->lane_polarities[0] = cfg->lane_polarities[1] =
cfg0->lane_polarities[0];
for (i = 0; i < cfg0->num_data_lanes; i++) {
cfg->data_lanes[i] = cfg0->data_lanes[i];
cfg->lane_polarities[i + 2] = cfg0->lane_polarities[i + 1];
}
} else if (!cfg0 && cfg1) {
st_debug(ST_CSIPHY, "CSIPHY cfg1 use 1 clk mode\n");
cfg->num_clks = 1;
cfg->num_data_lanes = cfg1->num_data_lanes;
cfg->clock_lane = cfg->clock1_lane = cfg1->clock_lane;
cfg->lane_polarities[0] = cfg->lane_polarities[1] =
cfg1->lane_polarities[0];
for (i = 0; i < cfg1->num_data_lanes; i++) {
cfg->data_lanes[i] = cfg1->data_lanes[i];
cfg->lane_polarities[i + 2] = cfg1->lane_polarities[i + 1];
}
} else {
return -EINVAL;
}
#ifndef USE_CSIDPHY_ONE_CLK_MODE
sort(cfg->data_lanes, cfg->num_data_lanes,
sizeof(cfg->data_lanes[0]), cmp_func, NULL);
#endif
for (i = 0; i < cfg->num_data_lanes; i++)
st_debug(ST_CSIPHY, "%d: %d\n", i, cfg->data_lanes[i]);
return 0;
}
static int csi2rx_dphy_config(struct stf_vin_dev *vin,
struct stf_csiphy_dev *csiphy_dev)
{
struct csi2phy_cfg2 cfg2 = {0};
struct csi2phy_cfg2 *cfg = &cfg2;
struct csi2phy_cfg *phycfg = csiphy_dev->csiphy;
if (!phycfg)
return -EINVAL;
if (try_cfg(cfg, phycfg, NULL))
return -EINVAL;
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_4, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_8, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_12, 0xfff0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_16, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_20, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_24, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_28, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_32, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_36, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_40, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_44, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_48, 0x24000000);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_52, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_56, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_60, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_64, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_68, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_72, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_76, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_80, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_84, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_88, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_92, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_96, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_100, 0x02000000);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_104, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_108, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_112, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_116, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_120, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_124, 0xc);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_128, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_132, 0xcc500000);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_136, 0xcc);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_140, 0x0);
reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_144, 0x0);
reg_set_bit(vin->rstgen_base, //r100_ctrl0_2d1c_efuse_en
M31DPHY_APBCFGSAIF__SYSCFG_0,
BIT(6), 1<<6);
reg_set_bit(vin->rstgen_base, //r100_ctrl0_2d1c_efuse_in
M31DPHY_APBCFGSAIF__SYSCFG_0,
BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0x1b<<7);
reg_set_bit(vin->rstgen_base, //r100_ctrl1_2d1c_efuse_en
M31DPHY_APBCFGSAIF__SYSCFG_0,
BIT(13), 1<<13);
reg_set_bit(vin->rstgen_base, //r100_ctrl1_2d1c_efuse_in
M31DPHY_APBCFGSAIF__SYSCFG_0,
BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14), 0x1b<<14);
reg_set_bit(vin->rstgen_base, //data_bus16_8
M31DPHY_APBCFGSAIF__SYSCFG_184,
BIT(8), 0<<8);
reg_set_bit(vin->rstgen_base, //debug_mode_sel
M31DPHY_APBCFGSAIF__SYSCFG_184,
BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9), 0x5a<<9);
reg_set_bit(vin->rstgen_base, //dpdn_swap_clk0
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(0), cfg->lane_polarities[0]<<0);
reg_set_bit(vin->rstgen_base, //dpdn_swap_clk1
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(1), cfg->lane_polarities[1]<<1);
reg_set_bit(vin->rstgen_base, //dpdn_swap_lan0
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(2), cfg->lane_polarities[2]<<2);
reg_set_bit(vin->rstgen_base, //dpdn_swap_lan1
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(3), cfg->lane_polarities[3]<<3);
reg_set_bit(vin->rstgen_base, //dpdn_swap_lan2
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(4), cfg->lane_polarities[4]<<4);
reg_set_bit(vin->rstgen_base, //dpdn_swap_lan3
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(5), cfg->lane_polarities[5]<<5);
reg_set_bit(vin->rstgen_base, //enable clk0
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(6), 1<<6);
reg_set_bit(vin->rstgen_base, //enable clk1
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(7), 1<<7);
reg_set_bit(vin->rstgen_base, //enable lan0
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(8), 1<<8);
reg_set_bit(vin->rstgen_base, //enable lan1
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(9), 1<<9);
reg_set_bit(vin->rstgen_base, //enable lan2
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(10), 1<<10);
reg_set_bit(vin->rstgen_base, //enable lan3
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(11), 1<<11);
reg_set_bit(vin->rstgen_base, //gpi_en
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
0<<12);
reg_set_bit(vin->rstgen_base, //hs_freq_change_clk0
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(18), 0<<18);
reg_set_bit(vin->rstgen_base, //hs_freq_change_clk1
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(19), 0<<19);
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(22)|BIT(21)|BIT(20), cfg->clock_lane<<20); //clock lane 0
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(25)|BIT(24)|BIT(23), cfg->clock1_lane<<23); //clock lane 1
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(28)|BIT(27)|BIT(26), cfg->data_lanes[0]<<26); //data lane 0
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_188,
BIT(31)|BIT(30)|BIT(29), cfg->data_lanes[1]<<29); //data lane 1
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(2)|BIT(1)|BIT(0), cfg->data_lanes[2]<<0); //data lane 2
reg_set_bit(vin->rstgen_base,
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(5)|BIT(4)|BIT(3), cfg->data_lanes[3]<<3); //data lane 3
reg_set_bit(vin->rstgen_base, //mp_test_en
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(6), 0<<6);
reg_set_bit(vin->rstgen_base, //mp_test_mode_sel
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0<<7);
reg_set_bit(vin->rstgen_base, //pll_clk_sel
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
0x37c<<12);
reg_set_bit(vin->rstgen_base, //rx_1c2c_sel
M31DPHY_APBCFGSAIF__SYSCFG_200,
BIT(8), 0<<8);
reg_set_bit(vin->rstgen_base, //precounter in clk0
M31DPHY_APBCFGSAIF__SYSCFG_192,
BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24)|BIT(23)|BIT(22),
8<<22);
reg_set_bit(vin->rstgen_base, //precounter in clk1
M31DPHY_APBCFGSAIF__SYSCFG_196,
BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
8<<0);
reg_set_bit(vin->rstgen_base, //precounter in lan0
M31DPHY_APBCFGSAIF__SYSCFG_196,
BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8),
7<<8);
reg_set_bit(vin->rstgen_base, //precounter in lan1
M31DPHY_APBCFGSAIF__SYSCFG_196,
BIT(23)|BIT(22)|BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16),
7<<16);
reg_set_bit(vin->rstgen_base, //precounter in lan2
M31DPHY_APBCFGSAIF__SYSCFG_196,
BIT(31)|BIT(30)|BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24),
7<<24);
reg_set_bit(vin->rstgen_base, //precounter in lan3
M31DPHY_APBCFGSAIF__SYSCFG_200,
BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
7<<0);
return 0;
}
static int stf_csiphy_config_set(struct stf_csiphy_dev *csiphy_dev)
{
struct stf_vin_dev *vin = csiphy_dev->stfcamss->vin;
csi2rx_dphy_config(vin, csiphy_dev);
return 0;
}
static int stf_csiphy_stream_set(struct stf_csiphy_dev *csiphy_dev, int on)
{
return 0;
}
struct csiphy_hw_ops csiphy_ops = {
.csiphy_clk_enable = stf_csiphy_clk_enable,
.csiphy_clk_disable = stf_csiphy_clk_disable,
.csiphy_config_set = stf_csiphy_config_set,
.csiphy_stream_set = stf_csiphy_stream_set,
};

View File

@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include <linux/dma-buf.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#include "stf_isp_ioctl.h"
#include "stf_dmabuf.h"
#define TOTAL_SIZE_LIMIT (64 * 1024 * 1024)
static size_t total_size;
static struct vb2_queue vb2_queue = {
.dma_attrs = 0,
.gfp_flags = 0,
.dma_dir = DMA_TO_DEVICE,
};
static struct vb2_buffer vb = {
.vb2_queue = &vb2_queue,
};
static int dmabuf_create(struct device *dev,
struct dmabuf_create *head)
{
struct dma_buf *dmabuf = NULL;
void *mem_priv = NULL;
dma_addr_t *paddr = NULL;
int ret = 0;
mem_priv = vb2_dma_contig_memops.alloc(&vb, dev, head->size);
if (IS_ERR_OR_NULL(mem_priv)) {
if (mem_priv)
ret = PTR_ERR(mem_priv);
goto exit;
}
dmabuf = vb2_dma_contig_memops.get_dmabuf(&vb, mem_priv, O_RDWR);
if (IS_ERR(dmabuf)) {
ret = PTR_ERR(dmabuf);
goto free;
}
head->fd = dma_buf_fd(dmabuf, O_CLOEXEC);
if (head->fd < 0) {
dma_buf_put(dmabuf);
ret = head->fd;
goto free;
}
paddr = vb2_dma_contig_memops.cookie(&vb, mem_priv);
head->paddr = *paddr;
return 0;
free:
vb2_dma_contig_memops.put(mem_priv);
exit:
return ret;
}
int stf_dmabuf_ioctl_alloc(struct device *dev, void *arg)
{
struct dmabuf_create *head = arg;
int ret = -EINVAL;
if (IS_ERR_OR_NULL(head))
return -EFAULT;
head->size = PAGE_ALIGN(head->size);
if (!head->size)
return -EINVAL;
if ((head->size + total_size) > TOTAL_SIZE_LIMIT)
return -ENOMEM;
ret = dmabuf_create(dev, head);
if (ret)
return -EFAULT;
total_size += head->size;
return ret;
}
int stf_dmabuf_ioctl_free(struct device *dev, void *arg)
{
struct dmabuf_create *head = arg;
struct dma_buf *dmabuf = NULL;
int ret = 0;
if (IS_ERR_OR_NULL(head))
return -EFAULT;
if (head->size != PAGE_ALIGN(head->size))
return -EINVAL;
if (head->size > total_size)
return -EINVAL;
dmabuf = dma_buf_get(head->fd);
if (IS_ERR_OR_NULL(dmabuf))
return -EINVAL;
dma_buf_put(dmabuf);
vb2_dma_contig_memops.put(dmabuf->priv);
total_size -= head->size;
return ret;
}
int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg)
{
int ret = -ENOIOCTLCMD;
switch (cmd) {
case VIDIOC_STF_DMABUF_ALLOC:
ret = stf_dmabuf_ioctl_alloc(dev, arg);
break;
case VIDIOC_STF_DMABUF_FREE:
ret = stf_dmabuf_ioctl_free(dev, arg);
break;
default:
break;
}
return ret;
}

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_DMABUF_H
#define STF_DMABUF_H
extern int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg);
#endif /* STF_DMABUF_H */

View File

@@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
static const struct dvp_format dvp_formats_st7110[] = {
{ MEDIA_BUS_FMT_YUYV8_2X8, 8},
{ MEDIA_BUS_FMT_RGB565_2X8_LE, 8},
{ MEDIA_BUS_FMT_SRGGB8_1X8, 8},
{ MEDIA_BUS_FMT_SGRBG8_1X8, 8},
{ MEDIA_BUS_FMT_SGBRG8_1X8, 8},
{ MEDIA_BUS_FMT_SBGGR8_1X8, 8},
{ MEDIA_BUS_FMT_SRGGB10_1X10, 8},
{ MEDIA_BUS_FMT_SGRBG10_1X10, 8},
{ MEDIA_BUS_FMT_SGBRG10_1X10, 8},
{ MEDIA_BUS_FMT_SBGGR10_1X10, 8},
};
static int dvp_find_format(u32 code,
const struct dvp_format *formats,
unsigned int nformats)
{
int i;
for (i = 0; i < nformats; i++)
if (formats[i].code == code)
return i;
return -EINVAL;
}
int stf_dvp_subdev_init(struct stfcamss *stfcamss)
{
struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
dvp_dev->s_type = SENSOR_VIN;
dvp_dev->hw_ops = &dvp_ops;
dvp_dev->stfcamss = stfcamss;
dvp_dev->formats = dvp_formats_st7110;
dvp_dev->nformats = ARRAY_SIZE(dvp_formats_st7110);
mutex_init(&dvp_dev->stream_lock);
dvp_dev->stream_count = 0;
return 0;
}
static int dvp_set_power(struct v4l2_subdev *sd, int on)
{
return 0;
}
static struct v4l2_mbus_framefmt *
__dvp_get_format(struct stf_dvp_dev *dvp_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(
&dvp_dev->subdev, state, pad);
return &dvp_dev->fmt[pad];
}
static int dvp_set_stream(struct v4l2_subdev *sd, int enable)
{
struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
int ret = 0;
format = __dvp_get_format(dvp_dev, NULL, STF_DVP_PAD_SRC,
V4L2_SUBDEV_FORMAT_ACTIVE);
if (format == NULL)
return -EINVAL;
ret = dvp_find_format(format->code,
dvp_dev->formats,
dvp_dev->nformats);
if (ret < 0)
return ret;
mutex_lock(&dvp_dev->stream_lock);
if (enable) {
if (dvp_dev->stream_count == 0) {
dvp_dev->hw_ops->dvp_clk_enable(dvp_dev);
dvp_dev->hw_ops->dvp_config_set(dvp_dev);
dvp_dev->hw_ops->dvp_set_format(dvp_dev,
format->width, dvp_dev->formats[ret].bpp);
dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 1);
}
dvp_dev->stream_count++;
} else {
if (dvp_dev->stream_count == 0)
goto exit;
if (dvp_dev->stream_count == 1) {
dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 0);
dvp_dev->hw_ops->dvp_clk_disable(dvp_dev);
}
dvp_dev->stream_count--;
}
exit:
mutex_unlock(&dvp_dev->stream_lock);
return 0;
}
static void dvp_try_format(struct stf_dvp_dev *dvp_dev,
struct v4l2_subdev_state *state,
unsigned int pad,
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
unsigned int i;
switch (pad) {
case STF_DVP_PAD_SINK:
/* Set format on sink pad */
for (i = 0; i < dvp_dev->nformats; i++)
if (fmt->code == dvp_dev->formats[i].code)
break;
if (i >= dvp_dev->nformats)
fmt->code = dvp_dev->formats[0].code;
fmt->width = clamp_t(u32,
fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
STFCAMSS_FRAME_MAX_WIDTH);
fmt->height = clamp_t(u32,
fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
STFCAMSS_FRAME_MAX_HEIGHT);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->flags = 0;
break;
case STF_DVP_PAD_SRC:
*fmt = *__dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK, which);
break;
}
}
static int dvp_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
if (code->index >= dvp_dev->nformats)
return -EINVAL;
if (code->pad == STF_DVP_PAD_SINK) {
code->code = dvp_dev->formats[code->index].code;
} else {
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK,
code->which);
code->code = sink_fmt->code;
if (!code->code)
return -EINVAL;
}
code->flags = 0;
return 0;
}
static int dvp_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
if (fse->index != 0)
return -EINVAL;
format.code = fse->code;
format.width = 1;
format.height = 1;
dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
fse->min_width = format.width;
fse->min_height = format.height;
if (format.code != fse->code)
return -EINVAL;
format.code = fse->code;
format.width = -1;
format.height = -1;
dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
fse->max_width = format.width;
fse->max_height = format.height;
return 0;
}
static int dvp_get_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
fmt->format = *format;
return 0;
}
static int dvp_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
if (format == NULL)
return -EINVAL;
mutex_lock(&dvp_dev->stream_lock);
if (dvp_dev->stream_count) {
fmt->format = *format;
mutex_unlock(&dvp_dev->stream_lock);
goto out;
} else {
dvp_try_format(dvp_dev, state, fmt->pad, &fmt->format, fmt->which);
*format = fmt->format;
}
mutex_unlock(&dvp_dev->stream_lock);
/* Propagate the format from sink to source */
if (fmt->pad == STF_DVP_PAD_SINK) {
format = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SRC,
fmt->which);
*format = fmt->format;
dvp_try_format(dvp_dev, state, STF_DVP_PAD_SRC, format,
fmt->which);
}
out:
return 0;
}
static int dvp_init_formats(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct v4l2_subdev_format format = {
.pad = STF_DVP_PAD_SINK,
.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
V4L2_SUBDEV_FORMAT_ACTIVE,
.format = {
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.width = 1920,
.height = 1080
}
};
return dvp_set_format(sd, fh ? fh->state : NULL, &format);
}
static int dvp_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
(flags & MEDIA_LNK_FL_ENABLED)) {
struct v4l2_subdev *sd;
struct stf_dvp_dev *dvp_dev;
struct vin_line *line;
if (media_pad_remote_pad_first(local))
return -EBUSY;
sd = media_entity_to_v4l2_subdev(entity);
dvp_dev = v4l2_get_subdevdata(sd);
sd = media_entity_to_v4l2_subdev(remote->entity);
line = v4l2_get_subdevdata(sd);
if (line->sdev_type == VIN_DEV_TYPE)
dvp_dev->s_type = SENSOR_VIN;
if (line->sdev_type == ISP_DEV_TYPE)
dvp_dev->s_type = SENSOR_ISP;
st_info(ST_DVP, "DVP device sensor type: %d\n", dvp_dev->s_type);
}
return 0;
}
static const struct v4l2_subdev_core_ops dvp_core_ops = {
.s_power = dvp_set_power,
};
static const struct v4l2_subdev_video_ops dvp_video_ops = {
.s_stream = dvp_set_stream,
};
static const struct v4l2_subdev_pad_ops dvp_pad_ops = {
.enum_mbus_code = dvp_enum_mbus_code,
.enum_frame_size = dvp_enum_frame_size,
.get_fmt = dvp_get_format,
.set_fmt = dvp_set_format,
};
static const struct v4l2_subdev_ops dvp_v4l2_ops = {
.core = &dvp_core_ops,
.video = &dvp_video_ops,
.pad = &dvp_pad_ops,
};
static const struct v4l2_subdev_internal_ops dvp_v4l2_internal_ops = {
.open = dvp_init_formats,
};
static const struct media_entity_operations dvp_media_ops = {
.link_setup = dvp_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
struct v4l2_device *v4l2_dev)
{
struct v4l2_subdev *sd = &dvp_dev->subdev;
struct media_pad *pads = dvp_dev->pads;
int ret;
v4l2_subdev_init(sd, &dvp_v4l2_ops);
sd->internal_ops = &dvp_v4l2_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
STF_DVP_NAME, 0);
v4l2_set_subdevdata(sd, dvp_dev);
ret = dvp_init_formats(sd, NULL);
if (ret < 0) {
st_err(ST_DVP, "Failed to init format: %d\n", ret);
return ret;
}
pads[STF_DVP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
pads[STF_DVP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &dvp_media_ops;
ret = media_entity_pads_init(&sd->entity, STF_DVP_PADS_NUM, pads);
if (ret < 0) {
st_err(ST_DVP, "Failed to init media entity: %d\n", ret);
return ret;
}
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0) {
st_err(ST_DVP, "Failed to register subdev: %d\n", ret);
goto err_sreg;
}
return 0;
err_sreg:
media_entity_cleanup(&sd->entity);
return ret;
}
int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev)
{
v4l2_device_unregister_subdev(&dvp_dev->subdev);
media_entity_cleanup(&dvp_dev->subdev.entity);
mutex_destroy(&dvp_dev->stream_lock);
return 0;
}

View File

@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_DVP_H
#define STF_DVP_H
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
#include <media/media-entity.h>
#include <video/stf-vin.h>
#define STF_DVP_NAME "stf_dvp"
#define STF_DVP_PAD_SINK 0
#define STF_DVP_PAD_SRC 1
#define STF_DVP_PADS_NUM 2
struct dvp_format {
u32 code;
u8 bpp;
};
enum sensor_type;
enum subdev_type;
struct dvp_cfg {
unsigned int flags;
unsigned char bus_width;
unsigned char data_shift;
};
struct stf_dvp_dev;
struct dvp_hw_ops {
int (*dvp_clk_enable)(struct stf_dvp_dev *dvp_dev);
int (*dvp_clk_disable)(struct stf_dvp_dev *dvp_dev);
int (*dvp_config_set)(struct stf_dvp_dev *dvp_dev);
int (*dvp_set_format)(struct stf_dvp_dev *dvp_dev,
u32 pix_width, u8 bpp);
int (*dvp_stream_set)(struct stf_dvp_dev *dvp_dev, int on);
};
struct stf_dvp_dev {
struct stfcamss *stfcamss;
struct dvp_cfg *dvp;
enum sensor_type s_type;
struct v4l2_subdev subdev;
struct media_pad pads[STF_DVP_PADS_NUM];
struct v4l2_mbus_framefmt fmt[STF_DVP_PADS_NUM];
const struct dvp_format *formats;
unsigned int nformats;
struct dvp_hw_ops *hw_ops;
struct mutex stream_lock;
int stream_count;
};
extern int stf_dvp_subdev_init(struct stfcamss *stfcamss);
extern int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
struct v4l2_device *v4l2_dev);
extern int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev);
extern struct dvp_hw_ops dvp_ops;
#endif /* STF_DVP_H */

View File

@@ -0,0 +1,187 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
static int stf_dvp_clk_enable(struct stf_dvp_dev *dvp_dev)
{
struct stfcamss *stfcamss = dvp_dev->stfcamss;
switch (dvp_dev->s_type) {
case SENSOR_VIN:
reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
stfcamss->sys_clk[STFCLK_DVP_INV].clk);
break;
case SENSOR_ISP:
clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
stfcamss->sys_clk[STFCLK_DVP_INV].clk);
break;
}
return 0;
}
static int stf_dvp_clk_disable(struct stf_dvp_dev *dvp_dev)
{
struct stfcamss *stfcamss = dvp_dev->stfcamss;
switch (dvp_dev->s_type) {
case SENSOR_VIN:
clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
break;
case SENSOR_ISP:
clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
break;
}
return 0;
}
static int stf_dvp_config_set(struct stf_dvp_dev *dvp_dev)
{
struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
unsigned int flags = 0;
unsigned char data_shift = 0;
u32 polarities = 0;
if (!dvp_dev->dvp)
return -EINVAL;
flags = dvp_dev->dvp->flags;
data_shift = dvp_dev->dvp->data_shift;
st_info(ST_DVP, "%s, polarities = 0x%x, flags = 0x%x\n",
__func__, polarities, flags);
if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
polarities |= BIT(1);
if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
polarities |= BIT(3);
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
U0_VIN_CNFG_DVP_HS_POS
| U0_VIN_CNFG_DVP_VS_POS,
polarities);
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
switch (data_shift) {
case 0:
data_shift = 0;
break;
case 2:
data_shift = 1;
break;
case 4:
data_shift = 2;
break;
case 6:
data_shift = 3;
break;
default:
data_shift = 0;
break;
};
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL,
data_shift << 15);
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
return 0;
}
static int set_vin_axiwr_pix_ct(struct stf_vin_dev *vin, u8 bpp)
{
u32 value = 0;
int cnfg_axiwr_pix_ct = 64 / bpp;
// need check
if (cnfg_axiwr_pix_ct == 2)
value = 1;
else if (cnfg_axiwr_pix_ct == 4)
value = 1;
else if (cnfg_axiwr_pix_ct == 8)
value = 0;
else
return 0;
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
reg_set_bit(vin->sysctrl_base,
SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_PIX_CT,
value<<13);
print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
return cnfg_axiwr_pix_ct;
}
static int stf_dvp_set_format(struct stf_dvp_dev *dvp_dev,
u32 pix_width, u8 bpp)
{
struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
int val, pix_ct;
if (dvp_dev->s_type == SENSOR_VIN) {
pix_ct = set_vin_axiwr_pix_ct(vin, bpp);
val = (pix_width / pix_ct) - 1;
print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
reg_set_bit(vin->sysctrl_base,
SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_PIX_CNT_END,
val << 2);
print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
}
return 0;
}
static int stf_dvp_stream_set(struct stf_dvp_dev *dvp_dev, int on)
{
struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
switch (dvp_dev->s_type) {
case SENSOR_VIN:
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
U0_VIN_CNFG_ISP_DVP_EN0,
0);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
U0_VIN_CNFG_AXI_DVP_EN,
!!on<<2);
break;
case SENSOR_ISP:
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
U0_VIN_CNFG_ISP_DVP_EN0,
!!on<<5);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
U0_VIN_CNFG_AXI_DVP_EN,
0);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
U0_VIN_CNFG_DVP_SWAP_EN,
0);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
U0_VIN_CNFG_GEN_EN_AXIRD,
0);
break;
}
return 0;
}
struct dvp_hw_ops dvp_ops = {
.dvp_clk_enable = stf_dvp_clk_enable,
.dvp_clk_disable = stf_dvp_clk_disable,
.dvp_config_set = stf_dvp_config_set,
.dvp_set_format = stf_dvp_set_format,
.dvp_stream_set = stf_dvp_stream_set,
};

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
static ATOMIC_NOTIFIER_HEAD(vin_notifier_list);
int vin_notifier_register(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&vin_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(vin_notifier_register);
void vin_notifier_unregister(struct notifier_block *nb)
{
atomic_notifier_chain_unregister(&vin_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(vin_notifier_unregister);
int vin_notifier_call(unsigned long e, void *v)
{
return atomic_notifier_call_chain(&vin_notifier_list, e, v);
}
EXPORT_SYMBOL_GPL(vin_notifier_call);
MODULE_AUTHOR("StarFive Technology Co., Ltd.");
MODULE_DESCRIPTION("Starfive VIC video in notifier");
MODULE_LICENSE("GPL");
//MODULE_SUPPORTED_DEVICE("video");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_ISP_H
#define STF_ISP_H
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/media-entity.h>
#include <video/stf-vin.h>
#define STF_ISP_NAME "stf_isp"
#define STF_ISP_SETFILE "stf_isp0_fw.bin"
#define ISP_SCD_BUFFER_SIZE (19 * 256 * 4) // align 128
#define ISP_YHIST_BUFFER_SIZE (64 * 4)
#define ISP_SCD_Y_BUFFER_SIZE (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
#define ISP_RAW_DATA_BITS 12
#define SCALER_RATIO_MAX 1 // no compose function
#define STF_ISP_REG_OFFSET_MAX 0x0FFF
#define STF_ISP_REG_DELAY_MAX 100
#define ISP_REG_CSIINTS_ADDR 0x00000008
#define ISP_REG_SENSOR 0x00000014
#define ISP_REG_DUMP_CFG_0 0x00000024
#define ISP_REG_DUMP_CFG_1 0x00000028
#define ISP_REG_SCD_CFG_0 0x00000098
#define ISP_REG_SCD_CFG_1 0x0000009C
#define ISP_REG_SC_CFG_1 0x000000BC
#define ISP_REG_IESHD_ADDR 0x00000A50
#define ISP_REG_SS0AY 0x00000A94
#define ISP_REG_SS0AUV 0x00000A98
#define ISP_REG_SS0S 0x00000A9C
#define ISP_REG_SS0IW 0x00000AA8
#define ISP_REG_SS1AY 0x00000AAC
#define ISP_REG_SS1AUV 0x00000AB0
#define ISP_REG_SS1S 0x00000AB4
#define ISP_REG_SS1IW 0x00000AC0
#define ISP_REG_YHIST_CFG_4 0x00000CD8
#define ISP_REG_ITIIWSR 0x00000B20
#define ISP_REG_ITIDWLSR 0x00000B24
#define ISP_REG_ITIDWYSAR 0x00000B28
#define ISP_REG_ITIDWUSAR 0x00000B2C
#define ISP_REG_ITIDRYSAR 0x00000B30
#define ISP_REG_ITIDRUSAR 0x00000B34
#define ISP_REG_ITIPDFR 0x00000B38
#define ISP_REG_ITIDRLSR 0x00000B3C
#define ISP_REG_ITIBSR 0x00000B40
#define ISP_REG_ITIAIR 0x00000B44
#define ISP_REG_ITIDPSR 0x00000B48
/* The output line of a isp controller */
enum isp_line_id {
STF_ISP_LINE_INVALID = -1,
STF_ISP_LINE_SRC = 1,
STF_ISP_LINE_SRC_SS0 = 2,
STF_ISP_LINE_SRC_SS1 = 3,
STF_ISP_LINE_SRC_ITIW = 4,
STF_ISP_LINE_SRC_ITIR = 5,
STF_ISP_LINE_SRC_RAW = 6,
STF_ISP_LINE_SRC_SCD_Y = 7,
STF_ISP_LINE_MAX = STF_ISP_LINE_SRC_SCD_Y
};
/* pad id for media framework */
enum isp_pad_id {
STF_ISP_PAD_SINK = 0,
STF_ISP_PAD_SRC = 1,
STF_ISP_PAD_SRC_SS0 = 2,
STF_ISP_PAD_SRC_SS1 = 3,
STF_ISP_PAD_SRC_ITIW = 4,
STF_ISP_PAD_SRC_ITIR = 5,
STF_ISP_PAD_SRC_RAW = 6,
STF_ISP_PAD_SRC_SCD_Y = 7,
STF_ISP_PAD_MAX = 8
};
enum {
EN_INT_NONE = 0,
EN_INT_ISP_DONE = (0x1 << 24),
EN_INT_CSI_DONE = (0x1 << 25),
EN_INT_SC_DONE = (0x1 << 26),
EN_INT_LINE_INT = (0x1 << 27),
EN_INT_ALL = (0xF << 24),
};
enum {
DVP_SENSOR = 0,
CSI_SENSOR,
};
#define ISP_AWB_OECF_SKIP_FRAME 0
// 0x0BC [31:30] SEL - sc0 input mux for sc awb
// 00 : after DEC, 01 : after OBC, 10 : after OECF, 11 : after AWB
enum scd_type {
DEC_TYPE = 0,
OBC_TYPE,
OECF_TYPE,
AWB_TYPE
};
struct isp_format {
u32 code;
u8 bpp;
};
struct isp_format_table {
const struct isp_format *fmts;
int nfmts;
};
struct regval_t {
u32 addr;
u32 val;
u32 mask;
u32 delay_ms;
};
struct reg_table {
struct regval_t *regval;
int regval_num;
};
struct isp_stream_format {
struct v4l2_rect rect;
u32 bpp;
};
struct stf_isp_dev;
enum subdev_type;
struct isp_hw_ops {
int (*isp_clk_enable)(struct stf_isp_dev *isp_dev);
int (*isp_clk_disable)(struct stf_isp_dev *isp_dev);
int (*isp_reset)(struct stf_isp_dev *isp_dev);
int (*isp_config_set)(struct stf_isp_dev *isp_dev);
int (*isp_set_format)(struct stf_isp_dev *isp_dev,
struct isp_stream_format *crop, u32 mcode,
int type);
// u32 width, u32 height);
int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on);
int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg);
int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg);
int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev);
};
struct isp_ctrls {
struct v4l2_ctrl_handler handler;
struct v4l2_ctrl *pixel_rate;
struct {
struct v4l2_ctrl *auto_exp;
struct v4l2_ctrl *exposure;
};
struct {
struct v4l2_ctrl *auto_wb;
struct v4l2_ctrl *blue_balance;
struct v4l2_ctrl *red_balance;
};
struct {
struct v4l2_ctrl *auto_gain;
struct v4l2_ctrl *gain;
};
struct v4l2_ctrl *brightness;
struct v4l2_ctrl *light_freq;
struct v4l2_ctrl *saturation;
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *hue;
struct v4l2_ctrl *test_pattern;
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vflip;
};
struct isp_setfile {
struct reg_table settings;
const u8 *data;
unsigned int size;
unsigned int state;
};
enum {
ISP_CROP = 0,
ISP_COMPOSE,
ISP_SCALE_SS0,
ISP_SCALE_SS1,
ISP_ITIWS,
ISP_RECT_MAX
};
struct stf_isp_dev {
enum subdev_type sdev_type; // must be frist
struct stfcamss *stfcamss;
struct v4l2_subdev subdev;
struct media_pad pads[STF_ISP_PAD_MAX];
struct v4l2_mbus_framefmt fmt[STF_ISP_PAD_MAX];
struct isp_stream_format rect[ISP_RECT_MAX];
const struct isp_format_table *formats;
unsigned int nformats;
struct isp_hw_ops *hw_ops;
struct mutex power_lock;
int power_count;
struct mutex stream_lock;
int stream_count;
atomic_t shadow_count;
struct isp_ctrls ctrls;
struct mutex setfile_lock;
struct isp_setfile setfile;
struct reg_table *context_regs;
};
extern int stf_isp_subdev_init(struct stfcamss *stfcamss);
extern int stf_isp_register(struct stf_isp_dev *isp_dev,
struct v4l2_device *v4l2_dev);
extern int stf_isp_unregister(struct stf_isp_dev *isp_dev);
extern struct isp_hw_ops isp_ops;
extern void dump_isp_reg(void *__iomem ispbase);
#endif /* STF_ISP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_ISP_IOCTL_H
#define STF_ISP_IOCTL_H
#include <media/v4l2-ctrls.h>
#define FILENAME_MAX_LEN 30
#define ISP_IOC ('V')
#define STF_ISP_REG_BUF_SIZE (768)
#define STF_ISP_REG_TBL_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2)
#define STF_ISP_REG_TBL_2_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 3)
#define STF_ISP_REG_TBL_3_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 4)
#define STF_ISP_REG_SMPL_PACK_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2)
#define RDMA_WR_ONE (0xA0)
#define RDMA_WR_SRL (0xA1)
#define RDMA_LINK (0xA2)
#define RDMA_SINT (0xA3)
#define RDMA_END (0xAF)
#define ENABLE_SS0_SS1
enum _STF_ISP_IOCTL {
STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1,
STF_ISP_IOCTL_DMABUF_ALLOC,
STF_ISP_IOCTL_DMABUF_FREE,
STF_ISP_IOCTL_GET_HW_VER,
STF_ISP_IOCTL_REG,
STF_ISP_IOCTL_SHADOW_LOCK,
STF_ISP_IOCTL_SHADOW_UNLOCK,
STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER,
STF_ISP_IOCTL_SET_USER_CONFIG_ISP,
STF_ISP_IOCTL_MAX
};
enum _STF_ISP_REG_METHOD {
STF_ISP_REG_METHOD_ONE_REG = 0,
STF_ISP_REG_METHOD_SERIES,
STF_ISP_REG_METHOD_MODULE,
STF_ISP_REG_METHOD_TABLE,
STF_ISP_REG_METHOD_TABLE_2,
STF_ISP_REG_METHOD_TABLE_3,
STF_ISP_REG_METHOD_SMPL_PACK,
STF_ISP_REG_METHOD_SOFT_RDMA,
STF_ISP_REG_METHOD_MAX
};
struct stfisp_fw_info {
char __user filename[FILENAME_MAX_LEN];
};
struct dmabuf_create {
__u32 fd;
__u32 size;
__u32 paddr;
};
struct isp_rdma_info {
u32 param;
union {
u32 value;
struct {
u32 offset : 24;
u32 tag : 8;
};
};
};
struct isp_reg_info {
/** @brief [in] access method of register */
u8 method;
/** @brief [in] offset indicated which register will be read/write */
u32 offset;
/** @brief [in] length for indicated how much register will be read/write */
u32 length;
};
union reg_buf {
u32 buffer[STF_ISP_REG_BUF_SIZE];
struct {
u32 offset;
u32 value;
} reg_tbl[STF_ISP_REG_TBL_BUF_SIZE];
struct {
u32 offset;
u32 value;
u32 mask;
} reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE];
struct {
u32 offset;
u32 value;
u32 mask;
u32 delay_ms;
} reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE];
struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE];
};
struct isp_reg_param {
/** @brief [in, out] register read/write information */
struct isp_reg_info reg_info;
/** @brief [in, out] buffer */
union reg_buf *reg_buf;
};
#define VIDIOC_STFISP_LOAD_FW \
_IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info)
#define VIDIOC_STF_DMABUF_ALLOC \
_IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_ALLOC, struct dmabuf_create)
#define VIDIOC_STF_DMABUF_FREE \
_IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_FREE, struct dmabuf_create)
#define VIDIOC_STFISP_GET_REG \
_IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
#define VIDIOC_STFISP_SET_REG \
_IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
#define VIDIOC_STFISP_SHADOW_LOCK \
_IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK)
#define VIDIOC_STFISP_SHADOW_UNLOCK \
_IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK)
#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \
_IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER)
#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \
_IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP)
#endif /* STF_ISP_IOCTL_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_VIDEO_H
#define STF_VIDEO_H
#include <linux/mutex.h>
#include <media/videobuf2-v4l2.h>
#include <linux/videodev2.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#define STFCAMSS_FRAME_MIN_WIDTH 64
#define STFCAMSS_FRAME_MAX_WIDTH 1920
#define STFCAMSS_FRAME_MIN_HEIGHT 64
#define STFCAMSS_FRAME_MAX_HEIGHT 1080
#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8
#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128
#define STFCAMSS_MIN_BUFFERS 2
#define STFCAMSS_MAX_ENTITY_NAME_LEN 27
struct stfcamss_buffer {
struct vb2_v4l2_buffer vb;
dma_addr_t addr[3];
void *vaddr_sc; /* Use for isp sc data */
struct list_head queue;
int sizeimage;
};
struct stfcamss_video;
struct stfcamss_video_ops {
int (*queue_buffer)(struct stfcamss_video *vid,
struct stfcamss_buffer *buf);
int (*flush_buffers)(struct stfcamss_video *vid,
enum vb2_buffer_state state);
};
struct fract {
u8 numerator;
u8 denominator;
};
struct stfcamss_format_info {
u32 code;
u32 pixelformat;
u8 planes;
struct fract hsub[3];
struct fract vsub[3];
u8 bpp[3];
};
struct stfcamss_video {
struct stfcamss *stfcamss;
u8 id;
struct vb2_queue vb2_q;
struct video_device vdev;
struct media_pad pad;
struct media_pipeline pipe;
struct v4l2_format active_fmt;
enum v4l2_buf_type type;
const struct stfcamss_video_ops *ops;
struct mutex lock;
struct mutex q_lock;
unsigned int bpl_alignment;
const struct stfcamss_format_info *formats;
unsigned int nformats;
unsigned int is_mp;
unsigned int pm_count;
};
int stf_video_register(struct stfcamss_video *video,
struct v4l2_device *v4l2_dev, const char *name, int is_mp);
void stf_video_unregister(struct stfcamss_video *video);
#endif /* STF_VIDEO_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STF_VIN_H
#define STF_VIN_H
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <linux/spinlock_types.h>
#include <video/stf-vin.h>
#include <linux/platform_device.h>
#include "stf_video.h"
#define STF_VIN_NAME "stf_vin"
#define STF_VIN_PAD_SINK 0
#define STF_VIN_PAD_SRC 1
#define STF_VIN_PADS_NUM 2
struct vin2_format {
u32 code;
u8 bpp;
};
struct vin2_format_table {
const struct vin2_format *fmts;
int nfmts;
};
enum vin_output_state {
VIN_OUTPUT_OFF,
VIN_OUTPUT_RESERVED,
VIN_OUTPUT_SINGLE,
VIN_OUTPUT_CONTINUOUS,
VIN_OUTPUT_IDLE,
VIN_OUTPUT_STOPPING
};
struct vin_output {
int active_buf;
struct stfcamss_buffer *buf[2];
struct stfcamss_buffer *last_buffer;
struct list_head pending_bufs;
struct list_head ready_bufs;
enum vin_output_state state;
unsigned int sequence;
unsigned int frame_skip;
};
/* The vin output lines include all isp controller lines,
* and one vin_wr output line.
*/
enum vin_line_id {
VIN_LINE_NONE = -1,
VIN_LINE_WR = 0,
VIN_LINE_ISP = 1,
VIN_LINE_ISP_SS0 = 2,
VIN_LINE_ISP_SS1 = 3,
VIN_LINE_ISP_ITIW = 4,
VIN_LINE_ISP_ITIR = 5,
VIN_LINE_ISP_RAW = 6,
VIN_LINE_ISP_SCD_Y = 7,
VIN_LINE_MAX = 8,
};
enum subdev_type;
struct vin_line {
enum subdev_type sdev_type; // must be frist
enum vin_line_id id;
struct v4l2_subdev subdev;
struct media_pad pads[STF_VIN_PADS_NUM];
struct v4l2_mbus_framefmt fmt[STF_VIN_PADS_NUM];
struct stfcamss_video video_out;
struct mutex stream_lock;
int stream_count;
struct mutex power_lock;
int power_count;
struct vin_output output;
spinlock_t output_lock;
const struct vin2_format *formats;
unsigned int nformats;
#ifdef CONFIG_PM_SLEEP
int pm_stream_count;
int pm_power_count;
#endif
};
struct stf_vin2_dev;
struct vin_hw_ops {
int (*vin_clk_enable)(struct stf_vin2_dev *vin_dev);
int (*vin_clk_disable)(struct stf_vin2_dev *vin_dev);
int (*vin_config_set)(struct stf_vin2_dev *vin_dev);
int (*vin_wr_stream_set)(struct stf_vin2_dev *vin_dev, int on);
void (*vin_wr_irq_enable)(struct stf_vin2_dev *vin_dev, int enable);
void (*vin_power_on)(struct stf_vin2_dev *vin_dev, int on);
void (*wr_rd_set_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t wr_addr, dma_addr_t rd_addr);
void (*vin_wr_set_ping_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t addr);
void (*vin_wr_set_pong_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t addr);
void (*vin_wr_get_ping_pong_status)(struct stf_vin2_dev *vin_dev);
void (*vin_isp_set_yuv_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr);
void (*vin_isp_set_raw_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t raw_addr);
void (*vin_isp_set_ss0_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr);
void (*vin_isp_set_ss1_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr);
void (*vin_isp_set_itiw_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr);
void (*vin_isp_set_itir_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr);
void (*vin_isp_set_scd_addr)(struct stf_vin2_dev *vin_dev,
dma_addr_t yhist_addr,
dma_addr_t scd_addr, int scd_type);
int (*vin_isp_get_scd_type)(struct stf_vin2_dev *vin_dev);
irqreturn_t (*vin_wr_irq_handler)(int irq, void *priv);
irqreturn_t (*vin_isp_irq_handler)(int irq, void *priv);
irqreturn_t (*vin_isp_csi_irq_handler)(int irq, void *priv);
irqreturn_t (*vin_isp_scd_irq_handler)(int irq, void *priv);
irqreturn_t (*vin_isp_irq_csiline_handler)(int irq, void *priv);
void (*isr_buffer_done)(struct vin_line *line,
struct vin_params *params);
void (*isr_change_buffer)(struct vin_line *line);
};
#define ISP_DUMMY_BUFFER_NUMS STF_ISP_PAD_MAX
#define VIN_DUMMY_BUFFER_NUMS 1
enum {
STF_DUMMY_VIN,
STF_DUMMY_ISP,
STF_DUMMY_MODULE_NUMS,
};
struct vin_dummy_buffer {
dma_addr_t paddr[3];
void *vaddr;
u32 buffer_size;
u32 width;
u32 height;
u32 mcode;
};
struct dummy_buffer {
struct vin_dummy_buffer *buffer;
u32 nums;
struct mutex stream_lock;
int stream_count;
atomic_t frame_skip;
};
struct stf_vin2_dev {
struct stfcamss *stfcamss;
struct vin_line line[VIN_LINE_MAX];
struct dummy_buffer dummy_buffer[STF_DUMMY_MODULE_NUMS];
struct vin_hw_ops *hw_ops;
atomic_t ref_count;
struct mutex power_lock;
int power_count;
};
extern void sifive_l2_flush64_range(unsigned long start, unsigned long len);
extern int stf_vin_subdev_init(struct stfcamss *stfcamss);
extern int stf_vin_register(struct stf_vin2_dev *vin_dev,
struct v4l2_device *v4l2_dev);
extern int stf_vin_unregister(struct stf_vin2_dev *vin_dev);
extern struct vin_hw_ops vin_ops;
extern void dump_vin_reg(void *__iomem regbase);
extern enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line,
enum isp_pad_id def);
#endif /* STF_VIN_H */

View File

@@ -0,0 +1,433 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#include "stfcamss.h"
#include <linux/of_graph.h>
#include <media/v4l2-async.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
static void vin_intr_clear(void __iomem *sysctrl_base)
{
reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
0x1);
reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
0x0);
}
static irqreturn_t stf_vin_wr_irq_handler(int irq, void *priv)
{
static struct vin_params params;
struct stf_vin2_dev *vin_dev = priv;
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
struct dummy_buffer *dummy_buffer =
&vin_dev->dummy_buffer[STF_DUMMY_VIN];
if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
vin_dev->hw_ops->isr_change_buffer(&vin_dev->line[VIN_LINE_WR]);
vin_dev->hw_ops->isr_buffer_done(&vin_dev->line[VIN_LINE_WR], &params);
}
vin_intr_clear(vin->sysctrl_base);
return IRQ_HANDLED;
}
static void __iomem *stf_vin_get_ispbase(struct stf_vin_dev *vin)
{
void __iomem *base = vin->isp_base;
return base;
}
static irqreturn_t stf_vin_isp_irq_handler(int irq, void *priv)
{
static struct vin_params params;
struct stf_vin2_dev *vin_dev = priv;
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase;
u32 int_status, value;
ispbase = stf_vin_get_ispbase(vin);
int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
if (int_status & BIT(24)) {
if ((int_status & BIT(11)))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_SS0], &params);
if ((int_status & BIT(12)))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_SS1], &params);
if ((int_status & BIT(20)))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP], &params);
value = reg_read(ispbase, ISP_REG_ITIDPSR);
if ((value & BIT(17)))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_ITIW], &params);
if ((value & BIT(16)))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_ITIR], &params);
#ifndef ISP_USE_CSI_AND_SC_DONE_INTERRUPT
if (int_status & BIT(25))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_RAW], &params);
if (int_status & BIT(26))
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_SCD_Y], &params);
/* clear interrupt */
reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL)
| EN_INT_ISP_DONE | EN_INT_CSI_DONE | EN_INT_SC_DONE);
#else
/* clear interrupt */
reg_write(ispbase, ISP_REG_ISP_CTRL_0,
(int_status & ~EN_INT_ALL) | EN_INT_ISP_DONE);
#endif
} else
st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t stf_vin_isp_csi_irq_handler(int irq, void *priv)
{
static struct vin_params params;
struct stf_vin2_dev *vin_dev = priv;
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase;
u32 int_status;
ispbase = stf_vin_get_ispbase(vin);
int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
if (int_status & BIT(25)) {
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_RAW], &params);
/* clear interrupt */
reg_write(ispbase, ISP_REG_ISP_CTRL_0,
(int_status & ~EN_INT_ALL) | EN_INT_CSI_DONE);
} else
st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t stf_vin_isp_scd_irq_handler(int irq, void *priv)
{
static struct vin_params params;
struct stf_vin2_dev *vin_dev = priv;
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase;
u32 int_status;
ispbase = stf_vin_get_ispbase(vin);
int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
if (int_status & BIT(26)) {
vin_dev->hw_ops->isr_buffer_done(
&vin_dev->line[VIN_LINE_ISP_SCD_Y], &params);
/* clear interrupt */
reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL) | EN_INT_SC_DONE);
} else
st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv)
{
struct stf_vin2_dev *vin_dev = priv;
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
struct stf_isp_dev *isp_dev;
void __iomem *ispbase;
u32 int_status, value;
ispbase = stf_vin_get_ispbase(vin);
isp_dev = vin_dev->stfcamss->isp_dev;
int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
if (int_status & BIT(27)) {
struct dummy_buffer *dummy_buffer =
&vin_dev->dummy_buffer[STF_DUMMY_ISP];
if (!atomic_read(&isp_dev->shadow_count)) {
if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
if ((int_status & BIT(11)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_SS0]);
if ((int_status & BIT(12)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_SS1]);
if ((int_status & BIT(20)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP]);
value = reg_read(ispbase, ISP_REG_ITIDPSR);
if ((value & BIT(17)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_ITIW]);
if ((value & BIT(16)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_ITIR]);
value = reg_read(ispbase, ISP_REG_CSI_MODULE_CFG);
if ((value & BIT(19)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_RAW]);
if ((value & BIT(17)))
vin_dev->hw_ops->isr_change_buffer(
&vin_dev->line[VIN_LINE_ISP_SCD_Y]);
}
// shadow update
reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, 0x30000, 0x30000);
reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, BIT(1) | BIT(0), 0x3);
} else {
st_warn(ST_VIN, "isp shadow_lock locked. skip this frame\n");
}
/* clear interrupt */
reg_write(ispbase, ISP_REG_ISP_CTRL_0,
(int_status & ~EN_INT_ALL) | EN_INT_LINE_INT);
} else
st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
return IRQ_HANDLED;
}
static int stf_vin_clk_enable(struct stf_vin2_dev *vin_dev)
{
struct stfcamss *stfcamss = vin_dev->stfcamss;
clk_prepare_enable(stfcamss->sys_clk[STFCLK_PCLK].clk);
clk_set_rate(stfcamss->sys_clk[STFCLK_APB_FUNC].clk, 49500000);
clk_set_rate(stfcamss->sys_clk[STFCLK_SYS_CLK].clk, 297000000);
reset_control_deassert(stfcamss->sys_rst[STFRST_PCLK].rstc);
reset_control_deassert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
return 0;
}
static int stf_vin_clk_disable(struct stf_vin2_dev *vin_dev)
{
struct stfcamss *stfcamss = vin_dev->stfcamss;
reset_control_assert(stfcamss->sys_rst[STFRST_PCLK].rstc);
reset_control_assert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PCLK].clk);
return 0;
}
static int stf_vin_config_set(struct stf_vin2_dev *vin_dev)
{
return 0;
}
static int stf_vin_wr_stream_set(struct stf_vin2_dev *vin_dev, int on)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
//make the axiwr alway on
if (on)
reg_set(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20, U0_VIN_CNFG_AXIWR0_EN);
return 0;
}
static void stf_vin_wr_irq_enable(struct stf_vin2_dev *vin_dev,
int enable)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
unsigned int value = 0;
if (enable) {
value = ~(0x1 << 1);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_MASK,
value);
} else {
/* clear vin interrupt */
value = 0x1 << 1;
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
0x1);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
0x0);
reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
U0_VIN_CNFG_AXIWR0_MASK,
value);
}
}
static void stf_vin_wr_rd_set_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t wr_addr, dma_addr_t rd_addr)
{
#ifdef UNUSED_CODE
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
/* set the start address*/
reg_write(vin->sysctrl_base,
SYSCTRL_VIN_WR_START_ADDR, (long)wr_addr);
reg_write(vin->sysctrl_base,
SYSCTRL_VIN_RD_END_ADDR, (long)rd_addr);
#endif
}
void stf_vin_wr_set_ping_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
/* set the start address */
reg_write(vin->sysctrl_base, SYSCONSAIF_SYSCFG_24, (long)addr);
}
void stf_vin_wr_set_pong_addr(struct stf_vin2_dev *vin_dev, dma_addr_t addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
/* set the start address */
reg_write(vin->sysctrl_base, SYSCONSAIF_SYSCFG_32, (long)addr);
}
void stf_vin_isp_set_yuv_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_Y_PLANE_START_ADDR, y_addr);
reg_write(ispbase, ISP_REG_UV_PLANE_START_ADDR, uv_addr);
// reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(0), 1);
}
void stf_vin_isp_set_raw_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t raw_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_DUMP_CFG_0, raw_addr);
}
void stf_vin_isp_set_ss0_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_SS0AY, y_addr);
reg_write(ispbase, ISP_REG_SS0AUV, uv_addr);
}
void stf_vin_isp_set_ss1_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_SS1AY, y_addr);
reg_write(ispbase, ISP_REG_SS1AUV, uv_addr);
}
void stf_vin_isp_set_itiw_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_ITIDWYSAR, y_addr);
reg_write(ispbase, ISP_REG_ITIDWUSAR, uv_addr);
}
void stf_vin_isp_set_itir_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t y_addr, dma_addr_t uv_addr)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_write(ispbase, ISP_REG_ITIDRYSAR, y_addr);
reg_write(ispbase, ISP_REG_ITIDRUSAR, uv_addr);
}
int stf_vin_isp_get_scd_type(struct stf_vin2_dev *vin_dev)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
return (reg_read(ispbase, ISP_REG_SC_CFG_1) & (0x3 << 30)) >> 30;
}
void stf_vin_isp_set_scd_addr(struct stf_vin2_dev *vin_dev,
dma_addr_t yhist_addr, dma_addr_t scd_addr, int scd_type)
{
struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
void __iomem *ispbase = stf_vin_get_ispbase(vin);
reg_set_bit(ispbase, ISP_REG_SC_CFG_1, 0x3 << 30, scd_type << 30);
reg_write(ispbase, ISP_REG_SCD_CFG_0, scd_addr);
reg_write(ispbase, ISP_REG_YHIST_CFG_4, yhist_addr);
}
void dump_vin_reg(void *__iomem regbase)
{
st_debug(ST_VIN, "DUMP VIN register:\n");
print_reg(ST_VIN, regbase, 0x00);
print_reg(ST_VIN, regbase, 0x04);
print_reg(ST_VIN, regbase, 0x08);
print_reg(ST_VIN, regbase, 0x0c);
print_reg(ST_VIN, regbase, 0x10);
print_reg(ST_VIN, regbase, 0x14);
print_reg(ST_VIN, regbase, 0x18);
print_reg(ST_VIN, regbase, 0x1c);
print_reg(ST_VIN, regbase, 0x20);
print_reg(ST_VIN, regbase, 0x24);
print_reg(ST_VIN, regbase, 0x28);
}
struct vin_hw_ops vin_ops = {
.vin_clk_enable = stf_vin_clk_enable,
.vin_clk_disable = stf_vin_clk_disable,
.vin_config_set = stf_vin_config_set,
.vin_wr_stream_set = stf_vin_wr_stream_set,
.vin_wr_irq_enable = stf_vin_wr_irq_enable,
.wr_rd_set_addr = stf_vin_wr_rd_set_addr,
.vin_wr_set_ping_addr = stf_vin_wr_set_ping_addr,
.vin_wr_set_pong_addr = stf_vin_wr_set_pong_addr,
.vin_isp_set_yuv_addr = stf_vin_isp_set_yuv_addr,
.vin_isp_set_raw_addr = stf_vin_isp_set_raw_addr,
.vin_isp_set_ss0_addr = stf_vin_isp_set_ss0_addr,
.vin_isp_set_ss1_addr = stf_vin_isp_set_ss1_addr,
.vin_isp_set_itiw_addr = stf_vin_isp_set_itiw_addr,
.vin_isp_set_itir_addr = stf_vin_isp_set_itir_addr,
.vin_isp_set_scd_addr = stf_vin_isp_set_scd_addr,
.vin_isp_get_scd_type = stf_vin_isp_get_scd_type,
.vin_wr_irq_handler = stf_vin_wr_irq_handler,
.vin_isp_irq_handler = stf_vin_isp_irq_handler,
.vin_isp_csi_irq_handler = stf_vin_isp_csi_irq_handler,
.vin_isp_scd_irq_handler = stf_vin_isp_scd_irq_handler,
.vin_isp_irq_csiline_handler = stf_vin_isp_irq_csiline_handler,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
*
*/
#ifndef STFCAMSS_H
#define STFCAMSS_H
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/reset.h>
#include <linux/clk.h>
enum sensor_type {
SENSOR_VIN,
/* need replace sensor */
SENSOR_ISP,
};
enum subdev_type {
VIN_DEV_TYPE,
ISP_DEV_TYPE,
};
#include "stf_common.h"
#include "stf_dvp.h"
#include "stf_csi.h"
#include "stf_csiphy.h"
#include "stf_isp.h"
#include "stf_vin.h"
#define STF_PAD_SINK 0
#define STF_PAD_SRC 1
#define STF_PADS_NUM 2
#define STF_CAMSS_SKIP_ITI
enum port_num {
DVP_SENSOR_PORT_NUMBER = 0,
CSI2RX_SENSOR_PORT_NUMBER
};
enum stf_clk_num {
STFCLK_APB_FUNC = 0,
STFCLK_PCLK,
STFCLK_SYS_CLK,
STFCLK_WRAPPER_CLK_C,
STFCLK_DVP_INV,
STFCLK_AXIWR,
STFCLK_MIPI_RX0_PXL,
STFCLK_PIXEL_CLK_IF0,
STFCLK_PIXEL_CLK_IF1,
STFCLK_PIXEL_CLK_IF2,
STFCLK_PIXEL_CLK_IF3,
STFCLK_M31DPHY_CFGCLK_IN,
STFCLK_M31DPHY_REFCLK_IN,
STFCLK_M31DPHY_TXCLKESC_LAN0,
STFCLK_ISPCORE_2X,
STFCLK_ISP_AXI,
STFCLK_NUM
};
enum stf_rst_num {
STFRST_WRAPPER_P = 0,
STFRST_WRAPPER_C,
STFRST_PCLK,
STFRST_SYS_CLK,
STFRST_AXIRD,
STFRST_AXIWR,
STFRST_PIXEL_CLK_IF0,
STFRST_PIXEL_CLK_IF1,
STFRST_PIXEL_CLK_IF2,
STFRST_PIXEL_CLK_IF3,
STFRST_M31DPHY_HW,
STFRST_M31DPHY_B09_ALWAYS_ON,
STFRST_ISP_TOP_N,
STFRST_ISP_TOP_AXI,
STFRST_NUM
};
struct stfcamss {
struct stf_vin_dev *vin; // stfcamss phy res
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct media_pipeline pipe;
struct device *dev;
struct stf_vin2_dev *vin_dev; // subdev
struct stf_dvp_dev *dvp_dev; // subdev
struct stf_csi_dev *csi_dev; // subdev
struct stf_csiphy_dev *csiphy_dev; // subdev
struct stf_isp_dev *isp_dev; // subdev
struct v4l2_async_notifier notifier;
struct clk_bulk_data *sys_clk;
int nclks;
struct reset_control_bulk_data *sys_rst;
int nrsts;
struct regmap *stf_aon_syscon;
uint32_t aon_gp_reg;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_entry;
struct dentry *vin_debugfs;
#endif
};
struct stfcamss_async_subdev {
struct v4l2_async_connection asd; // must be first
enum port_num port;
struct {
struct dvp_cfg dvp;
struct csi2phy_cfg csiphy;
} interface;
};
extern struct media_entity *stfcamss_find_sensor(struct media_entity *entity);
#endif /* STFCAMSS_H */

View File

@@ -0,0 +1,253 @@
/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
/*
* jh7110-isp.h
*
* JH7110 ISP driver - user space header file.
*
* Copyright © 2023 Starfive Technology Co., Ltd.
*
* Author: Su Zejian (zejian.su@starfivetech.com)
*
*/
#ifndef __JH7110_ISP_H_
#define __JH7110_ISP_H_
#include <linux/v4l2-controls.h>
#define V4L2_CID_USER_JH7110_ISP_WB_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
#define V4L2_CID_USER_JH7110_ISP_CAR_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0002)
#define V4L2_CID_USER_JH7110_ISP_CCM_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0003)
#define V4L2_CID_USER_JH7110_ISP_CFA_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0004)
#define V4L2_CID_USER_JH7110_ISP_CTC_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0005)
#define V4L2_CID_USER_JH7110_ISP_DBC_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0006)
#define V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0007)
#define V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0008)
#define V4L2_CID_USER_JH7110_ISP_LCCF_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x0009)
#define V4L2_CID_USER_JH7110_ISP_OBC_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000a)
#define V4L2_CID_USER_JH7110_ISP_OECF_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000b)
#define V4L2_CID_USER_JH7110_ISP_R2Y_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000c)
#define V4L2_CID_USER_JH7110_ISP_SAT_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000d)
#define V4L2_CID_USER_JH7110_ISP_SHRP_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
#define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING \
(V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
struct jh7110_isp_wb_gain {
__u16 gain_r;
__u16 gain_g;
__u16 gain_b;
};
struct jh7110_isp_wb_setting {
__u32 enabled;
struct jh7110_isp_wb_gain gains;
};
struct jh7110_isp_car_setting {
__u32 enabled;
};
struct jh7110_isp_ccm_smlow {
__s32 ccm[3][3];
__s32 offsets[3];
};
struct jh7110_isp_ccm_setting {
__u32 enabled;
struct jh7110_isp_ccm_smlow ccm_smlow;
};
struct jh7110_isp_cfa_params {
__s32 hv_width;
__s32 cross_cov;
};
struct jh7110_isp_cfa_setting {
__u32 enabled;
struct jh7110_isp_cfa_params settings;
};
struct jh7110_isp_ctc_params {
__u8 saf_mode;
__u8 daf_mode;
__s32 max_gt;
__s32 min_gt;
};
struct jh7110_isp_ctc_setting {
__u32 enabled;
struct jh7110_isp_ctc_params settings;
};
struct jh7110_isp_dbc_params {
__s32 bad_gt;
__s32 bad_xt;
};
struct jh7110_isp_dbc_setting {
__u32 enabled;
struct jh7110_isp_dbc_params settings;
};
struct jh7110_isp_dnyuv_params {
__u8 y_sweight[10];
__u16 y_curve[7];
__u8 uv_sweight[10];
__u16 uv_curve[7];
};
struct jh7110_isp_dnyuv_setting {
__u32 enabled;
struct jh7110_isp_dnyuv_params settings;
};
struct jh7110_isp_gmargb_point {
__u16 g_val;
__u16 sg_val;
};
struct jh7110_isp_gmargb_setting {
__u32 enabled;
struct jh7110_isp_gmargb_point curve[15];
};
struct jh7110_isp_lccf_circle {
__s16 center_x;
__s16 center_y;
__u8 radius;
};
struct jh7110_isp_lccf_curve_param {
__s16 f1;
__s16 f2;
};
struct jh7110_isp_lccf_setting {
__u32 enabled;
struct jh7110_isp_lccf_circle circle;
struct jh7110_isp_lccf_curve_param r_param;
struct jh7110_isp_lccf_curve_param gr_param;
struct jh7110_isp_lccf_curve_param gb_param;
struct jh7110_isp_lccf_curve_param b_param;
};
struct jh7110_isp_blacklevel_win_size {
__u32 width;
__u32 height;
};
struct jh7110_isp_blacklevel_gain {
__u8 tl_gain;
__u8 tr_gain;
__u8 bl_gain;
__u8 br_gain;
};
struct jh7110_isp_blacklevel_offset {
__u8 tl_offset;
__u8 tr_offset;
__u8 bl_offset;
__u8 br_offset;
};
struct jh7110_isp_blacklevel_setting {
__u32 enabled;
struct jh7110_isp_blacklevel_win_size win_size;
struct jh7110_isp_blacklevel_gain gain[4];
struct jh7110_isp_blacklevel_offset offset[4];
};
struct jh7110_isp_oecf_point {
__u16 x;
__u16 y;
__s16 slope;
};
struct jh7110_isp_oecf_setting {
__u32 enabled;
struct jh7110_isp_oecf_point r_curve[16];
struct jh7110_isp_oecf_point gr_curve[16];
struct jh7110_isp_oecf_point gb_curve[16];
struct jh7110_isp_oecf_point b_curve[16];
};
struct jh7110_isp_r2y_matrix {
__s16 m[9];
};
struct jh7110_isp_r2y_setting {
__u32 enabled;
struct jh7110_isp_r2y_matrix matrix;
};
struct jh7110_isp_sat_curve {
__s16 yi_min;
__s16 yo_ir;
__s16 yo_min;
__s16 yo_max;
};
struct jh7110_isp_sat_hue_info {
__s16 sin;
__s16 cos;
};
struct jh7110_isp_sat_info {
__s16 gain_cmab;
__s16 gain_cmad;
__s16 threshold_cmb;
__s16 threshold_cmd;
__s16 offset_u;
__s16 offset_v;
__s16 cmsf;
};
struct jh7110_isp_sat_setting {
__u32 enabled;
struct jh7110_isp_sat_curve curve;
struct jh7110_isp_sat_hue_info hue_info;
struct jh7110_isp_sat_info sat_info;
};
struct jh7110_isp_sharp_weight {
__u8 weight[15];
__u32 recip_wei_sum;
};
struct jh7110_isp_sharp_strength {
__s16 diff[4];
__s16 f[4];
};
struct jh7110_isp_sharp_setting {
__u32 enabled;
struct jh7110_isp_sharp_weight weight;
struct jh7110_isp_sharp_strength strength;
__s8 pdirf;
__s8 ndirf;
};
struct jh7110_isp_ycrv_curve {
__s16 y[64];
};
struct jh7110_isp_ycrv_setting {
__u32 enabled;
struct jh7110_isp_ycrv_curve curve;
};
#endif

View File

@@ -203,6 +203,12 @@ enum v4l2_colorfx {
*/
#define V4L2_CID_USER_ASPEED_BASE (V4L2_CID_USER_BASE + 0x11a0)
/*
* The base for the jh7110-isp driver controls.
* We reserve 16 controls for this driver.
*/
#define V4L2_CID_USER_JH7110_ISP_BASE (V4L2_CID_USER_BASE + 0x1170)
/* MPEG-class control IDs */
/* The MPEG controls are applicable to all codec controls
* and the 'MPEG' part of the define is historical */

443
include/video/stf-vin.h Normal file
View File

@@ -0,0 +1,443 @@
/* include/video/stf-vin.h
*
* Copyright 2020 starfive tech.
* Eric Tang <eric.tang@starfivetech.com>
*
* Generic vin notifier interface
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef _VIDEO_VIN_H
#define _VIDEO_VIN_H
#include <linux/cdev.h>
#define DRV_NAME "jh7110-vin"
#define FB_FIRST_ADDR 0xf9000000
#define FB_SECOND_ADDR 0xf97e9000
#define RESERVED_MEM_SIZE 0x1000000
#define VIN_MIPI_CONTROLLER0_OFFSET 0x00000
#define VIN_CLKGEN_OFFSET 0x10000
#define VIN_RSTGEN_OFFSET 0x20000
#define VIN_MIPI_CONTROLLER1_OFFSET 0x30000
#define VIN_SYSCONTROLLER_OFFSET 0x40000
#define VD_1080P 1080
#define VD_720P 720
#define VD_PAL 480
#define VD_HEIGHT_1080P VD_1080P
#define VD_WIDTH_1080P 1920
#define VD_HEIGHT_720P VD_720P
#define VD_WIDTH_720P 1080
#define VD_HEIGHT_480 480
#define VD_WIDTH_640 640
#define SEEED_WIDTH_800 800
#define SEEED_HIGH_480 480
#define VIN_TOP_CLKGEN_BASE_ADDR 0x11800000
#define VIN_TOP_RSTGEN_BASE_ADDR 0x11840000
#define VIN_TOP_IOPAD_BASE_ADDR 0x11858000
#define ISP_BASE_MIPI0_ADDR 0x19800000
#define ISP_BASE_CLKGEN_ADDR 0x19810000
#define ISP_BASE_RSTGEN_ADDR 0x19820000
#define ISP_BASE_MIPI1_ADDR 0x19830000
#define ISP_BASE_SYSCTRL_ADDR 0x19840000
#define ISP_BASE_ISP0_ADDR 0x19870000
#define ISP_BASE_ISP1_ADDR 0x198a0000
//vin clk registers
#define CLK_VIN_SRC_CTRL 0x188
#define CLK_ISP0_AXI_CTRL 0x190
#define CLK_ISP0NOC_AXI_CTRL 0x194
#define CLK_ISPSLV_AXI_CTRL 0x198
#define CLK_ISP1_AXI_CTRL 0x1A0
#define CLK_ISP1NOC_AXI_CTRL 0x1A4
#define CLK_VIN_AXI 0x1AC
#define CLK_VINNOC_AXI 0x1B0
#define CLK_DOM4_APB_FUNC 0x0
#define CLK_MUX_SEL 0xffffff
#define CLK_MIPI_RX0_PXL 0x4
#define CLK_DVP_INV 0x8
#define CLK_U0_VIN_PCLK 0x18
#define CLK_U0_VIN_PCLK_ICG (0x1<<31)
#define CLK_U0_VIN_SYS_CLK 0x1c
#define CLK_U0_VIN_CLK_P_AXIWR 0x30
#define CLK_U0_VIN_MUX_SEL (BIT(24) | BIT(25) | BIT(26) | BIT(27) | BIT(28) | BIT(29))
#define CLK_U0_VIN_PIXEL_CLK_IF0 0x20
#define CLK_U0_VIN_PIXEL_CLK_IF1 0x24
#define CLK_U0_VIN_PIXEL_CLK_IF2 0x28
#define CLK_U0_VIN_PIXEL_CLK_IF3 0x2c
#define CLK_U0_VIN_CLK_P_AXIWR 0x30
#define CLK_U0_ISPV2_TOP_WRAPPER_CLK_C 0x34u
#define CLK_U0_ISPV2_MUX_SEL (0x1<<24 | 0x1<<25 | 0x1<<26 | 0x1<<27 | 0x1<<28 | 0x1<< 29)
#define CLK_U0_ISPV2_CLK_ICG (0x1<<31)
#define SOFTWARE_RESET_ASSERT0_ASSERT_SET 0x38U
#define SOFTWARE_RESET_ASSERT0_ASSERT_SET_STATE 0x3CU
#define RST_U0_ISPV2_TOP_WRAPPER_RST_P BIT(0)
#define RST_U0_ISPV2_TOP_WRAPPER_RST_C BIT(1)
#define RSTN_U0_VIN_RST_N_PCLK BIT(4)
#define RSTN_U0_VIN_RST_N_SYS_CLK BIT(9)
#define RSTN_U0_VIN_RST_P_AXIRD BIT(10)
#define RSTN_U0_VIN_RST_P_AXIWR BIT(11)
#define CLK_POLARITY (0x1<<30)
#define M31DPHY_APBCFGSAIF__SYSCFG_0 0x0
#define M31DPHY_APBCFGSAIF__SYSCFG_4 0x4
#define M31DPHY_APBCFGSAIF__SYSCFG_8 0x8
#define M31DPHY_APBCFGSAIF__SYSCFG_12 0xc
#define M31DPHY_APBCFGSAIF__SYSCFG_16 0x10
#define M31DPHY_APBCFGSAIF__SYSCFG_20 0x14
#define M31DPHY_APBCFGSAIF__SYSCFG_24 0x18
#define M31DPHY_APBCFGSAIF__SYSCFG_28 0x1c
#define M31DPHY_APBCFGSAIF__SYSCFG_32 0x20
#define M31DPHY_APBCFGSAIF__SYSCFG_36 0x24
#define M31DPHY_APBCFGSAIF__SYSCFG_40 0x28
#define M31DPHY_APBCFGSAIF__SYSCFG_44 0x2c
#define M31DPHY_APBCFGSAIF__SYSCFG_48 0x30
#define M31DPHY_APBCFGSAIF__SYSCFG_52 0x34
#define M31DPHY_APBCFGSAIF__SYSCFG_56 0x38
#define M31DPHY_APBCFGSAIF__SYSCFG_60 0x3c
#define M31DPHY_APBCFGSAIF__SYSCFG_64 0x40
#define M31DPHY_APBCFGSAIF__SYSCFG_68 0x44
#define M31DPHY_APBCFGSAIF__SYSCFG_72 0x48
#define M31DPHY_APBCFGSAIF__SYSCFG_76 0x4c
#define M31DPHY_APBCFGSAIF__SYSCFG_80 0x50
#define M31DPHY_APBCFGSAIF__SYSCFG_84 0x54
#define M31DPHY_APBCFGSAIF__SYSCFG_88 0x58
#define M31DPHY_APBCFGSAIF__SYSCFG_92 0x5c
#define M31DPHY_APBCFGSAIF__SYSCFG_96 0x60
#define M31DPHY_APBCFGSAIF__SYSCFG_100 0x64
#define M31DPHY_APBCFGSAIF__SYSCFG_104 0x68
#define M31DPHY_APBCFGSAIF__SYSCFG_108 0x6c
#define M31DPHY_APBCFGSAIF__SYSCFG_112 0x70
#define M31DPHY_APBCFGSAIF__SYSCFG_116 0x74
#define M31DPHY_APBCFGSAIF__SYSCFG_120 0x78
#define M31DPHY_APBCFGSAIF__SYSCFG_124 0x7c
#define M31DPHY_APBCFGSAIF__SYSCFG_128 0x80
#define M31DPHY_APBCFGSAIF__SYSCFG_132 0x84
#define M31DPHY_APBCFGSAIF__SYSCFG_136 0x88
#define M31DPHY_APBCFGSAIF__SYSCFG_140 0x8c
#define M31DPHY_APBCFGSAIF__SYSCFG_144 0x90
#define M31DPHY_APBCFGSAIF__SYSCFG_184 0xb8
//pmu registers
#define SW_DEST_POWER_ON 0x0C
#define SW_DEST_POWER_OFF 0x10
#define SW_ENCOURAGE 0x44
//isp clk registers
#define CLK_DPHY_CFGCLK_ISPCORE_2X_CTRL 0x00
#define CLK_DPHY_REFCLK_ISPCORE_2X_CTRL 0x04
#define CLK_DPHY_TXCLKESC_IN_CTRL 0x08
#define CLK_MIPI_RX0_PXL_CTRL 0x0c
#define CLK_MIPI_RX1_PXL_CTRL 0x10
#define CLK_MIPI_RX0_PXL_0_CTRL 0X14
#define CLK_MIPI_RX0_PXL_1_CTRL 0X18
#define CLK_MIPI_RX0_PXL_2_CTRL 0X1C
#define CLK_MIPI_RX0_PXL_3_CTRL 0X20
#define CLK_MIPI_RX0_SYS0_CTRL 0x24
#define CLK_MIPI_RX1_PXL_0_CTRL 0X28
#define CLK_MIPI_RX1_PXL_1_CTRL 0X2C
#define CLK_MIPI_RX1_PXL_2_CTRL 0X30
#define CLK_MIPI_RX1_PXL_3_CTRL 0X34
#define CLK_MIPI_RX1_SYS1_CTRL 0x38
#define CLK_ISP_CTRL 0x3c
#define CLK_ISP_2X_CTRL 0x40
#define CLK_ISP_MIPI_CTRL 0x44
#define CLK_C_ISP_CTRL 0x64
#define CLK_CSI2RX0_APB_CTRL 0x58
#define CLK_VIN_AXI_WR_CTRL 0x5C
#define SOFTWARE_RESET_ASSERT0 0x0
#define SOFTWARE_RESET_ASSERT1 0x4
#define SOFTWARE_RESET_STATUS 0x4
#define IOPAD_REG81 0x144
#define IOPAD_REG82 0x148
#define IOPAD_REG83 0x14C
#define IOPAD_REG84 0x150
#define IOPAD_REG85 0x154
#define IOPAD_REG86 0x158
#define IOPAD_REG87 0x15C
#define IOPAD_REG88 0x160
#define IOPAD_REG89 0x164
//sys control REG DEFINE
#define SYSCONSAIF_SYSCFG_0 0X0
#define U0_VIN_SCFG_SRAM_CONFIG (BIT(0) | BIT(1))
#define SYSCONSAIF_SYSCFG_4 0x4
#define U0_VIN_CNFG_AXIRD_END_ADDR 0xffffffff
#define SYSCONSAIF_SYSCFG_8 0x8
#define U0_VIN_CNFG_AXIRD_LINE_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
#define U0_VIN_CNFG_AXIRD_LINE_CNT_START (BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
#define SYSCONSAIF_SYSCFG_12 0xc
#define U0_VIN_CNFG_AXIRD_PIX_CNT_END (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
#define U0_VIN_CNFG_AXIRD_PIX_CNT_START (BIT(13) | BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
#define U0_VIN_CNFG_AXIRD_PIX_CT (BIT(26) | BIT(27))
#define SYSCONSAIF_SYSCFG_16 0x10
#define U0_VIN_CNFG_AXIRD_START_ADDR 0xFFFFFFFF
#define SYSCONSAIF_SYSCFG_20 0x14
#define U0_VIN_CNFG_AXIWR0_EN BIT(4)
#define U0_VIN_CNFG_AXIWR0_CHANNEL_SEL (BIT(0) | BIT(1) | BIT(2) | BIT(3))
#define SYSCONSAIF_SYSCFG_24 0x18
#define U0_VIN_CNFG_AXIWR0_END_ADDR 0xFFFFFFFF
#define SYSCONSAIF_SYSCFG_28 0x1c
#define U0_VIN_CNFG_AXIWR0_INTR_CLEAN BIT(0)
#define U0_VIN_CNFG_AXIWR0_MASK BIT(1)
#define U0_VIN_CNFG_AXIWR0_PIX_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
#define U0_VIN_CNFG_AXIWR0_PIX_CT (BIT(13) | BIT(14))
#define UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL (BIT(15) | BIT(16))
#define SYSCONSAIF_SYSCFG_32 0x20
#define SYSCONSAIF_SYSCFG_36 0x24
#define UO_VIN_CNFG_COLOR_BAR_EN BIT(0)
#define U0_VIN_CNFG_DVP_HS_POS (0x1<<1)
#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2)
#define U0_VIN_CNFG_DVP_VS_POS (0x1<<3)
#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4)
#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5)
#define U0_VIN_CNFG_MIPI_BYTE_EN_ISP0 (BIT(6) |BIT(7))
#define U0_VIN_CNFG_P_I_MIPI_CHANNEL_SEL0 (BIT(8) |BIT(9) | BIT(10) | BIT(11))
#define U0_VIN_CNFG_P_I_MIPI_HEADER_EN0 BIT(12)
#define U0_VIN_CNFG_PIX_NUM (0x1<<13 | 0x1<<14 | 0x1<<15 | 0x1<<16)
#define U0_VIN_CNFG_AXIRD_AXI_CNT_END (BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
#define U0_VIN_CNFG_AXI_DVP_EN BIT(2)
#define U0_VIN_CNFG_AXIRD_INTR_MASK BIT(1)
#define U0_VIN_CNFG_AXIWRD_INTR_MASK BIT(1)
#define U0_VIN_CNFG_AXIWR0_START_ADDR 0xffffffff
#define U0_VIN_CNFG_COLOR_BAR_EN 0X0
#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CT (BIT(13) | BIT(14))
#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
#define U0_VIN_CNFG_AXIWR0_PIXEL_HITH_BIT_SEL (BIT(15) | BIT(16))
#define SYSCTRL_REG4 0x10
#define SYSCTRL_DPHY_CTRL 0x14
#define SYSCTRL_VIN_AXI_CTRL 0x18
#define SYSCTRL_VIN_WR_START_ADDR 0x28
#define SYSCTRL_VIN_RD_END_ADDR 0x2C
#define SYSCTRL_VIN_WR_PIX_TOTAL 0x30
#define SYSCTRL_VIN_RD_PIX_TOTAL 0x34
#define SYSCTRL_VIN_RW_CTRL 0x38
#define SYSCTRL_VIN_SRC_CHAN_SEL 0x24
#define SYSCTRL_VIN_SRC_DW_SEL 0x40
#define SYSCTRL_VIN_RD_VBLANK 0x44
#define SYSCTRL_VIN_RD_VEND 0x48
#define SYSCTRL_VIN_RD_HBLANK 0x4C
#define SYSCTRL_VIN_RD_HEND 0x50
#define SYSCTRL_VIN_INTP_CTRL 0x54
#define ISP_NO_SCALE_ENABLE (0x1<<20)
#define ISP_MULTI_FRAME_ENABLE (0x1<<17)
#define ISP_SS0_ENABLE (0x1<<11)
#define ISP_SS1_ENABLE (0x1<<12)
#define ISP_RESET (0x1<<1)
#define ISP_ENBALE (0x1)
//ISP REG DEFINE
#define ISP_REG_DVP_POLARITY_CFG 0x00000014
#define ISP_REG_RAW_FORMAT_CFG 0x00000018
#define ISP_REG_CFA_MODE 0x00000A1C
#define ISP_REG_PIC_CAPTURE_START_CFG 0x0000001C
#define ISP_REG_PIC_CAPTURE_END_CFG 0x00000020
#define ISP_REG_PIPELINE_XY_SIZE 0x00000A0C
#define ISP_REG_Y_PLANE_START_ADDR 0x00000A80
#define ISP_REG_UV_PLANE_START_ADDR 0x00000A84
#define ISP_REG_STRIDE 0x00000A88
#define ISP_REG_PIXEL_COORDINATE_GEN 0x00000A8C
#define ISP_REG_PIXEL_AXI_CONTROL 0x00000A90
#define ISP_REG_SS_AXI_CONTROL 0x00000AC4
#define ISP_REG_RGB_TO_YUV_COVERSION0 0x00000E40
#define ISP_REG_RGB_TO_YUV_COVERSION1 0x00000E44
#define ISP_REG_RGB_TO_YUV_COVERSION2 0x00000E48
#define ISP_REG_RGB_TO_YUV_COVERSION3 0x00000E4C
#define ISP_REG_RGB_TO_YUV_COVERSION4 0x00000E50
#define ISP_REG_RGB_TO_YUV_COVERSION5 0x00000E54
#define ISP_REG_RGB_TO_YUV_COVERSION6 0x00000E58
#define ISP_REG_RGB_TO_YUV_COVERSION7 0x00000E5C
#define ISP_REG_RGB_TO_YUV_COVERSION8 0x00000E60
#define ISP_REG_CSI_MODULE_CFG 0x00000010
#define ISP_REG_ISP_CTRL_1 0x00000A08
#define ISP_REG_ISP_CTRL_0 0x00000A00
#define ISP_REG_DC_AXI_ID 0x00000044
#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x00000000
//CSI registers
#define DEVICE_CONFIG 0x00
#define SOFT_RESET 0x04
#define STATIC_CFG 0x08
#define ERROR_BYPASS_CFG 0x10
#define MONITOR_IRQS 0x18
#define MONITOR_IRQS_MASK_CFG 0x1c
#define INFO_IRQS 0x20
#define INFO_IRQS_MASK_CFG 0x24
#define ERROR_IRQS 0x28
#define ERROR_IRQS_MASK_CFG 0x2c
#define DPHY_LANE_CONTROL 0x40
#define DPHY_STATUS 0x48
#define DPHY_ERR_STATUS_IRQ 0x4C
#define DPHY_ERR_IRQ_MASK_CFG 0x50
#define INTEGRATION_DEBUG 0x60
#define ERROR_DEBUG 0x74
#define STREAM0_CTRL 0x100
#define STREAM0_STATUS 0x104
#define STREAM0_DATA_CFG 0x108
#define STREAM0_CFG 0x10c
#define STREAM0_MONITOR_CTRL 0x110
#define STREAM0_MONITOR_FRAME 0x114
#define STREAM0_MONITOR_LB 0x118
#define STREAM0_TIMER 0x11c
#define STREAM0_FCC_CFG 0x120
#define STREAM0_FCC_CTRL 0x124
#define STREAM0_FIFO_FILL_LVL 0x128
//m31_dphy registers
#define M31DPHY_APBCFGSAIF__SYSCFG_188 0xbc
#define M31DPHY_APBCFGSAIF__SYSCFG_192 0xc0
#define M31DPHY_APBCFGSAIF__SYSCFG_196 0xc4
#define M31DPHY_APBCFGSAIF__SYSCFG_200 0xc8
typedef enum
{
DT_RAW6 = 0x28,
DT_RAW7 = 0x29,
DT_RAW8 = 0x2a,
DT_RAW10 = 0x2b,
DT_RAW12 = 0x2c,
DT_RAW14 = 0x2d,
} mipicam_data_type_t;
enum VIN_SOURCE_FORMAT {
SRC_COLORBAR_VIN_ISP = 0,
SRC_DVP_SENSOR_VIN,
SRC_DVP_SENSOR_VIN_ISP,//need replace sensor
SRC_CSI2RX_VIN_ISP,
SRC_DVP_SENSOR_VIN_OV5640,
};
struct reg_name {
char name[10];
};
typedef struct
{
int dlane_nb;
int dlane_map[4];
int dlane_en[4];
int dlane_pn_swap[4];
int clane_nb;
int clane_map[2];
int clane_pn_swap[2];
} csi2rx_dphy_cfg_t;
typedef struct
{
int lane_nb;
int dlane_map[4];
int dt;
int hsize;
int vsize;
} csi2rx_cfg_t;
typedef struct
{
int mipi_id, w, h, dt, bpp, fps,lane;
u8 clane_swap;
u8 clane_pn_swap;
u8 dlane_swap[4];
u8 dlane_pn_swap[4];
} csi_format;
struct vin_params {
void *paddr;
unsigned long size;
};
struct vin_buf {
void *vaddr;
dma_addr_t paddr;
u32 size;
};
struct vin_framesize {
u32 width;
u32 height;
};
struct vin_format {
enum VIN_SOURCE_FORMAT format;
u8 fps;
};
struct stf_vin_dev {
/* Protects the access of variables shared within the interrupt */
spinlock_t irqlock;
int irq;
struct device *dev;
struct cdev vin_cdev;
void __iomem *base;
void __iomem *csi2rx_base;
void __iomem *clkgen_base;
void __iomem *rstgen_base;
void __iomem *sysctrl_base;
void __iomem *isp_base;
void __iomem *vin_top_clkgen_base;
void __iomem *vin_top_rstgen_base;
void __iomem *vin_top_iopad_base;
void __iomem *pmu_test;
void __iomem *sys_crg;
struct vin_framesize frame;
struct vin_format format;
bool isp;
int isp_irq;
int isp_csi_irq;
int isp_scd_irq;
int isp_irq_csiline;
u32 major;
struct vin_buf buf;
wait_queue_head_t wq;
bool condition;
int odd;
csi_format csi_fmt;
};
extern int vin_notifier_register(struct notifier_block *nb);
extern void vin_notifier_unregister(struct notifier_block *nb);
extern int vin_notifier_call(unsigned long e, void *v);
#endif