483 lines
12 KiB
C
483 lines
12 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|