sdk-hwV1.3/lichee/rtos-hal/hal/source/vin/vin_csi/sunxi_csi.c

483 lines
12 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* sunxi_csi.c for csi parser v4l2 subdev
*
* Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Authors: Zhao Wei <zhaowei@allwinnertech.com>
*
* 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.
*/
#include <hal_mem.h>
#include "../vin.h"
#include "sunxi_csi.h"
#include "../platform/platform_cfg.h"
struct csi_dev *global_csi[VIN_MAX_CSI];
bool csi_probe_flag[VIN_MAX_CSI];
#define IS_FLAG(x, y) (((x)&(y)) == y)
static struct csi_format sunxi_csi_formats[] = {
{
.code = MEDIA_BUS_FMT_YUYV8_2X8,
.seq = SEQ_YUYV,
.infmt = FMT_YUV422,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_YVYU8_2X8,
.seq = SEQ_YVYU,
.infmt = FMT_YUV422,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.seq = SEQ_UYVY,
.infmt = FMT_YUV422,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_VYUY8_2X8,
.seq = SEQ_VYUY,
.infmt = FMT_YUV422,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_YUYV8_1X16,
.seq = SEQ_YUYV,
.infmt = FMT_YUV422,
.data_width = 16,
}, {
.code = MEDIA_BUS_FMT_YVYU8_1X16,
.seq = SEQ_YVYU,
.infmt = FMT_YUV422,
.data_width = 16,
}, {
.code = MEDIA_BUS_FMT_UYVY8_1X16,
.seq = SEQ_UYVY,
.infmt = FMT_YUV422,
.data_width = 16,
}, {
.code = MEDIA_BUS_FMT_VYUY8_1X16,
.seq = SEQ_VYUY,
.infmt = FMT_YUV422,
.data_width = 16,
}, {
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.infmt = FMT_RAW,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
.infmt = FMT_RAW,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
.infmt = FMT_RAW,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
.infmt = FMT_RAW,
.data_width = 8,
}, {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.infmt = FMT_RAW,
.data_width = 10,
}, {
.code = MEDIA_BUS_FMT_SGBRG10_1X10,
.infmt = FMT_RAW,
.data_width = 10,
}, {
.code = MEDIA_BUS_FMT_SGRBG10_1X10,
.infmt = FMT_RAW,
.data_width = 10,
}, {
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
.infmt = FMT_RAW,
.data_width = 10,
}, {
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
.infmt = FMT_RAW,
.data_width = 12,
}, {
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
.infmt = FMT_RAW,
.data_width = 12,
}, {
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
.infmt = FMT_RAW,
.data_width = 12,
}, {
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
.infmt = FMT_RAW,
.data_width = 12,
}
};
static struct csi_format *__csi_try_format(unsigned int id, unsigned int vinc_id)
{
struct csi_format *csi_fmt = NULL;
struct sensor_format_struct *sensor_format;
int isp_sel = global_video[vinc_id].isp_sel;
int i;
if (global_sensors[id].sensor_core->sensor_g_format)
sensor_format = global_sensors[id].sensor_core->sensor_g_format(id, isp_sel);
else
return NULL;
for (i = 0; i < ARRAY_SIZE(sunxi_csi_formats); i++)
if (sensor_format->mbus_code == sunxi_csi_formats[i].code)
csi_fmt = &sunxi_csi_formats[i];
if (csi_fmt == NULL)
csi_fmt = &sunxi_csi_formats[0];
sensor_format->mbus_code = csi_fmt->code;
return csi_fmt;
}
static int __csi_set_fmt_hw(struct csi_dev *csi, unsigned int vinc_id)
{
struct prs_ncsi_bt656_header bt656_header;
struct prs_mcsi_if_cfg mcsi_if;
struct prs_cap_mode mode;
struct sensor_format_struct *sensor_format;
int isp_sel = global_video[vinc_id].isp_sel;
int i;
memset(&bt656_header, 0, sizeof(bt656_header));
memset(&mcsi_if, 0, sizeof(mcsi_if));
csi->ncsi_if.seq = csi->csi_fmt->seq;
mcsi_if.seq = csi->csi_fmt->seq;
switch (csi->csi_fmt->data_width) {
case 8:
csi->ncsi_if.dw = DW_8BIT;
break;
case 10:
csi->ncsi_if.dw = DW_10BIT;
break;
case 12:
csi->ncsi_if.dw = DW_12BIT;
break;
default:
csi->ncsi_if.dw = DW_8BIT;
break;
}
if (global_sensors[csi->id].sensor_core->sensor_g_format)
sensor_format = global_sensors[csi->id].sensor_core->sensor_g_format(csi->id, isp_sel);
else
return -1;
switch (sensor_format->field) {
case V4L2_FIELD_ANY:
case V4L2_FIELD_NONE:
csi->ncsi_if.type = PROGRESSED;
csi->ncsi_if.mode = FRAME_MODE;
mcsi_if.mode = FIELD_MODE;
break;
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
csi->ncsi_if.type = INTERLACE;
csi->ncsi_if.mode = FIELD_MODE;
mcsi_if.mode = FIELD_MODE;
break;
case V4L2_FIELD_INTERLACED:
csi->ncsi_if.type = INTERLACE;
csi->ncsi_if.mode = FRAME_MODE;
mcsi_if.mode = FRAME_MODE;
break;
default:
csi->ncsi_if.type = PROGRESSED;
csi->ncsi_if.mode = FRAME_MODE;
mcsi_if.mode = FIELD_MODE;
break;
}
switch (csi->bus_info.bus_if) {
case V4L2_MBUS_PARALLEL:
if (csi->csi_fmt->data_width == 16)
csi->ncsi_if.intf = PRS_IF_INTLV_16BIT;
else
csi->ncsi_if.intf = PRS_IF_INTLV;
csic_prs_mode(csi->id, PRS_NCSI);
csic_prs_ncsi_if_cfg(csi->id, &csi->ncsi_if);
csic_prs_ncsi_en(csi->id, 1);
break;
case V4L2_MBUS_BT656:
if (csi->csi_fmt->data_width == 16) {
if (csi->bus_info.ch_total_num == 1)
csi->ncsi_if.intf = PRS_IF_BT1120_1CH;
else if (csi->bus_info.ch_total_num == 2)
csi->ncsi_if.intf = PRS_IF_BT1120_2CH;
else if (csi->bus_info.ch_total_num == 4)
csi->ncsi_if.intf = PRS_IF_BT1120_4CH;
if (csi->ncsi_if.ddr_sample == 1)
csic_prs_set_pclk_dly(csi->id, 0xb);
else
csic_prs_set_pclk_dly(csi->id, 0x9);
} else {
if (csi->bus_info.ch_total_num == 1)
csi->ncsi_if.intf = PRS_IF_BT656_1CH;
else if (csi->bus_info.ch_total_num == 2)
csi->ncsi_if.intf = PRS_IF_BT656_2CH;
else if (csi->bus_info.ch_total_num == 4)
csi->ncsi_if.intf = PRS_IF_BT656_4CH;
if (csi->ncsi_if.ddr_sample == 1)
csic_prs_set_pclk_dly(csi->id, 0x9);
}
csic_prs_mode(csi->id, PRS_NCSI);
bt656_header.ch0_id = 0;
bt656_header.ch1_id = 1;
bt656_header.ch2_id = 2;
bt656_header.ch3_id = 3;
csic_prs_ncsi_bt656_header_cfg(csi->id, &bt656_header);
csic_prs_ncsi_if_cfg(csi->id, &csi->ncsi_if);
csic_prs_ncsi_en(csi->id, 1);
break;
case V4L2_MBUS_CSI2:
csic_prs_mode(csi->id, PRS_MCSI);
csic_prs_mcsi_if_cfg(csi->id, &mcsi_if);
csic_prs_mcsi_en(csi->id, 1);
break;
default:
csic_prs_mode(csi->id, PRS_MCSI);
csic_prs_mcsi_if_cfg(csi->id, &mcsi_if);
csic_prs_mcsi_en(csi->id, 1);
break;
}
if (csi->capture_mode == V4L2_MODE_IMAGE)
mode.mode = SCAP;
else
mode.mode = VCAP;
if (csi->out_size.hor_len != sensor_format->width ||
csi->out_size.ver_len != sensor_format->height) {
csi->out_size.hor_len = sensor_format->width;
csi->out_size.ver_len = sensor_format->height;
csi->out_size.hor_start = 0;
csi->out_size.ver_start = 0;
}
csi->out_size.ver_start = sensor_format->voffset;
csi->out_size.hor_start = sensor_format->hoffset;
if (sensor_format->field == V4L2_FIELD_INTERLACED || sensor_format->field == V4L2_FIELD_TOP ||
sensor_format->field == V4L2_FIELD_BOTTOM)
csi->out_size.ver_len = csi->out_size.ver_len / 2;
for (i = 0; i < csi->bus_info.ch_total_num; i++) {
csic_prs_input_fmt_cfg(csi->id, i, csi->csi_fmt->infmt);
csic_prs_output_size_cfg(csi->id, i, &csi->out_size);
}
csic_prs_capture_start(csi->id, csi->bus_info.ch_total_num, &mode);
return 0;
}
int sunxi_csi_subdev_s_stream(unsigned int id, unsigned int vinc_id, int enable)
{
struct csi_dev *csi = global_csi[id];
int *stream_count;
stream_count = &csi->stream_count;
if (enable && (*stream_count)++ > 0)
return 0;
else if (!enable && (*stream_count == 0 || --(*stream_count) > 0))
return 0;
csic_prs_pclk_en(csi->id, enable);
if (enable) {
csi->csi_fmt = __csi_try_format(csi->id, vinc_id);
vin_log(VIN_LOG_CSI, "csi_fmt:code is 0x%lx, data_width is %d\n",
csi->csi_fmt->code, csi->csi_fmt->data_width);
csic_prs_enable(csi->id);
csic_prs_disable(csi->id);
csic_prs_enable(csi->id);
__csi_set_fmt_hw(csi, vinc_id);
} else {
switch (csi->bus_info.bus_if) {
case V4L2_MBUS_PARALLEL:
case V4L2_MBUS_BT656:
csic_prs_ncsi_en(csi->id, 0);
break;
case V4L2_MBUS_CSI2:
csic_prs_mcsi_en(csi->id, 0);
break;
default:
return -1;
}
csic_prs_capture_stop(csi->id);
csic_prs_disable(csi->id);
}
vin_log(VIN_LOG_FMT, "parser%d %s, %d*%d hoff: %d voff: %d\n",
csi->id, enable ? "stream on" : "stream off",
csi->out_size.hor_len, csi->out_size.ver_len,
csi->out_size.hor_start, csi->out_size.ver_start);
return 0;
}
int sunxi_csi_s_mbus_config(unsigned int id, const struct v4l2_mbus_config *cfg)
{
struct csi_dev *csi = global_csi[id];
if (cfg->type == V4L2_MBUS_CSI2 || cfg->type == V4L2_MBUS_SUBLVDS ||
cfg->type == V4L2_MBUS_HISPI) {
csi->bus_info.bus_if = V4L2_MBUS_CSI2;
csi->bus_info.ch_total_num = 0;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_0))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_1))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_2))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_3))
csi->bus_info.ch_total_num++;
} else if (cfg->type == V4L2_MBUS_PARALLEL) {
csi->bus_info.bus_if = V4L2_MBUS_PARALLEL;
csi->bus_info.ch_total_num = 1;
if (IS_FLAG(cfg->flags, V4L2_MBUS_MASTER)) {
if (IS_FLAG(cfg->flags, V4L2_MBUS_HSYNC_ACTIVE_HIGH)) {
csi->bus_info.bus_tmg.href_pol = ACTIVE_HIGH;
csi->ncsi_if.href = REF_POSITIVE;
} else {
csi->bus_info.bus_tmg.href_pol = ACTIVE_LOW;
csi->ncsi_if.href = REF_NEGATIVE;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_VSYNC_ACTIVE_HIGH)) {
csi->bus_info.bus_tmg.vref_pol = ACTIVE_HIGH;
csi->ncsi_if.vref = REF_POSITIVE;
} else {
csi->bus_info.bus_tmg.vref_pol = ACTIVE_LOW;
csi->ncsi_if.vref = REF_NEGATIVE;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_RISING) &&
IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
csi->ncsi_if.ddr_sample = 1;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_RISING)) {
csi->bus_info.bus_tmg.pclk_sample = RISING;
csi->ncsi_if.clk = CLK_RISING;
csi->ncsi_if.ddr_sample = 0;
} else {
csi->bus_info.bus_tmg.pclk_sample = FALLING;
csi->ncsi_if.clk = CLK_FALLING;
csi->ncsi_if.ddr_sample = 0;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_FIELD_EVEN_HIGH)) {
csi->bus_info.bus_tmg.field_even_pol = ACTIVE_HIGH;
csi->ncsi_if.field = FIELD_POS;
} else {
csi->bus_info.bus_tmg.field_even_pol = ACTIVE_LOW;
csi->ncsi_if.field = FIELD_NEG;
}
} else {
vin_err("Do not support V4L2_MBUS_SLAVE!\n");
return -1;
}
} else if (cfg->type == V4L2_MBUS_BT656) {
csi->bus_info.bus_if = V4L2_MBUS_BT656;
csi->bus_info.ch_total_num = 0;
if (IS_FLAG(cfg->flags, CSI_CH_0))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, CSI_CH_1))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, CSI_CH_2))
csi->bus_info.ch_total_num++;
if (IS_FLAG(cfg->flags, CSI_CH_3))
csi->bus_info.ch_total_num++;
if (csi->bus_info.ch_total_num == 4) {
csi->arrange.column = 2;
csi->arrange.row = 2;
} else if (csi->bus_info.ch_total_num == 2) {
csi->arrange.column = 2;
csi->arrange.row = 1;
} else {
csi->bus_info.ch_total_num = 1;
csi->arrange.column = 1;
csi->arrange.row = 1;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_RISING) &&
IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
csi->ncsi_if.ddr_sample = 1;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_PCLK_SAMPLE_RISING)) {
csi->bus_info.bus_tmg.pclk_sample = RISING;
csi->ncsi_if.clk = CLK_RISING;
csi->ncsi_if.ddr_sample = 0;
} else {
csi->bus_info.bus_tmg.pclk_sample = FALLING;
csi->ncsi_if.clk = CLK_FALLING;
csi->ncsi_if.ddr_sample = 0;
}
}
vin_log(VIN_LOG_CSI, "csi%d total ch = %d\n", csi->id, csi->bus_info.ch_total_num);
return 0;
}
int csi_probe(unsigned int id)
{
struct csi_dev *csi = NULL;
int ret = 0;
if (csi_probe_flag[id])
return 0;
else
csi_probe_flag[id] = true;
if (id > VIN_MAX_CSI) {
vin_err("csi%d is not existing, max is %d\n", id, VIN_MAX_CSI);
return -1;
}
csi = hal_malloc(sizeof(struct csi_dev));
if (!csi) {
ret = -1;
goto ekzalloc;
}
memset(csi, 0, sizeof(struct csi_dev));
csi->id = id;
csi->arrange.row = 1;
csi->arrange.column = 1;
csi->bus_info.ch_total_num = 1;
csi->base = sunxi_vin_get_csi_base(csi->id);
if (!csi->base) {
vin_err("Fail to get CSI base addr!\n");
ret = -1;
goto freedev;
}
vin_log(VIN_LOG_MD, "csi%d reg is 0x%lx\n", csi->id, csi->base);
ret = csic_prs_set_base_addr(csi->id, csi->base);
if (ret < 0)
goto freedev;
global_csi[id] = csi;
vin_log(VIN_LOG_CSI, "csi%d probe end!\n", csi->id);
return 0;
freedev:
free(csi);
ekzalloc:
vin_log(VIN_LOG_CSI, "csi probe err!\n");
return ret;
}
int csi_remove(unsigned int id)
{
struct csi_dev *csi = global_csi[id];
if (!csi_probe_flag[id])
return 0;
else
csi_probe_flag[id] = false;
hal_free(csi);
return 0;
}