media: sunxi-vin: bring up BPI-M8 IMX415 CSI

This commit is contained in:
Qubot 2026-06-14 22:59:05 +08:00
parent beb389776e
commit e49ff5bdd9
4 changed files with 201 additions and 25 deletions

View File

@ -57,15 +57,15 @@ static struct regval_list sensor_default_regs[] = {
};
static struct regval_list sensor_12bit_30fps_regs[] = {
/* All pixel - 891Mbps - 27MHZ */
/* All pixel - 891Mbps - 74.25MHz external INCK */
{0x3000, 0x01},/* stanby */
{0x3001, 0x00},
{0x3002, 0x01},/* XMSTA start */
{0x3003, 0x00},/* XMASTER SELECT MASTER */
{0x3008, 0x5D},
{0x3008, 0xFF},
{0x3009, 0x00},
{0x300A, 0x42},
{0x300B, 0xA0},
{0x300A, 0xB6},
{0x300B, 0x00},
{0x301C, 0x00},
{0x301D, 0x08},
{0x3020, 0x00},
@ -121,12 +121,12 @@ static struct regval_list sensor_12bit_30fps_regs[] = {
{0x30E2, 0x32},/* BLV */
{0x30E3, 0x00},
{0x3115, 0x00},
{0x3116, 0x23},
{0x3118, 0xC6},
{0x3116, 0x28},
{0x3118, 0xC0},
{0x3119, 0x00},
{0x311A, 0xE7},
{0x311A, 0xE0},
{0x311B, 0x00},
{0x311E, 0x23},
{0x311E, 0x28},
{0x3260, 0x01},
{0x32C8, 0x01},
{0x32D4, 0x21},
@ -203,8 +203,8 @@ static struct regval_list sensor_12bit_30fps_regs[] = {
{0x3BCA, 0xBD},
{0x4000, 0x10},
{0x4001, 0x03},
{0x4004, 0xC0},
{0x4005, 0x06},
{0x4004, 0x90},
{0x4005, 0x12},
{0x400C, 0x00},
{0x4018, 0x7F},
{0x4019, 0x00},
@ -855,19 +855,14 @@ static int sensor_power(struct v4l2_subdev *sd, int on)
vin_gpio_set_status(sd, RESET, 1);
vin_gpio_set_status(sd, POWER_EN, 1);
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
vin_gpio_write(sd, POWER_EN, CSI_GPIO_HIGH);
vin_set_pmu_channel(sd, IOVDD, ON);
usleep_range(2000, 2200);
vin_set_pmu_channel(sd, AVDD, ON);
vin_set_pmu_channel(sd, DVDD, ON);
usleep_range(20000, 22000);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
usleep_range(100, 120);
vin_set_mclk(sd, ON);
usleep_range(100, 120);
vin_set_mclk_freq(sd, MCLK);
usleep_range(3000, 3200);
usleep_range(50000, 52000);
cci_unlock(sd);
break;
case PWR_OFF:
@ -914,10 +909,14 @@ static int sensor_reset(struct v4l2_subdev *sd, u32 val)
static int sensor_detect(struct v4l2_subdev *sd)
{
data_type rdval = 0;
int cnt = 0;
sensor_read(sd, 0x3008, &rdval);
sensor_print("%s read value is 0x%x\n", __func__, rdval);
data_type id_l = 0, id_h = 0;
int ret, ret_l;
ret_l = sensor_read(sd, 0x3f12, &id_l);
ret = sensor_read(sd, 0x3f13, &id_h);
sensor_dbg("%s read id ret=%d/%d value=0x%x\n",
__func__, ret_l, ret, ((id_h & 0xff) << 8) | (id_l & 0xff));
return 0;
}
@ -1352,11 +1351,14 @@ static int sensor_probe(struct i2c_client *client,
info->fmt = &sensor_formats[0];
info->fmt_pt = &sensor_formats[0];
info->win_pt = &sensor_win_sizes[0];
info->current_wins = &sensor_win_sizes[0];
info->fmt_num = N_FMTS;
info->win_size_num = N_WIN_SIZES;
info->sensor_field = V4L2_FIELD_NONE;
info->width = sensor_win_sizes[0].width;
info->height = sensor_win_sizes[0].height;
info->stream_seq = MIPI_BEFORE_SENSOR;
info->combo_mode = CMB_TERMINAL_RES | CMB_PHYA_OFFSET2 | MIPI_NORMAL_MODE;
info->combo_mode = CMB_TERMINAL_RES | CMB_PHYA_OFFSET0 | MIPI_NORMAL_MODE;
info->af_first_flag = 1;
info->exp = 0;
info->gain = 0;

View File

@ -36,6 +36,8 @@
struct csi_dev *glb_parser[VIN_MAX_CSI];
static struct csi_format *__csi_try_format(struct v4l2_mbus_framefmt *mf);
static struct csi_format sunxi_csi_formats[] = {
{
.code = MEDIA_BUS_FMT_YUYV8_2X8,
@ -519,6 +521,15 @@ static int sunxi_csi_subdev_s_stream(struct v4l2_subdev *sd, int enable)
csic_prs_enable(csi->id);
csic_prs_disable(csi->id);
csic_prs_enable(csi->id);
if (!csi->csi_fmt) {
if (!csi->mf.code) {
vin_err("parser%d stream on without active format\n", csi->id);
return -EINVAL;
}
csi->csi_fmt = __csi_try_format(&csi->mf);
vin_warn("parser%d missing active format, fallback to code 0x%x\n",
csi->id, csi->mf.code);
}
__csi_set_fmt_hw(csi);
} else {
csi->tvin.flag = false;

View File

@ -382,6 +382,11 @@ void vin_set_next_buf_addr(struct vin_core *vinc)
vinc->vin_status.lost_cnt = 0;
#ifndef BUF_AUTO_UPDATE
if (list_empty(&cap->vidq_active)) {
vin_err("vinc%d stream on without queued buffer\n", vinc->id);
return;
}
buf = list_entry(cap->vidq_active.next, struct vin_buffer, list);
vin_set_addr(vinc, &buf->vb.vb2_buf, &vinc->vid_cap.frame, &vinc->vid_cap.frame.paddr);
#else
@ -963,6 +968,7 @@ static int vin_pipeline_set_mbus_config(struct vin_core *vinc)
struct vin_pipeline *pipe = &vinc->vid_cap.pipe;
struct v4l2_subdev *sd = pipe->sd[VIN_IND_SENSOR];
struct v4l2_mbus_config mcfg;
struct v4l2_subdev *cfg_sd;
struct media_entity *me;
struct media_graph graph;
struct csi_dev *csi = NULL;
@ -973,6 +979,33 @@ static int vin_pipeline_set_mbus_config(struct vin_core *vinc)
vin_err("%s get_mbus_config error!\n", sd->name);
goto out;
}
cfg_sd = pipe->sd[VIN_IND_MIPI];
if (cfg_sd) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
ret = cfg_sd->ops->pad->get_mbus_config(cfg_sd, 0, &mcfg);
#else
ret = cfg_sd->ops->pad->set_mbus_config(cfg_sd, 0, &mcfg);
#endif
if (ret < 0) {
vin_err("%s set_mbus_config error!\n", cfg_sd->name);
goto out;
}
}
cfg_sd = pipe->sd[VIN_IND_CSI];
if (cfg_sd) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
ret = cfg_sd->ops->pad->get_mbus_config(cfg_sd, 0, &mcfg);
#else
ret = cfg_sd->ops->pad->set_mbus_config(cfg_sd, 0, &mcfg);
#endif
if (ret < 0) {
vin_err("%s set_mbus_config error!\n", cfg_sd->name);
goto out;
}
}
/* s_mbus_config on all mipi and csi */
me = &vinc->vid_cap.subdev.entity;
mutex_lock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
@ -1043,6 +1076,7 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
return -EINVAL;
}
memset(&mf, 0, sizeof(mf));
mf.width = f->fmt.pix_mp.width;
mf.height = f->fmt.pix_mp.height;
mf.code = ffmt->mbus_code;
@ -1061,6 +1095,8 @@ static int __vin_set_fmt(struct vin_core *vinc, struct v4l2_format *f)
struct vin_vid_cap *cap = &vinc->vid_cap;
struct sensor_win_size win_cfg;
struct v4l2_mbus_framefmt mf;
struct v4l2_subdev_format mipi_fmt;
struct v4l2_subdev_format csi_fmt;
struct vin_fmt *ffmt = NULL;
struct mbus_framefmt_res *res = (void *)mf.reserved;
__maybe_unused struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
@ -1086,6 +1122,7 @@ static int __vin_set_fmt(struct vin_core *vinc, struct v4l2_format *f)
}
cap->frame.fmt = *ffmt;
memset(&mf, 0, sizeof(mf));
mf.width = f->fmt.pix_mp.width;
mf.height = f->fmt.pix_mp.height;
mf.field = f->fmt.pix_mp.field;
@ -1106,7 +1143,11 @@ static int __vin_set_fmt(struct vin_core *vinc, struct v4l2_format *f)
vin_log(VIN_LOG_FMT, "pipeline try fmt %d*%d code %x field %d colorspace %d\n",
mf.width, mf.height, mf.code, mf.field, mf.colorspace);
vin_pipeline_set_mbus_config(vinc);
ret = vin_pipeline_set_mbus_config(vinc);
if (ret < 0) {
vin_err("vin_pipeline_set_mbus_config failed\n");
return ret;
}
/* get current win configs */
memset(&win_cfg, 0, sizeof(win_cfg));
@ -1139,7 +1180,52 @@ static int __vin_set_fmt(struct vin_core *vinc, struct v4l2_format *f)
sel.which = V4L2_SUBDEV_FORMAT_TRY;
sel.pad = cap->pipe.sd[VIN_IND_CSI]->entity.num_pads - 1;
if (cap->pipe.sd[VIN_IND_SENSOR]) {
struct v4l2_subdev_format sensor_fmt;
memset(&sensor_fmt, 0, sizeof(sensor_fmt));
sensor_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sensor_fmt.pad = SENSOR_PAD_SOURCE;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], pad,
get_fmt, NULL, &sensor_fmt);
if (ret < 0) {
vin_err("sensor get_fmt error! code = %d\n", ret);
goto out;
}
mf = sensor_fmt.format;
}
ret = vin_pipeline_set_mbus_config(vinc);
if (ret < 0) {
vin_err("vin_pipeline_set_mbus_config failed\n");
goto out;
}
if (cap->pipe.sd[VIN_IND_MIPI]) {
memset(&mipi_fmt, 0, sizeof(mipi_fmt));
mipi_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
mipi_fmt.pad = MIPI_PAD_SINK;
mipi_fmt.format = mf;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_MIPI], pad,
set_fmt, NULL, &mipi_fmt);
if (ret < 0) {
vin_err("mipi set_fmt error! code = %d\n", ret);
goto out;
}
}
if (cap->pipe.sd[VIN_IND_CSI]) {
memset(&csi_fmt, 0, sizeof(csi_fmt));
csi_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
csi_fmt.pad = CSI_PAD_SINK;
csi_fmt.format = mf;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_CSI], pad,
set_fmt, NULL, &csi_fmt);
if (ret < 0) {
vin_err("csi parser set_fmt error! code = %d\n", ret);
goto out;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
memset(&state, 0, sizeof(state));
state.pads = &cfg;

View File

@ -563,6 +563,7 @@ static void vin_md_clk_disable(struct vin_md *vind)
if (vind->mipi_clk[VIN_MIPI_CLK].clock)
clk_disable_unprepare(vind->mipi_clk[VIN_MIPI_CLK].clock);
vind->clk_en = false;
#endif
}
@ -803,6 +804,12 @@ static void vin_md_set_power(struct vin_md *vind, int on)
#if IS_ENABLED(CONFIG_VIN_INIT_MELIS) && !defined CONFIG_RV_RUN_CAR_REVERSE
if (on) {
if (vind->sensor_power_on) {
#if IS_ENABLED(CONFIG_PM)
pm_runtime_get_sync(&vind->pdev->dev);
#else
vin_md_clk_enable(vind);
#endif
usleep_range(100, 120);
csic_top_version_read_en(vind->id, 1);
csic_feature_list_get(vind->id, &vind->csic_fl);
csic_version_get(vind->id, &vind->csic_ver);
@ -817,7 +824,11 @@ static void vin_md_set_power(struct vin_md *vind, int on)
#endif
if (on) {
#if IS_ENABLED(CONFIG_PM)
pm_runtime_get_sync(&vind->pdev->dev);
#else
vin_md_clk_enable(vind);
#endif
usleep_range(100, 120);
#if IS_ENABLED(CONFIG_ARCH_SUN60IW1)
#ifdef MULTI_FRM_MERGE_INT
@ -881,7 +892,11 @@ static void vin_md_set_power(struct vin_md *vind, int on)
csic_ccu_bk_intpool_clk_gating_en(0);
#endif
#if IS_ENABLED(CONFIG_PM)
pm_runtime_put_sync(&vind->pdev->dev);
#else
vin_md_clk_disable(vind);
#endif
}
}
@ -2225,7 +2240,7 @@ static int vin_create_media_links(struct vin_md *vind)
static int vin_setup_default_links(struct vin_md *vind)
{
struct v4l2_subdev *isp, *scaler;
struct v4l2_subdev *sensor, *mipi, *csi, *tdm_rx, *isp, *scaler;
int i, ret = 0;
for (i = 0; i < VIN_MAX_DEV; i++) {
@ -2237,6 +2252,56 @@ static int vin_setup_default_links(struct vin_md *vind)
if (vinc == NULL)
continue;
/* SENSOR */
if (vinc->rear_sensor >= VIN_MAX_DEV ||
vind->modules[vinc->rear_sensor].sensors.valid_idx == NO_VALID_SENSOR)
sensor = NULL;
else
sensor = vind->modules[vinc->rear_sensor].modules.sensor[
vind->modules[vinc->rear_sensor].sensors.valid_idx].sd;
/* MIPI */
if (vinc->mipi_sel == 0xff)
mipi = NULL;
else
mipi = vind->mipi[vinc->mipi_sel].sd;
/* CSI */
if (vinc->csi_sel == 0xff)
csi = NULL;
else
csi = vind->csi[vinc->csi_sel].sd;
/* TDM RX */
if (vinc->tdm_rx_sel == 0xff)
tdm_rx = NULL;
else
tdm_rx = vind->tdm[vinc->tdm_rx_sel / TDM_RX_NUM].tdm_rx[vinc->tdm_rx_sel].sd;
if (sensor && mipi) {
link = media_entity_find_link(&sensor->entity.pads[SENSOR_PAD_SOURCE],
&mipi->entity.pads[0]);
if (link) {
ret = media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
if (ret)
vin_err("sensor to mipi setup link error\n");
} else {
vin_err("sensor to mipi link null\n");
}
}
if (csi && tdm_rx) {
link = media_entity_find_link(&csi->entity.pads[CSI_PAD_SOURCE],
&tdm_rx->entity.pads[0]);
if (link) {
ret = media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
if (ret)
vin_err("csi to tdm_rx setup link error\n");
} else {
vin_err("csi to tdm_rx link null\n");
}
}
/* ISP */
if (vinc->isp_sel == 0xff)
isp = NULL;
@ -2655,10 +2720,22 @@ static void vin_shutdown(struct platform_device *pdev)
int vin_runtime_suspend(struct device *d)
{
struct vin_md *vind = dev_get_drvdata(d);
if (vind) {
vin_log(VIN_LOG_POWER, "%s\n", __func__);
vin_md_clk_disable(vind);
}
return 0;
}
int vin_runtime_resume(struct device *d)
{
struct vin_md *vind = dev_get_drvdata(d);
if (vind) {
vin_log(VIN_LOG_POWER, "%s\n", __func__);
return vin_md_clk_enable(vind);
}
return 0;
}