media: starfive: Add vin driver support
Add vin driver support. Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
This commit is contained in:
committed by
Hal Feng
parent
01326bebd9
commit
7f3fb7fc3c
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
56
drivers/media/platform/starfive/Kconfig
Normal file
56
drivers/media/platform/starfive/Kconfig
Normal 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.
|
||||
24
drivers/media/platform/starfive/Makefile
Normal file
24
drivers/media/platform/starfive/Makefile
Normal 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 \
|
||||
11
drivers/media/platform/starfive/v4l2_driver/Readme.txt
Normal file
11
drivers/media/platform/starfive/v4l2_driver/Readme.txt
Normal 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.
|
||||
1583
drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
Normal file
1583
drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
Normal file
File diff suppressed because it is too large
Load Diff
1921
drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
Normal file
1921
drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
Normal file
File diff suppressed because it is too large
Load Diff
2975
drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
Normal file
2975
drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
Normal file
File diff suppressed because it is too large
Load Diff
3227
drivers/media/platform/starfive/v4l2_driver/ov5640.c
Normal file
3227
drivers/media/platform/starfive/v4l2_driver/ov5640.c
Normal file
File diff suppressed because it is too large
Load Diff
1914
drivers/media/platform/starfive/v4l2_driver/sc2235.c
Normal file
1914
drivers/media/platform/starfive/v4l2_driver/sc2235.c
Normal file
File diff suppressed because it is too large
Load Diff
185
drivers/media/platform/starfive/v4l2_driver/stf_common.h
Normal file
185
drivers/media/platform/starfive/v4l2_driver/stf_common.h
Normal 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 */
|
||||
465
drivers/media/platform/starfive/v4l2_driver/stf_csi.c
Normal file
465
drivers/media/platform/starfive/v4l2_driver/stf_csi.c
Normal 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;
|
||||
}
|
||||
61
drivers/media/platform/starfive/v4l2_driver/stf_csi.h
Normal file
61
drivers/media/platform/starfive/v4l2_driver/stf_csi.h
Normal 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 */
|
||||
310
drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
Normal file
310
drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
Normal 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,
|
||||
};
|
||||
357
drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
Normal file
357
drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
Normal 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;
|
||||
}
|
||||
188
drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
Normal file
188
drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
Normal 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 */
|
||||
335
drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
Normal file
335
drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
Normal 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,
|
||||
};
|
||||
123
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
Normal file
123
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
Normal 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;
|
||||
}
|
||||
12
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
Normal file
12
drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
Normal 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 */
|
||||
385
drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
Normal file
385
drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
Normal 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;
|
||||
}
|
||||
67
drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
Normal file
67
drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
Normal 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 */
|
||||
187
drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
Normal file
187
drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
Normal 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,
|
||||
};
|
||||
36
drivers/media/platform/starfive/v4l2_driver/stf_event.c
Normal file
36
drivers/media/platform/starfive/v4l2_driver/stf_event.c
Normal 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");
|
||||
1521
drivers/media/platform/starfive/v4l2_driver/stf_isp.c
Normal file
1521
drivers/media/platform/starfive/v4l2_driver/stf_isp.c
Normal file
File diff suppressed because it is too large
Load Diff
222
drivers/media/platform/starfive/v4l2_driver/stf_isp.h
Normal file
222
drivers/media/platform/starfive/v4l2_driver/stf_isp.h
Normal 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 */
|
||||
1550
drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
Normal file
1550
drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
Normal file
File diff suppressed because it is too large
Load Diff
133
drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
Normal file
133
drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
Normal 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 */
|
||||
1552
drivers/media/platform/starfive/v4l2_driver/stf_video.c
Normal file
1552
drivers/media/platform/starfive/v4l2_driver/stf_video.c
Normal file
File diff suppressed because it is too large
Load Diff
83
drivers/media/platform/starfive/v4l2_driver/stf_video.h
Normal file
83
drivers/media/platform/starfive/v4l2_driver/stf_video.h
Normal 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 */
|
||||
1515
drivers/media/platform/starfive/v4l2_driver/stf_vin.c
Normal file
1515
drivers/media/platform/starfive/v4l2_driver/stf_vin.c
Normal file
File diff suppressed because it is too large
Load Diff
182
drivers/media/platform/starfive/v4l2_driver/stf_vin.h
Normal file
182
drivers/media/platform/starfive/v4l2_driver/stf_vin.h
Normal 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 */
|
||||
433
drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
Normal file
433
drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
Normal 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], ¶ms);
|
||||
}
|
||||
|
||||
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], ¶ms);
|
||||
|
||||
if ((int_status & BIT(12)))
|
||||
vin_dev->hw_ops->isr_buffer_done(
|
||||
&vin_dev->line[VIN_LINE_ISP_SS1], ¶ms);
|
||||
|
||||
if ((int_status & BIT(20)))
|
||||
vin_dev->hw_ops->isr_buffer_done(
|
||||
&vin_dev->line[VIN_LINE_ISP], ¶ms);
|
||||
|
||||
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], ¶ms);
|
||||
if ((value & BIT(16)))
|
||||
vin_dev->hw_ops->isr_buffer_done(
|
||||
&vin_dev->line[VIN_LINE_ISP_ITIR], ¶ms);
|
||||
|
||||
#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], ¶ms);
|
||||
|
||||
if (int_status & BIT(26))
|
||||
vin_dev->hw_ops->isr_buffer_done(
|
||||
&vin_dev->line[VIN_LINE_ISP_SCD_Y], ¶ms);
|
||||
|
||||
/* 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], ¶ms);
|
||||
|
||||
/* 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], ¶ms);
|
||||
|
||||
/* 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,
|
||||
};
|
||||
1369
drivers/media/platform/starfive/v4l2_driver/stfcamss.c
Normal file
1369
drivers/media/platform/starfive/v4l2_driver/stfcamss.c
Normal file
File diff suppressed because it is too large
Load Diff
117
drivers/media/platform/starfive/v4l2_driver/stfcamss.h
Normal file
117
drivers/media/platform/starfive/v4l2_driver/stfcamss.h
Normal 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 */
|
||||
253
include/uapi/linux/jh7110-isp.h
Normal file
253
include/uapi/linux/jh7110-isp.h
Normal 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
|
||||
@@ -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
443
include/video/stf-vin.h
Normal 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
|
||||
Reference in New Issue
Block a user