diff --git a/Makefile b/Makefile index fc8356197b56..5abd564e6f02 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ obj-y += drivers/media/pci/intel/ export CONFIG_VIDEO_AR0234 = m export CONFIG_VIDEO_LT6911UXC = m +export CONFIG_I2C_IOEXPANDER_SER_MAX9295 = m +export CONFIG_I2C_IOEXPANDER_DESER_MAX9296 = m + obj-y += drivers/media/i2c/ KERNEL_SRC := /lib/modules/$(shell uname -r)/build diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e84dde2c125d..e3dd28e46013 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -1,6 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2021 Intel Corporation. +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +ccflags-y += -I$(srcpath)/$(src)/../../../include/ +ccflags-y += -DCONFIG_VIDEO_D4XX_SERDES + obj-$(CONFIG_VIDEO_AR0234) += ar0234.o obj-$(CONFIG_VIDEO_LT6911UXC) += lt6911uxc.o -obj-$(CONFIG_VIDEO_D4XX) += d457.o +obj-$(CONFIG_I2C_IOEXPANDER_SER_MAX9295) += max9295.o +obj-$(CONFIG_I2C_IOEXPANDER_DESER_MAX9296) += max9296.o +obj-$(CONFIG_VIDEO_D4XX) += d4xx.o diff --git a/drivers/media/i2c/d4xx.c b/drivers/media/i2c/d4xx.c index 2c8c6e36595d..f3aa90b35ea0 100644 --- a/drivers/media/i2c/d4xx.c +++ b/drivers/media/i2c/d4xx.c @@ -39,6 +39,16 @@ #include #include #include +#ifdef CONFIG_VIDEO_D4XX_SERDES +#include +#include +#else +#include +#define GMSL_CSI_DT_YUV422_8 0x1E +#define GMSL_CSI_DT_RGB_888 0x24 +#define GMSL_CSI_DT_RAW_8 0x2A +#define GMSL_CSI_DT_EMBED 0x12 +#endif //#define DS5_DRIVER_NAME "DS5 RealSense camera driver" #define DS5_DRIVER_NAME "d4xx" @@ -328,6 +338,7 @@ struct ds5_ctrls { struct v4l2_ctrl_handler handler_depth; struct v4l2_ctrl_handler handler_rgb; struct v4l2_ctrl_handler handler_y8; + struct v4l2_ctrl_handler handler_imu; struct { struct v4l2_ctrl *log; struct v4l2_ctrl *fw_version; @@ -383,6 +394,7 @@ struct ds5_sensor { /*struct ds5_vchan *vchan;*/ const struct ds5_format *formats; unsigned int n_formats; + int pipe_id; }; struct ds5_des { @@ -456,8 +468,10 @@ struct ds5 { /* All below pointers are used for writing, cannot be const */ struct mutex lock; struct regmap *regmap; +#ifndef CONFIG_VIDEO_D4XX_SERDES struct regmap *regmap_max9296; struct regmap *regmap_max9295; +#endif struct regulator *vcc; const struct ds5_variant *variant; int is_depth; @@ -466,6 +480,13 @@ struct ds5 { int is_imu; u16 fw_version; u16 fw_build; +#ifdef CONFIG_VIDEO_D4XX_SERDES + struct gmsl_link_ctx g_ctx; + struct device *ser_dev; + struct device *dser_dev; + struct i2c_client *ser_i2c; + struct i2c_client *dser_i2c; +#endif }; struct ds5_counters { @@ -482,7 +503,7 @@ static inline void msleep_range(unsigned int delay_base) { usleep_range(delay_base * 1000, delay_base * 1000 + 500); } - +#ifndef CONFIG_VIDEO_D4XX_SERDES static int max9296_write_8(struct ds5 *state, u16 reg, u8 val) { int ret; @@ -529,6 +550,7 @@ static int max9295_write_8(struct ds5 *state, u16 reg, u8 val) return ret; } +#endif static int ds5_write_8(struct ds5 *state, u16 reg, u8 val) { @@ -540,7 +562,7 @@ static int ds5_write_8(struct ds5 *state, u16 reg, u8 val) __func__, ret, reg, val); else if (state->dfu_dev.dfu_state_flag == DS5_DFU_IDLE) - dev_info(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", + dev_dbg(&state->client->dev, "%s(): i2c write 0x%04x: 0x%x\n", __func__, reg, val); return ret; @@ -674,8 +696,6 @@ static u8 d4xx_set_sub_stream[] = { 0, 0, 0, 0, 0, 0 }; -static int ds5_mux_s_stream_vc(struct ds5 *state, u16 vc_id, u16 on); - /* Pad ops */ static const u16 ds5_default_framerate = 30; @@ -901,17 +921,17 @@ static const struct ds5_resolution ds5_size_imu[] = { static const struct ds5_format ds5_depth_formats_d43x[] = { { // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* Z16 */ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), .resolutions = d43x_depth_sizes, }, { - .data_type = 0x2a, /* Y8 */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(d43x_depth_sizes), .resolutions = d43x_depth_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), .resolutions = d43x_calibration_sizes, @@ -921,18 +941,18 @@ static const struct ds5_format ds5_depth_formats_d43x[] = { static const struct ds5_format ds5_depth_formats_d46x[] = { { // TODO: 0x31 is replaced with 0x1e since it caused low FPS in Jetson. - .data_type = 0x1e, /* Z16 */ + .data_type = GMSL_CSI_DT_YUV422_8, /* Z16 */ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), .resolutions = d46x_depth_sizes, }, { /* First format: default */ - .data_type = 0x2a, /* Y8 */ + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(d46x_depth_sizes), .resolutions = d46x_depth_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ .n_resolutions = ARRAY_SIZE(d46x_calibration_sizes), .resolutions = d46x_calibration_sizes, @@ -944,17 +964,17 @@ static const struct ds5_format ds5_depth_formats_d46x[] = { static const struct ds5_format ds5_y_formats_ds5u[] = { { /* First format: default */ - .data_type = 0x2a, /* Y8 */ - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .data_type = GMSL_CSI_DT_RAW_8, /* Y8 */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(y8_sizes), .resolutions = y8_sizes, }, { - .data_type = 0x1e, /* Y8I */ - .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .data_type = GMSL_CSI_DT_YUV422_8, /* Y8I */ + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, .n_resolutions = ARRAY_SIZE(y8_sizes), .resolutions = y8_sizes, }, { - .data_type = 0x24, /* 24-bit Calibration */ + .data_type = GMSL_CSI_DT_RGB_888, /* 24-bit Calibration */ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, /* FIXME */ .n_resolutions = ARRAY_SIZE(d43x_calibration_sizes), .resolutions = d43x_calibration_sizes, @@ -962,7 +982,7 @@ static const struct ds5_format ds5_y_formats_ds5u[] = { }; static const struct ds5_format ds5_rlt_rgb_format = { - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .n_resolutions = ARRAY_SIZE(ds5_rlt_rgb_sizes), .resolutions = ds5_rlt_rgb_sizes, @@ -970,7 +990,7 @@ static const struct ds5_format ds5_rlt_rgb_format = { #define DS5_RLT_RGB_N_FORMATS 1 static const struct ds5_format ds5_onsemi_rgb_format = { - .data_type = 0x1e, /* UYVY */ + .data_type = GMSL_CSI_DT_YUV422_8, /* UYVY */ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .n_resolutions = ARRAY_SIZE(ds5_onsemi_rgb_sizes), .resolutions = ds5_onsemi_rgb_sizes, @@ -987,7 +1007,7 @@ static const struct ds5_variant ds5_variants[] = { static const struct ds5_format ds5_imu_formats[] = { { /* First format: default */ - .data_type = 0x2a, /* IMU DT */ + .data_type = GMSL_CSI_DT_RAW_8, /* IMU DT */ .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .n_resolutions = ARRAY_SIZE(ds5_size_imu), .resolutions = ds5_size_imu, @@ -1046,7 +1066,7 @@ static void ds5_sensor_format_init(struct ds5_sensor *sensor) if (sensor->config.format) return; - dev_info(sensor->sd.dev, "%s(): on pad %u\n", __func__, sensor->mux_pad); + dev_dbg(sensor->sd.dev, "%s(): on pad %u\n", __func__, sensor->mux_pad); ffmt = &sensor->format; *ffmt = ds5_mbus_framefmt_template; @@ -1080,7 +1100,7 @@ static int ds5_sensor_enum_mbus_code(struct v4l2_subdev *sd, { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); //struct ds5_vchan *vchan = sensor->vchan; - dev_info(sensor->sd.dev, "%s(): sensor %s pad: %d index: %d\n", + dev_dbg(sensor->sd.dev, "%s(): sensor %s pad: %d index: %d\n", __func__, sensor->sd.name, mce->pad, mce->index); if (mce->pad) return -EINVAL; @@ -1106,7 +1126,7 @@ static int ds5_sensor_enum_frame_size(struct v4l2_subdev *sd, const struct ds5_format *fmt; unsigned int i; - dev_info(sensor->sd.dev, "%s(): sensor %s is %s\n", + dev_dbg(sensor->sd.dev, "%s(): sensor %s is %s\n", __func__, sensor->sd.name, ds5_get_sensor_name(state)); for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) @@ -1138,9 +1158,6 @@ static int ds5_sensor_enum_frame_interval(struct v4l2_subdev *sd, const struct ds5_resolution *res; unsigned int i; - //if (fie->pad) - // return -EINVAL; - for (i = 0, fmt = sensor->formats; i < sensor->n_formats; i++, fmt++) if (fie->code == fmt->mbus_code) break; @@ -1175,8 +1192,6 @@ static int ds5_sensor_get_fmt(struct v4l2_subdev *sd, struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); struct ds5 *state = v4l2_get_subdevdata(sd); - //fmt->pad = sensor->mux_pad; - if (fmt->pad) return -EINVAL; @@ -1381,15 +1396,40 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, #endif } -#if 0 /* function not called */ +#ifdef CONFIG_VIDEO_D4XX_SERDES +static int ds5_setup_pipeline(struct ds5 *state, u8 data_type1, u8 data_type2, + int pipe_id, u32 vc_id) +{ + int ret = 0; +dev_info(&state->client->dev, + "set pipe %d, data_type1: 0x%x, \ + data_type2: 0x%x, vc_id: %u\n", + pipe_id, data_type1, data_type2, vc_id); + ret |= max9295_set_pipe(state->ser_dev, pipe_id, + data_type1, data_type2, vc_id); + ret |= max9296_set_pipe(state->dser_dev, pipe_id, + data_type1, data_type2, vc_id); + if (ret) + dev_warn(&state->client->dev, + "failed to set pipe %d, data_type1: 0x%x, \ + data_type2: 0x%x, vc_id: %u\n", + pipe_id, data_type1, data_type2, vc_id); + + return ret; +} +#endif + static int ds5_configure(struct ds5 *state) { struct ds5_sensor *sensor; u16 fmt, md_fmt, vc_id; +#ifdef CONFIG_VIDEO_D4XX_SERDES + u16 data_type1, data_type2; +#endif u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; int ret; - // if (state->is_depth) { + if (state->is_depth) { sensor = &state->depth.sensor; dt_addr = DS5_DEPTH_STREAM_DT; md_addr = DS5_DEPTH_STREAM_MD; @@ -1397,44 +1437,9 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_DEPTH_FPS; width_addr = DS5_DEPTH_RES_WIDTH; height_addr = DS5_DEPTH_RES_HEIGHT; - // TODO: read VC from device tree + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 0; - md_fmt = 0x12; - - fmt = sensor->streaming ? sensor->config.format->data_type : 0; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - if (fmt != 0) - ret = ds5_write(state, dt_addr, 0x31); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - // } else if (state->is_rgb) { + } else if (state->is_rgb) { sensor = &state->rgb.sensor; dt_addr = DS5_RGB_STREAM_DT; md_addr = DS5_RGB_STREAM_MD; @@ -1442,42 +1447,9 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_RGB_FPS; width_addr = DS5_RGB_RES_WIDTH; height_addr = DS5_RGB_RES_HEIGHT; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 1; - md_fmt = 0x12; - - fmt = sensor->streaming ? sensor->config.format->data_type : 0; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - ret = ds5_write(state, dt_addr, fmt); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - // } else if (state->is_y8) { + } else if (state->is_y8) { sensor = &state->motion_t.sensor; dt_addr = DS5_IR_STREAM_DT; md_addr = DS5_IR_STREAM_MD; @@ -1485,44 +1457,9 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_IR_FPS; width_addr = DS5_IR_RES_WIDTH; height_addr = DS5_IR_RES_HEIGHT; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 2; - md_fmt = 0x12; - - fmt = sensor->streaming ? sensor->config.format->data_type : 0; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - if (fmt != 0 && - sensor->config.format->data_type == 0x1e) - ret = ds5_write(state, dt_addr, 0x32); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - // } else if (state->is_imu) { + } else if (state->is_imu) { sensor = &state->imu.sensor; dt_addr = DS5_IMU_STREAM_DT; md_addr = DS5_IMU_STREAM_MD; @@ -1530,231 +1467,40 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_IMU_FPS; width_addr = DS5_IMU_RES_WIDTH; height_addr = DS5_IMU_RES_HEIGHT; - vc_id = 3; md_fmt = 0x0; - - fmt = sensor->streaming ? sensor->config.format->data_type : 0; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - ret = ds5_write(state, dt_addr, fmt); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; + vc_id = 3; + } else { + return -EINVAL; } - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); +#ifdef CONFIG_VIDEO_D4XX_SERDES + data_type1 = sensor->config.format->data_type; + data_type2 = state->is_y8 ? 0x00 : md_fmt; + // usable for multiple sensors on one des, need to configure vc in pdata + // vc_id = state->g_ctx.dst_vc; + + ret = ds5_setup_pipeline(state, data_type1, data_type2, sensor->pipe_id, + vc_id); + // reset data path when switching to Y12I + if (state->is_y8 && data_type1 == GMSL_CSI_DT_RGB_888) + max9296_reset_oneshot(state->dser_dev); if (ret < 0) return ret; - - return 0; -} #endif -static int ds5_configure_depth(struct ds5 *state) -{ - struct ds5_sensor *sensor; - u16 fmt, md_fmt, vc_id; - u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; - int ret = -1; - - // if (state->is_depth) { - sensor = &state->depth.sensor; - dt_addr = DS5_DEPTH_STREAM_DT; - md_addr = DS5_DEPTH_STREAM_MD; - override_addr = DS5_DEPTH_OVERRIDE; - fps_addr = DS5_DEPTH_FPS; - width_addr = DS5_DEPTH_RES_WIDTH; - height_addr = DS5_DEPTH_RES_HEIGHT; - // TODO: read VC from device tree - vc_id = 0; - md_fmt = 0x12; - - fmt = 0x1e; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - //if (fmt != 0) - ret = ds5_write(state, dt_addr, 0x31); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - return 0; -} -static int ds5_configure_rgb(struct ds5 *state) -{ - struct ds5_sensor *sensor; - u16 fmt, md_fmt, vc_id; - u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; - int ret = -1; - - // } else if (state->is_rgb) { - sensor = &state->rgb.sensor; - dt_addr = DS5_RGB_STREAM_DT; - md_addr = DS5_RGB_STREAM_MD; - override_addr = 0; - fps_addr = DS5_RGB_FPS; - width_addr = DS5_RGB_RES_WIDTH; - height_addr = DS5_RGB_RES_HEIGHT; - vc_id = 1; - md_fmt = 0x12; - - fmt = sensor->streaming ? sensor->config.format->data_type : 0; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - dev_info(NULL, "%s, fmt 0x%08x, data_type 0x%08x\n", __func__, fmt, sensor->config.format->data_type); -// ret = ds5_write(state, dt_addr, fmt); - ret = ds5_write(state, dt_addr, 0x1e); - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - //ret = ds5_write(state, width_addr, 480); - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - //ret = ds5_write(state, height_addr, 270); - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - return 0; -} - -static int ds5_configure_ir(struct ds5 *state) -{ - struct ds5_sensor *sensor; - u16 fmt, md_fmt, vc_id; - u16 dt_addr, md_addr, override_addr, fps_addr, width_addr, height_addr; - int ret = -1; - - // } else if (state->is_y8) { - sensor = &state->motion_t.sensor; - dt_addr = DS5_IR_STREAM_DT; - md_addr = DS5_IR_STREAM_MD; - override_addr = DS5_IR_OVERRIDE; - fps_addr = DS5_IR_FPS; - width_addr = DS5_IR_RES_WIDTH; - height_addr = DS5_IR_RES_HEIGHT; - vc_id = 2; - md_fmt = 0x12; - - fmt = sensor->config.format->data_type; - - /* - * Set depth stream Z16 data type as 0x31 - * Set IR stream Y8I data type as 0x32 - */ - if (sensor->config.format->data_type == 0x1e) - ret = ds5_write(state, dt_addr, 0x32); - else if (sensor->config.format->data_type == 0x2a) - ret = ds5_write(state, dt_addr, 0x2a); - - if (ret < 0) - return ret; - - ret = ds5_write(state, md_addr, (vc_id << 8) | md_fmt); - if (ret < 0) - return ret; - - if (override_addr != 0) { - ret = ds5_write(state, override_addr, fmt); - if (ret < 0) - return ret; - } - - ret = ds5_write(state, fps_addr, sensor->config.framerate); - if (ret < 0) - return ret; - - ret = ds5_write(state, width_addr, sensor->config.resolution->width); - if (ret < 0) - return ret; - - ret = ds5_write(state, height_addr, sensor->config.resolution->height); - if (ret < 0) - return ret; - - return 0; -} - -static int ds5_configure_imu(struct ds5 *state) -{ - struct ds5_sensor *sensor; - u16 fmt, md_fmt, vc_id; - u16 dt_addr, md_addr, fps_addr, width_addr, height_addr; - int ret = -1; + fmt = sensor->streaming ? sensor->config.format->data_type : 0; - sensor = &state->imu.sensor; - dt_addr = DS5_IMU_STREAM_DT; - md_addr = DS5_IMU_STREAM_MD; - fps_addr = DS5_IMU_FPS; - width_addr = DS5_IMU_RES_WIDTH; - height_addr = DS5_IMU_RES_HEIGHT; - vc_id = 3; - md_fmt = 0; - - fmt = sensor->config.format->data_type; - ret = ds5_write(state, dt_addr, fmt); + /* + * Set depth stream Z16 data type as 0x31 + * Set IR stream Y8I data type as 0x32 + */ + if (state->is_depth && fmt != 0) + ret = ds5_write(state, dt_addr, 0x31); + else if (state->is_y8 && fmt != 0 && + sensor->config.format->data_type == GMSL_CSI_DT_YUV422_8) + ret = ds5_write(state, dt_addr, 0x32); + else + ret = ds5_write(state, dt_addr, fmt); if (ret < 0) return ret; @@ -1762,6 +1508,15 @@ static int ds5_configure_imu(struct ds5 *state) if (ret < 0) return ret; + if (!sensor->streaming) + return ret; + + if (override_addr != 0) { + ret = ds5_write(state, override_addr, fmt); + if (ret < 0) + return ret; + } + ret = ds5_write(state, fps_addr, sensor->config.framerate); if (ret < 0) return ret; @@ -1796,7 +1551,7 @@ static int ds5_sensor_g_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = sensor->config.framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, fi->interval.denominator); return 0; @@ -1921,7 +1676,7 @@ static int ds5_hw_set_auto_exposure(struct ds5 *state, u32 base, s32 val) */ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) { - int ret = -1; + int ret; if (val < 1) val = 1; @@ -1935,8 +1690,6 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) * Color: 1 is 100 us * Depth: 1 is 1 us */ -// if (!state->is_rgb) -// val *= 100; ret = ds5_write(state, base | DS5_MANUAL_EXPOSURE_MSB, (u16)(val >> 16)); if (!ret) @@ -1977,11 +1730,24 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val) */ #define DS5_CAMERA_CID_HWMC_RW (DS5_CAMERA_CID_BASE+32) -static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, - bool isRead, u16 *dataLen) +#define DS5_HWMC_DATA 0x4900 +#define DS5_HWMC_STATUS 0x4904 +#define DS5_HWMC_RESP_LEN 0x4908 +#define DS5_HWMC_EXEC 0x490C + +#define DS5_HWMC_STATUS_OK 0 +#define DS5_HWMC_STATUS_ERR 1 +#define DS5_HWMC_STATUS_WIP 2 +#define DS5_HWMC_BUFFER_SIZE 1024 + +static int ds5_send_hwmc(struct ds5 *state, + u16 cmdLen, + struct hwm_cmd *cmd, + bool isRead, + u16 *dataLen) { int ret = 0; - u16 status = 2; + u16 status = DS5_HWMC_STATUS_WIP; int retries = 100; int errorCode; int iter = retries; @@ -1991,7 +1757,14 @@ static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, "param1: %d, param2: %d, param3: %d, param4: %d\n", __func__, cmd->header, cmd->magic_word, cmd->opcode, cmd->param1, cmd->param2, cmd->param3, cmd->param4); - + if(cmd->opcode == 0x7d) { + dev_warn(&state->client->dev, + "%s(): SKIP ISSUE HWMC header: 0x%x, magic: 0x%x, opcode: 0x%x, " + "param1: %d, param2: %d, param3: %d, param4: %d\n", + __func__, cmd->header, cmd->magic_word, cmd->opcode, + cmd->param1, cmd->param2, cmd->param3, cmd->param4); + return 0; + } ds5_raw_write_with_check(state, 0x4900, cmd, cmdLen); ds5_write_with_check(state, 0x490C, 0x01); /* execute cmd */ @@ -2006,7 +1779,12 @@ static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, dev_err(&state->client->dev, "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", __func__, ret, status, errorCode); - ret = -EAGAIN; + dev_warn(&state->client->dev, + "%s(): HWMC header: 0x%x, magic: 0x%x, opcode: 0x%x, " + "param1: %d, param2: %d, param3: %d, param4: %d\n", + __func__, cmd->header, cmd->magic_word, cmd->opcode, + cmd->param1, cmd->param2, cmd->param3, cmd->param4); + return -EAGAIN; } if (isRead) { @@ -2029,16 +1807,6 @@ static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, return 0; } -#define DS5_HWMC_DATA 0x4900 -#define DS5_HWMC_STATUS 0x4904 -#define DS5_HWMC_RESP_LEN 0x4908 -#define DS5_HWMC_EXEC 0x490C - -#define DS5_HWMC_STATUS_OK 0 -#define DS5_HWMC_STATUS_ERR 1 -#define DS5_HWMC_STATUS_WIP 2 -#define DS5_HWMC_BUFFER_SIZE 1024 - static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) { int ret = 0; @@ -2099,7 +1867,7 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) static int ds5_set_calibration_data(struct ds5 *state, struct hwm_cmd *cmd, u16 length) { - int ret = -1; + int ret; int retries = 10; u16 status = 2; @@ -2122,11 +1890,13 @@ static int ds5_set_calibration_data(struct ds5 *state, return -EINVAL; } +static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on); + static int ds5_s_state(struct ds5 *state, int vc) { int ret = 0; - dev_dbg(&state->client->dev, "%s(): set state for vc: %d\n", __func__, vc); + dev_warn(&state->client->dev, "%s(): set state for vc: %d\n", __func__, vc); switch (vc) { case 0: @@ -2158,6 +1928,7 @@ static int ds5_s_state(struct ds5 *state, int vc) ret = -EINVAL; break; } + ds5_set_state_last_set(state); return ret; } @@ -2196,6 +1967,13 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) state->is_y8 = 1; state->is_imu = 0; break; + case DS5_MUX_PAD_IMU_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); + state->is_rgb = 0; + state->is_depth = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; default: state->is_rgb = 0; state->is_depth = 0; @@ -2208,8 +1986,6 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) if (state->is_rgb) base = DS5_RGB_CONTROL_BASE; - else if (state->is_imu) - return ret; v4l2_dbg(3, 1, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); dev_dbg(&state->client->dev, "%s(): %s - ctrl: %s, value: %d\n", @@ -2466,17 +2242,19 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x\n", val); vc_id = (val >> 8) & 0x00FF; on = val & 0x00FF; - if (on == 0xff) { + if (vc_id < DS5_MUX_PAD_COUNT) ret = ds5_s_state(state, vc_id); + if (on == 0xff) { break; } if (vc_id > NR_OF_DS5_STREAMS - 1) dev_err(&state->client->dev, "invalid vc %d\n", vc_id); else d4xx_set_sub_stream[vc_id] = on; - - ret = ds5_mux_s_stream_vc(state, vc_id, on); - +#ifndef CONFIG_VIDEO_D4XX_SERDES + ret = ds5_mux_s_stream(sd, on); +#endif + ret = 0; break; } @@ -2489,7 +2267,7 @@ static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, unsigned char *table, unsigned int length) { struct hwm_cmd *cmd; - int ret = -1; + int ret; int retries = 3; u16 status = 2; u16 table_length; @@ -2535,7 +2313,7 @@ static int ds5_get_calibration_data(struct ds5 *state, enum table_id id, static int ds5_gvd(struct ds5 *state, unsigned char *data) { struct hwm_cmd cmd; - int ret = -1; + int ret; u16 length = 0; u16 status = 2; u8 retries = 3; @@ -2600,6 +2378,13 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) state->is_y8 = 1; state->is_imu = 0; break; + case DS5_MUX_PAD_IMU_A: + state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); + state->is_rgb = 0; + state->is_depth = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; default: state->is_rgb = 0; state->is_depth = 0; @@ -2778,6 +2563,7 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d vc:[%d]\n", __func__, sensor->mux_pad, vc_id); *ctrl->p_new.p_s32 = pad_to_substream[sensor->mux_pad]; + state->mux.last_set = sensor; } else { /* we are in DS5 MUX case */ *ctrl->p_new.p_s32 = -1; @@ -3050,7 +2836,7 @@ static int ds5_mux_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ds5 *state = v4l2_get_subdevdata(sd); - dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); if (state->dfu_dev.dfu_state_flag) return -EBUSY; state->dfu_dev.device_open_count++; @@ -3062,7 +2848,7 @@ static int ds5_mux_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ds5 *state = v4l2_get_subdevdata(sd); - dev_info(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); + dev_dbg(sd->dev, "%s(): %s (%p)\n", __func__, sd->name, fh); state->dfu_dev.device_open_count--; return 0; }; @@ -3071,6 +2857,473 @@ static const struct v4l2_subdev_internal_ops ds5_sensor_internal_ops = { .open = ds5_mux_open, .close = ds5_mux_close, }; +//#define CONFIG_VIDEO_D4XX_SERDES 1 +#ifdef CONFIG_VIDEO_D4XX_SERDES + +/* + * FIXME + * temporary solution before changing GMSL data structure or merging all 4 D457 + * sensors into one i2c device. Only first sensor node per max9295 sets up the + * link. + * + * max 24 number from this link: + * https://docs.nvidia.com/jetson/archives/r35.1/DeveloperGuide/text/ + * SD/CameraDevelopment/JetsonVirtualChannelWithGmslCameraFramework.html + * #jetson-agx-xavier-series + */ +#define MAX_DEV_NUM 24 +static struct ds5 *serdes_inited[MAX_DEV_NUM]; +#ifdef CONFIG_OF +static int ds5_board_setup(struct ds5 *state) +{ + struct device *dev = &state->client->dev; + struct d4xx_pdata *pdata = dev->platform_data; + struct device_node *node = dev->of_node; + struct device_node *ser_node; + struct i2c_client *ser_i2c = NULL; + struct device_node *dser_node; + struct i2c_client *dser_i2c = NULL; + struct device_node *gmsl; + int value = 0xFFFF; + const char *str_value; + int err; + int i; + + err = of_property_read_u32(node, "reg", &state->g_ctx.sdev_reg); //12 + if (err < 0) { + dev_err(dev, "reg not found\n"); + goto error; + } + + err = of_property_read_u32(node, "def-addr", + &state->g_ctx.sdev_def);//10 + if (err < 0) { + dev_err(dev, "def-addr not found\n"); + goto error; + } + + ser_node = of_parse_phandle(node, "maxim,gmsl-ser-device", 0); + if (ser_node == NULL) { + dev_err(dev, "missing %s handle\n", "maxim,gmsl-ser-device"); + goto error; + } + + err = of_property_read_u32(ser_node, "reg", &state->g_ctx.ser_reg); + dev_info(dev, "serializer reg: 0x%x\n", state->g_ctx.ser_reg); + if (err < 0) { + dev_err(dev, "serializer reg not found\n"); + goto error; + } + + ser_i2c = of_find_i2c_device_by_node(ser_node); + of_node_put(ser_node); + + if (ser_i2c == NULL) { + err = -EPROBE_DEFER; + goto error; + } + if (ser_i2c->dev.driver == NULL) { + dev_err(dev, "missing serializer driver\n"); + goto error; + } + + state->ser_dev = &ser_i2c->dev; + + dser_node = of_parse_phandle(node, "maxim,gmsl-dser-device", 0); + if (dser_node == NULL) { + dev_err(dev, "missing %s handle\n", "maxim,gmsl-dser-device"); + goto error; + } + + dser_i2c = of_find_i2c_device_by_node(dser_node); + of_node_put(dser_node); + + if (dser_i2c == NULL) { + err = -EPROBE_DEFER; + goto error; + } + if (dser_i2c->dev.driver == NULL) { + dev_err(dev, "missing deserializer driver\n"); + goto error; + } + + state->dser_dev = &dser_i2c->dev; + + /* populate g_ctx from DT */ + gmsl = of_get_child_by_name(node, "gmsl-link"); + if (gmsl == NULL) { + dev_err(dev, "missing gmsl-link device node\n"); + err = -EINVAL; + goto error; + } + + err = of_property_read_string(gmsl, "dst-csi-port", &str_value); + if (err < 0) { + dev_err(dev, "No dst-csi-port found\n"); + goto error; + } + state->g_ctx.dst_csi_port = + (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B; + + err = of_property_read_string(gmsl, "src-csi-port", &str_value); + if (err < 0) { + dev_err(dev, "No src-csi-port found\n"); + goto error; + } + state->g_ctx.src_csi_port = + (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B; + + err = of_property_read_string(gmsl, "csi-mode", &str_value); + if (err < 0) { + dev_err(dev, "No csi-mode found\n"); + goto error; + } + + if (!strcmp(str_value, "1x4")) { + state->g_ctx.csi_mode = GMSL_CSI_1X4_MODE; + } else if (!strcmp(str_value, "2x4")) { + state->g_ctx.csi_mode = GMSL_CSI_2X4_MODE; + } else if (!strcmp(str_value, "4x2")) { + state->g_ctx.csi_mode = GMSL_CSI_4X2_MODE; + } else if (!strcmp(str_value, "2x2")) { + state->g_ctx.csi_mode = GMSL_CSI_2X2_MODE; + } else { + dev_err(dev, "invalid csi mode\n"); + goto error; + } + + err = of_property_read_string(gmsl, "serdes-csi-link", &str_value); + if (err < 0) { + dev_err(dev, "No serdes-csi-link found\n"); + goto error; + } + state->g_ctx.serdes_csi_link = + (!strcmp(str_value, "a")) ? + GMSL_SERDES_CSI_LINK_A : GMSL_SERDES_CSI_LINK_B; + + err = of_property_read_u32(gmsl, "st-vc", &value); + if (err < 0) { + dev_err(dev, "No st-vc info\n"); + goto error; + } + state->g_ctx.st_vc = value; + + err = of_property_read_u32(gmsl, "vc-id", &value); + if (err < 0) { + dev_err(dev, "No vc-id info\n"); + goto error; + } + state->g_ctx.dst_vc = value; + + err = of_property_read_u32(gmsl, "num-lanes", &value); + if (err < 0) { + dev_err(dev, "No num-lanes info\n"); + goto error; + } + state->g_ctx.num_csi_lanes = value; + state->g_ctx.s_dev = dev; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (!serdes_inited[i]) { + serdes_inited[i] = state; + return 0; + } else if (serdes_inited[i]->ser_dev == state->ser_dev) { + return -ENOTSUPP; + } + } + err = -EINVAL; + dev_err(dev, "cannot handle more than %d D457 cameras\n", MAX_DEV_NUM); + +error: + return err; +} +#else +// ds5mux i2c ser des +// mux a - 2 0x42 0x48 +// mux b - 2 0x44 0x4a +// mux c - 4 0x42 0x48 +// mux d - 4 0x44 0x4a +// axiomtek +// mux a - 2 0x42 0x48 +// mux b - 2 0x44 0x4a +// mux c - 4 0x62 0x68 +// mux d - 4 0x64 0x6a + +static int ds5_board_setup(struct ds5 *state) +{ + struct device *dev = &state->client->dev; + struct d4xx_pdata *pdata = dev->platform_data; + struct i2c_adapter *adapter = state->client->adapter; + int bus = adapter->nr; + int err = 0; + int i; + + static struct max9295_pdata max9295_pdata = { + .is_prim_ser = 1, // todo: configurable + .def_addr = 0x40, // todo: configurable + }; + static struct i2c_board_info i2c_info_des = { + I2C_BOARD_INFO("max9296", 0x48), + }; + static struct i2c_board_info i2c_info_ser = { + I2C_BOARD_INFO("max9295", 0x42), + .platform_data = &max9295_pdata, + }; + + dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", + pdata->suffix, + bus, pdata->subdev_info[0].board_info.addr, //48 + bus, pdata->subdev_info[0].ser_alias); //42 + i2c_info_ser.addr = pdata->subdev_info[0].ser_alias; //0x42, 0x44, 0x62, 0x64 + state->ser_i2c = i2c_new_client_device(adapter, &i2c_info_ser); + i2c_info_des.addr = pdata->subdev_info[0].board_info.addr; //0x48, 0x4a, 0x68, 0x6a + state->dser_i2c = i2c_new_client_device(adapter, &i2c_info_des); + + if (state->ser_i2c == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing serializer client\n"); + goto error; + } + if (state->ser_i2c->dev.driver == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing serializer driver\n"); + goto error; + } + if (state->dser_i2c == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing deserializer client\n"); + goto error; + } + if (state->dser_i2c->dev.driver == NULL) { + err = -EPROBE_DEFER; + dev_err(dev, "missing deserializer driver\n"); + goto error; + } + + // reg + + state->g_ctx.sdev_reg = state->client->addr; + state->g_ctx.sdev_def = 0x10;// def-addr TODO: configurable + // Address reassignment for d4xx-a 0x10->0x12 + dev_info(dev, "Address reassignment for %s-%c 0x%x->0x%x\n", + pdata->subdev_info[0].board_info.type, pdata->suffix, + state->g_ctx.sdev_def, state->g_ctx.sdev_reg); + //0x42, 0x44, 0x62, 0x64 + state->g_ctx.ser_reg = pdata->subdev_info[0].ser_alias; + dev_info(dev, "serializer: i2c-%d@0x%x\n", + state->ser_i2c->adapter->nr, state->g_ctx.ser_reg); + + if (err < 0) { + dev_err(dev, "serializer reg not found\n"); + goto error; + } + + state->ser_dev = &state->ser_i2c->dev; + + dev_info(dev, "deserializer: i2c-%d@0x%x\n", + state->dser_i2c->adapter->nr, state->dser_i2c->addr); + + + state->dser_dev = &state->dser_i2c->dev; + + /* populate g_ctx from pdata */ + state->g_ctx.dst_csi_port = GMSL_CSI_PORT_A; + state->g_ctx.src_csi_port = GMSL_CSI_PORT_B; + state->g_ctx.csi_mode = GMSL_CSI_2X4_MODE; + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_A; + state->g_ctx.st_vc = 0; + state->g_ctx.dst_vc = 0; + + state->g_ctx.num_csi_lanes = 2; + state->g_ctx.s_dev = dev; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (!serdes_inited[i]) { + serdes_inited[i] = state; + return 0; + } else if (serdes_inited[i]->ser_dev == state->ser_dev) { + return -ENOTSUPP; + } + } + err = -EINVAL; + dev_err(dev, "cannot handle more than %d D457 cameras\n", MAX_DEV_NUM); + +error: + return err; +} + +#endif +static const struct regmap_config ds5_regmap_max9296 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static const struct regmap_config ds5_regmap_max9295 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; +static struct mutex serdes_lock__; + +static int ds5_gmsl_serdes_setup(struct ds5 *state) +{ + int err = 0; + int des_err = 0; + struct device *dev; + + if (!state || !state->ser_dev || !state->dser_dev || !state->client) + return -EINVAL; + + dev = &state->client->dev; + + mutex_lock(&serdes_lock__); + + max9296_power_off(state->dser_dev); + /* For now no separate power on required for serializer device */ + max9296_power_on(state->dser_dev); + + dev_info(dev, "Setup SERDES addressing and control pipeline\n"); + /* setup serdes addressing and control pipeline */ + err = max9296_setup_link(state->dser_dev, &state->client->dev); + if (err) { + dev_err(dev, "gmsl deserializer link config failed\n"); + goto error; + } + msleep(100); + err = max9295_setup_control(state->ser_dev); + + /* proceed even if ser setup failed, to setup deser correctly */ + if (err) + dev_err(dev, "gmsl serializer setup failed\n"); + + des_err = max9296_setup_control(state->dser_dev, &state->client->dev); + if (des_err) { + dev_err(dev, "gmsl deserializer setup failed\n"); + /* overwrite err only if deser setup also failed */ + err = des_err; + } + +error: + mutex_unlock(&serdes_lock__); + return err; +} +#define PLATFORM_AXIOMTEK 1 +#ifdef PLATFORM_AXIOMTEK +static short serdes_bus[4] = {5, 5, 5, 5}; +#else +static short serdes_bus[4] = {2, 2, 4, 4}; +#endif +module_param_array(serdes_bus, ushort, NULL, 0444); +MODULE_PARM_DESC(serdes_bus, "max9295/6 deserializer i2c bus, " + "serdes_bus=muxa,muxb,muxc,muxd" + "default =2,2,4,4"); + +// Deserializer addresses can be 0x40 0x48 0x4a +#ifdef PLATFORM_AXIOMTEK +static unsigned short des_addr[4] = {0x48, 0x4a, 0x68, 0x6c}; +#else +static unsigned short des_addr[4] = {0x48, 0x4a, 0x48, 0x4a}; +#endif +module_param_array(des_addr, ushort, NULL, 0444); +MODULE_PARM_DESC(des_addr, "max9296 deserializer i2c address, " + "ser_addr=muxa,muxb,muxc,muxd" + "default =0x48,0x4a,0x48,0x4a"); + +static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) +{ + int i = 0; + int c_addr_save = c->addr; + int c_bus = c->adapter->nr; + for (i = 0; i < 4; i++) { + if (c_bus == serdes_bus[i]) { + c->addr = des_addr[i]; + dev_info(&c->dev, "Set max9296@%d-0x%x to link B\n", + c_bus, c->addr); + ds5_write_8(state, 0x1000, 0x22); // move to link B + } + } + // restore original slave address + c->addr = c_addr_save; + + return 0; +} + +static int ds5_serdes_setup(struct ds5 *state) +{ + int ret = 0; + int i = 0, c_bus = 0; + struct i2c_client *c = state->client; + int c_bus_new = c->adapter->nr; + + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { + c_bus = serdes_inited[i]->dser_i2c->adapter->nr; + if (c_bus == c->adapter->nr) { + dev_info(&c->dev, "Already configured multiple camera for bus %d\n", c_bus); + c_bus_new = 0; + break; + } + } else { + break; + } + } + + if (c_bus_new) { + dev_info(&c->dev, "Apply multiple camera i2c addr setting for bus %d\n", c_bus_new); + ret = ds5_i2c_addr_setting(c, state); + if (ret) { + dev_err(&c->dev, "failed apply i2c addr setting\n"); + return ret; + } + } + ret = ds5_board_setup(state); + if (ret) { + if (ret == -ENOTSUPP) + return 0; + dev_err(&c->dev, "board setup failed\n"); + return ret; + } + + /* Pair sensor to serializer dev */ + ret = max9295_sdev_pair(state->ser_dev, &state->g_ctx); + if (ret) { + dev_err(&c->dev, "gmsl ser pairing failed\n"); + return ret; + } + + /* Register sensor to deserializer dev */ + ret = max9296_sdev_register(state->dser_dev, &state->g_ctx); + if (ret) { + dev_err(&c->dev, "gmsl deserializer register failed\n"); + return ret; + } + + ret = ds5_gmsl_serdes_setup(state); + if (ret) { + dev_err(&c->dev, "%s gmsl serdes setup failed\n", __func__); + return ret; + } + + ret = max9295_init_settings(state->ser_dev); + if (ret) { + dev_warn(&c->dev, "%s, failed to init max9295 settings\n", + __func__); + return ret; + } + + ret = max9296_init_settings(state->dser_dev); + if (ret) { + dev_warn(&c->dev, "%s, failed to init max9296 settings\n", + __func__); + return ret; + } + + return ret; +} +#endif static int ds5_ctrl_init(struct ds5 *state, int sid) { @@ -3078,7 +3331,7 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) struct ds5_ctrls *ctrls = &state->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; struct v4l2_subdev *sd = &state->mux.sd.subdev; - int ret = -1; + int ret; struct ds5_sensor *sensor = NULL; switch (sid) { @@ -3094,13 +3347,17 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) hdl = &ctrls->handler_y8; sensor = &state->motion_t.sensor; break; + case 3: + hdl = &ctrls->handler_imu; + sensor = &state->imu.sensor; + break; default: hdl = &ctrls->handler; sensor = NULL; break; } - dev_info(NULL, "%s(), line %d sid: %d\n", __func__, __LINE__, sid); + dev_dbg(NULL, "%s(), line %d sid: %d\n", __func__, __LINE__, sid); ret = v4l2_ctrl_handler_init(hdl, DS5_N_CONTROLS); if (ret < 0) { v4l2_err(sd, "cannot init ctrl handler (%d)\n", ret); @@ -3132,21 +3389,21 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) ctrls->gain->flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; } -if (sid >= 0) { - - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_APERTURE_PRIORITY, - ~((1 << V4L2_EXPOSURE_MANUAL) | - (1 << V4L2_EXPOSURE_APERTURE_PRIORITY)), - V4L2_EXPOSURE_APERTURE_PRIORITY); - - if (ctrls->auto_exp) { - ctrls->auto_exp->flags |= - V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; - ctrls->auto_exp->priv = sensor; + if (sid >= 0 && sid < 3) { + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_APERTURE_PRIORITY, + ~((1 << V4L2_EXPOSURE_MANUAL) | + (1 << V4L2_EXPOSURE_APERTURE_PRIORITY)), + V4L2_EXPOSURE_APERTURE_PRIORITY); + + if (ctrls->auto_exp) { + ctrls->auto_exp->flags |= + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + ctrls->auto_exp->priv = sensor; + } } -} /* Exposure time: V4L2_CID_EXPOSURE_ABSOLUTE default unit: 100 us. */ if (sid == 0 || sid == 2) { ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, @@ -3167,16 +3424,16 @@ if (sid >= 0) { } ctrls->link_freq = v4l2_ctrl_new_custom(hdl, &d4xx_controls_link_freq, sensor); - dev_info(sd->dev, "%s(): %p\n", __func__, ctrls->link_freq); + if (ctrls->link_freq) - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->query_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_q_sub_stream, sensor); ctrls->query_sub_stream->flags |= - V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; ctrls->set_sub_stream = v4l2_ctrl_new_custom(hdl, &d4xx_controls_s_sub_stream, sensor); - dev_info(NULL, "%s(), line %d\n", __func__, __LINE__); + if (hdl->error) { v4l2_err(sd, "error creating controls (%d)\n", hdl->error); ret = hdl->error; @@ -3184,63 +3441,65 @@ if (sid >= 0) { return ret; } - // ret = v4l2_ctrl_handler_setup(hdl); - // if (ret < 0) { - // dev_err(&state->client->dev, - // "failed to set default values for controls\n"); - // v4l2_ctrl_handler_free(hdl); - // return ret; - // } - // Add these after v4l2_ctrl_handler_setup so they won't be set up -if (sid >= 0) { - ctrls->log = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_log, sensor); - ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); - ctrls->gvd = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_gvd, sensor); - ctrls->get_depth_calib = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_depth_calib, sensor); - ctrls->set_depth_calib = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_depth_calib, sensor); - ctrls->get_coeff_calib = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_coeff_calib, sensor); - ctrls->set_coeff_calib = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_coeff_calib, sensor); - ctrls->ae_roi_get = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_get, sensor); - ctrls->ae_roi_set = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_set, sensor); - ctrls->ae_setpoint_get = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_get, sensor); - ctrls->ae_setpoint_set = - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_set, sensor); - ctrls->erb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_erb, sensor); - ctrls->ewb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ewb, sensor); - ctrls->hwmc = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc, sensor); - v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor); -} + if (sid >= 0 && sid < 3) { + ctrls->log = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_log, sensor); + ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); + ctrls->gvd = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_gvd, sensor); + ctrls->get_depth_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_depth_calib, sensor); + ctrls->set_depth_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_depth_calib, sensor); + ctrls->get_coeff_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_get_coeff_calib, sensor); + ctrls->set_coeff_calib = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_set_coeff_calib, sensor); + ctrls->ae_roi_get = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_get, sensor); + ctrls->ae_roi_set = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_roi_set, sensor); + ctrls->ae_setpoint_get = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_get, sensor); + ctrls->ae_setpoint_set = + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ae_setpoint_set, sensor); + ctrls->erb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_erb, sensor); + ctrls->ewb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ewb, sensor); + ctrls->hwmc = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc, sensor); + v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor); + } + // DEPTH custom if (sid == 0) v4l2_ctrl_new_custom(hdl, &ds5_ctrl_pwm, sensor); + // IMU custom + if (sid == 3) + ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); switch (sid) { case 0: state->depth.sensor.sd.ctrl_handler = hdl; - dev_info(state->depth.sensor.sd.dev, + dev_dbg(state->depth.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->depth.sensor.mux_pad); break; case 1: state->rgb.sensor.sd.ctrl_handler = hdl; - dev_info(state->rgb.sensor.sd.dev, + dev_dbg(state->rgb.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->rgb.sensor.mux_pad); break; case 2: state->motion_t.sensor.sd.ctrl_handler = hdl; - dev_info(state->motion_t.sensor.sd.dev, + dev_dbg(state->motion_t.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", __func__, __LINE__, state->motion_t.sensor.mux_pad); break; + case 3: + state->imu.sensor.sd.ctrl_handler = hdl; + dev_dbg(state->imu.sensor.sd.dev, + "%s():%d set ctrl_handler pad:%d\n", + __func__, __LINE__, state->imu.sensor.mux_pad); + break; default: state->mux.sd.subdev.ctrl_handler = hdl; - dev_info(state->mux.sd.subdev.dev, + dev_dbg(state->mux.sd.subdev.dev, "%s():%d set ctrl_handler for MUX\n", __func__, __LINE__); break; } @@ -3266,7 +3525,7 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, /* * TODO: suffix for 2 D457 connected to 1 Deser */ - snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[0].suffix); + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->suffix); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -3280,7 +3539,7 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) { struct v4l2_subdev *sd = &sensor->sd; struct media_entity *entity = &sensor->sd.entity; - int ret = -1; + int ret; // FIXME: is async needed? ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); @@ -3297,7 +3556,7 @@ static int ds5_sensor_register(struct ds5 *state, struct ds5_sensor *sensor) goto e_sd; } - dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); + dev_dbg(sd->dev, "%s(): 0 -> %d\n", __func__, sensor->mux_pad); return 0; @@ -3326,38 +3585,38 @@ static int ds5_motion_t_init(struct i2c_client *c, struct ds5 *state) { state->motion_t.sensor.mux_pad = DS5_MUX_PAD_MOTION_T_A; return ds5_sensor_init(c, state, &state->motion_t.sensor, - &ds5_motion_t_subdev_ops, "motion detection"); + &ds5_motion_t_subdev_ops, "motion detection"); } static int ds5_rgb_init(struct i2c_client *c, struct ds5 *state) { state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB_A; return ds5_sensor_init(c, state, &state->rgb.sensor, - &ds5_rgb_subdev_ops, "rgb"); + &ds5_rgb_subdev_ops, "rgb"); } static int ds5_imu_init(struct i2c_client *c, struct ds5 *state) { state->imu.sensor.mux_pad = DS5_MUX_PAD_IMU_A; return ds5_sensor_init(c, state, &state->imu.sensor, - &ds5_imu_subdev_ops, "imu"); + &ds5_imu_subdev_ops, "imu"); } /* No locking needed */ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_pad_config *cfg, #else - struct v4l2_subdev_state *v4l2_state, + struct v4l2_subdev_state *v4l2_state, #endif struct v4l2_subdev_mbus_code_enum *mce) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); struct v4l2_subdev_mbus_code_enum tmp = *mce; struct v4l2_subdev *remote_sd; - int ret = -1; + int ret; - dev_info(&state->client->dev, "%s(): %s \n", __func__, sd->name); + dev_dbg(&state->client->dev, "%s(): %s \n", __func__, sd->name); switch (mce->pad) { case DS5_MUX_PAD_MOTION_T_A: remote_sd = &state->motion_t.sensor.sd; @@ -3429,7 +3688,7 @@ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum tmp = *fse; struct v4l2_subdev *remote_sd; u32 pad = fse->pad; - int ret = -1; + int ret; tmp.pad = 0; @@ -3497,11 +3756,11 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval_enum tmp = *fie; struct v4l2_subdev *remote_sd; u32 pad = fie->pad; - int ret = -1; + int ret; tmp.pad = 0; - dev_err(state->depth.sensor.sd.dev, + dev_dbg(state->depth.sensor.sd.dev, "%s(): pad %d code %x width %d height %d\n", __func__, pad, tmp.code, tmp.width, tmp.height); @@ -3572,7 +3831,9 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, // u32 pad = fmt->pad; int ret = 0; int substream = -1; - + if (pad != DS5_MUX_PAD_EXTERNAL) + ds5_s_state(state, pad - 1); + sensor = state->mux.last_set; switch (pad) { case DS5_MUX_PAD_DEPTH_A: case DS5_MUX_PAD_MOTION_T_A: @@ -3618,27 +3879,27 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, /* No locking needed */ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) - struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_pad_config *cfg, #else - struct v4l2_subdev_state *v4l2_state, + struct v4l2_subdev_state *v4l2_state, #endif struct v4l2_subdev_format *fmt) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - // u32 pad = fmt->pad; int ret = 0; struct ds5_sensor *sensor = state->mux.last_set; u32 pad = sensor->mux_pad; - - dev_info(sd->dev, "%s(): %u %p\n", __func__, pad, state->mux.last_set); + if (pad != DS5_MUX_PAD_EXTERNAL) + ds5_s_state(state, pad - 1); + sensor = state->mux.last_set; + dev_info(sd->dev, "%s(): %u %s %p\n", __func__, pad, ds5_get_sensor_name(state), state->mux.last_set); switch (pad) { case DS5_MUX_PAD_DEPTH_A: case DS5_MUX_PAD_MOTION_T_A: case DS5_MUX_PAD_RGB_A: case DS5_MUX_PAD_IMU_A: - //fmt->format = ds5_ffmts[pad]; - fmt->format = sensor->format;//ds5_ffmts[pad]; + fmt->format = sensor->format; break; case DS5_MUX_PAD_EXTERNAL: fmt->format = ds5_ffmts[pad]; @@ -3647,7 +3908,7 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, return -EINVAL; } - dev_dbg(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad:%u size:%d-%d, code:0x%x field:%d, color:%d\n", + dev_info(sd->dev, "%s(): fmt->pad:%d, sensor->mux_pad:%u size:%d-%d, code:0x%x field:%d, color:%d\n", __func__, fmt->pad, pad, fmt->format.width, fmt->format.height, fmt->format.code, fmt->format.field, fmt->format.colorspace); @@ -3707,7 +3968,7 @@ static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, fi->interval.numerator = 1; fi->interval.denominator = framerate; - dev_info(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); + dev_dbg(sd->dev, "%s(): %s %u\n", __func__, sd->name, framerate); return 0; } @@ -3715,204 +3976,76 @@ static int ds5_mux_s_frame_interval(struct v4l2_subdev *sd, static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - u16 streaming_depth, streaming_rgb, streaming_y8, rate, depth_status, rgb_status, y8_status; - int ret = 0; u16 streaming, status; - u16 config_status_base, stream_status_base, stream_id; + int ret = 0; unsigned int i = 0; - u16 err_status; - u16 tmp; - - dev_info(&state->client->dev, "%s(): %s on = %d\n", __func__, state->mux.last_set->sd.name, on); - - state->mux.last_set->streaming = on; - - // TODO: remove, workaround for FW crash in start - msleep_range(100); - - if (!on) { - ds5_read(state, 0x1004, &streaming_depth); - ds5_read(state, 0x4800, &depth_status); - ds5_read(state, 0x4802, &rgb_status); - - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_DEPTH); - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_RGB); - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_IMU); - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_IR); + int restore_val = 0; + u16 config_status_base, stream_status_base, stream_id, vc_id; + struct ds5_sensor *sensor = state->mux.last_set; + // spare duplicate calls + if (sensor->streaming == on) return 0; - } - - msleep_range(100); - if (on) - ret = ds5_configure_depth(state); - msleep_range(100); - ds5_write(state, 0x1000, on ? 0x200 : 0x100); - + if (state->is_depth) { config_status_base = DS5_DEPTH_CONFIG_STATUS; stream_status_base = DS5_DEPTH_STREAM_STATUS; - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); - if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) - break; - - msleep_range(DS5_START_POLL_TIME); - } - - if (i == DS5_START_MAX_COUNT) { - dev_err(&state->client->dev, - "start depth streaming failed, exit on timeout\n"); - } - - msleep_range(100); - if (on) - ret = ds5_configure_rgb(state); - msleep_range(100); - /* RGB */ - ds5_write(state, 0x1000, on ? 0x201 : 0x101); - + stream_id = DS5_STREAM_DEPTH; + vc_id = 0; + } else if (state->is_rgb) { config_status_base = DS5_RGB_CONFIG_STATUS; stream_status_base = DS5_RGB_STREAM_STATUS; - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); - if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) - break; - - msleep_range(DS5_START_POLL_TIME); - } - - if (i == DS5_START_MAX_COUNT) { - dev_err(&state->client->dev, - "start RGB streaming failed, exit on timeout\n"); - } - - /* IR */ - msleep_range(100); - if (on) - ret = ds5_configure_ir(state); - msleep_range(100); - ds5_write(state, 0x1000, on ? 0x204 : 0x104); - + stream_id = DS5_STREAM_RGB; + vc_id = 1; + } else if (state->is_y8) { config_status_base = DS5_IR_CONFIG_STATUS; stream_status_base = DS5_IR_STREAM_STATUS; stream_id = DS5_STREAM_IR; + vc_id = 2; + } else if (state->is_imu) { + config_status_base = DS5_IMU_CONFIG_STATUS; + stream_status_base = DS5_IMU_STREAM_STATUS; + stream_id = DS5_STREAM_IMU; + vc_id = 3; + } else { + return -EINVAL; + } - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); - if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) - break; + dev_warn(&state->client->dev, "s_stream for stream %s, vc:%d, SENSOR=%s on = %d\n", + sensor->sd.name, vc_id, ds5_get_sensor_name(state), on); - msleep_range(DS5_START_POLL_TIME); - } + restore_val = sensor->streaming; + sensor->streaming = on; - if (DS5_START_MAX_COUNT == i) { + if (on) { +#ifdef CONFIG_VIDEO_D4XX_SERDES + // set manually, need to configure vc in pdata + state->g_ctx.dst_vc = vc_id; + sensor->pipe_id = + max9296_get_available_pipe_id(state->dser_dev, + (int)state->g_ctx.dst_vc); + if (sensor->pipe_id < 0) { dev_err(&state->client->dev, - "start imu streaming failed, exit on timeout\n"); + "No free pipe in max9296\n"); + ret = sensor->pipe_id; + goto restore_s_state; } +#endif - // TODO: this read seems to cause FW crash, need to debug - //ds5_read(state, 0x402, &rate); - rate = 0; - - ds5_read(state, 0x401a, &err_status); - ds5_read(state, 0x1004, &streaming_depth); - ds5_read(state, 0x1008, &streaming_rgb); - ds5_read(state, 0x1010, &streaming_y8); - ds5_read(state, 0x4800, &depth_status); - ds5_read(state, 0x4802, &rgb_status); - ds5_read(state, 0x4808, &y8_status); - ds5_read(state, 0x4002, &tmp); - ds5_read(state, 0x4022, &tmp); - ds5_read(state, 0x4082, &tmp); - - //ds5_write(state, 0x4002, 0x100 ); - //ds5_write(state, 0x4022, 0x0 ); - //ds5_read(state, 0x4002, &tmp); - //ds5_read(state, 0x4022, &tmp); - msleep_range(DS5_START_POLL_TIME*50); - - dev_info(&state->client->dev, "%s(): streaming %x-%x-%x depth status 0x%04x, rgb status 0x%04x, rate %u\n", __func__, - streaming_depth, streaming_rgb, streaming_y8, depth_status, rgb_status, rate); - - return ret; -} - -static int ds5_mux_s_stream_vc(struct ds5 *state, u16 vc_id, u16 on) -{ - u16 streaming_depth, streaming_rgb, streaming_y8, rate, depth_status, rgb_status, y8_status; - int ret = 0; - u16 streaming, status; - u16 config_status_base, stream_status_base, stream_id; - unsigned int i = 0; - u16 err_status; - u16 tmp; - - dev_info(&state->client->dev, "%s(): %s on = %d\n", __func__, state->mux.last_set->sd.name, on); - - state->mux.last_set->streaming = on; - - // TODO: remove, workaround for FW crash in start - msleep_range(100); - - if (!on) { - ds5_read(state, 0x1004, &streaming_depth); - ds5_read(state, 0x4800, &depth_status); - ds5_read(state, 0x4802, &rgb_status); - - if ((vc_id == DS5_MUX_PAD_DEPTH_A - 1) || (vc_id == DS5_MUX_PAD_DEPTH_B - 1)) { - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_DEPTH); - } - if ((vc_id == DS5_MUX_PAD_RGB_A - 1) || (vc_id == DS5_MUX_PAD_RGB_B - 1)) { - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_RGB); - } - if ((vc_id == DS5_MUX_PAD_IMU_A - 1) || (vc_id == DS5_MUX_PAD_IMU_B - 1)) { - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_IMU); - } - if ((vc_id == DS5_MUX_PAD_MOTION_T_A - 1) || (vc_id == DS5_MUX_PAD_MOTION_T_B - 1)) { - msleep_range(100); - ret = ds5_write(state, DS5_START_STOP_STREAM, - DS5_STREAM_STOP | DS5_STREAM_IR); - } - return 0; - } + ret = ds5_configure(state); + if (ret) + goto restore_s_state; - msleep_range(100); - if ((on) && ((vc_id == DS5_MUX_PAD_DEPTH_A - 1) || (vc_id == DS5_MUX_PAD_DEPTH_B - 1))) { - ret = ds5_configure_depth(state); - msleep_range(100); - ds5_write(state, 0x1000, on ? 0x200 : 0x100); + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_START | stream_id); + if (ret < 0) + goto restore_s_state; - config_status_base = DS5_DEPTH_CONFIG_STATUS; - stream_status_base = DS5_DEPTH_STREAM_STATUS; // check streaming status from FW for (i = 0; i < DS5_START_MAX_COUNT; i++) { ds5_read(state, stream_status_base, &streaming); ds5_read(state, config_status_base, &status); if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) + streaming == DS5_STREAM_STREAMING) break; msleep_range(DS5_START_POLL_TIME); @@ -3920,163 +4053,136 @@ static int ds5_mux_s_stream_vc(struct ds5 *state, u16 vc_id, u16 on) if (DS5_START_MAX_COUNT == i) { dev_err(&state->client->dev, - "start depth streaming failed, exit on timeout\n"); + "start streaming failed, exit on timeout\n"); + /* notify fw */ + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_STOP | stream_id); + ret = -EAGAIN; + goto restore_s_state; + } else { + dev_dbg(&state->client->dev, "started after %dms\n", + i * DS5_START_POLL_TIME); } - } - - if ((on) && ((vc_id == DS5_MUX_PAD_RGB_A - 1) || (vc_id == DS5_MUX_PAD_RGB_B - 1))) { - msleep_range(100); - if (on) - ret = ds5_configure_rgb(state); - msleep_range(100); - /* RGB */ - ds5_write(state, 0x1000, on ? 0x201 : 0x101); - - config_status_base = DS5_RGB_CONFIG_STATUS; - stream_status_base = DS5_RGB_STREAM_STATUS; - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); - if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) - break; - - msleep_range(DS5_START_POLL_TIME); + } else { // off + ret = ds5_write(state, DS5_START_STOP_STREAM, + DS5_STREAM_STOP | stream_id); + if (ret < 0) + goto restore_s_state; + +#ifdef CONFIG_VIDEO_D4XX_SERDES + // reset data path when Y12I streaming is done + if (state->is_y8 && + state->motion_t.sensor.config.format->data_type == + GMSL_CSI_DT_RGB_888) { + max9296_reset_oneshot(state->dser_dev); } - - if (DS5_START_MAX_COUNT == i) { - dev_err(&state->client->dev, - "start RGB streaming failed, exit on timeout\n"); +#ifndef CONFIG_TEGRA_CAMERA_PLATFORM + // reset for IPU6 + if (!(state->depth.sensor.streaming || \ + state->rgb.sensor.streaming || \ + state->motion_t.sensor.streaming || \ + state->imu.sensor.streaming)) { + dev_warn(&state->client->dev, "max9296_reset_oneshot\n"); + max9296_reset_oneshot(state->dser_dev); } +#endif + if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) + dev_warn(&state->client->dev, "release pipe failed\n"); + sensor->pipe_id = -1; +#else +{ // ipu6 yavta compatibility + int s_addr = state->client->addr; + int n_addr; + if (s_addr == 0x12) + n_addr = 0x48; + if (s_addr == 0x14) + n_addr = 0x4a; + if (s_addr == 0x16) + n_addr = 0x68; + if (s_addr == 0x18) + n_addr = 0x6c; + if (n_addr) { + state->client->addr = n_addr; + dev_warn(&state->client->dev, "One-shot reset 0x%x enable auto-link\n", n_addr); + max9296_write_8(state, 0x0010, 0x31); // One-shot reset enable auto-link + state->client->addr = s_addr; + /* delay to settle link */ + msleep(100); + } +} +#endif } - if ((on) && ((vc_id == DS5_MUX_PAD_MOTION_T_A - 1) || (vc_id == DS5_MUX_PAD_MOTION_T_B - 1))) { - msleep_range(100); - if (on) - ret = ds5_configure_ir(state); - msleep_range(100); - ds5_write(state, 0x1000, on ? 0x204 : 0x104); - - config_status_base = DS5_IR_CONFIG_STATUS; - stream_status_base = DS5_IR_STREAM_STATUS; - stream_id = DS5_STREAM_IR; - - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); - if ((status & DS5_STATUS_STREAMING) && - streaming == DS5_STREAM_STREAMING) - break; + ds5_read(state, config_status_base, &status); + ds5_read(state, stream_status_base, &streaming); + dev_info(&state->client->dev, + "%s %s, stream_status 0x%x:%x, config_status 0x%x:%x ret=%d\n", + ds5_get_sensor_name(state), + (on)?"START":"STOP", + stream_status_base, streaming, + config_status_base, status, ret); - msleep_range(DS5_START_POLL_TIME); - } + return ret; - if (DS5_START_MAX_COUNT == i) { - dev_err(&state->client->dev, - "start imu streaming failed, exit on timeout\n"); - } +restore_s_state: +#ifdef CONFIG_VIDEO_D4XX_SERDES + if (on && sensor->pipe_id >= 0) { + if (max9296_release_pipe(state->dser_dev, sensor->pipe_id) < 0) + dev_warn(&state->client->dev, "release pipe failed\n"); + sensor->pipe_id = -1; } +#endif - if ((on) && ((vc_id == DS5_MUX_PAD_IMU_A - 1) || - (vc_id == DS5_MUX_PAD_IMU_B - 1))) { - msleep_range(100); + ds5_read(state, config_status_base, &status); + dev_err(&state->client->dev, + "%s stream toggle failed! %x status 0x%04x\n", + ds5_get_sensor_name(state) ,restore_val, status); - if (on) - ret = ds5_configure_imu(state); + sensor->streaming = restore_val; - msleep_range(100); - ds5_write(state, 0x1000, on ? 0x202 : 0x102); + return ret; +} +#if 0 +static int ds5_set_power(struct ds5 *state, int on) +{ + int ret = 0; - config_status_base = DS5_IMU_CONFIG_STATUS; - stream_status_base = DS5_IMU_STREAM_STATUS; - stream_id = DS5_STREAM_IMU; + mutex_lock(&state->lock); - // check streaming status from FW - for (i = 0; i < DS5_START_MAX_COUNT; i++) { - ds5_read(state, stream_status_base, &streaming); - ds5_read(state, config_status_base, &status); + if (state->power != !on) { + mutex_unlock(&state->lock); + return 0; + } - if ((status & DS5_STATUS_STREAMING) && - (streaming == DS5_STREAM_STREAMING)) - break; +// gpio_set_value_cansleep(state->pwdn_gpio, on); - msleep_range(DS5_START_POLL_TIME); - } + dev_info(&state->client->dev, "%s(): power %d\n", __func__, on); - if (i == DS5_START_MAX_COUNT) { - dev_err(&state->client->dev, - "start imu streaming failed, exit on timeout\n"); - } + usleep_range(100, 200); + + if (on) { + state->power = true; + } else { + state->power = false; } - // TODO: this read seems to cause FW crash, need to debug - //ds5_read(state, 0x402, &rate); - rate = 0; - ds5_read(state, 0x401a, &err_status); - - ds5_read(state, 0x1004, &streaming_depth); - ds5_read(state, 0x1008, &streaming_rgb); - ds5_read(state, 0x1010, &streaming_y8); - ds5_read(state, 0x4800, &depth_status); - ds5_read(state, 0x4802, &rgb_status); - ds5_read(state, 0x4808, &y8_status); - ds5_read(state, 0x4002, &tmp); - ds5_read(state, 0x4022, &tmp); - ds5_read(state, 0x4082, &tmp); - - //ds5_write(state, 0x4002, 0x100 ); - //ds5_write(state, 0x4022, 0x0 ); - //ds5_read(state, 0x4002, &tmp); - //ds5_read(state, 0x4022, &tmp); - msleep_range(DS5_START_POLL_TIME*50); - - dev_info(&state->client->dev, "%s(): streaming %x-%x-%x depth status 0x%04x, rgb status 0x%04x, rate %u\n", __func__, - streaming_depth, streaming_rgb, streaming_y8, depth_status, rgb_status, rate); + mutex_unlock(&state->lock); + + /* TODO: Restore controls when powering on */ + //if (on) + // ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); return ret; } -//static int ds5_set_power(struct ds5 *state, int on) -//{ -// int ret = 0; -// -// mutex_lock(&state->lock); -// -// if (state->power != !on) { -// mutex_unlock(&state->lock); -// return 0; -// } -// -//// gpio_set_value_cansleep(state->pwdn_gpio, on); -// -// dev_info(&state->client->dev, "%s(): power %d\n", __func__, on); -// -// usleep_range(100, 200); -// -// if (on) { -// state->power = true; -// } else { -// state->power = false; -// } -// -// mutex_unlock(&state->lock); -// -// /* TODO: Restore controls when powering on */ -// //if (on) -// // ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); -// -// return ret; -//} - /* Core ops */ -/*static int ds5_mux_set_power(struct v4l2_subdev *sd, int on) +static int ds5_mux_set_power(struct v4l2_subdev *sd, int on) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); return ds5_set_power(state, on); -}*/ +} +#endif #define DS5_N_STREAMS 4 #define DS5_PAD_SOURCE 0 @@ -4142,7 +4248,7 @@ static int ds5_des_register(struct ds5 *state, struct ds5_des *des) // MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); //if (ret < 0) { // goto e_sd; - // } + // } dev_info(sd->dev, "%s(): 0 -> %d\n", __func__, des->mux_pad); @@ -4224,7 +4330,15 @@ static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) if (!ret) ret = ds5_read(state, DS5_MIPI_DATARATE_MAX, &drate_max); + if (!ret) + dev_dbg(sd->dev, "%s(): %d: %u lanes, phy %x, data rate %u-%u\n", + __func__, __LINE__, n_lanes, phy, drate_min, drate_max); + +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + n_lanes = state->mux.sd.numlanes; +#else n_lanes = 2; +#endif ret = ds5_write(state, DS5_MIPI_LANE_NUMS, n_lanes - 1); if (!ret) @@ -4232,6 +4346,11 @@ static int ds5_hw_init(struct i2c_client *c, struct ds5 *state) ret = ds5_read(state, DS5_MIPI_CONF_STATUS, &mipi_status); +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + dev_dbg(sd->dev, "%s(): %d phandle %x node %s status %x\n", __func__, __LINE__, + c->dev.of_node->phandle, c->dev.of_node->full_name, mipi_status); +#endif + return ret; } @@ -4306,17 +4425,14 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) return ret; /*set for y8*/ ret = ds5_ctrl_init(state, 2); + if (ret < 0) + return ret; + /*set for imu*/ + ret = ds5_ctrl_init(state, 3); if (ret < 0) return ret; - if (state->is_depth) - state->mux.last_set = &state->depth.sensor; - else if (state->is_rgb) - state->mux.last_set = &state->rgb.sensor; - else if (state->is_y8) - state->mux.last_set = &state->motion_t.sensor; - else - state->mux.last_set = &state->imu.sensor; + ds5_set_state_last_set(state); #ifdef CONFIG_TEGRA_CAMERA_PLATFORM state->mux.sd.dev = &c->dev; @@ -4332,10 +4448,10 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) #ifdef CONFIG_TEGRA_CAMERA_PLATFORM e_ctrl: v4l2_ctrl_handler_free(sd->ctrl_handler); -#endif media_entity_cleanup(entity); return ret; +#endif } #define USE_Y @@ -4367,10 +4483,10 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) if (ret < 0) return ret; - dev_info(&client->dev, "%s(): cfg0 %x %ux%u cfg0_md %x %ux%u\n", __func__, + dev_dbg(&client->dev, "%s(): cfg0 %x %ux%u cfg0_md %x %ux%u\n", __func__, cfg0, dw, dh, cfg0_md, yw, yh); - dev_info(&client->dev, "%s(): cfg1 %x %ux%u cfg1_md %x %ux%u\n", __func__, + dev_dbg(&client->dev, "%s(): cfg1 %x %ux%u cfg1_md %x %ux%u\n", __func__, cfg1, dw, dh, cfg1_md, yw, yh); sensor = &state->depth.sensor; @@ -4636,16 +4752,20 @@ static ssize_t ds5_dfu_device_read(struct file *flip, char __user *buffer, size_t len, loff_t *offset) { struct ds5 *state = flip->private_data; - u16 fw_ver; - char msg[20]; + u16 fw_ver, fw_build; + char msg[32]; int ret = 0; if (mutex_lock_interruptible(&state->lock)) return -ERESTARTSYS; - ret = ds5_read(state, DS5_FW_VERSION, &fw_ver); + ret |= ds5_read(state, DS5_FW_VERSION, &fw_ver); + ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); if (ret < 0) goto e_dfu_read_failed; - snprintf(msg, sizeof(msg), "DFU info: \tver: (0x%x)\n", fw_ver); + snprintf(msg, sizeof(msg) ,"DFU info: \tver: %d.%d.%d.%d\n", + (fw_ver >> 8) & 0xff, fw_ver & 0xff, + (fw_build >> 8) & 0xff, fw_build & 0xff); + if (copy_to_user(buffer, msg, strlen(msg))) ret = -EFAULT; else { @@ -4663,6 +4783,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, { struct ds5 *state = flip->private_data; int ret = 0; + (void)offset; if (mutex_lock_interruptible(&state->lock)) return -ERESTARTSYS; @@ -4675,7 +4796,8 @@ static ssize_t ds5_dfu_device_write(struct file *flip, __func__, ret); goto dfu_write_error; } - /*no break - proceed to recovery*/ + /*fallthrough - procceed to recovery*/ + __attribute__((__fallthrough__)); case DS5_DFU_RECOVERY: ret = ds5_dfu_detach(state); if (ret < 0) { @@ -4685,8 +4807,8 @@ static ssize_t ds5_dfu_device_write(struct file *flip, } state->dfu_dev.dfu_state_flag = DS5_DFU_IN_PROGRESS; state->dfu_dev.init_v4l_f = 1; - - /*no break - proceed to download*/ + /*fallthrough - procceed to download*/ + __attribute__((__fallthrough__)); case DS5_DFU_IN_PROGRESS: { unsigned int dfu_full_blocks = len / DFU_BLOCK_SIZE; unsigned int dfu_part_blocks = len % DFU_BLOCK_SIZE; @@ -4737,8 +4859,10 @@ static ssize_t ds5_dfu_device_write(struct file *flip, return len; dfu_write_error: - //TODO: Reset device here state->dfu_dev.dfu_state_flag = DS5_DFU_ERROR; + // Reset DFU device to IDLE states + if (!ds5_write(state, 0x5010, 0x0)) + state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; mutex_unlock(&state->lock); return ret; }; @@ -4747,8 +4871,6 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) { struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( - state->client->adapter); if (state->dfu_dev.device_open_count) return -EBUSY; @@ -4848,22 +4970,25 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) { struct cdev *ds5_cdev = &state->dfu_dev.ds5_cdev; struct class **ds5_class = &state->dfu_dev.ds5_class; + struct d4xx_pdata *pdata = c->dev.platform_data; + struct device *chr_dev; - char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 5]; + char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 8]; dev_t *dev_num = &c->dev.devt; int ret; - dev_info(&c->dev, "%s()\n", __func__); + dev_dbg(&c->dev, "%s()\n", __func__); /* Request the kernel for N_MINOR devices */ ret = alloc_chrdev_region(dev_num, 0, 1, DS5_DRIVER_NAME_DFU); if (ret < 0) return ret; - if (!atomic_cmpxchg(&primary_chardev, 0, MAJOR(*dev_num))) { + if (!atomic_read(&primary_chardev)) { dev_dbg(&c->dev, "%s(): : <%d, %d>\n", __func__, MAJOR(*dev_num), MINOR(*dev_num)); /* Create a class : appears at /sys/class */ *ds5_class = class_create(THIS_MODULE, DS5_DRIVER_NAME_CLASS); + dev_warn(&state->client->dev, "%s() class_create\n", __func__); if (IS_ERR(*ds5_class)) { dev_err(&c->dev, "Could not create class device\n"); unregister_chrdev_region(0, 1); @@ -4878,8 +5003,8 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) /* Build up the current device number. To be used further */ *dev_num = MKDEV(MAJOR(*dev_num), MINOR(*dev_num)); /* Create a device node for this device. */ - snprintf(dev_name, sizeof(dev_name), "%s%d", - DS5_DRIVER_NAME_DFU, MAJOR(*dev_num)); + snprintf(dev_name, sizeof(dev_name), "%s-%c", + DS5_DRIVER_NAME_DFU, pdata->suffix); chr_dev = device_create(*ds5_class, NULL, *dev_num, NULL, dev_name); if (IS_ERR(chr_dev)) { ret = PTR_ERR(chr_dev); @@ -4889,6 +5014,7 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) return ret; } cdev_add(ds5_cdev, *dev_num, 1); + atomic_inc(&primary_chardev); return 0; }; @@ -4896,12 +5022,16 @@ static int ds5_chrdev_remove(struct ds5 *state) { struct class **ds5_class = &state->dfu_dev.ds5_class; dev_t *dev_num = &state->client->dev.devt; - + if (!ds5_class) { + return 0; + } dev_dbg(&state->client->dev, "%s()\n", __func__); unregister_chrdev_region(*dev_num, 1); device_destroy(*ds5_class, *dev_num); - if (atomic_cmpxchg(&primary_chardev, MAJOR(*dev_num), 0) == MAJOR(*dev_num)) + if (atomic_dec_and_test(&primary_chardev)) { + dev_warn(&state->client->dev, "%s() class_destroy\n", __func__); class_destroy(*ds5_class); + } return 0; } @@ -5087,6 +5217,7 @@ static const struct attribute_group ds5_attr_group = { #define NR_DESER 4 +#ifndef CONFIG_VIDEO_D4XX_SERDES static const struct regmap_config ds5_regmap_max9296 = { .reg_bits = 16, .val_bits = 8, @@ -5181,14 +5312,16 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) return 0; } +#endif static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) { struct ds5 *state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); u16 rec_state; int ret, retry, err = 0; +#ifdef CONFIG_OF const char *str; - +#endif if (!state) return -ENOMEM; @@ -5222,6 +5355,13 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) goto e_regulator; } +#ifdef CONFIG_VIDEO_D4XX_SERDES + ret = ds5_serdes_setup(state); + if (ret < 0) + goto e_regulator; +#endif + +#ifndef CONFIG_VIDEO_D4XX_SERDES if (c->addr == 0x48) c->addr = 0x12; if (c->addr == 0x4a) @@ -5238,42 +5378,60 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) return ret; } } +#endif - ret = ds5_chrdev_init(c, state); - if (ret < 0) - goto e_regulator; - - retry = 100; + // Verify communication + retry = 5; do { ret = ds5_read(state, 0x5020, &rec_state); } while (retry-- && ret < 0); if (ret < 0) { - dev_err(&c->dev, "%s(): cannot communicate with D4XX: %d\n", - __func__, ret); - goto e_chardev; - } - - if (rec_state == 0x201) { - dev_info(&c->dev, "%s(): D4XX recovery state\n", __func__); - state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY; - return 0; + dev_err(&c->dev, + "%s(): cannot communicate with D4XX: %d on addr: 0x%x\n", + __func__, ret, c->addr); + goto e_regulator; } state->is_depth = 0; state->is_y8 = 0; state->is_rgb = 0; state->is_imu = 0; - - err = of_property_read_string(c->dev.of_node, "cam-type", - &str); - if (!err && !strncmp(str, "Depth", strlen("Depth"))) +#ifdef CONFIG_OF + ret = of_property_read_string(c->dev.of_node, "cam-type", &str); + if (!ret && !strncmp(str, "Depth", strlen("Depth"))) { state->is_depth = 1; - if (!err && !strncmp(str, "Y8", strlen("Y8"))) + } + if (!ret && !strncmp(str, "Y8", strlen("Y8"))) { state->is_y8 = 1; - if (!err && !strncmp(str, "RGB", strlen("RGB"))) + } + if (!ret && !strncmp(str, "RGB", strlen("RGB"))) { state->is_rgb = 1; - if (!err && !strncmp(str, "IMU", strlen("IMU"))) + } + if (!ret && !strncmp(str, "IMU", strlen("IMU"))) { state->is_imu = 1; + } +#else + state->is_depth = 1; +#endif + /* create DFU chardev once */ + if (state->is_depth) { + ret = ds5_chrdev_init(c, state); + if (ret < 0) + goto e_regulator; + } + + ret = ds5_read(state, 0x5020, &rec_state); + if (ret < 0) { + dev_err(&c->dev, "%s(): cannot communicate with D4XX: %d\n", + __func__, ret); + goto e_chardev; + } + + if (rec_state == 0x201) { + dev_info(&c->dev, "%s(): D4XX recovery state\n", __func__); + state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY; + return 0; + } ds5_read_with_check(state, DS5_FW_VERSION, &state->fw_version); ds5_read_with_check(state, DS5_FW_BUILD, &state->fw_build); @@ -5306,29 +5464,74 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) return 0; e_chardev: - ds5_chrdev_remove(state); + if(state->dfu_dev.ds5_class) + ds5_chrdev_remove(state); e_regulator: if (state->vcc) regulator_disable(state->vcc); + if (state->ser_i2c) + i2c_unregister_device(state->ser_i2c); + if (state->dser_i2c) + i2c_unregister_device(state->dser_i2c); return ret; } static int ds5_remove(struct i2c_client *c) { struct ds5 *state = container_of(i2c_get_clientdata(c), struct ds5, mux.sd.subdev); - +#ifdef CONFIG_VIDEO_D4XX_SERDES + int i, ret; + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i] == state) { + serdes_inited[i] = NULL; + mutex_lock(&serdes_lock__); + + ret = max9295_reset_control(state->ser_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9295 reset control\n"); + ret = max9296_reset_control(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed in 9296 reset control\n"); + + ret = max9295_sdev_unpair(state->ser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, "failed to unpair sdev\n"); + ret = max9296_sdev_unregister(state->dser_dev, + state->g_ctx.s_dev); + if (ret) + dev_warn(&c->dev, + "failed to sdev unregister sdev\n"); + max9296_power_off(state->dser_dev); + + mutex_unlock(&serdes_lock__); + break; + } + } + if (state->ser_i2c) + i2c_unregister_device(state->ser_i2c); + if (state->dser_i2c) + i2c_unregister_device(state->dser_i2c); +#endif dev_info(&c->dev, "D4XX remove %s\n", ds5_get_sensor_name(state)); if (state->vcc) regulator_disable(state->vcc); // gpio_free(state->pwdn_gpio); - ds5_chrdev_remove(state); + if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) { #ifdef CONFIG_SYSFS sysfs_remove_group(&c->dev.kobj, &ds5_attr_group); #endif ds5_mux_remove(state); + if (state->is_depth) { + ds5_chrdev_remove(state); + } } + return 0; } @@ -5363,4 +5566,4 @@ MODULE_AUTHOR("Guennadi Liakhovetski ,\n\ Shikun Ding "); MODULE_AUTHOR("Dmitry Perchanov "); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("1.0.2.13"); +MODULE_VERSION("1.0.2.14"); diff --git a/drivers/media/i2c/max9295.c b/drivers/media/i2c/max9295.c new file mode 100644 index 000000000000..ca9d3b0911c6 --- /dev/null +++ b/drivers/media/i2c/max9295.c @@ -0,0 +1,767 @@ +/* + * max9295.c - max9295 GMSL Serializer driver + * + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// #include +#include +#include +#include +#include +#include +#include +#include + +/* register specifics */ +#define MAX9295_MIPI_RX0_ADDR 0x330 +#define MAX9295_MIPI_RX1_ADDR 0x331 +#define MAX9295_MIPI_RX2_ADDR 0x332 +#define MAX9295_MIPI_RX3_ADDR 0x333 + +#define MAX9295_PIPE_X_DT_ADDR 0x314 +#define MAX9295_PIPE_Y_DT_ADDR 0x316 +#define MAX9295_PIPE_Z_DT_ADDR 0x318 +#define MAX9295_PIPE_U_DT_ADDR 0x31A + +#define MAX9295_CTRL0_ADDR 0x10 +#define MAX9295_SRC_CTRL_ADDR 0x2BF +#define MAX9295_SRC_PWDN_ADDR 0x02BE +#define MAX9295_SRC_OUT_RCLK_ADDR 0x3F1 +#define MAX9295_START_PIPE_ADDR 0x311 +#define MAX9295_PIPE_EN_ADDR 0x2 +#define MAX9295_CSI_PORT_SEL_ADDR 0x308 + +#define MAX9295_I2C4_ADDR 0x44 +#define MAX9295_I2C5_ADDR 0x45 + +#define MAX9295_DEV_ADDR 0x00 + +#define MAX9295_STREAM_PIPE_UNUSED 0x22 +#define MAX9295_CSI_MODE_1X4 0x00 +#define MAX9295_CSI_MODE_2X2 0x03 +#define MAX9295_CSI_MODE_2X4 0x06 + +#define MAX9295_CSI_PORT_B(num_lanes) (((num_lanes) << 4) & 0xF0) +#define MAX9295_CSI_PORT_A(num_lanes) ((num_lanes) & 0x0F) + +#define MAX9295_CSI_1X4_MODE_LANE_MAP1 0xE0 +#define MAX9295_CSI_1X4_MODE_LANE_MAP2 0x04 + +#define MAX9295_CSI_2X4_MODE_LANE_MAP1 0xEE +#define MAX9295_CSI_2X4_MODE_LANE_MAP2 0xE4 + +#define MAX9295_CSI_2X2_MODE_LANE_MAP1 MAX9295_CSI_2X4_MODE_LANE_MAP1 +#define MAX9295_CSI_2X2_MODE_LANE_MAP2 MAX9295_CSI_2X4_MODE_LANE_MAP2 + +#define MAX9295_ST_ID_0 0x0 +#define MAX9295_ST_ID_1 0x1 +#define MAX9295_ST_ID_2 0x2 +#define MAX9295_ST_ID_3 0x3 + +#define MAX9295_PIPE_X_START_B 0x80 +#define MAX9295_PIPE_Y_START_B 0x40 +#define MAX9295_PIPE_Z_START_B 0x20 +#define MAX9295_PIPE_U_START_B 0x10 + +#define MAX9295_PIPE_X_START_A 0x1 +#define MAX9295_PIPE_Y_START_A 0x2 +#define MAX9295_PIPE_Z_START_A 0x4 +#define MAX9295_PIPE_U_START_A 0x8 + +#define MAX9295_START_PORT_A 0x10 +#define MAX9295_START_PORT_B 0x20 + +#define MAX9295_CSI_LN2 0x1 +#define MAX9295_CSI_LN4 0x3 + +#define MAX9295_EN_LINE_INFO 0x40 + +#define MAX9295_VID_TX_EN_X 0x10 +#define MAX9295_VID_TX_EN_Y 0x20 +#define MAX9295_VID_TX_EN_Z 0x40 +#define MAX9295_VID_TX_EN_U 0x80 + +#define MAX9295_VID_INIT 0x3 +#define MAX9295_SRC_RCLK 0x89 + +#define MAX9295_RESET_ALL 0x80 +#define MAX9295_RESET_SRC 0x60 +#define MAX9295_PWDN_GPIO 0x90 + +#define MAX9295_MAX_PIPES 0x4 + +struct max9295_client_ctx { + struct gmsl_link_ctx *g_ctx; + bool st_done; +}; + +struct max9295 { + struct i2c_client *i2c_client; + struct regmap *regmap; + struct max9295_client_ctx g_client; + struct mutex lock; + /* primary serializer properties */ + __u32 def_addr; + __u32 pst2_ref; +}; + +static struct max9295 *prim_priv__; + +struct map_ctx { + u8 dt; + u16 addr; + u8 val; + u8 st_id; +}; + +static int max9295_write_reg(struct device *dev, u16 addr, u8 val) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err; + + err = regmap_write(priv->regmap, addr, val); + if (err) + dev_err(dev, "%s:i2c write failed, 0x%x = %x\n", + __func__, addr, val); + + /* delay before next i2c command as required for SERDES link */ + usleep_range(100, 110); + + return err; +} + +int max9295_setup_streaming(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + u32 csi_mode; + u32 lane_map1; + u32 lane_map2; + u32 port; + u32 rx1_lanes; + u32 st_pipe; + u32 pipe_en; + u32 port_sel = 0; + struct gmsl_link_ctx *g_ctx; + u32 i; + u32 j; + u32 st_en; + + struct map_ctx map_pipe_dtype[] = { + {GMSL_CSI_DT_RAW_12, MAX9295_PIPE_Z_DT_ADDR, 0x2C, + MAX9295_ST_ID_2}, + {GMSL_CSI_DT_UED_U1, MAX9295_PIPE_X_DT_ADDR, 0x30, + MAX9295_ST_ID_0}, + {GMSL_CSI_DT_EMBED, MAX9295_PIPE_Y_DT_ADDR, 0x12, + MAX9295_ST_ID_1}, + }; + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sdev client found\n", __func__); + err = -EINVAL; + goto error; + } + + if (priv->g_client.st_done) { + dev_dbg(dev, "%s: stream setup is already done\n", __func__); + goto error; + } + + g_ctx = priv->g_client.g_ctx; + + switch (g_ctx->csi_mode) { + case GMSL_CSI_1X4_MODE: + csi_mode = MAX9295_CSI_MODE_1X4; + lane_map1 = MAX9295_CSI_1X4_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_1X4_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN4; + break; + case GMSL_CSI_2X2_MODE: + csi_mode = MAX9295_CSI_MODE_2X2; + lane_map1 = MAX9295_CSI_2X2_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_2X2_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN2; + break; + case GMSL_CSI_2X4_MODE: + csi_mode = MAX9295_CSI_MODE_2X4; + lane_map1 = MAX9295_CSI_2X4_MODE_LANE_MAP1; + lane_map2 = MAX9295_CSI_2X4_MODE_LANE_MAP2; + rx1_lanes = MAX9295_CSI_LN4; + break; + default: + dev_err(dev, "%s: invalid csi mode\n", __func__); + err = -EINVAL; + goto error; + } + + port = (g_ctx->src_csi_port == GMSL_CSI_PORT_B) ? + MAX9295_CSI_PORT_B(rx1_lanes) : + MAX9295_CSI_PORT_A(rx1_lanes); + + max9295_write_reg(dev, MAX9295_MIPI_RX0_ADDR, csi_mode); + max9295_write_reg(dev, MAX9295_MIPI_RX1_ADDR, port); + max9295_write_reg(dev, MAX9295_MIPI_RX2_ADDR, lane_map1); + max9295_write_reg(dev, MAX9295_MIPI_RX3_ADDR, lane_map2); + + for (i = 0; i < g_ctx->num_streams; i++) { + struct gmsl_stream *g_stream = &g_ctx->streams[i]; + + g_stream->st_id_sel = GMSL_ST_ID_UNUSED; + for (j = 0; j < ARRAY_SIZE(map_pipe_dtype); j++) { + if (map_pipe_dtype[j].dt == g_stream->st_data_type) { + /* + * TODO: + * 1) Remove link specific overrides, depends + * on #2. + * 2) Add support for vc id based stream sel + * overrides TX_SRC_SEL. would be useful in + * using same mappings in all ser devs. + */ + if (g_ctx->serdes_csi_link == + GMSL_SERDES_CSI_LINK_B) { + map_pipe_dtype[j].addr += 2; + map_pipe_dtype[j].st_id += 1; + } + + g_stream->st_id_sel = map_pipe_dtype[j].st_id; + st_en = (map_pipe_dtype[j].addr == + MAX9295_PIPE_X_DT_ADDR) ? + 0xC0 : 0x40; + + max9295_write_reg(dev, map_pipe_dtype[j].addr, + (st_en | map_pipe_dtype[j].val)); + } + } + } + + for (i = 0; i < g_ctx->num_streams; i++) + if (g_ctx->streams[i].st_id_sel != GMSL_ST_ID_UNUSED) + port_sel |= (1 << g_ctx->streams[i].st_id_sel); + + if (g_ctx->src_csi_port == GMSL_CSI_PORT_B) { + st_pipe = (MAX9295_PIPE_X_START_B | MAX9295_PIPE_Y_START_B | + MAX9295_PIPE_Z_START_B | MAX9295_PIPE_U_START_B); + port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_B); + } else { + st_pipe = MAX9295_PIPE_X_START_A | MAX9295_PIPE_Y_START_A | + MAX9295_PIPE_Z_START_A | MAX9295_PIPE_U_START_A; + port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_A); + } + + pipe_en = (MAX9295_VID_TX_EN_X | MAX9295_VID_TX_EN_Y | + MAX9295_VID_TX_EN_Z | MAX9295_VID_TX_EN_U | MAX9295_VID_INIT); + + max9295_write_reg(dev, MAX9295_START_PIPE_ADDR, st_pipe); + max9295_write_reg(dev, MAX9295_CSI_PORT_SEL_ADDR, port_sel); + max9295_write_reg(dev, MAX9295_PIPE_EN_ADDR, pipe_en); + + priv->g_client.st_done = true; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_setup_streaming); + +int max9295_setup_control(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + struct gmsl_link_ctx *g_ctx; + u32 offset1 = 0; + u32 offset2 = 0; + u32 i; + + u8 i2c_ovrd[] = { + 0x6B, 0x10, + 0x73, 0x11, + 0x7B, 0x30, + 0x83, 0x30, + 0x93, 0x30, + 0x9B, 0x30, + 0xA3, 0x30, + 0xAB, 0x30, + 0x8B, 0x30, + }; + + u8 addr_offset[] = { + 0x80, 0x00, 0x00, + 0x84, 0x00, 0x01, + 0x88, 0x00, 0x01, + 0xC0, 0x02, 0x02, + 0xC4, 0x02, 0x03, + 0xC8, 0x02, 0x03, + }; + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sensor dev client found\n", __func__); + err = -EINVAL; + goto error; + } + + g_ctx = priv->g_client.g_ctx; + +#ifdef CONFIG_OF + if (prim_priv__) { + /* update address reassignment */ + max9295_write_reg(&prim_priv__->i2c_client->dev, + MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); + } +#else + if (priv->def_addr) { + /* update address reassignment */ + struct i2c_client *c = to_i2c_client(dev); + int addr = c->addr; + dev_info(dev, "%s: update address reassignment 0x%x->0x%x\n", __func__,priv->def_addr, g_ctx->ser_reg); + c->addr = priv->def_addr; + max9295_write_reg(dev, + MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1)); + c->addr = addr; + prim_priv__ = priv; + } else { + prim_priv__ = NULL; + } +#endif + if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A) + err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x21); + else + err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x22); + + /* check if serializer device exists */ + if (err) { + dev_err(dev, "%s: ERROR: ser device not found\n", __func__); + goto error; + } + + /* delay to settle link */ + msleep(100); + + for (i = 0; i < ARRAY_SIZE(addr_offset); i += 3) { + if ((g_ctx->ser_reg << 1) == addr_offset[i]) { + offset1 = addr_offset[i+1]; + offset2 = addr_offset[i+2]; + break; + } + } + + if (i == ARRAY_SIZE(addr_offset)) { + dev_err(dev, "%s: invalid ser slave address\n", __func__); + err = -EINVAL; + goto error; + } + + for (i = 0; i < ARRAY_SIZE(i2c_ovrd); i += 2) { + /* update address overrides */ + i2c_ovrd[i+1] += (i < 4) ? offset1 : offset2; + + /* i2c passthrough2 must be configured once for all devices */ + if ((i2c_ovrd[i] == 0x8B) && prim_priv__ && + prim_priv__->pst2_ref) + continue; + + max9295_write_reg(dev, i2c_ovrd[i], i2c_ovrd[i+1]); + } + + /* dev addr pass-through2 ref */ + if (prim_priv__) + prim_priv__->pst2_ref++; + + max9295_write_reg(dev, MAX9295_I2C4_ADDR, (g_ctx->sdev_reg << 1)); + max9295_write_reg(dev, MAX9295_I2C5_ADDR, (g_ctx->sdev_def << 1)); + msleep(100); + max9295_write_reg(dev, MAX9295_SRC_PWDN_ADDR, MAX9295_PWDN_GPIO); + max9295_write_reg(dev, MAX9295_SRC_CTRL_ADDR, MAX9295_RESET_SRC); + max9295_write_reg(dev, MAX9295_SRC_OUT_RCLK_ADDR, MAX9295_SRC_RCLK); + + g_ctx->serdev_found = true; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_setup_control); + +int max9295_reset_control(struct device *dev) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: no sdev client found\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.st_done = false; +#ifndef CONFIG_OF + if (priv->def_addr) { + prim_priv__ = priv; + } else { + prim_priv__ = NULL; + } +#endif + if (prim_priv__) { + prim_priv__->pst2_ref--; + + max9295_write_reg(dev, MAX9295_DEV_ADDR, + (prim_priv__->def_addr << 1)); + if (prim_priv__->pst2_ref == 0) +#ifdef CONFIG_OF + max9295_write_reg(&prim_priv__->i2c_client->dev, + MAX9295_CTRL0_ADDR, MAX9295_RESET_ALL); +#else + if (priv->def_addr) { + /* update address reassingment */ + struct i2c_client *c = to_i2c_client(dev); + int addr = c->addr; + c->addr = priv->def_addr; + max9295_write_reg(dev, + MAX9295_CTRL0_ADDR, MAX9295_RESET_ALL); + c->addr = addr; + } +#endif + } + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_reset_control); + +int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx) +{ + struct max9295 *priv; + int err = 0; + + if (!dev || !g_ctx || !g_ctx->s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + mutex_lock(&priv->lock); + if (priv->g_client.g_ctx) { + dev_err(dev, "%s: device already paired\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.st_done = false; + + priv->g_client.g_ctx = g_ctx; + +error: + mutex_unlock(&priv->lock); + return 0; +} +EXPORT_SYMBOL(max9295_sdev_pair); + +int max9295_sdev_unpair(struct device *dev, struct device *s_dev) +{ + struct max9295 *priv = NULL; + int err = 0; + + if (!dev || !s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + if (!priv->g_client.g_ctx) { + dev_err(dev, "%s: device is not paired\n", __func__); + err = -ENOMEM; + goto error; + } + + if (priv->g_client.g_ctx->s_dev != s_dev) { + dev_err(dev, "%s: invalid device\n", __func__); + err = -EINVAL; + goto error; + } + + priv->g_client.g_ctx = NULL; + priv->g_client.st_done = false; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9295_sdev_unpair); + +static struct regmap_config max9295_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +struct reg_pair { + u16 addr; + u8 val; +}; + +static int max9295_set_registers(struct device *dev, struct reg_pair *map, + u32 count) +{ + int err = 0; + u32 j = 0; + + for (j = 0; j < count; j++) { + err = max9295_write_reg(dev, + map[j].addr, map[j].val); + if (err != 0) + break; + } + + return err; +} + +static int __max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) +{ + int err = 0; + u8 bpp = 0x30; + static u8 pipe_x_val = 0x0; + struct reg_pair map_multi_pipe_en[] = { + {0x0315, 0x80}, + }; + struct reg_pair map_bpp8dbl[] = { + {0x0312, 0x0F}, + }; + struct reg_pair map_pipe_control[] = { + /* addr, val */ + {MAX9295_PIPE_X_DT_ADDR, 0x5E}, // Pipe X pulls data_type1 + {0x0315, 0x52}, // Pipe X pulls data_type2 + {0x0309, 0x01}, // # Pipe X pulls vc_id + {0x030A, 0x00}, + {0x031C, 0x30}, // BPP in pipe X + {0x0102, 0x0E}, // LIM_HEART Pipe X: Disabled + }; + + if (data_type1 == GMSL_CSI_DT_RAW_8 || data_type1 == GMSL_CSI_DT_EMBED + || data_type2 == GMSL_CSI_DT_RAW_8 || data_type2 == GMSL_CSI_DT_EMBED) { + map_bpp8dbl[0].val |= (1 << pipe_id); + } else { + map_bpp8dbl[0].val &= ~(1 << pipe_id); + } + err |= max9295_set_registers(dev, map_bpp8dbl, ARRAY_SIZE(map_bpp8dbl)); + + if (data_type1 == GMSL_CSI_DT_RGB_888) + bpp = 0x18; + + map_pipe_control[0].addr += 0x2 * pipe_id; + map_pipe_control[1].addr += 0x2 * pipe_id; + map_pipe_control[2].addr += 0x2 * pipe_id; + map_pipe_control[3].addr += 0x2 * pipe_id; + map_pipe_control[4].addr += 0x1 * pipe_id; + map_pipe_control[5].addr += 0x8 * pipe_id; + + map_pipe_control[0].val = 0x40 | data_type1; + map_pipe_control[1].val = 0x40 | data_type2; + map_pipe_control[2].val = 1 << vc_id; + map_pipe_control[3].val = 0x00; + map_pipe_control[4].val = bpp; + map_pipe_control[5].val = 0x0E; + + if (pipe_id == 0) + pipe_x_val = map_pipe_control[1].val; + + err |= max9295_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + map_multi_pipe_en[0].val = 0x80 | pipe_x_val; + err |= max9295_set_registers(dev, map_multi_pipe_en, + ARRAY_SIZE(map_multi_pipe_en)); + + return err; +} + +int max9295_init_settings(struct device *dev) +{ + int err = 0; + int i; + struct max9295 *priv = dev_get_drvdata(dev); + + struct reg_pair map_pipe_opt[] = { + // Enable all pipes + {MAX9295_PIPE_EN_ADDR, 0xF3}, + // Write 0x33 for 4 lanes + {MAX9295_MIPI_RX1_ADDR, 0x11}, + // All pipes pull clock from port B + {MAX9295_CSI_PORT_SEL_ADDR, 0x6F}, + // All pipes pull data from port B + {MAX9295_START_PIPE_ADDR, 0xF0}, + }; + + mutex_lock(&priv->lock); + + // Init control + err |= max9295_set_registers(dev, map_pipe_opt, + ARRAY_SIZE(map_pipe_opt)); + + for (i = 0; i < MAX9295_MAX_PIPES; i++) + err |= __max9295_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9295_init_settings); + +int max9295_set_pipe(struct device *dev, int pipe_id, + u8 data_type1, u8 data_type2, u32 vc_id) +{ + struct max9295 *priv = dev_get_drvdata(dev); + int err = 0; + + if (pipe_id > (MAX9295_MAX_PIPES - 1)) { + dev_info(dev, "%s, input pipe_id: %d exceed max9295 max pipes\n", + __func__, pipe_id); + return -EINVAL; + } + + dev_dbg(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", + __func__, pipe_id, data_type1, data_type2, vc_id); + + mutex_lock(&priv->lock); + + err = __max9295_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9295_set_pipe); + +static int max9295_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max9295 *priv; + struct max9295_pdata *pdata = client->dev.platform_data; + int err = 0; +#ifdef CONFIG_OF + struct device_node *node = client->dev.of_node; +#endif + + dev_info(&client->dev, "[MAX9295]: probing GMSL Serializer\n"); + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv->i2c_client = client; + priv->regmap = devm_regmap_init_i2c(priv->i2c_client, + &max9295_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } + + mutex_init(&priv->lock); +#ifdef CONFIG_OF + if (of_get_property(node, "is-prim-ser", NULL)) { + if (prim_priv__) { + dev_err(&client->dev, + "prim-ser already exists\n"); + return -EEXIST; + } + + err = of_property_read_u32(node, "reg", &priv->def_addr); + if (err < 0) { + dev_err(&client->dev, "reg not found\n"); + return -EINVAL; + } + + prim_priv__ = priv; + } +#else + if (pdata->is_prim_ser) { + prim_priv__ = priv; + priv->def_addr = pdata->def_addr; + } else { + priv->def_addr = 0; + } +#endif + dev_set_drvdata(&client->dev, priv); + + /* dev communication gets validated when GMSL link setup is done */ + dev_info(&client->dev, "%s: success\n", __func__); + + return err; +} + +static int max9295_remove(struct i2c_client *client) +{ + struct max9295 *priv; + + if (client != NULL) { + priv = dev_get_drvdata(&client->dev); + dev_info(&client->dev, "[MAX9295]: remove GMSL Serializer\n"); + mutex_destroy(&priv->lock); +#ifdef CONFIG_OF + i2c_unregister_device(client); + client = NULL; +#endif + } + + return 0; +} + +static const struct i2c_device_id max9295_id[] = { + { "max9295", 0 }, + { }, +}; + +static const struct of_device_id max9295_of_match[] = { + { .compatible = "maxim,max9295", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max9295_of_match); +MODULE_DEVICE_TABLE(i2c, max9295_id); + +static struct i2c_driver max9295_i2c_driver = { + .driver = { + .name = "max9295", + .owner = THIS_MODULE, + }, + .probe = max9295_probe, + .remove = max9295_remove, + .id_table = max9295_id, +}; + +static int __init max9295_init(void) +{ + return i2c_add_driver(&max9295_i2c_driver); +} + +static void __exit max9295_exit(void) +{ + i2c_del_driver(&max9295_i2c_driver); +} + +module_init(max9295_init); +module_exit(max9295_exit); + +MODULE_DESCRIPTION("GMSL Serializer driver max9295"); +MODULE_AUTHOR("Sudhir Vyas "); +MODULE_AUTHOR("Dmitry Perchanov "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/max9296.c b/drivers/media/i2c/max9296.c new file mode 100644 index 000000000000..2a5e34f46000 --- /dev/null +++ b/drivers/media/i2c/max9296.c @@ -0,0 +1,1167 @@ +/* + * max9296.c - max9296 GMSL Deserializer driver + * + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include + +/* register specifics */ +#define MAX9296_DST_CSI_MODE_ADDR 0x330 +#define MAX9296_LANE_MAP1_ADDR 0x333 +#define MAX9296_LANE_MAP2_ADDR 0x334 + +#define MAX9296_LANE_CTRL0_ADDR 0x40A +#define MAX9296_LANE_CTRL1_ADDR 0x44A +#define MAX9296_LANE_CTRL2_ADDR 0x48A +#define MAX9296_LANE_CTRL3_ADDR 0x4CA + +#define MAX9296_TX11_PIPE_X_EN_ADDR 0x40B +#define MAX9296_TX45_PIPE_X_DST_CTRL_ADDR 0x42D + +#define MAX9296_PIPE_X_SRC_0_MAP_ADDR 0x40D +#define MAX9296_PIPE_X_DST_0_MAP_ADDR 0x40E +#define MAX9296_PIPE_X_SRC_1_MAP_ADDR 0x40F +#define MAX9296_PIPE_X_DST_1_MAP_ADDR 0x410 +#define MAX9296_PIPE_X_SRC_2_MAP_ADDR 0x411 +#define MAX9296_PIPE_X_DST_2_MAP_ADDR 0x412 +#define MAX9296_PIPE_X_SRC_3_MAP_ADDR 0x413 +#define MAX9296_PIPE_X_DST_3_MAP_ADDR 0x414 + +#define MAX9296_PIPE_X_ST_SEL_ADDR 0x50 + +#define MAX9296_PWDN_PHYS_ADDR 0x332 +#define MAX9296_PHY1_CLK_ADDR 0x320 +#define MAX9296_CTRL0_ADDR 0x10 + +/* data defines */ +#define MAX9296_CSI_MODE_4X2 0x1 +#define MAX9296_CSI_MODE_2X4 0x4 +#define MAX9296_LANE_MAP1_4X2 0x44 +#define MAX9296_LANE_MAP2_4X2 0x44 +#define MAX9296_LANE_MAP1_2X4 0x4E +#define MAX9296_LANE_MAP2_2X4 0xE4 + +#define MAX9296_LANE_CTRL_MAP(num_lanes) \ + (((num_lanes) << 6) & 0xF0) + +#define MAX9296_ALLPHYS_NOSTDBY 0xF0 +#define MAX9296_ST_ID_SEL_INVALID 0xF + +#define MAX9296_PHY1_CLK 0x2C + +#define MAX9296_RESET_ALL 0x80 + +/* Dual GMSL MAX9296A/B */ +#define MAX9296_MAX_SOURCES 2 + +#define MAX9296_MAX_PIPES 4 + +#define MAX9296_PIPE_X 0 +#define MAX9296_PIPE_Y 1 +#define MAX9296_PIPE_Z 2 +#define MAX9296_PIPE_U 3 +#define MAX9296_PIPE_INVALID 0xF + + +#define MAX9296_CSI_CTRL_0 0 +#define MAX9296_CSI_CTRL_1 1 +#define MAX9296_CSI_CTRL_2 2 +#define MAX9296_CSI_CTRL_3 3 + +#define MAX9296_INVAL_ST_ID 0xFF + +/* Use reset value as per spec, confirm with vendor */ +#define MAX9296_RESET_ST_ID 0x00 + +struct max9296_source_ctx { + struct gmsl_link_ctx *g_ctx; + bool st_enabled; +}; + +struct pipe_ctx { + u32 id; + u32 dt_type; + u32 dst_csi_ctrl; + u32 st_count; + u32 st_id_sel; +}; + +struct max9296 { + struct i2c_client *i2c_client; + struct regmap *regmap; + u32 num_src; + u32 max_src; + u32 num_src_found; + u32 src_link; + bool splitter_enabled; + struct max9296_source_ctx sources[MAX9296_MAX_SOURCES]; + struct mutex lock; + u32 sdev_ref; + bool lane_setup; + bool link_setup; + struct pipe_ctx pipe[MAX9296_MAX_PIPES]; + u8 csi_mode; + u8 lane_mp1; + u8 lane_mp2; + int reset_gpio; + int pw_ref; + struct regulator *vdd_cam_1v2; +}; + +static int max9296_write_reg(struct device *dev, + u16 addr, u8 val) +{ + struct max9296 *priv; + int err; + + priv = dev_get_drvdata(dev); + + err = regmap_write(priv->regmap, addr, val); + if (err) + dev_err(dev, + "%s:i2c write failed, 0x%x = %x\n", + __func__, addr, val); + + /* delay before next i2c command as required for SERDES link */ + usleep_range(100, 110); + + return err; +} + +static int max9296_get_sdev_idx(struct device *dev, + struct device *s_dev, unsigned int *idx) +{ + struct max9296 *priv = dev_get_drvdata(dev); + unsigned int i; + int err = 0; + + mutex_lock(&priv->lock); + for (i = 0; i < priv->max_src; i++) { + if (priv->sources[i].g_ctx->s_dev == s_dev) + break; + } + if (i == priv->max_src) { + dev_err(dev, "no sdev found\n"); + err = -EINVAL; + goto ret; + } + + if (idx) + *idx = i; + +ret: + mutex_unlock(&priv->lock); + return err; +} + +static void max9296_pipes_reset(struct max9296 *priv) +{ + /* + * This is default pipes combination. add more mappings + * for other combinations and requirements. + */ + struct pipe_ctx pipe_defaults[] = { + {MAX9296_PIPE_X, GMSL_CSI_DT_RAW_12, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_Y, GMSL_CSI_DT_RAW_12, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_Z, GMSL_CSI_DT_EMBED, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID}, + {MAX9296_PIPE_U, GMSL_CSI_DT_EMBED, + MAX9296_CSI_CTRL_1, 0, MAX9296_INVAL_ST_ID} + }; + + /* + * Add DT props for num-streams and stream sequence, and based on that + * set the appropriate pipes defaults. + * For now default it supports "2 RAW12 and 2 EMBED" 1:1 mappings. + */ + memcpy(priv->pipe, pipe_defaults, sizeof(pipe_defaults)); +} + +static void max9296_reset_ctx(struct max9296 *priv) +{ + unsigned int i; + + priv->link_setup = false; + priv->lane_setup = false; + priv->num_src_found = 0; + priv->src_link = 0; + priv->splitter_enabled = false; + max9296_pipes_reset(priv); + for (i = 0; i < priv->num_src; i++) + priv->sources[i].st_enabled = false; +} + +int max9296_power_on(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (priv->pw_ref == 0) { + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + usleep_range(30, 50); + + if (priv->vdd_cam_1v2) { + err = regulator_enable(priv->vdd_cam_1v2); + if (unlikely(err)) + goto ret; + } + + usleep_range(30, 50); + + /*exit reset mode: XCLR */ + if (priv->reset_gpio) { + gpio_set_value(priv->reset_gpio, 0); + usleep_range(30, 50); + gpio_set_value(priv->reset_gpio, 1); + usleep_range(30, 50); + } + + /* delay to settle reset */ + msleep(20); + } + + priv->pw_ref++; + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_power_on); + +void max9296_power_off(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + priv->pw_ref--; + + if (priv->pw_ref < 0) + priv->pw_ref = 0; + + if (priv->pw_ref == 0) { + /* enter reset mode: XCLR */ + usleep_range(1, 2); + if (priv->reset_gpio) + gpio_set_value(priv->reset_gpio, 0); + + if (priv->vdd_cam_1v2) + regulator_disable(priv->vdd_cam_1v2); + } + + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max9296_power_off); + +static int max9296_write_link(struct device *dev, u32 link) +{ + if (link == GMSL_SERDES_CSI_LINK_A) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x01); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x21); + } else if (link == GMSL_SERDES_CSI_LINK_B) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x02); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x22); + } else { + dev_err(dev, "%s: invalid gmsl link\n", __func__); + return -EINVAL; + } + + /* delay to settle link */ + msleep(100); + + return 0; +} + +int max9296_setup_link(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + + if (!priv->splitter_enabled) { + err = max9296_write_link(dev, + priv->sources[i].g_ctx->serdes_csi_link); + if (err) + goto ret; + + priv->link_setup = true; + } + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_setup_link); + +int max9296_setup_control(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + + if (!priv->link_setup) { + dev_err(dev, "%s: invalid state\n", __func__); + err = -EINVAL; + goto error; + } + + if (priv->sources[i].g_ctx->serdev_found) { + priv->num_src_found++; + priv->src_link = priv->sources[i].g_ctx->serdes_csi_link; + } + + /* Enable splitter mode */ + if ((priv->max_src > 1U) && + (priv->num_src_found > 0U) && + (priv->splitter_enabled == false)) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x03); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x23); + + priv->splitter_enabled = true; + + /* delay to settle link */ + msleep(100); + } + + max9296_write_reg(dev, + MAX9296_PWDN_PHYS_ADDR, MAX9296_ALLPHYS_NOSTDBY); + + priv->sdev_ref++; + + /* Reset splitter mode if all devices are not found */ + if ((priv->sdev_ref == priv->max_src) && + (priv->splitter_enabled == true) && + (priv->num_src_found > 0U) && + (priv->num_src_found < priv->max_src)) { + err = max9296_write_link(dev, priv->src_link); + if (err) + goto error; + + priv->splitter_enabled = false; + } + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_setup_control); + +int max9296_reset_control(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + mutex_lock(&priv->lock); + if (!priv->sdev_ref) { + dev_info(dev, "%s: dev is already in reset state\n", __func__); + goto ret; + } + + priv->sdev_ref--; + if (priv->sdev_ref == 0) { + max9296_reset_ctx(priv); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, MAX9296_RESET_ALL); + + /* delay to settle reset */ + msleep(100); + } + +ret: + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_reset_control); + +int max9296_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx) +{ + struct max9296 *priv = NULL; + unsigned int i; + int err = 0; + + if (!dev || !g_ctx || !g_ctx->s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + if (priv->num_src > priv->max_src) { + dev_err(dev, + "%s: MAX9296 inputs size exhausted\n", __func__); + err = -ENOMEM; + goto error; + } + + /* Check csi mode compatibility */ + if (!((priv->csi_mode == MAX9296_CSI_MODE_2X4) ? + ((g_ctx->csi_mode == GMSL_CSI_1X4_MODE) || + (g_ctx->csi_mode == GMSL_CSI_2X4_MODE)) : + ((g_ctx->csi_mode == GMSL_CSI_2X2_MODE) || + (g_ctx->csi_mode == GMSL_CSI_4X2_MODE)))) { + dev_err(dev, + "%s: csi mode not supported\n", __func__); + err = -EINVAL; + goto error; + } + + for (i = 0; i < priv->num_src; i++) { + if (g_ctx->serdes_csi_link == + priv->sources[i].g_ctx->serdes_csi_link) { + dev_err(dev, + "%s: serdes csi link is in use\n", __func__); + err = -EINVAL; + goto error; + } + /* + * All sdevs should have same num-csi-lanes regardless of + * dst csi port selected. + * Later if there is any usecase which requires each port + * to be configured with different num-csi-lanes, then this + * check should be performed per port. + */ + if (g_ctx->num_csi_lanes != + priv->sources[i].g_ctx->num_csi_lanes) { + dev_err(dev, + "%s: csi num lanes mismatch\n", __func__); + err = -EINVAL; + goto error; + } + } + + priv->sources[priv->num_src].g_ctx = g_ctx; + priv->sources[priv->num_src].st_enabled = false; + + priv->num_src++; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_sdev_register); + +int max9296_sdev_unregister(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = NULL; + int err = 0; + unsigned int i = 0; + + if (!dev || !s_dev) { + dev_err(dev, "%s: invalid input params\n", __func__); + return -EINVAL; + } + + priv = dev_get_drvdata(dev); + mutex_lock(&priv->lock); + + if (priv->num_src == 0) { + dev_err(dev, "%s: no source found\n", __func__); + err = -ENODATA; + goto error; + } + + for (i = 0; i < priv->num_src; i++) { + if (s_dev == priv->sources[i].g_ctx->s_dev) { + priv->sources[i].g_ctx = NULL; + break; + } + } + + if (i == priv->num_src) { + dev_err(dev, + "%s: requested device not found\n", __func__); + err = -EINVAL; + goto error; + } + priv->num_src--; + +error: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_sdev_unregister); + +static int max9296_get_available_pipe(struct device *dev, + u32 st_data_type, u32 dst_csi_port) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < MAX9296_MAX_PIPES; i++) { + /* + * TODO: Enable a pipe for multi stream configuration having + * similar stream data type. For now use st_count as a flag + * for 1 to 1 mapping in pipe and stream data type, same can + * be extended as count for many to 1 mapping. Would also need + * few more checks such as input stream id select, dst port etc. + */ + if ((priv->pipe[i].dt_type == st_data_type) && + ((dst_csi_port == GMSL_CSI_PORT_A) ? + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_0) || + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_1) : + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_2) || + (priv->pipe[i].dst_csi_ctrl == + MAX9296_CSI_CTRL_3)) && + (!priv->pipe[i].st_count)) + break; + } + + if (i == MAX9296_MAX_PIPES) { + dev_err(dev, "%s: all pipes are busy\n", __func__); + return -ENOMEM; + } + + return i; +} + +struct reg_pair { + u16 addr; + u8 val; +}; + +static int max9296_setup_pipeline(struct device *dev, + struct gmsl_link_ctx *g_ctx) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_stream *g_stream; + struct reg_pair *map_list; + u32 arr_sz = 0; + int pipe_id = 0; + u32 i = 0; + u32 j = 0; + u32 vc_idx = 0; + + for (i = 0; i < g_ctx->num_streams; i++) { + /* Base data type mapping: pipeX/RAW12/CSICNTR1 */ + struct reg_pair map_pipe_raw12[] = { + /* addr, val */ + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x7}, + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x15}, + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x2C}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x2C}, + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + }; + + /* Base data type mapping: pipeX/EMBED/CSICNTR1 */ + struct reg_pair map_pipe_embed[] = { + /* addr, val */ + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x7}, + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x15}, + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + }; + + g_stream = &g_ctx->streams[i]; + g_stream->des_pipe = MAX9296_PIPE_INVALID; + + if (g_stream->st_data_type == GMSL_CSI_DT_RAW_12) { + map_list = map_pipe_raw12; + arr_sz = ARRAY_SIZE(map_pipe_raw12); + } else if (g_stream->st_data_type == GMSL_CSI_DT_EMBED) { + map_list = map_pipe_embed; + arr_sz = ARRAY_SIZE(map_pipe_embed); + } else if (g_stream->st_data_type == GMSL_CSI_DT_UED_U1) { + dev_dbg(dev, + "%s: No mapping for GMSL_CSI_DT_UED_U1\n", + __func__); + continue; + } else { + dev_err(dev, "%s: Invalid data type\n", __func__); + return -EINVAL; + } + + pipe_id = max9296_get_available_pipe(dev, + g_stream->st_data_type, g_ctx->dst_csi_port); + if (pipe_id < 0) + return pipe_id; + + for (j = 0, vc_idx = 3; j < arr_sz; j++, vc_idx += 2) { + /* update pipe configuration */ + map_list[j].addr += (0x40 * pipe_id); + /* update vc id configuration */ + if (vc_idx < arr_sz) + map_list[vc_idx].val |= + (g_ctx->dst_vc << 6); + + max9296_write_reg(dev, map_list[j].addr, + map_list[j].val); + } + + /* Set stream id select input */ + if (g_stream->st_id_sel == GMSL_ST_ID_UNUSED) { + dev_err(dev, "%s: Invalid stream st_id_sel\n", + __func__); + return -EINVAL; + } + + g_stream->des_pipe = MAX9296_PIPE_X_ST_SEL_ADDR + pipe_id; + + /* Update pipe internals */ + priv->pipe[pipe_id].st_count++; + priv->pipe[pipe_id].st_id_sel = g_stream->st_id_sel; + } + + return 0; +} + +int max9296_start_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + struct gmsl_stream *g_stream; + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + g_ctx = priv->sources[i].g_ctx; + + for (i = 0; i < g_ctx->num_streams; i++) { + g_stream = &g_ctx->streams[i]; + + if (g_stream->des_pipe != MAX9296_PIPE_INVALID) + max9296_write_reg(dev, g_stream->des_pipe, + g_stream->st_id_sel); + } + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_start_streaming); + +int max9296_stop_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + struct gmsl_stream *g_stream; + int err = 0; + unsigned int i = 0; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + g_ctx = priv->sources[i].g_ctx; + + for (i = 0; i < g_ctx->num_streams; i++) { + g_stream = &g_ctx->streams[i]; + + if (g_stream->des_pipe != MAX9296_PIPE_INVALID) + max9296_write_reg(dev, g_stream->des_pipe, + MAX9296_RESET_ST_ID); + } + + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_stop_streaming); + +int max9296_setup_streaming(struct device *dev, struct device *s_dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + struct gmsl_link_ctx *g_ctx; + int err = 0; + unsigned int i = 0; + u16 lane_ctrl_addr; + + err = max9296_get_sdev_idx(dev, s_dev, &i); + if (err) + return err; + + mutex_lock(&priv->lock); + if (priv->sources[i].st_enabled) + goto ret; + + g_ctx = priv->sources[i].g_ctx; + + err = max9296_setup_pipeline(dev, g_ctx); + if (err) + goto ret; + + /* Derive CSI lane map register */ + switch(g_ctx->dst_csi_port) { + case GMSL_CSI_PORT_A: + case GMSL_CSI_PORT_D: + lane_ctrl_addr = MAX9296_LANE_CTRL1_ADDR; + break; + case GMSL_CSI_PORT_B: + case GMSL_CSI_PORT_E: + lane_ctrl_addr = MAX9296_LANE_CTRL2_ADDR; + break; + case GMSL_CSI_PORT_C: + lane_ctrl_addr = MAX9296_LANE_CTRL0_ADDR; + break; + case GMSL_CSI_PORT_F: + lane_ctrl_addr = MAX9296_LANE_CTRL3_ADDR; + break; + default: + dev_err(dev, "%s: invalid gmsl csi port!\n", __func__); + err = -EINVAL; + goto ret; + }; + + /* + * rewrite num_lanes to same dst port should not be an issue, + * as the device compatibility is already + * checked during sdev registration against the des properties. + */ + max9296_write_reg(dev, lane_ctrl_addr, + MAX9296_LANE_CTRL_MAP(g_ctx->num_csi_lanes-1)); + + if (!priv->lane_setup) { + max9296_write_reg(dev, + MAX9296_DST_CSI_MODE_ADDR, priv->csi_mode); + max9296_write_reg(dev, + MAX9296_LANE_MAP1_ADDR, priv->lane_mp1); + max9296_write_reg(dev, + MAX9296_LANE_MAP2_ADDR, priv->lane_mp2); + max9296_write_reg(dev, + MAX9296_PHY1_CLK_ADDR, MAX9296_PHY1_CLK); + + priv->lane_setup = true; + } + + priv->sources[i].st_enabled = true; + +ret: + mutex_unlock(&priv->lock); + return err; +} +EXPORT_SYMBOL(max9296_setup_streaming); + +static int max9296_set_registers(struct device *dev, struct reg_pair *map, + u32 count) +{ + int err = 0; + u32 j = 0; + + for (j = 0; j < count; j++) { + err = max9296_write_reg(dev, + map[j].addr, map[j].val); + if (err != 0) + break; + } + + return err; +} + +int max9296_get_available_pipe_id(struct device *dev, int vc_id) +{ + int i; + int pipe_id = -ENOMEM; + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + for (i = 0; i < MAX9296_MAX_PIPES; i++) { + if (i == vc_id && !priv->pipe[i].st_count) { + priv->pipe[i].st_count++; + pipe_id = i; + break; + } + } + mutex_unlock(&priv->lock); + + return pipe_id; +} +EXPORT_SYMBOL(max9296_get_available_pipe_id); + +int max9296_release_pipe(struct device *dev, int pipe_id) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + if (pipe_id < 0 || pipe_id >= MAX9296_MAX_PIPES) + return -EINVAL; + + mutex_lock(&priv->lock); + priv->pipe[pipe_id].st_count = 0; + mutex_unlock(&priv->lock); + + return 0; +} +EXPORT_SYMBOL(max9296_release_pipe); + +void max9296_reset_oneshot(struct device *dev) +{ + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + if (priv->splitter_enabled) { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x03); + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x23); + } else { + max9296_write_reg(dev, MAX9296_CTRL0_ADDR, 0x31); + } + /* delay to settle link */ + msleep(100); + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(max9296_reset_oneshot); + +static int __max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id) +{ + int err = 0; + int i = 0; + u8 en_mapping_num = 0x0F; + u8 all_mapping_phy = 0x55; + struct reg_pair map_pipe_opt[] = { + {0x1458, 0x28}, // PHY A Optimization + {0x1459, 0x68}, // PHY A Optimization + {0x1558, 0x28}, // PHY B Optimization + {0x1559, 0x68}, // PHY B Optimization + // 4 lanes on port A, write 0x50 for 2 lanes + {MAX9296_LANE_CTRL1_ADDR, 0x50}, + // 1500Mbps/lane on port A + {MAX9296_PHY1_CLK_ADDR, 0x2F}, + // Do not un-double 8bpp (Un-double 8bpp data) + //{0x031C, 0x00}, + // Do not un-double 8bpp + //{0x031F, 0x00}, + // 0x02: ALT_MEM_MAP8, 0x10: ALT2_MEM_MAP8 + {0x0473, 0x10}, + }; + struct reg_pair map_pipe_control[] = { + // Enable 4 mappings for Pipe X + {MAX9296_TX11_PIPE_X_EN_ADDR, 0x0F}, + // Map data_type1 on vc_id + {MAX9296_PIPE_X_SRC_0_MAP_ADDR, 0x1E}, + {MAX9296_PIPE_X_DST_0_MAP_ADDR, 0x1E}, + // Map frame_start on vc_id + {MAX9296_PIPE_X_SRC_1_MAP_ADDR, 0x00}, + {MAX9296_PIPE_X_DST_1_MAP_ADDR, 0x00}, + // Map frame end on vc_id + {MAX9296_PIPE_X_SRC_2_MAP_ADDR, 0x01}, + {MAX9296_PIPE_X_DST_2_MAP_ADDR, 0x01}, + // Map data_type2 on vc_id + {MAX9296_PIPE_X_SRC_3_MAP_ADDR, 0x12}, + {MAX9296_PIPE_X_DST_3_MAP_ADDR, 0x12}, + // All mappings to PHY1 (master for port A) + {MAX9296_TX45_PIPE_X_DST_CTRL_ADDR, 0x55}, + // SEQ_MISS_EN: Disabled / DIS_PKT_DET: Disabled + {0x0100, 0x23}, // pipe X + }; + + for (i = 0; i < 10; i++) { + map_pipe_control[i].addr += 0x40 * pipe_id; + } + map_pipe_control[10].addr += 0x12 * pipe_id; + + if (data_type2 == 0x0) { + en_mapping_num = 0x07; + all_mapping_phy = 0x15; + } + map_pipe_control[0].val = en_mapping_num; + map_pipe_control[1].val = (vc_id << 6) | data_type1; + map_pipe_control[2].val = (vc_id << 6) | data_type1; + map_pipe_control[3].val = (vc_id << 6) | 0x00; + map_pipe_control[4].val = (vc_id << 6) | 0x00; + map_pipe_control[5].val = (vc_id << 6) | 0x01; + map_pipe_control[6].val = (vc_id << 6) | 0x01; + map_pipe_control[7].val = (vc_id << 6) | data_type2; + map_pipe_control[8].val = (vc_id << 6) | data_type2; + map_pipe_control[9].val = all_mapping_phy; + map_pipe_control[10].val = 0x23; + + err |= max9296_set_registers(dev, map_pipe_control, + ARRAY_SIZE(map_pipe_control)); + + err |= max9296_set_registers(dev, map_pipe_opt, + ARRAY_SIZE(map_pipe_opt)); + + return err; +} +int max9296_init_settings(struct device *dev) +{ + int err = 0; + int i; + struct max9296 *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->lock); + + for (i = 0; i < MAX9296_MAX_PIPES; i++) + err |= __max9296_set_pipe(dev, i, GMSL_CSI_DT_YUV422_8, + GMSL_CSI_DT_EMBED, i); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_init_settings); + +int max9296_set_pipe(struct device *dev, int pipe_id, + u8 data_type1, u8 data_type2, u32 vc_id) +{ + struct max9296 *priv = dev_get_drvdata(dev); + int err = 0; + + if (pipe_id > (MAX9296_MAX_PIPES - 1)) { + dev_info(dev, "%s, input pipe_id: %d exceed max9296 max pipes\n", + __func__, pipe_id); + return -EINVAL; + } + + dev_dbg(dev, "%s pipe_id %d, data_type1 %u, data_type2 %u, vc_id %u\n", + __func__, pipe_id, data_type1, data_type2, vc_id); + + mutex_lock(&priv->lock); + + err = __max9296_set_pipe(dev, pipe_id, data_type1, data_type2, vc_id); + + mutex_unlock(&priv->lock); + + return err; +} +EXPORT_SYMBOL(max9296_set_pipe); + +static const struct of_device_id max9296_of_match[] = { + { .compatible = "maxim,max9296", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max9296_of_match); + +#ifdef CONFIG_OF +static int max9296_parse_dt(struct max9296 *priv, + struct i2c_client *client) +{ + struct device_node *node = client->dev.of_node; + int err = 0; + const char *str_value; + int value; + const struct of_device_id *match; + + if (!node) + return -EINVAL; + + match = of_match_device(max9296_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return -EFAULT; + } + + err = of_property_read_string(node, "csi-mode", &str_value); + if (err < 0) { + dev_err(&client->dev, "csi-mode property not found\n"); + return err; + } + + if (!strcmp(str_value, "2x4")) { + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + } else if (!strcmp(str_value, "4x2")) { + priv->csi_mode = MAX9296_CSI_MODE_4X2; + priv->lane_mp1 = MAX9296_LANE_MAP1_4X2; + priv->lane_mp2 = MAX9296_LANE_MAP2_4X2; + } else { + dev_err(&client->dev, "invalid csi mode\n"); + return -EINVAL; + } + + err = of_property_read_u32(node, "max-src", &value); + if (err < 0) { + dev_err(&client->dev, "No max-src info\n"); + return err; + } + priv->max_src = value; + + priv->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0); + if (priv->reset_gpio < 0) { + dev_err(&client->dev, "reset-gpios not found %d\n", err); + return err; + } + + /* digital 1.2v */ + if (of_get_property(node, "vdd_cam_1v2-supply", NULL)) { + priv->vdd_cam_1v2 = regulator_get(&client->dev, "vdd_cam_1v2"); + if (IS_ERR(priv->vdd_cam_1v2)) { + dev_err(&client->dev, + "vdd_cam_1v2 regulator get failed\n"); + err = PTR_ERR(priv->vdd_cam_1v2); + priv->vdd_cam_1v2 = NULL; + return err; + } + } else { + priv->vdd_cam_1v2 = NULL; + } + + return 0; +} +#else +static int max9296_parse_pdata(struct max9296 *priv, + struct i2c_client *client) +{ + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + priv->max_src = 1; + priv->reset_gpio = 0; + priv->vdd_cam_1v2 = NULL; + return 0; +} + +#endif +static struct regmap_config max9296_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static int max9296_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max9296 *priv; + int err = 0; + + dev_info(&client->dev, "[MAX9296]: probing GMSL Deserializer\n"); + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv->i2c_client = client; + priv->regmap = devm_regmap_init_i2c(priv->i2c_client, + &max9296_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } +#ifdef CONFIG_OF + err = max9296_parse_dt(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse dt\n"); + return -EFAULT; + } +#else + err = max9296_parse_pdata(priv, client); + if (err) { + dev_err(&client->dev, "unable to parse pdata\n"); + return -EFAULT; + } +#endif + max9296_pipes_reset(priv); + + if (priv->max_src > MAX9296_MAX_SOURCES) { + dev_err(&client->dev, + "max sources more than currently supported\n"); + return -EINVAL; + } + + mutex_init(&priv->lock); + + dev_set_drvdata(&client->dev, priv); + + /* dev communication gets validated when GMSL link setup is done */ + dev_info(&client->dev, "%s: success\n", __func__); + + return err; +} + + +static int max9296_remove(struct i2c_client *client) +{ + struct max9296 *priv; + + if (client != NULL) { + priv = dev_get_drvdata(&client->dev); + dev_info(&client->dev, "[MAX9296]: remove GMSL Deserializer\n"); + mutex_destroy(&priv->lock); +#ifdef CONFIG_OF + i2c_unregister_device(client); + client = NULL; +#endif + } + + return 0; +} + +static const struct i2c_device_id max9296_id[] = { + { "max9296", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max9296_id); + +static struct i2c_driver max9296_i2c_driver = { + .driver = { + .name = "max9296", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max9296_of_match), + }, + .probe = max9296_probe, + .remove = max9296_remove, + .id_table = max9296_id, +}; + +static int __init max9296_init(void) +{ + return i2c_add_driver(&max9296_i2c_driver); +} + +static void __exit max9296_exit(void) +{ + i2c_del_driver(&max9296_i2c_driver); +} + +module_init(max9296_init); +module_exit(max9296_exit); + +MODULE_DESCRIPTION("Dual GMSL Deserializer driver max9296"); +MODULE_AUTHOR("Sudhir Vyas . + */ + +/** + * @file + * GMSL API: Gigabit Multimedia Serial Link protocol + * + * @b Description: Defines elements used to set up and use a GMSL link. + */ + +#ifndef __GMSL_LINK_H__ +/** + * \defgroup GMSL Gigabit Multimedia Serial Link (GMSL) + * + * Defines the interface used to control the MAX9295 serializer and + * MAX9296 deserializer modules. + * + * @ingroup serdes_group + * @{ + */ + +#define __GMSL_LINK_H__ + +#define GMSL_CSI_1X4_MODE 0x1 +#define GMSL_CSI_2X4_MODE 0x2 +#define GMSL_CSI_2X2_MODE 0x3 +#define GMSL_CSI_4X2_MODE 0x4 + +#define GMSL_CSI_PORT_A 0x0 +#define GMSL_CSI_PORT_B 0x1 +#define GMSL_CSI_PORT_C 0x2 +#define GMSL_CSI_PORT_D 0x3 +#define GMSL_CSI_PORT_E 0x4 +#define GMSL_CSI_PORT_F 0x5 + +#define GMSL_SERDES_CSI_LINK_A 0x1 +#define GMSL_SERDES_CSI_LINK_B 0x2 + +/* Didn't find kernel defintions, for now adding here */ +#define GMSL_CSI_DT_RAW_12 0x2C +#define GMSL_CSI_DT_UED_U1 0x30 +#define GMSL_CSI_DT_EMBED 0x12 +#define GMSL_CSI_DT_YUV422_8 0x1E +#define GMSL_CSI_DT_RGB_888 0x24 +#define GMSL_CSI_DT_RAW_8 0x2A + +#define GMSL_ST_ID_UNUSED 0xFF + +/** + * Maximum number of data streams (\ref gmsl_stream elements) in a GMSL link + * (\ref gmsl_link_ctx). + */ +#define GMSL_DEV_MAX_NUM_DATA_STREAMS 4 + +/** + * Holds information about a data stream in a GMSL link (\ref gmsl_link_ctx). + */ +struct gmsl_stream { + __u32 st_id_sel; + __u32 st_data_type; + __u32 des_pipe; +}; + +/** + * Holds the configuration of the GMSL links from a sensor to its serializer to + * its deserializer. + */ +struct gmsl_link_ctx { + __u32 st_vc; /**< Default sensor virtual channel. */ + __u32 dst_vc; /**< Destination virtual channel (user-defined). */ + __u32 src_csi_port; /**< Sensor to serializer CSI port connection. */ + __u32 dst_csi_port; /**< Deserializer to Jetson CSI port connection. */ + __u32 serdes_csi_link; /**< GMSL link between serializer and deserializer + devices. */ + __u32 num_streams; /**< Number of active streams to be mapped + from sensor. */ + __u32 num_csi_lanes; /**< Sensor's CSI lane configuration. */ + __u32 csi_mode; /**< Deserializer CSI mode. */ + __u32 ser_reg; /**< Serializer slave address. */ + __u32 sdev_reg; /**< Sensor proxy slave address. */ + __u32 sdev_def; /**< Sensor default slave address. */ + bool serdev_found; /**< Indicates whether the serializer device for + the specified sensor source was found. Set by + the serializer driver during setup; used by + the deserializer driver to choose certain + configuration settings during setup. */ + struct gmsl_stream streams[GMSL_DEV_MAX_NUM_DATA_STREAMS]; + /*< An array of information about the data streams in the link. */ + struct device *s_dev; /**< Sensor device handle. */ +}; + +/** @} */ + +#endif /* __GMSL_LINK_H__ */ diff --git a/include/media/max9295.h b/include/media/max9295.h new file mode 100644 index 000000000000..8937d04649cd --- /dev/null +++ b/include/media/max9295.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * MAX9295 API: For Maxim Integrated MAX9295 serializer + * + * @b Description: Defines elements used to set up and use a + * Maxim Integrated MAX9295 serializer. + */ + +#ifndef __MAX9295_H__ +#define __MAX9295_H__ + +#include +#include + +struct max9295_pdata { + /* primary serializer properties */ + __u32 is_prim_ser; + __u32 def_addr; +}; + +/** + * \defgroup max9295 MAX9295 serializer driver + * + * Controls the MAX9295 serializer module. + * + * @ingroup serdes_group + * @{ + */ + +int max9295_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id); + +/** + * @brief Powers on a serializer device and performs the I2C overrides + * for sensor and serializer devices. + * + * The I2C overrides include setting proxy I2C slave addresses for the devices. + * + * Before the client calls this function it must ensure that + * the deserializer device is in link_ex exclusive link mode + * by calling the deserializer driver's max9296_setup_link() function. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_setup_control(struct device *dev); + +/** + * Reverts I2C overrides and resets a serializer device. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_reset_control(struct device *dev); + +/** + * @brief Pairs a sensor device with a serializer device. + * + * To be called by sensor client driver. + * + * @param [in] dev The deserializer device handle. + * @param [in] g_ctx The @ref gmsl_link_ctx structure handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx); + +/** + * @brief Unpairs a sensor device from a serializer device. + * + * To be called by sensor client driver. + * + * @param [in] dev The serializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_sdev_unpair(struct device *dev, struct device *s_dev); + +/** + * Sets up the serializer device's internal pipeline for a specified + * sensor/serializer pair. + * + * @param [in] dev The serializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9295_setup_streaming(struct device *dev); + +int max9295_init_settings(struct device *dev); +/** @} */ + +#endif /* __MAX9295_H__ */ diff --git a/include/media/max9296.h b/include/media/max9296.h new file mode 100644 index 000000000000..20f3a66575de --- /dev/null +++ b/include/media/max9296.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-2020, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * MAX9296 API: For Maxim Integrated MAX9296 deserializer + * + * @b Description: Defines elements used to set up and use a + * Maxim Integrated MAX9296 deserializer. + */ + +#ifndef __MAX9296_H__ +#define __MAX9296_H__ + +#include +#include +/** + * \defgroup max9296 MAX9296 deserializer driver + * + * Controls the MAX9296 deserializer module. + * + * @ingroup serdes_group + * @{ + */ + +int max9296_get_available_pipe_id(struct device *dev, int vc_id); +int max9296_set_pipe(struct device *dev, int pipe_id, u8 data_type1, + u8 data_type2, u32 vc_id); +int max9296_release_pipe(struct device *dev, int pipe_id); +void max9296_reset_oneshot(struct device *dev); + +/** + * Puts a deserializer device in single exclusive link mode, so link-specific + * I2C overrides can be performed for sensor and serializer devices. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_setup_link(struct device *dev, struct device *s_dev); + +/** + * @brief Sets up a deserializer link's control pipeline. + * + * Puts the deserializer in dual splitter mode. You must call this function + * during device boot, after max9296_setup_link(). + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_setup_control(struct device *dev, struct device *s_dev); + +/** + * @brief Resets a deserializer device's link control pipeline. + * + * The deserializer driver internally decrements the reference count and + * resets the deserializer device if all the source sensor devices are + * powered off, resetting all control and streaming configuration. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_reset_control(struct device *dev, struct device *s_dev); + +/** + * @brief Registers a source sensor device with a deserializer device. + * + * The deserializer driver internally checks all perquisites and compatibility + * factors. If it finds that the registration request is valid, + * it stores the source's @ref gmsl_link_ctx context handle in the source list + * maintained by the deserializer driver instance. + * + * @param [in] dev The deserializer device handle. + * @param [in] g_ctx A @c gmsl_link_ctx structure handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_sdev_register(struct device *dev, struct gmsl_link_ctx *g_ctx); + +/** + * Unregisters a source sensor device from its deserializer device. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_sdev_unregister(struct device *dev, struct device *s_dev); + +/** + * Performs internal pipeline configuration for a link in context to set up + * streaming, and puts the deserializer link in ready-to-stream state. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 or success, or -1 otherwise. + */ +int max9296_setup_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Enables streaming. + * + * This function is to be called by the sensor client driver. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_start_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Disables streaming. + * + * This function is to be called by the sensor client driver. + * + * @note Both @c max9296_start_streaming and @c max9296_stop_streaming + * are mainly added to enable and disable sensor streaming on the fly + * while other sensors are active. + * + * @param [in] dev The deserializer device handle. + * @param [in] s_dev The sensor device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_stop_streaming(struct device *dev, struct device *s_dev); + +/** + * @brief Powers on the max9296 deserializer module. + * + * Asserts shared reset GPIO and powers on the regulator; + * maintains the reference count internally for source devices. + * + * @param [in] dev The deserializer device handle. + * + * @return 0 for success, or -1 otherwise. + */ +int max9296_power_on(struct device *dev); + +/** + * @brief Powers off the max9296 deserializer module. + * + * Deasserts the shared reset GPIO and powers off the regulator based on + * the reference count. + * + * @param [in] dev The deserializer device handle. + */ +void max9296_power_off(struct device *dev); + +int max9296_init_settings(struct device *dev); +/** @} */ + +#endif /* __MAX9296_H__ */