sdk-hwV1.3/lichee/rtos-hal/hal/source/vin/vin_mipi/sunxi_mipi.c

518 lines
14 KiB
C

/*
* mipi subdev driver module
*
* 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_mipi.h"
struct mipi_dev *global_mipi[VIN_MAX_MIPI];
bool mipi_probe_flag[VIN_MAX_MIPI];
#define IS_FLAG(x, y) (((x)&(y)) == y)
#if 0
static struct combo_format sunxi_mipi_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_YUYV8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_SGBRG10_1X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_SGRBG10_1X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
.bit_width = RAW12,
}, {
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
.bit_width = RAW12,
}, {
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
.bit_width = RAW12,
}, {
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
.bit_width = RAW12,
}, {
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_VYUY8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_YUYV8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_YVYU8_2X8,
.bit_width = RAW8,
}, {
.code = MEDIA_BUS_FMT_UYVY10_2X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_VYUY10_2X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_YUYV10_2X10,
.bit_width = RAW10,
}, {
.code = MEDIA_BUS_FMT_YVYU10_2X10,
.bit_width = RAW10,
}
};
static unsigned int data_formats_type(u32 code)
{
switch (code) {
case MIPI_RAW8:
case MIPI_RAW12:
return RAW;
case MIPI_YUV422:
case MIPI_YUV422_10:
return YUV;
case MIPI_RGB565:
return RGB;
default:
return RAW;
}
}
#endif
static enum pkt_fmt get_pkt_fmt(u32 code)
{
switch (code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
return MIPI_RGB565;
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_VYUY8_2X8:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
return MIPI_YUV422;
case MEDIA_BUS_FMT_UYVY10_2X10:
case MEDIA_BUS_FMT_VYUY10_2X10:
case MEDIA_BUS_FMT_YUYV10_2X10:
case MEDIA_BUS_FMT_YVYU10_2X10:
return MIPI_YUV422_10;
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SRGGB8_1X8:
return MIPI_RAW8;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
return MIPI_RAW10;
case MEDIA_BUS_FMT_SBGGR12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
return MIPI_RAW12;
default:
return MIPI_RAW8;
}
}
static int __mcsi_pin_config(int id, int enable)
{
struct vin_mipi_gpio_info *mipi_gpio = &vin_mipi_gpio[id];
int i;
if (enable) {
for (i = 0; i < 6; i++) {
hal_gpio_pinmux_set_function(mipi_gpio->pin[i], mipi_gpio->pin_func[0]);
}
} else {
for (i = 0; i < 6; i++) {
hal_gpio_pinmux_set_function(mipi_gpio->pin[i], mipi_gpio->pin_func[1]);
}
}
return 0;
}
static void cmb_phy_init(struct mipi_dev *mipi)
{
cmb_phy_lane_num_en(mipi->id + mipi->phy_offset, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_phy0_work_mode(mipi->id + mipi->phy_offset, 0);
cmb_phy0_ofscal_cfg(mipi->id + mipi->phy_offset);
//cmb_phy_deskew_en(mipi->id + mipi->phy_offset, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_term_ctl(mipi->id + mipi->phy_offset, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_hs_ctl(mipi->id + mipi->phy_offset, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_s2p_ctl(mipi->id + mipi->phy_offset, mipi->time_hs, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_mipirx_ctl(mipi->id + mipi->phy_offset, mipi->cmb_csi_cfg.phy_lane_cfg);
cmb_phy0_en(mipi->id + mipi->phy_offset, 1);
cmb_phy_deskew1_cfg(mipi->id + mipi->phy_offset, mipi->deskew, mipi->cmb_mode == MIPI_VC_WDR_MODE ? true : false);
}
static void combo_csi_mipi_init(struct mipi_dev *mipi, unsigned int vinc_id)
{
int i;
int isp_sel = global_video[vinc_id].isp_sel;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_laneck_en = CK_1LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpck_en = LPCK_1LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_termck_en = TERMCK_CLOSE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_termdt_en = TERMDT_CLOSE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_s2p_en = S2PDT_CLOSE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_hsck_en = HSCK_CLOSE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_hsdt_en = HSDT_CLOSE;
cmb_phy_init(mipi);
/* V853 4lane needs cfg link mode */
if (mipi->cmb_csi_cfg.phy_link_mode == ONE_4LANE) {
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpck_en = LPCK_CLOSE;
mipi->phy_offset = 1;
cmb_phy_link_mode_set(ONE_4LANE);
cmb_phy_init(mipi);
mipi->phy_offset = 0;
}
for (i = 0; i < mipi->cmb_csi_cfg.lane_num; i++)
mipi->cmb_csi_cfg.mipi_lane[i] = cmb_port_set_lane_map(mipi->id, i);
for (i = 0; i < mipi->cmb_csi_cfg.total_rx_ch; i++) {
if (global_sensors[mipi->id].sensor_core->sensor_g_format)
mipi->cmb_csi_cfg.mipi_datatype[i] = get_pkt_fmt(global_sensors[mipi->id].sensor_core->sensor_g_format(mipi->id, isp_sel)->mbus_code);
mipi->cmb_csi_cfg.vc[i] = i;
}
cmb_port_lane_num(mipi->id, mipi->cmb_csi_cfg.lane_num);
cmb_port_out_num(mipi->id, mipi->cmb_csi_cfg.pix_num);
if (mipi->cmb_mode == MIPI_DOL_WDR_MODE)
cmb_port_out_chnum(mipi->id, 0);
else
cmb_port_out_chnum(mipi->id, mipi->cmb_csi_cfg.total_rx_ch - 1);
cmb_port_lane_map(mipi->id, mipi->cmb_csi_cfg.mipi_lane);
cmb_port_mipi_cfg(mipi->id, YUYV); //mipi->cmb_fmt->yuv_seq //no support yuv mipi sensor
cmb_port_set_mipi_datatype(mipi->id, &mipi->cmb_csi_cfg);
cmb_port_mipi_ch_trigger_en(mipi->id, 1);
if (mipi->cmb_mode == MIPI_DOL_WDR_MODE)
cmb_port_set_mipi_wdr(mipi->id, 0, 2);
cmb_port_enable(mipi->id);
}
void combo_csi_init(struct mipi_dev *mipi, unsigned int vinc_id)
{
switch (mipi->if_type) {
case V4L2_MBUS_PARALLEL:
case V4L2_MBUS_BT656:
break;
case V4L2_MBUS_CSI2:
combo_csi_mipi_init(mipi, vinc_id);
break;
case V4L2_MBUS_SUBLVDS:
break;
case V4L2_MBUS_HISPI:
break;
default:
break;
}
}
static int sunxi_mipi_top_s_stream(int on)
{
struct mipi_dev *mipi = global_mipi[0];
if (on && (mipi->top_stream_count)++ > 0)
return 0;
else if (!on && (mipi->top_stream_count == 0 || --(mipi->stream_count) > 0))
return 0;
if (on) {
cmb_phy_top_enable();
} else {
cmb_phy_top_disable();
}
return 0;
}
int sunxi_mipi_subdev_s_stream(unsigned int id, unsigned int vinc_id, int enable)
{
struct mipi_dev *mipi = global_mipi[id];
int *stream_count;
int i;
stream_count = &mipi->stream_count;
if (enable && (*stream_count)++ > 0)
return 0;
else if (!enable && (*stream_count == 0 || --(*stream_count) > 0))
return 0;
if (mipi->res->res_time_hs)
mipi->time_hs = mipi->res->res_time_hs;
else
mipi->time_hs = 0x28;
if (mipi->res->deskew)
mipi->deskew = mipi->res->deskew;
mipi->cmb_mode = mipi->res->res_combo_mode;
if (mipi->cmb_csi_cfg.phy_link_mode == ONE_4LANE) {
__mcsi_pin_config(mipi->id, enable);
__mcsi_pin_config(mipi->id + 1, enable);
} else
__mcsi_pin_config(mipi->id, enable);
sunxi_mipi_top_s_stream(enable);
if (enable) {
combo_csi_init(mipi, vinc_id);
} else {
mipi->cmb_csi_cfg.phy_link_mode = TWO_2LANE;
mipi->cmb_csi_cfg.pix_num = ONE_DATA;
cmb_port_disable(mipi->id);
cmb_phy0_en(mipi->id, 0);
}
vin_log(VIN_LOG_FMT, "%s%d %s, lane_num %d\n",
mipi->if_name, mipi->id, enable ? "stream on" : "stream off",
mipi->cmb_csi_cfg.lane_num);
return 0;
}
int sunxi_mipi_s_mbus_config(unsigned int id, const struct v4l2_mbus_config *cfg, const struct mbus_framefmt_res *res)
{
struct mipi_dev *mipi = global_mipi[id];
#if 0
if (cfg->type == V4L2_MBUS_CSI2) {
mipi->if_type = V4L2_MBUS_CSI2;
memcpy(mipi->if_name, "mipi", sizeof("mipi"));
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_4_LANE)) {
mipi->csi2_cfg.lane_num = 4;
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.mipi_ln = MIPI_4LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_3_LANE)) {
mipi->csi2_cfg.lane_num = 3;
mipi->cmb_cfg.lane_num = 3;
mipi->cmb_cfg.mipi_ln = MIPI_3LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_2_LANE)) {
mipi->csi2_cfg.lane_num = 2;
mipi->cmb_cfg.lane_num = 2;
mipi->cmb_cfg.mipi_ln = MIPI_2LANE;
} else {
mipi->cmb_cfg.lane_num = 1;
mipi->csi2_cfg.lane_num = 1;
mipi->cmb_cfg.mipi_ln = MIPI_1LANE;
}
} else if (cfg->type == V4L2_MBUS_SUBLVDS) {
mipi->if_type = V4L2_MBUS_SUBLVDS;
memcpy(mipi->if_name, "sublvds", sizeof("sublvds"));
if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_12_LANE)) {
mipi->cmb_cfg.lane_num = 12;
mipi->cmb_cfg.lvds_ln = LVDS_12LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_10_LANE)) {
mipi->cmb_cfg.lane_num = 10;
mipi->cmb_cfg.lvds_ln = LVDS_10LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_8_LANE)) {
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_4_LANE)) {
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_2_LANE)) {
mipi->cmb_cfg.lane_num = 2;
mipi->cmb_cfg.lvds_ln = LVDS_2LANE;
} else {
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
}
} else if (cfg->type == V4L2_MBUS_HISPI) {
mipi->if_type = V4L2_MBUS_HISPI;
memcpy(mipi->if_name, "hispi", sizeof("hispi"));
if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_12_LANE)) {
mipi->cmb_cfg.lane_num = 12;
mipi->cmb_cfg.lvds_ln = LVDS_12LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_10_LANE)) {
mipi->cmb_cfg.lane_num = 10;
mipi->cmb_cfg.lvds_ln = LVDS_10LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_8_LANE)) {
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_4_LANE)) {
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_SUBLVDS_2_LANE)) {
mipi->cmb_cfg.lane_num = 2;
mipi->cmb_cfg.lvds_ln = LVDS_2LANE;
} else {
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
}
} else {
memcpy(mipi->if_name, "combo_parallel", sizeof("combo_parallel"));
mipi->if_type = V4L2_MBUS_PARALLEL;
}
mipi->csi2_cfg.total_rx_ch = 0;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_0)) {
mipi->csi2_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_1)) {
mipi->csi2_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_2)) {
mipi->csi2_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_3)) {
mipi->csi2_cfg.total_rx_ch++;
}
#else
if (cfg->type == V4L2_MBUS_CSI2) {
mipi->if_type = V4L2_MBUS_CSI2;
memcpy(mipi->if_name, "mipi", sizeof("mipi"));
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_4_LANE)) {
mipi->cmb_csi_cfg.phy_lane_cfg.phy_lanedt_en = DT_4LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpdt_en = LPDT_4LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_deskew_en = DK_4LANE;
mipi->cmb_csi_cfg.lane_num = 4;
#if defined CONFIG_ARCH_SUN20IW3
mipi->cmb_csi_cfg.phy_link_mode = ONE_4LANE;
if (mipi->id)
vin_err("PORT1 supports a maximum of 2lane!\n");
#endif
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_3_LANE)) {
mipi->cmb_csi_cfg.phy_lane_cfg.phy_lanedt_en = DT_3LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpdt_en = LPDT_3LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_deskew_en = DK_3LANE;
mipi->cmb_csi_cfg.lane_num = 3;
#if defined CONFIG_ARCH_SUN20IW3
mipi->cmb_csi_cfg.phy_link_mode = ONE_4LANE;
if (mipi->id)
vin_err("PORT1 supports a maximum of 2lane!\n");
#endif
} else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_2_LANE)) {
mipi->cmb_csi_cfg.phy_lane_cfg.phy_lanedt_en = DT_2LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpdt_en = LPDT_2LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_deskew_en = DK_2LANE;
mipi->cmb_csi_cfg.lane_num = 2;
} else {
mipi->cmb_csi_cfg.phy_lane_cfg.phy_lanedt_en = DT_1LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_mipi_lpdt_en = LPDT_1LANE;
mipi->cmb_csi_cfg.phy_lane_cfg.phy_deskew_en = DK_1LANE;
mipi->cmb_csi_cfg.lane_num = 1;
}
}
mipi->cmb_csi_cfg.total_rx_ch = 0;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_0)) {
mipi->cmb_csi_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_1)) {
mipi->cmb_csi_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_2)) {
mipi->cmb_csi_cfg.total_rx_ch++;
}
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_3)) {
mipi->cmb_csi_cfg.total_rx_ch++;
}
#endif
memcpy(mipi->res, res, sizeof(struct mbus_framefmt_res));
return 0;
}
int mipi_probe(unsigned int id)
{
struct mipi_dev *mipi = NULL;
int ret = 0;
if (mipi_probe_flag[id])
return 0;
else
mipi_probe_flag[id] = true;
if (id > VIN_MAX_MIPI) {
vin_err("mipi%d is not existing, max is %d\n", id, VIN_MAX_MIPI);
return -1;
}
mipi = hal_malloc(sizeof(struct mipi_dev));
if (!mipi) {
vin_err("Fail to init MIPI dev!\n");
ret = -1;
goto ekzalloc;
}
memset(mipi, 0, sizeof(struct mipi_dev));
mipi->id = id;
mipi->res = hal_malloc(sizeof(struct mbus_framefmt_res));
if (!mipi->res) {
vin_err("Fail to init MIPI res!\n");
ret = -1;
goto freedev;
}
memset(mipi->res, 0, sizeof(struct mbus_framefmt_res));
mipi->base = sunxi_vin_get_mipi_base();
cmb_csi_set_top_base_addr((unsigned long)mipi->base);
vin_log(VIN_LOG_MD, "mipi%d reg is 0x%lx\n", mipi->id, mipi->base);
mipi->phy_base = sunxi_vin_get_mipiphy_base(mipi->id);
cmb_csi_set_phy_base_addr(mipi->id, (unsigned long)mipi->phy_base);
mipi->port_base = sunxi_vin_get_mipiport_base(mipi->id);
cmb_csi_set_port_base_addr(mipi->id, (unsigned long)mipi->port_base);
global_mipi[id] = mipi;
vin_log(VIN_LOG_MIPI, "mipi%d probe end!\n", mipi->id);
return 0;
freedev:
hal_free(mipi);
ekzalloc:
vin_err("mipi probe err!\n");
return ret;
}
int mipi_remove(unsigned int id)
{
struct mipi_dev *mipi = global_mipi[id];
if (!mipi_probe_flag[id])
return 0;
else
mipi_probe_flag[id] = false;
hal_free(mipi->res);
hal_free(mipi);
return 0;
}