From b8828ee90af66557d863a019c05cb1e231535787 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Thu, 20 Apr 2023 10:21:26 +0300 Subject: [PATCH] d4xx: Update d4xx.c driver to match RealSense latest development. RealSense PRQ d4xx version is 1.0.1.13. ADL-P - support for sensor vc. - support for sensor state. - sensor own v4l2 controls. - max9295/6 serdes drivers and control for multiple pipe. - aggregation support Bump version for ADL-P to 1.0.2.15. PR to tag v1.0.0-adl-realsense-pv Signed-off-by: Dmitry Perchanov --- Makefile | 4 + drivers/media/i2c/Makefile | 18 + drivers/media/i2c/d4xx.c | 1941 ++++++++++++++++++++++++----------- drivers/media/i2c/max9295.c | 771 ++++++++++++++ drivers/media/i2c/max9296.c | 1184 +++++++++++++++++++++ include/media/gmsl-link.h | 107 ++ include/media/max9295.h | 111 ++ include/media/max9296.h | 177 ++++ 8 files changed, 3719 insertions(+), 594 deletions(-) create mode 100644 drivers/media/i2c/Makefile create mode 100644 drivers/media/i2c/max9295.c create mode 100644 drivers/media/i2c/max9296.c create mode 100644 include/media/gmsl-link.h create mode 100644 include/media/max9295.h create mode 100644 include/media/max9296.h diff --git a/Makefile b/Makefile index fc8356197b56..526d2a055f06 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,13 @@ export CONFIG_VIDEO_INTEL_IPU_USE_PLATFORMDATA = y export CONFIG_INTEL_SKL_INT3472 = m export CONFIG_INTEL_IPU6_ACPI = m obj-y += drivers/media/pci/intel/ +obj-y += drivers/media/platform/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 new file mode 100644 index 000000000000..00855ae7e233 --- /dev/null +++ b/drivers/media/i2c/Makefile @@ -0,0 +1,18 @@ +# 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 +ifeq ($(CONFIG_VIDEO_INTEL_IPU6), m) + ccflags-y += -DCONFIG_VIDEO_INTEL_IPU6 +endif + +obj-$(CONFIG_VIDEO_AR0234) += ar0234.o +obj-$(CONFIG_VIDEO_LT6911UXC) += lt6911uxc.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 b70977afd2b3..dcaea862e37b 100644 --- a/drivers/media/i2c/d4xx.c +++ b/drivers/media/i2c/d4xx.c @@ -30,15 +30,26 @@ #include #include #include - +#ifdef CONFIG_VIDEO_INTEL_IPU6 #include - +#include +#endif #include #include #include #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" @@ -133,12 +144,7 @@ #define MAX_RGB_EXP 10000 #define DEF_DEPTH_EXP 33000 #define DEF_RGB_EXP 1660 - -/* Currently both depth and IR use VC 0 */ -#define DS5_DEPTH_VCHAN_N 0 -#define DS5_MOTION_T_VCHAN_N 0 -//#define DS5_DEBUG_VCHAN_N 1 -//#define DS5_MOTION_T_VCHAN_N 2 +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define MAX9295_REG0 0x0000 #define MAX9295_I2C_4 0x0044 @@ -156,17 +162,13 @@ #define MAX9295_I2C_ADDR_DEF 0x40 #define D457_I2C_ADDR 0x10 - +#endif enum ds5_mux_pad { DS5_MUX_PAD_EXTERNAL, - DS5_MUX_PAD_DEPTH_A, - DS5_MUX_PAD_RGB_A, - DS5_MUX_PAD_MOTION_T_A, - DS5_MUX_PAD_IMU_A, - DS5_MUX_PAD_DEPTH_B, - DS5_MUX_PAD_RGB_B, - DS5_MUX_PAD_MOTION_T_B, - DS5_MUX_PAD_IMU_B, + DS5_MUX_PAD_DEPTH, + DS5_MUX_PAD_RGB, + DS5_MUX_PAD_IR, + DS5_MUX_PAD_IMU, DS5_MUX_PAD_COUNT, }; @@ -183,6 +185,11 @@ enum ds5_mux_pad { /* DFU definition section */ #define DFU_MAGIC_NUMBER "/0x01/0x02/0x03/0x04" #define DFU_BLOCK_SIZE 1024 +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM +#define DFU_I2C_STANDARD_MODE 100000 +#define DFU_I2C_FAST_MODE 400000 +#define DFU_I2C_BUS_CLK_RATE DFU_I2C_FAST_MODE +#endif #define ds5_read_with_check(state, addr, val) {\ if (ds5_read(state, addr, val)) \ return -EINVAL; } @@ -195,6 +202,7 @@ enum ds5_mux_pad { #define ds5_raw_write_with_check(state, addr, buf, size) {\ if (ds5_raw_write(state, addr, buf, size)) \ return -EINVAL; } +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define max9296_write_8_with_check(state, addr, buf) {\ if (max9296_write_8(state, addr, buf)) \ return -EINVAL; \ @@ -208,7 +216,7 @@ enum ds5_mux_pad { #define D4XX_LINK_FREQ_288MHZ 288000000ULL #define D4XX_LINK_FREQ_240MHZ 240000000ULL #define D4XX_LINK_FREQ_225MHZ 22500000ULL - +#endif enum dfu_fw_state { appIDLE = 0x0000, appDETACH = 0x0001, @@ -316,7 +324,7 @@ static const struct hwm_cmd ewb = { .magic_word = 0xCDAB, .opcode = 0x18, }; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_360MHZ, D4XX_LINK_FREQ_300MHZ, @@ -324,7 +332,7 @@ static const s64 link_freq_menu_items[] = { D4XX_LINK_FREQ_240MHZ, D4XX_LINK_FREQ_225MHZ, }; - +#endif struct __fw_status { uint32_t spare1; uint32_t FW_lastVersion; @@ -399,13 +407,7 @@ struct ds5_sensor { /*struct ds5_vchan *vchan;*/ const struct ds5_format *formats; unsigned int n_formats; -}; - -struct ds5_des { - struct v4l2_subdev sd; - struct media_pad pads[2]; - struct v4l2_mbus_framefmt format; - u16 mux_pad; + int pipe_id; }; #ifdef CONFIG_TEGRA_CAMERA_PLATFORM @@ -430,6 +432,7 @@ struct ds5_dfu_dev { unsigned char *dfu_msg; u16 msg_write_once; unsigned char init_v4l_f; + u32 bus_clk_rate; }; enum { @@ -438,27 +441,17 @@ enum { DS5_AWG, }; +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define NR_OF_DS5_PADS 7 -#define NR_OF_DS5_STREAMS (NR_OF_DS5_PADS - 1) - +#define NR_OF_DS5_STREAMS 4 struct v4l2_mbus_framefmt ds5_ffmts[NR_OF_DS5_PADS]; +#endif struct ds5 { - struct { - struct ds5_sensor sensor; - } depth; - struct { - struct ds5_sensor sensor; - } motion_t; - struct { - struct ds5_sensor sensor; - } rgb; - struct { - struct ds5_sensor sensor; - } imu; - struct { - struct ds5_des des; - } max9296; + struct { struct ds5_sensor sensor; } depth; + struct { struct ds5_sensor sensor; } ir; + struct { struct ds5_sensor sensor; } rgb; + struct { struct ds5_sensor sensor; } imu; struct { struct ds5_mux_subdev sd; struct media_pad pads[DS5_MUX_PAD_COUNT]; @@ -472,16 +465,29 @@ 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; - int is_y8; - int is_rgb; - int is_imu; + int is_depth, is_y8, is_rgb, is_imu; + int aggregated; 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 +#ifdef CONFIG_VIDEO_INTEL_IPU6 +#define NR_OF_CSI2_BE_SOC_STREAMS 16 +#define NR_OF_DS5_SUB_STREAMS 6 /*d+d.md,c+c.md,ir,imu*/ + int pad_to_vc[DS5_MUX_PAD_COUNT]; + int pad_to_substream[NR_OF_CSI2_BE_SOC_STREAMS]; +#endif }; struct ds5_counters { @@ -491,14 +497,14 @@ struct ds5_counters { }; #define ds5_from_depth_sd(sd) container_of(sd, struct ds5, depth.sd) -#define ds5_from_motion_t_sd(sd) container_of(sd, struct ds5, motion_t.sd) +#define ds5_from_ir_sd(sd) container_of(sd, struct ds5, ir.sd) #define ds5_from_rgb_sd(sd) container_of(sd, struct ds5, rgb.sd) - +#ifdef CONFIG_VIDEO_INTEL_IPU6 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; @@ -574,6 +580,7 @@ static int max9295_read_8(struct ds5 *state, u16 reg, u8 *val) return ret; } +#else static int ds5_write_8(struct ds5 *state, u16 reg, u8 val) { int ret; @@ -584,11 +591,13 @@ 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; } +#endif +#endif static int ds5_write(struct ds5 *state, u16 reg, u16 val) { @@ -657,12 +666,9 @@ static int ds5_raw_read(struct ds5 *state, u16 reg, void *val, size_t val_len) return ret; } -static int pad_to_substream[DS5_MUX_PAD_COUNT]; - -static s64 d4xx_query_sub_stream[] = { - 0, 0, 0, 0, 0, 0 -}; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static s64 d4xx_query_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; +static u8 d4xx_set_sub_stream[NR_OF_CSI2_BE_SOC_STREAMS]; static void set_sub_stream_fmt(int index, u32 code) { d4xx_query_sub_stream[index] &= 0xFFFFFFFFFFFF0000; @@ -713,10 +719,7 @@ static int get_sub_stream_vc_id(int index) val &= 0xFF; return (int)val; } - -static u8 d4xx_set_sub_stream[] = { - 0, 0, 0, 0, 0, 0 -}; +#endif /* Pad ops */ @@ -943,17 +946,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, @@ -963,21 +966,21 @@ 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(d43x_calibration_sizes), - .resolutions = d43x_calibration_sizes, + .n_resolutions = ARRAY_SIZE(d46x_calibration_sizes), + .resolutions = d46x_calibration_sizes, }, }; @@ -986,17 +989,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 */ + .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, @@ -1004,7 +1007,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, @@ -1012,7 +1015,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, @@ -1029,7 +1032,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, @@ -1069,7 +1072,7 @@ static void ds5_set_state_last_set(struct ds5 *state) 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; + state->mux.last_set = &state->ir.sensor; else state->mux.last_set = &state->imu.sensor; } @@ -1086,7 +1089,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; @@ -1119,8 +1122,8 @@ static int ds5_sensor_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *mce) { 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; @@ -1146,7 +1149,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++) @@ -1228,7 +1231,7 @@ static int ds5_sensor_get_fmt(struct v4l2_subdev *sd, mutex_unlock(&state->lock); - dev_info(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", + dev_dbg(sd->dev, "%s(): pad %x, code %x, res %ux%u\n", __func__, fmt->pad, fmt->format.code, fmt->format.width, fmt->format.height); @@ -1250,7 +1253,7 @@ static const struct ds5_format *ds5_sensor_find_format( if (fmt->mbus_code == ffmt->code) break; } - dev_info(sensor->sd.dev, "%s(): mbus_code = %x, code = %x \n", + dev_dbg(sensor->sd.dev, "%s(): mbus_code = %x, code = %x \n", __func__, fmt->mbus_code, ffmt->code); if (i == sensor->n_formats) { @@ -1294,7 +1297,7 @@ static const struct ds5_format *ds5_sensor_find_format( #define MIPI_CSI2_TYPE_RAW14 0x2d /* 1-8 */ #define MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i) - 1) - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static unsigned int mbus_code_to_mipi(u32 code) { switch (code) { @@ -1335,6 +1338,68 @@ static unsigned int mbus_code_to_mipi(u32 code) return -EINVAL; } } +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static int ds5_s_state_pad(struct ds5 *state, int pad) +{ + int ret = 0; + + dev_dbg(&state->client->dev, "%s(): set state for pad: %d\n", __func__, pad); + + switch (pad) { + case DS5_MUX_PAD_DEPTH: + state->is_depth = 1; + state->is_rgb = 0; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_RGB: + state->is_depth = 0; + state->is_rgb = 1; + state->is_y8 = 0; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IR: + state->is_depth = 0; + state->is_rgb = 0; + state->is_y8 = 1; + state->is_imu = 0; + break; + case DS5_MUX_PAD_IMU: + state->is_depth = 0; + state->is_rgb = 0; + state->is_y8 = 0; + state->is_imu = 1; + break; + default: + dev_warn(&state->client->dev, "%s(): unknown pad: %d\n", __func__, pad); + ret = -EINVAL; + break; + } + ds5_set_state_last_set(state); + return ret; +} + +static int ds5_s_state(struct ds5 *state, int vc) +{ + int ret = 0; + int i = 0; + int pad = 0; + for (i = 0; i < ARRAY_SIZE(state->pad_to_vc); i++) { + if (state->pad_to_vc[i] == vc) { + pad = i; + break; + } + } + + dev_info(&state->client->dev, "%s(): set state for vc: %d on pad: %d\n", __func__, vc, pad); + + ret = ds5_s_state_pad(state, pad); + return ret; +} + +#endif static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) @@ -1345,7 +1410,9 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf;// = &fmt->format; +#ifdef CONFIG_VIDEO_INTEL_IPU6 int substream = -1; +#endif //unsigned r; dev_dbg(sensor->sd.dev, "%s(): state %p, " @@ -1377,12 +1444,14 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, else // FIXME: use this format in .s_stream() sensor->format = *mf; - - state->mux.last_set = sensor; - + if (state->mux.last_set != sensor) { + dev_warn(sensor->sd.dev, "%s:%d mux sensor != sensor. set\n", + __func__, __LINE__); + state->mux.last_set = sensor; + } mutex_unlock(&state->lock); - - substream = pad_to_substream[sensor->mux_pad]; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + substream = state->pad_to_substream[sensor->mux_pad]; if (substream != -1) { set_sub_stream_fmt(substream, mf->code); @@ -1394,7 +1463,7 @@ static int __ds5_sensor_set_fmt(struct ds5 *state, struct ds5_sensor *sensor, dev_info(sensor->sd.dev, "%s(): fmt->pad: %d, sensor->mux_pad: %d, code: 0x%x, %ux%u substream:%d\n", __func__, fmt->pad, sensor->mux_pad, fmt->format.code, fmt->format.width, fmt->format.height, substream); - +#endif return 0; } @@ -1408,7 +1477,10 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); struct ds5 *state = v4l2_get_subdevdata(sd); - +#ifdef CONFIG_VIDEO_INTEL_IPU6 + /* set state by vc */ + ds5_s_state_pad(state, sensor->mux_pad); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) return __ds5_sensor_set_fmt(state, sensor, cfg, fmt); #else @@ -1416,10 +1488,36 @@ static int ds5_sensor_set_fmt(struct v4l2_subdev *sd, #endif } +#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; @@ -1431,7 +1529,7 @@ static int ds5_configure(struct ds5 *state) fps_addr = DS5_DEPTH_FPS; width_addr = DS5_DEPTH_RES_WIDTH; height_addr = DS5_DEPTH_RES_HEIGHT; - md_fmt = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 0; } else if (state->is_rgb) { sensor = &state->rgb.sensor; @@ -1441,17 +1539,17 @@ 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 = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 1; } else if (state->is_y8) { - sensor = &state->motion_t.sensor; + sensor = &state->ir.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; - md_fmt = 0x12; + md_fmt = GMSL_CSI_DT_EMBED; vc_id = 2; } else if (state->is_imu) { sensor = &state->imu.sensor; @@ -1461,12 +1559,27 @@ 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; + vc_id = 3; } else { return -EINVAL; } +#ifdef CONFIG_VIDEO_D4XX_SERDES + data_type1 = sensor->config.format->data_type; + data_type2 = state->is_y8 ? 0x00 : md_fmt; + + 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; +#endif + fmt = sensor->streaming ? sensor->config.format->data_type : 0; /* @@ -1476,7 +1589,7 @@ static int ds5_configure(struct ds5 *state) 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 == 0x1E) + 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); @@ -1530,7 +1643,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; @@ -1561,7 +1674,8 @@ static int ds5_sensor_s_stream(struct v4l2_subdev *sd, int on) { struct ds5_sensor *sensor = container_of(sd, struct ds5_sensor, sd); - dev_info(sensor->sd.dev, "%s(): sensor: name=%s state=%d\n", __func__, sensor->sd.name, on); + dev_dbg(sensor->sd.dev, "%s(): sensor: name=%s state=%d\n", + __func__, sensor->sd.name, on); sensor->streaming = on; @@ -1579,10 +1693,10 @@ static const struct v4l2_subdev_ops ds5_depth_subdev_ops = { .video = &ds5_sensor_video_ops, }; -/* Motion detection */ +/* InfraRed stream Y8/Y16 */ /* FIXME: identical to ds5_depth_pad_ops, use one for both */ -static const struct v4l2_subdev_pad_ops ds5_motion_t_pad_ops = { +static const struct v4l2_subdev_pad_ops ds5_ir_pad_ops = { .enum_mbus_code = ds5_sensor_enum_mbus_code, .enum_frame_size = ds5_sensor_enum_frame_size, .enum_frame_interval = ds5_sensor_enum_frame_interval, @@ -1590,8 +1704,8 @@ static const struct v4l2_subdev_pad_ops ds5_motion_t_pad_ops = { .set_fmt = ds5_sensor_set_fmt, }; -static const struct v4l2_subdev_ops ds5_motion_t_subdev_ops = { - .pad = &ds5_motion_t_pad_ops, +static const struct v4l2_subdev_ops ds5_ir_subdev_ops = { + .pad = &ds5_ir_pad_ops, .video = &ds5_sensor_video_ops, }; @@ -1709,58 +1823,6 @@ 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) -{ - int ret = 0; - u16 status = 2; - int retries = 100; - int errorCode; - int iter = retries; - - dev_dbg(&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); - - ds5_raw_write_with_check(state, 0x4900, cmd, cmdLen); - - ds5_write_with_check(state, 0x490C, 0x01); /* execute cmd */ - do { - if (iter != retries) - msleep_range(10); - ret = ds5_read(state, 0x4904, &status); - } while (iter-- && status != 0); - - if (ret || status != 0) { - ds5_raw_read(state, 0x4900, &errorCode, 4); - dev_err(&state->client->dev, - "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", - __func__, ret, status, errorCode); - ret = -EAGAIN; - } - - if (isRead) { - if (*dataLen == 0) { - ret = regmap_raw_read(state->regmap, 0x4908, dataLen, sizeof(u16)); - if (ret) - return -EAGAIN; - } - - dev_dbg(&state->client->dev, "%s(): HWMC read len: %d\n", - __func__, *dataLen); - // First 4 bytes of cmd->Data after read will include opcode - ds5_raw_read_with_check(state, 0x4900, cmd->Data, *dataLen); - - /*This is neede for libreealsense, to align there code with UVC*/ - cmd->Data[1000] = (unsigned char)((*dataLen) & 0x00FF); - cmd->Data[1001] = (unsigned char)(((*dataLen) & 0xFF00) >> 8); - } - - return 0; -} - #define DS5_HWMC_DATA 0x4900 #define DS5_HWMC_STATUS 0x4904 #define DS5_HWMC_RESP_LEN 0x4908 @@ -1771,6 +1833,13 @@ static int ds5_send_hwmc(struct ds5 *state, u16 cmdLen, struct hwm_cmd *cmd, #define DS5_HWMC_STATUS_WIP 2 #define DS5_HWMC_BUFFER_SIZE 1024 +enum DS5_HWMC_ERR { + DS5_HWMC_ERR_SUCCESS = 0, + DS5_HWMC_ERR_CMD = -1, + DS5_HWMC_ERR_PARAM = -6, + DS5_HWMC_ERR_NODATA = -21, +}; + static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) { int ret = 0; @@ -1786,26 +1855,38 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) if (retries != 100) msleep_range(1); ret = ds5_read(state, DS5_HWMC_STATUS, &status); - } while (!ret && retries-- && status != DS5_HWMC_STATUS_OK); + } while (!ret && retries-- && status == DS5_HWMC_STATUS_WIP); if (ret || status != DS5_HWMC_STATUS_OK) { if (status == DS5_HWMC_STATUS_ERR) { ds5_raw_read(state, DS5_HWMC_DATA, &errorCode, sizeof(errorCode)); - dev_err(&state->client->dev, - "%s(): HWMC failed, ret: %d, error code: %d\n", - __func__, ret, errorCode); - } else { - dev_err(&state->client->dev, - "%s(): HWMC failed because of timeout, ret: %d\n", - __func__, ret); + switch(errorCode) { + case (DS5_HWMC_ERR_CMD): + case (DS5_HWMC_ERR_PARAM): + ret = -EBADMSG; + break; + case (DS5_HWMC_ERR_NODATA): + ret = -ENODATA; + break; + + default: + dev_dbg(&state->client->dev, + "%s(): HWMC failed, ret: %d, status: %x, error code: %d\n", + __func__, ret, status, errorCode); + ret = -EBADMSG; + break; + } } - return -EAGAIN; + } + + if (status != DS5_HWMC_STATUS_OK) { + return ret; } ret = regmap_raw_read(state->regmap, DS5_HWMC_RESP_LEN, &tmp_len, sizeof(tmp_len)); if (ret) - return -EAGAIN; + return -EBADMSG; if (tmp_len > DS5_HWMC_BUFFER_SIZE) return -ENOBUFS; @@ -1817,15 +1898,39 @@ static int ds5_get_hwmc(struct ds5 *state, unsigned char *data) ds5_raw_read_with_check(state, DS5_HWMC_DATA, data, tmp_len); /* This is needed for librealsense, to align there code with UVC, - * last word is length - 4 bytes header length - */ + * last word is length - 4 bytes header length */ tmp_len -= SIZE_OF_HW_MONITOR_HEADER; data[DS5_HWMC_BUFFER_SIZE - 4] = (unsigned char)(tmp_len & 0x00FF); data[DS5_HWMC_BUFFER_SIZE - 3] = (unsigned char)((tmp_len & 0xFF00) >> 8); data[DS5_HWMC_BUFFER_SIZE - 2] = 0; data[DS5_HWMC_BUFFER_SIZE - 1] = 0; - return 0; + return ret; +} + +static int ds5_send_hwmc(struct ds5 *state, + u16 cmdLen, + struct hwm_cmd *cmd, + bool isRead, + u16 *dataLen) +{ + int ret = 0; + + dev_dbg(&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); + + ds5_raw_write_with_check(state, DS5_HWMC_DATA, cmd, cmdLen); + + ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */ + + if (isRead && *dataLen) { + ret = ds5_get_hwmc(state, cmd->Data); + } + + return ret; } static int ds5_set_calibration_data(struct ds5 *state, @@ -1856,46 +1961,6 @@ static int ds5_set_calibration_data(struct ds5 *state, 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); - - switch (vc) { - case 0: - state->is_depth = 1; - state->is_rgb = 0; - state->is_y8 = 0; - state->is_imu = 0; - break; - case 1: - state->is_depth = 0; - state->is_rgb = 1; - state->is_y8 = 0; - state->is_imu = 0; - break; - case 2: - state->is_depth = 0; - state->is_rgb = 0; - state->is_y8 = 1; - state->is_imu = 0; - break; - case 3: - state->is_depth = 0; - state->is_rgb = 0; - state->is_y8 = 0; - state->is_imu = 1; - break; - default: - dev_warn(&state->client->dev, "%s(): unknown vc: %d\n", __func__, vc); - ret = -EINVAL; - break; - } - ds5_set_state_last_set(state); - return ret; -} - static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) { struct ds5 *state = container_of(ctrl->handler, struct ds5, @@ -1904,34 +1969,31 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) struct ds5_sensor *sensor = (struct ds5_sensor *)ctrl->priv; int ret = -EINVAL; u16 base = DS5_DEPTH_CONTROL_BASE; - u32 val; - u16 on; - u16 vc_id; if (sensor) { switch (sensor->mux_pad) { - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); state->is_rgb = 0; state->is_depth = 1; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); state->is_rgb = 1; state->is_depth = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_MOTION_T_A: + case DS5_MUX_PAD_IR: state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); state->is_rgb = 0; state->is_depth = 0; state->is_y8 = 1; state->is_imu = 0; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); state->is_rgb = 0; state->is_depth = 0; @@ -1950,7 +2012,10 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) if (state->is_rgb) base = DS5_RGB_CONTROL_BASE; - +#ifndef CONFIG_VIDEO_INTEL_IPU6 + else if (state->is_imu) + return -EINVAL; +#endif 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", __func__, ds5_get_sensor_name(state), ctrl->name, ctrl->val); @@ -2002,7 +2067,7 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) memcpy(calib_cmd, &set_calib_data, sizeof(set_calib_data)); calib_cmd->header = 276; calib_cmd->param1 = DEPTH_CALIBRATION_ID; - memcpy (calib_cmd->Data, (u8 *)ctrl->p_new.p, 256); + memcpy(calib_cmd->Data, (u8 *)ctrl->p_new.p, 256); ret = ds5_set_calibration_data(state, calib_cmd, sizeof(struct hwm_cmd) + 256); devm_kfree(&state->client->dev, calib_cmd); @@ -2029,7 +2094,7 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) ret = -ENOMEM; break; } - memcpy (calib_cmd, &set_calib_data, sizeof (set_calib_data)); + memcpy(calib_cmd, &set_calib_data, sizeof (set_calib_data)); calib_cmd->header = 532; calib_cmd->param1 = COEF_CALIBRATION_ID; memcpy(calib_cmd->Data, (u8 *)ctrl->p_new.p, 512); @@ -2041,7 +2106,6 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) break; case DS5_CAMERA_CID_AE_ROI_SET: { struct hwm_cmd ae_roi_cmd; - memcpy(&ae_roi_cmd, &set_ae_roi, sizeof(ae_roi_cmd)); ae_roi_cmd.param1 = *((u16 *)ctrl->p_new.p_u16); ae_roi_cmd.param2 = *((u16 *)ctrl->p_new.p_u16 + 1); @@ -2053,7 +2117,6 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) } case DS5_CAMERA_CID_AE_SETPOINT_SET: { struct hwm_cmd *ae_setpoint_cmd; - if (ctrl->p_new.p_s32) { dev_dbg(&state->client->dev, "%s():0x%x \n", __func__, *(ctrl->p_new.p_s32)); @@ -2191,7 +2254,6 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) case DS5_CAMERA_CID_HWMC_RW: if (ctrl->p_new.p_u8) { u16 size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8; - size |= *((u8 *)ctrl->p_new.p_u8 + 0); ret = ds5_send_hwmc(state, size + 4, (struct hwm_cmd *)ctrl->p_new.p_u8, false, NULL); @@ -2201,25 +2263,30 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl) if (state->is_depth) ret = ds5_write(state, base | DS5_PWM_FREQUENCY, ctrl->val); break; +#ifdef CONFIG_VIDEO_INTEL_IPU6 case V4L2_CID_IPU_SET_SUB_STREAM: - val = (*ctrl->p_new.p_s64 & 0xFFFF); - dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x\n", val); - vc_id = (val >> 8) & 0x00FF; - on = val & 0x00FF; + { + u32 val = (*ctrl->p_new.p_s64 & 0xFFFF); + u16 on = val & 0x00FF; + u16 vc_id = (val >> 8) & 0x00FF; + int substream = -1; if (vc_id < DS5_MUX_PAD_COUNT) ret = ds5_s_state(state, vc_id); + substream = state->pad_to_substream[state->mux.last_set->mux_pad]; + dev_info(&state->client->dev, "V4L2_CID_IPU_SET_SUB_STREAM %x vc_id:%d, substream:%d, on:%d\n", val, vc_id, substream, on); 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; - - if (on == 0) - ret = ds5_s_state(state, vc_id); - + d4xx_set_sub_stream[substream] = on; + ret = 0; +#ifndef CONFIG_VIDEO_D4XX_SERDES ret = ds5_mux_s_stream(sd, on); +#endif + } break; +#endif } mutex_unlock(&state->lock); @@ -2321,28 +2388,28 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (sensor) { switch (sensor->mux_pad) { - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: state = container_of(ctrl->handler, struct ds5, ctrls.handler_depth); state->is_rgb = 0; state->is_depth = 1; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: state = container_of(ctrl->handler, struct ds5, ctrls.handler_rgb); state->is_rgb = 1; state->is_depth = 0; state->is_y8 = 0; state->is_imu = 0; break; - case DS5_MUX_PAD_MOTION_T_A: + case DS5_MUX_PAD_IR: state = container_of(ctrl->handler, struct ds5, ctrls.handler_y8); state->is_rgb = 0; state->is_depth = 0; state->is_y8 = 1; state->is_imu = 0; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: state = container_of(ctrl->handler, struct ds5, ctrls.handler_imu); state->is_rgb = 0; state->is_depth = 0; @@ -2519,23 +2586,25 @@ static int ds5_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (state->is_depth) ds5_read(state, base | DS5_PWM_FREQUENCY, ctrl->p_new.p_u16); break; +#ifdef CONFIG_VIDEO_INTEL_IPU6 case V4L2_CID_IPU_QUERY_SUB_STREAM: { if (sensor) { - int vc_id = get_sub_stream_vc_id(pad_to_substream[sensor->mux_pad]); + int substream = state->pad_to_substream[sensor->mux_pad]; + int vc_id = get_sub_stream_vc_id(substream); dev_dbg(sensor->sd.dev, - "%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]; + "%s(): V4L2_CID_IPU_QUERY_SUB_STREAM sensor->mux_pad:%d vc:[%d] %d\n", + __func__, sensor->mux_pad, vc_id, substream); + *ctrl->p_new.p_s32 = substream; state->mux.last_set = sensor; - } else { + } else { /* we are in DS5 MUX case */ *ctrl->p_new.p_s32 = -1; - } } + } break; +#endif } - return ret; } @@ -2760,7 +2829,7 @@ static const struct v4l2_ctrl_config ds5_ctrl_pwm = { .def = 1, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, }; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_LINK_FREQ, @@ -2773,12 +2842,12 @@ static const struct v4l2_ctrl_config d4xx_controls_link_freq = { .qmenu_int = link_freq_menu_items, }; -static const struct v4l2_ctrl_config d4xx_controls_q_sub_stream = { +static struct v4l2_ctrl_config d4xx_controls_q_sub_stream = { .ops = &ds5_ctrl_ops, .id = V4L2_CID_IPU_QUERY_SUB_STREAM, .name = "query virtual channel", .type = V4L2_CTRL_TYPE_INTEGER_MENU, - .max = ARRAY_SIZE(d4xx_query_sub_stream) - 1, + .max = NR_OF_DS5_SUB_STREAMS - 1, .min = 0, .def = 0, .menu_skip_mask = 0, @@ -2795,12 +2864,12 @@ static const struct v4l2_ctrl_config d4xx_controls_s_sub_stream = { .def = 0, .step = 1, }; - +#endif 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++; @@ -2812,7 +2881,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; }; @@ -2822,46 +2891,557 @@ static const struct v4l2_subdev_internal_ops ds5_sensor_internal_ops = { .close = ds5_mux_close, }; -static int ds5_ctrl_init(struct ds5 *state, int sid) -{ - const struct v4l2_ctrl_ops *ops = &ds5_ctrl_ops; - struct ds5_ctrls *ctrls = &state->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - struct v4l2_subdev *sd = &state->mux.sd.subdev; - int ret = -1; - struct ds5_sensor *sensor = NULL; +#ifdef CONFIG_VIDEO_D4XX_SERDES - switch (sid) { - case 0: - hdl = &ctrls->handler_depth; - sensor = &state->depth.sensor; - break; - case 1: +/* + * 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 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); + 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); + 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; + char suffix = pdata->suffix; + static struct max9295_pdata max9295_pdata = { + .is_prim_ser = 1, // todo: configurable + .def_addr = 0x40, // todo: configurable + }; + static struct max9296_pdata max9296_pdata = { + .max_src = 2, + .csi_mode = GMSL_CSI_2X4_MODE, + }; + static struct i2c_board_info i2c_info_des = { + I2C_BOARD_INFO("max9296", 0x48), + .platform_data = &max9296_pdata, + }; + static struct i2c_board_info i2c_info_ser = { + I2C_BOARD_INFO("max9295", 0x42), + .platform_data = &max9295_pdata, + }; + + 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 + + /* look for already registered max9296, use same context if found */ + for (i = 0; i < MAX_DEV_NUM; i++) { + if (serdes_inited[i] && serdes_inited[i]->dser_i2c) { + dev_info(dev, "MAX9296 found device on 0x%x\n", serdes_inited[i]->dser_i2c->addr); + if (bus == serdes_inited[i]->dser_i2c->adapter->nr + && serdes_inited[i]->dser_i2c->addr == i2c_info_des.addr) { + dev_info(dev, "MAX9296 AGGREGATION found device on 0x%x\n", i2c_info_des.addr); + state->dser_i2c = serdes_inited[i]->dser_i2c; + state->aggregated = 1; + } + } + } + if (state->aggregated) + suffix += 4; + dev_info(dev, "Init SerDes %c on %d@0x%x<->%d@0x%x\n", + suffix, + bus, pdata->subdev_info[0].board_info.addr, //48 + bus, pdata->subdev_info[0].ser_alias); //42 + + if (!state->dser_i2c) + 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, 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_1X4_MODE; + if (state->aggregated) { // aggregation + dev_info(dev, "configure GMSL port B\n"); + state->g_ctx.serdes_csi_link = GMSL_SERDES_CSI_LINK_B; + } else { + dev_info(dev, "configure GMSL port A\n"); + 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_dbg(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; +} + +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static short sensor_vc[NR_OF_DS5_STREAMS * 2] = {0,1,2,3, 2,3,0,1}; +module_param_array(sensor_vc, ushort, NULL, 0444); +MODULE_PARM_DESC(sensor_vc, "VC set for sensors\n" + "\t\tsensor_vc=0,1,2,3,2,3,0,1"); + +//#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\n" + "\t\tserdes_bus=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\n" + "\t\tdes_addr=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 Link reset\n", + c_bus, c->addr); + ds5_write_8(state, 0x1000, 0x40); // reset link + } + } + // restore original slave address + c->addr = c_addr_save; + + return 0; +} +#endif +static int ds5_serdes_setup(struct ds5 *state) +{ + int ret = 0; + struct i2c_client *c = state->client; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + int i = 0, c_bus = 0; + 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; + } + } +#endif + 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 +enum state_sid { + DEPTH_SID = 0, + RGB_SID, + IR_SID, + IMU_SID, + MUX_SID = -1 +}; + +static int ds5_ctrl_init(struct ds5 *state, int sid) +{ + const struct v4l2_ctrl_ops *ops = &ds5_ctrl_ops; + struct ds5_ctrls *ctrls = &state->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_subdev *sd = &state->mux.sd.subdev; + int ret = -1; + struct ds5_sensor *sensor = NULL; + + switch (sid) { + case DEPTH_SID: + hdl = &ctrls->handler_depth; + sensor = &state->depth.sensor; + break; + case RGB_SID: hdl = &ctrls->handler_rgb; sensor = &state->rgb.sensor; break; - case 2: + case IR_SID: hdl = &ctrls->handler_y8; - sensor = &state->motion_t.sensor; + sensor = &state->ir.sensor; break; - case 3: + case IMU_SID: hdl = &ctrls->handler_imu; sensor = &state->imu.sensor; break; default: + /* control for MUX */ hdl = &ctrls->handler; sensor = NULL; break; } - dev_info(NULL, "%s(), line %d sid: %d\n", __func__, __LINE__, sid); + dev_dbg(NULL, "%s():%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); return ret; } - if (sid == 0 || sid == 2) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->laser_power = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_laser_power, sensor); @@ -2871,22 +3451,22 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) } /* Total gain */ - if (sid == 0 || sid == 2) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 16, 248, 1, 16); - } else if (sid == 1) { + } else if (sid == RGB_SID) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 0, 128, 1, 64); } - if ((ctrls->gain) && (sid >= 0 && sid < 3)) { + if ((ctrls->gain) && (sid >= DEPTH_SID && sid < IMU_SID)) { ctrls->gain->priv = sensor; ctrls->gain->flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; } - if (sid >= 0 && sid < 3) { + if (sid >= DEPTH_SID && sid < IMU_SID) { ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_EXPOSURE_AUTO, @@ -2901,36 +3481,44 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) ctrls->auto_exp->priv = sensor; } } + /* Exposure time: V4L2_CID_EXPOSURE_ABSOLUTE default unit: 100 us. */ - if (sid == 0 || sid == 2) { + if (sid == DEPTH_SID || sid == IR_SID) { ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, MAX_DEPTH_EXP, 1, DEF_DEPTH_EXP); - } else if (sid == 1) { + } else if (sid == RGB_SID) { ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, MAX_RGB_EXP, 1, DEF_RGB_EXP); } - if ((ctrls->exposure) && (sid >= 0 && sid < 3)) { + if ((ctrls->exposure) && (sid >= DEPTH_SID && sid < IMU_SID)) { ctrls->exposure->priv = sensor; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; /* override default int type to u32 to match SKU & UVC */ ctrls->exposure->type = V4L2_CTRL_TYPE_U32; } - +#ifdef CONFIG_VIDEO_INTEL_IPU6 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; + if (state->aggregated) { + d4xx_controls_q_sub_stream.def = NR_OF_DS5_SUB_STREAMS; + d4xx_controls_q_sub_stream.min = NR_OF_DS5_SUB_STREAMS; + d4xx_controls_q_sub_stream.max = NR_OF_DS5_SUB_STREAMS * 2 - 1; + } 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; + + if (ctrls->query_sub_stream) + ctrls->query_sub_stream->flags |= + 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__); +#endif if (hdl->error) { v4l2_err(sd, "error creating controls (%d)\n", hdl->error); ret = hdl->error; @@ -2938,16 +3526,8 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) 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 && sid < 3) { + if (sid >= DEPTH_SID && sid < IMU_SID) { 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); @@ -2971,40 +3551,40 @@ static int ds5_ctrl_init(struct ds5 *state, int sid) v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor); } // DEPTH custom - if (sid == 0) + if (sid == DEPTH_SID) v4l2_ctrl_new_custom(hdl, &ds5_ctrl_pwm, sensor); // IMU custom - if (sid == 3) + if (sid == IMU_SID) ctrls->fw_version = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_fw_version, sensor); switch (sid) { - case 0: + case DEPTH_SID: 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: + case RGB_SID: 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, + case IR_SID: + state->ir.sensor.sd.ctrl_handler = hdl; + dev_dbg(state->ir.sensor.sd.dev, "%s():%d set ctrl_handler pad:%d\n", - __func__, __LINE__, state->motion_t.sensor.mux_pad); + __func__, __LINE__, state->ir.sensor.mux_pad); break; - case 3: + case IMU_SID: state->imu.sensor.sd.ctrl_handler = hdl; - dev_info(state->imu.sensor.sd.dev, + 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; } @@ -3020,17 +3600,28 @@ static int ds5_sensor_init(struct i2c_client *c, struct ds5 *state, struct media_entity *entity = &sensor->sd.entity; struct media_pad *pad = &sensor->pad; dev_t *dev_num = &state->client->dev.devt; +#ifndef CONFIG_OF struct d4xx_pdata *dpdata = c->dev.platform_data; - + char suffix = dpdata->suffix; +#endif v4l2_i2c_subdev_init(sd, c, ops); + // See tegracam_v4l2.c tegracam_v4l2subdev_register() + // Set owner to NULL so we can unload the driver module sd->owner = NULL; sd->internal_ops = &ds5_sensor_internal_ops; sd->grp_id = *dev_num; v4l2_set_subdevdata(sd, state); +#ifndef CONFIG_OF /* * TODO: suffix for 2 D457 connected to 1 Deser */ - snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, dpdata->subdev_info[0].suffix); + if (state->aggregated & 1) + suffix += 4; + snprintf(sd->name, sizeof(sd->name), "D4XX %s %c", name, suffix); +#else + snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", + name, i2c_adapter_id(c->adapter), c->addr); +#endif sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -3061,7 +3652,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; @@ -3081,28 +3672,28 @@ static void ds5_sensor_remove(struct ds5_sensor *sensor) static int ds5_depth_init(struct i2c_client *c, struct ds5 *state) { /* Which mux pad we're connecting to */ - state->depth.sensor.mux_pad = DS5_MUX_PAD_DEPTH_A; + state->depth.sensor.mux_pad = DS5_MUX_PAD_DEPTH; return ds5_sensor_init(c, state, &state->depth.sensor, &ds5_depth_subdev_ops, "depth"); } -static int ds5_motion_t_init(struct i2c_client *c, struct ds5 *state) +static int ds5_ir_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"); + state->ir.sensor.mux_pad = DS5_MUX_PAD_IR; + return ds5_sensor_init(c, state, &state->ir.sensor, + &ds5_ir_subdev_ops, "ir"); } static int ds5_rgb_init(struct i2c_client *c, struct ds5 *state) { - state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB_A; + state->rgb.sensor.mux_pad = DS5_MUX_PAD_RGB; return ds5_sensor_init(c, state, &state->rgb.sensor, &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; + state->imu.sensor.mux_pad = DS5_MUX_PAD_IMU; return ds5_sensor_init(c, state, &state->imu.sensor, &ds5_imu_subdev_ops, "imu"); } @@ -3121,22 +3712,22 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev *remote_sd; int ret = -1; - 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; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: - if (mce->index >= state->motion_t.sensor.n_formats + + if (mce->index >= state->ir.sensor.n_formats + state->depth.sensor.n_formats) return -EINVAL; @@ -3145,10 +3736,10 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, * This should also help because D16 doesn't have a direct * analog in MIPI CSI-2. */ - if (mce->index < state->motion_t.sensor.n_formats) { - remote_sd = &state->motion_t.sensor.sd; + if (mce->index < state->ir.sensor.n_formats) { + remote_sd = &state->ir.sensor.sd; } else { - tmp.index = mce->index - state->motion_t.sensor.n_formats; + tmp.index = mce->index - state->ir.sensor.n_formats; remote_sd = &state->depth.sensor.sd; } @@ -3163,10 +3754,9 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, if (state->is_depth) remote_sd = &state->depth.sensor.sd; if (state->is_y8) - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; if (state->is_imu) remote_sd = &state->imu.sensor.sd; - /* Locks internally */ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 10) ret = ds5_sensor_enum_mbus_code(remote_sd, cfg, &tmp); @@ -3179,6 +3769,18 @@ static int ds5_mux_enum_mbus_code(struct v4l2_subdev *sd, return ret; } +static int ds5_state_to_pad(struct ds5 *state) { + int pad = -1; + if (state->is_depth) + pad = DS5_MUX_PAD_DEPTH; + if (state->is_y8) + pad = DS5_MUX_PAD_IR; + if (state->is_rgb) + pad = DS5_MUX_PAD_RGB; + if (state->is_imu) + pad = DS5_MUX_PAD_IMU; + return pad; +} /* No locking needed */ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, @@ -3196,27 +3798,19 @@ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, int ret = -1; tmp.pad = 0; - - if (state->is_depth) - pad = DS5_MUX_PAD_DEPTH_A; - if (state->is_y8) - pad = DS5_MUX_PAD_MOTION_T_A; - if (state->is_rgb) - pad = DS5_MUX_PAD_RGB_A; - if (state->is_imu) - pad = DS5_MUX_PAD_IMU_A; + pad = ds5_state_to_pad(state); switch (pad) { - case DS5_MUX_PAD_MOTION_T_A: - remote_sd = &state->motion_t.sensor.sd; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: @@ -3232,7 +3826,7 @@ static int ds5_mux_enum_frame_size(struct v4l2_subdev *sd, return 0; } - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; break; default: return -EINVAL; @@ -3265,30 +3859,23 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, 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); - if (state->is_depth) - pad = DS5_MUX_PAD_DEPTH_A; - if (state->is_y8) - pad = DS5_MUX_PAD_MOTION_T_A; - if (state->is_rgb) - pad = DS5_MUX_PAD_RGB_A; - if (state->is_imu) - pad = DS5_MUX_PAD_IMU_A; + pad = ds5_state_to_pad(state); switch (pad) { - case DS5_MUX_PAD_MOTION_T_A: - remote_sd = &state->motion_t.sensor.sd; + case DS5_MUX_PAD_IR: + remote_sd = &state->ir.sensor.sd; break; - case DS5_MUX_PAD_DEPTH_A: + case DS5_MUX_PAD_DEPTH: remote_sd = &state->depth.sensor.sd; break; - case DS5_MUX_PAD_RGB_A: + case DS5_MUX_PAD_RGB: remote_sd = &state->rgb.sensor.sd; break; - case DS5_MUX_PAD_IMU_A: + case DS5_MUX_PAD_IMU: remote_sd = &state->imu.sensor.sd; break; case DS5_MUX_PAD_EXTERNAL: @@ -3296,7 +3883,7 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, if (state->is_rgb) remote_sd = &state->rgb.sensor.sd; else - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; ret = ds5_sensor_enum_frame_interval(remote_sd, NULL, &tmp); if (!ret) { *fie = tmp; @@ -3304,7 +3891,7 @@ static int ds5_mux_enum_frame_interval(struct v4l2_subdev *sd, return 0; } - remote_sd = &state->motion_t.sensor.sd; + remote_sd = &state->ir.sensor.sd; break; default: return -EINVAL; @@ -3333,17 +3920,25 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *ffmt; struct ds5_sensor *sensor = state->mux.last_set; u32 pad = sensor->mux_pad; - // u32 pad = fmt->pad; int ret = 0; +#ifdef CONFIG_VIDEO_INTEL_IPU6 int substream = -1; + dev_info(sd->dev, "%s:%d: fmt->pad:%d, sensor->mux_pad: %d, \ + for sensor: %s\n", + __func__, __LINE__, + fmt->pad, pad, + sensor->sd.name); + +#endif + if (pad != DS5_MUX_PAD_EXTERNAL) - ds5_s_state(state, pad - 1); + ds5_s_state_pad(state, pad); sensor = 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: + case DS5_MUX_PAD_DEPTH: + case DS5_MUX_PAD_IR: + case DS5_MUX_PAD_RGB: + case DS5_MUX_PAD_IMU: //ffmt = &ds5_ffmts[pad]; ffmt = &sensor->format;//ds5_ffmts[pad]; break; @@ -3360,9 +3955,9 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, ffmt->code = fmt->format.code; } fmt->format = *ffmt; - +#ifdef CONFIG_VIDEO_INTEL_IPU6 // substream = pad_to_substream[fmt->pad]; - substream = pad_to_substream[pad]; + substream = state->pad_to_substream[pad]; if (substream != -1) { set_sub_stream_fmt(substream, ffmt->code); @@ -3377,7 +3972,7 @@ static int ds5_mux_set_fmt(struct v4l2_subdev *sd, fmt->pad, pad, fmt->format.code, fmt->format.width, fmt->format.height, substream, sensor->sd.name); - +#endif return ret; } @@ -3391,22 +3986,25 @@ static int ds5_mux_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - // u32 pad = fmt->pad; + u32 pad = fmt->pad; int ret = 0; struct ds5_sensor *sensor = state->mux.last_set; - u32 pad = sensor->mux_pad; +#ifdef CONFIG_VIDEO_INTEL_IPU6 + pad = sensor->mux_pad; if (pad != DS5_MUX_PAD_EXTERNAL) - ds5_s_state(state, pad - 1); + ds5_s_state_pad(state, pad); +#else + pad = ds5_state_to_pad(state); +#endif 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]; + case DS5_MUX_PAD_DEPTH: + case DS5_MUX_PAD_IR: + case DS5_MUX_PAD_RGB: + case DS5_MUX_PAD_IMU: + fmt->format = sensor->format; break; case DS5_MUX_PAD_EXTERNAL: fmt->format = ds5_ffmts[pad]; @@ -3437,7 +4035,7 @@ static int ds5_mux_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; @@ -3475,11 +4073,11 @@ 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; } - +#ifndef CONFIG_VIDEO_D4XX_SERDES int d4xx_reset_oneshot(struct ds5 *state) { struct d4xx_pdata *dpdata = state->client->dev.platform_data; @@ -3501,7 +4099,26 @@ int d4xx_reset_oneshot(struct ds5 *state) return ret; } +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static int ds5_state_to_vc(struct ds5 *state) { + int pad = 0; + if (state->is_depth) { + pad = DS5_MUX_PAD_DEPTH; + } + if (state->is_rgb) { + pad = DS5_MUX_PAD_RGB; + } + if (state->is_y8) { + pad = DS5_MUX_PAD_IR; + } + if (state->is_imu) { + pad = DS5_MUX_PAD_IMU; + } + return state->pad_to_vc[pad]; +} +#endif static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) { struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); @@ -3510,40 +4127,58 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) unsigned int i = 0; 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 (state->mux.last_set->streaming == on) + if (sensor->streaming == on) return 0; if (state->is_depth) { config_status_base = DS5_DEPTH_CONFIG_STATUS; stream_status_base = DS5_DEPTH_STREAM_STATUS; 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; 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; } +#ifdef CONFIG_VIDEO_INTEL_IPU6 + vc_id = ds5_state_to_vc(state); +#endif +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + vc_id = state->g_ctx.dst_vc; +#endif + 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); - dev_warn(&state->client->dev, "s_stream for stream %s, on = %d\n", - state->mux.last_set->sd.name, on); - - restore_val = state->mux.last_set->streaming; - state->mux.last_set->streaming = on; + restore_val = sensor->streaming; + sensor->streaming = on; if (on) { +#ifdef CONFIG_VIDEO_D4XX_SERDES +#ifdef CONFIG_VIDEO_INTEL_IPU6 + // set manually, need to configure vc in pdata + state->g_ctx.dst_vc = vc_id; +#endif + 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, + "No free pipe in max9296\n"); + ret = -(ENOSR); + goto restore_s_state; + } +#endif ret = ds5_configure(state); if (ret) @@ -3565,7 +4200,7 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) msleep_range(DS5_START_POLL_TIME); } - if (i == DS5_START_MAX_COUNT) { + if (DS5_START_MAX_COUNT == i) { dev_err(&state->client->dev, "start streaming failed, exit on timeout\n"); /* notify fw */ @@ -3583,75 +4218,65 @@ static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on) 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->ir.sensor.config.format->data_type == + GMSL_CSI_DT_RGB_888) { + max9296_reset_oneshot(state->dser_dev); + } +#ifndef CONFIG_TEGRA_CAMERA_PLATFORM + // reset for IPU6 + streaming = 0; + for (i = 0; i < ARRAY_SIZE(d4xx_set_sub_stream); i++) { + if (d4xx_set_sub_stream[i]) { + streaming = 1; + break; + } + } + if (!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 d4xx_reset_oneshot(state); +#endif } 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\n", + "%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); + config_status_base, status, ret); return ret; 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 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); - state->mux.last_set->streaming = restore_val; + sensor->streaming = restore_val; 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) -{ - struct ds5 *state = container_of(sd, struct ds5, mux.sd.subdev); - - return ds5_set_power(state, on); -}*/ - -#define DS5_N_STREAMS 4 -#define DS5_PAD_SOURCE 0 - static int ds5_mux_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *desc) { @@ -3697,35 +4322,6 @@ static const struct v4l2_subdev_ops ds5_mux_subdev_ops = { .video = &ds5_mux_video_ops, }; -#if 0 /* function not used */ -static int ds5_des_register(struct ds5 *state, struct ds5_des *des) -{ - struct v4l2_subdev *sd = &des->sd; - int ret; - - // FIXME: is async needed? - ret = v4l2_device_register_subdev(state->mux.sd.subdev.v4l2_dev, sd); - if (ret < 0) { - return ret; - } - - //ret = media_create_pad_link(entity, 1, &state->mux.sd.subdev.entity, des->mux_pad, - // 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); - - return 0; - -e_sd: - v4l2_device_unregister_subdev(sd); - - return ret; -} -#endif - static int ds5_mux_registered(struct v4l2_subdev *sd) { struct ds5 *state = v4l2_get_subdevdata(sd); @@ -3733,7 +4329,7 @@ static int ds5_mux_registered(struct v4l2_subdev *sd) if (ret < 0) return ret; - ret = ds5_sensor_register(state, &state->motion_t.sensor); + ret = ds5_sensor_register(state, &state->ir.sensor); if (ret < 0) goto e_depth; @@ -3751,7 +4347,7 @@ static int ds5_mux_registered(struct v4l2_subdev *sd) v4l2_device_unregister_subdev(&state->rgb.sensor.sd); e_rgb: - v4l2_device_unregister_subdev(&state->motion_t.sensor.sd); + v4l2_device_unregister_subdev(&state->ir.sensor.sd); e_depth: v4l2_device_unregister_subdev(&state->depth.sensor.sd); @@ -3762,10 +4358,9 @@ static int ds5_mux_registered(struct v4l2_subdev *sd) static void ds5_mux_unregistered(struct v4l2_subdev *sd) { struct ds5 *state = v4l2_get_subdevdata(sd); - ds5_sensor_remove(&state->imu.sensor); ds5_sensor_remove(&state->rgb.sensor); - ds5_sensor_remove(&state->motion_t.sensor); + ds5_sensor_remove(&state->ir.sensor); ds5_sensor_remove(&state->depth.sensor); } @@ -3795,46 +4390,31 @@ 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) ret = ds5_write(state, DS5_MIPI_LANE_DATARATE, MIPI_LANE_RATE); - ret = ds5_read(state, DS5_MIPI_CONF_STATUS, &mipi_status); + if (!ret) + 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; } -#if 0 /* not used function */ -static int ds5_des_init(struct i2c_client *c, struct ds5 *state) -{ - struct v4l2_subdev *sd = &state->max9296.des.sd; - struct media_entity *entity = &state->max9296.des.sd.entity; - struct media_pad *pads = state->max9296.des.pads; - dev_t *dev_num = &state->client->dev.devt; - - state->max9296.des.mux_pad = DS5_MUX_PAD_EXTERNAL; - - dev_info(sd->dev, "%s(): %p %s %p %p", __func__, c, c->name, state, state->client); - - v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); - sd->owner = THIS_MODULE; - sd->internal_ops = &ds5_sensor_internal_ops; - sd->grp_id = *dev_num; - v4l2_set_subdevdata(sd, state); - snprintf(sd->name, sizeof(sd->name), "D4XX %s %d-%04x", - "max9296", i2c_adapter_id(c->adapter), 0x48); - - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - pads[0].flags = MEDIA_PAD_FL_SOURCE; - pads[1].flags = MEDIA_PAD_FL_SINK; - entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; - entity->function = MEDIA_ENT_F_CAM_SENSOR; - return media_entity_pads_init(entity, 2, pads); -} -#endif static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) { struct v4l2_subdev *sd = &state->mux.sd.subdev; @@ -3842,15 +4422,24 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) struct media_pad *pads = state->mux.pads, *pad; unsigned int i; int ret; - +#ifndef CONFIG_OF struct d4xx_pdata *dpdata = c->dev.platform_data; + char suffix = dpdata->suffix; +#endif v4l2_i2c_subdev_init(sd, c, &ds5_mux_subdev_ops); + // See tegracam_v4l2.c tegracam_v4l2subdev_register() // Set owner to NULL so we can unload the driver module sd->owner = NULL; sd->internal_ops = &ds5_mux_internal_ops; v4l2_set_subdevdata(sd, state); - snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", dpdata->suffix); - +#ifdef CONFIG_OF + snprintf(sd->name, sizeof(sd->name), "DS5 mux %d-%04x", + i2c_adapter_id(c->adapter), c->addr); +#else + if (state->aggregated) + suffix += 4; + snprintf(sd->name, sizeof(sd->name), "DS5 mux %c", suffix); +#endif sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; entity->obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; entity->function = MEDIA_ENT_F_CAM_SENSOR; @@ -3864,29 +4453,48 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) return ret; /*set for mux*/ - ret = ds5_ctrl_init(state, -1); + ret = ds5_ctrl_init(state, MUX_SID); if (ret < 0) - return ret; + goto e_entity; + /*set for depth*/ - ret = ds5_ctrl_init(state, 0); + ret = ds5_ctrl_init(state, DEPTH_SID); if (ret < 0) return ret; /*set for rgb*/ - ret = ds5_ctrl_init(state, 1); + ret = ds5_ctrl_init(state, RGB_SID); if (ret < 0) return ret; /*set for y8*/ - ret = ds5_ctrl_init(state, 2); + ret = ds5_ctrl_init(state, IR_SID); if (ret < 0) return ret; /*set for imu*/ - ret = ds5_ctrl_init(state, 3); + ret = ds5_ctrl_init(state, IMU_SID); if (ret < 0) return ret; ds5_set_state_last_set(state); #ifdef CONFIG_TEGRA_CAMERA_PLATFORM + if (state->is_depth) { + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_depth, NULL, true); + state->mux.last_set = &state->depth.sensor; + } + else if (state->is_rgb) { + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_rgb, NULL, true); + state->mux.last_set = &state->rgb.sensor; + } + else if (state->is_y8) { + v4l2_ctrl_add_handler(&state->ctrls.handler, + &state->ctrls.handler_y8, NULL, true); + state->mux.last_set = &state->ir.sensor; + } + else + state->mux.last_set = &state->imu.sensor; + state->mux.sd.dev = &c->dev; ret = camera_common_initialize(&state->mux.sd, "d4xx"); if (ret) { @@ -3901,6 +4509,7 @@ static int ds5_mux_init(struct i2c_client *c, struct ds5 *state) e_ctrl: v4l2_ctrl_handler_free(sd->ctrl_handler); #endif +e_entity: media_entity_cleanup(entity); return ret; @@ -3935,10 +4544,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; @@ -3954,12 +4563,13 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = ds5_depth_formats_d46x; } sensor->n_formats = 1; - sensor->mux_pad = DS5_MUX_PAD_DEPTH_A; + sensor->mux_pad = DS5_MUX_PAD_DEPTH; - sensor = &state->motion_t.sensor; + sensor = &state->ir.sensor; sensor->formats = state->variant->formats; sensor->n_formats = state->variant->n_formats; - sensor->mux_pad = DS5_MUX_PAD_MOTION_T_A; + sensor->mux_pad = DS5_MUX_PAD_IR; + switch (dev_type) { // case DS5_DEVICE_TYPE_D45X: case DS5_DEVICE_TYPE_D43X: { @@ -3999,17 +4609,12 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) sensor->formats = &ds5_onsemi_rgb_format; sensor->n_formats = DS5_ONSEMI_RGB_N_FORMATS; } - sensor->mux_pad = DS5_MUX_PAD_RGB_A; - - /*sensor->formats = &ds5_onsemi_rgb_format; - *sensor->n_formats = DS5_RGB_N_FORMATS; - *sensor->mux_pad = DS5_MUX_PAD_RGB; - */ + sensor->mux_pad = DS5_MUX_PAD_RGB; sensor = &state->imu.sensor; sensor->formats = ds5_imu_formats; sensor->n_formats = 1; - sensor->mux_pad = DS5_MUX_PAD_IMU_A; + sensor->mux_pad = DS5_MUX_PAD_IMU; /* Development: set a configuration during probing */ if ((cfg0 & 0xff00) == 0x1800) { @@ -4022,15 +4627,26 @@ static int ds5_fixed_configuration(struct i2c_client *client, struct ds5 *state) }; //#undef USE_Y +#ifdef USE_Y /* Override .width, .height, .code */ fmt.format.width = yw; fmt.format.height = yh; fmt.format.code = MEDIA_BUS_FMT_UYVY8_2X8; //state->mux.sd.mode_prop_idx = 0; - state->motion_t.sensor.streaming = true; + state->ir.sensor.streaming = true; state->depth.sensor.streaming = true; - ret = __ds5_sensor_set_fmt(state, &state->motion_t.sensor, NULL, &fmt); + ret = __ds5_sensor_set_fmt(state, &state->ir.sensor, NULL, &fmt); +#else + fmt.format.width = dw; + fmt.format.height = dh; + fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; + + state->mux.sd.mode_prop_idx = 1; + state->ir.sensor.streaming = false; + state->depth.sensor.streaming = true; + ret = __ds5_sensor_set_fmt(state, &state->depth.sensor, NULL, &fmt); +#endif if (ret < 0) return ret; } @@ -4047,7 +4663,7 @@ static int ds5_parse_cam(struct i2c_client *client, struct ds5 *state) return ret; ds5_sensor_format_init(&state->depth.sensor); - ds5_sensor_format_init(&state->motion_t.sensor); + ds5_sensor_format_init(&state->ir.sensor); ds5_sensor_format_init(&state->rgb.sensor); ds5_sensor_format_init(&state->imu.sensor); @@ -4214,7 +4830,7 @@ static ssize_t ds5_dfu_device_read(struct file *flip, ret |= ds5_read(state, DS5_FW_BUILD, &fw_build); if (ret < 0) goto e_dfu_read_failed; - snprintf(msg, sizeof(msg), "DFU info: \tver: %d.%d.%d.%d\n", + 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); @@ -4235,6 +4851,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; @@ -4312,8 +4929,7 @@ static ssize_t ds5_dfu_device_write(struct file *flip, dfu_write_error: state->dfu_dev.dfu_state_flag = DS5_DFU_ERROR; // Reset DFU device to IDLE states - ret = ds5_write(state, 0x5010, 0x0); - if (!ret) + if (!ds5_write(state, 0x5010, 0x0)) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; mutex_unlock(&state->lock); return ret; @@ -4323,7 +4939,10 @@ 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); - +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( + state->client->adapter); +#endif if (state->dfu_dev.device_open_count) return -EBUSY; state->dfu_dev.device_open_count++; @@ -4335,6 +4954,23 @@ static int ds5_dfu_device_open(struct inode *inode, struct file *file) return -ENOMEM; file->private_data = state; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + /* get i2c controller and set dfu bus clock rate */ + while (parent && i2c_parent_is_i2c_adapter(parent)) + parent = i2c_parent_is_i2c_adapter(state->client->adapter); + + if (!parent) + return 0; + + dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk = %d, set %d\n", + __func__, + i2c_adapter_id(parent), + i2c_get_adapter_bus_clk_rate(parent), + DFU_I2C_BUS_CLK_RATE); + + state->dfu_dev.bus_clk_rate = i2c_get_adapter_bus_clk_rate(parent); + i2c_set_adapter_bus_clk_rate(parent, DFU_I2C_BUS_CLK_RATE); +#endif return 0; }; @@ -4350,13 +4986,13 @@ static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) if (ret < 0) return ret; - ret = ds5_motion_t_init(c, state); + ret = ds5_ir_init(c, state); if (ret < 0) goto e_depth; ret = ds5_rgb_init(c, state); if (ret < 0) - goto e_motion_t; + goto e_ir; ret = ds5_imu_init(c, state); if (ret < 0) @@ -4381,18 +5017,20 @@ static int ds5_v4l_init(struct i2c_client *c, struct ds5 *state) media_entity_cleanup(&state->imu.sensor.sd.entity); e_rgb: media_entity_cleanup(&state->rgb.sensor.sd.entity); -e_motion_t: - media_entity_cleanup(&state->motion_t.sensor.sd.entity); +e_ir: + media_entity_cleanup(&state->ir.sensor.sd.entity); e_depth: media_entity_cleanup(&state->depth.sensor.sd.entity); - return ret; } static int ds5_dfu_device_release(struct inode *inode, struct file *file) { struct ds5 *state = container_of(inode->i_cdev, struct ds5, dfu_dev.ds5_cdev); - +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter( + state->client->adapter); +#endif state->dfu_dev.device_open_count--; if (state->dfu_dev.dfu_state_flag != DS5_DFU_RECOVERY) state->dfu_dev.dfu_state_flag = DS5_DFU_IDLE; @@ -4403,7 +5041,19 @@ static int ds5_dfu_device_release(struct inode *inode, struct file *file) if (state->dfu_dev.dfu_msg) devm_kfree(&state->client->dev, state->dfu_dev.dfu_msg); state->dfu_dev.dfu_msg = NULL; +#ifdef CONFIG_TEGRA_CAMERA_PLATFORM + /* get i2c controller and restore bus clock rate */ + while (parent && i2c_parent_is_i2c_adapter(parent)) + parent = i2c_parent_is_i2c_adapter(state->client->adapter); + if (!parent) + return 0; + dev_dbg(&state->client->dev, "%s(): i2c-%d bus_clk %d, restore to %d\n", + __func__, i2c_adapter_id(parent), + i2c_get_adapter_bus_clk_rate(parent), + state->dfu_dev.bus_clk_rate); + i2c_set_adapter_bus_clk_rate(parent, state->dfu_dev.bus_clk_rate); +#endif return 0; }; @@ -4422,8 +5072,10 @@ 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; +#ifndef CONFIG_OF struct d4xx_pdata *pdata = c->dev.platform_data; - + char suffix = pdata->suffix; +#endif struct device *chr_dev; char dev_name[sizeof(DS5_DRIVER_NAME_DFU) + 8]; dev_t *dev_num = &c->dev.devt; @@ -4435,11 +5087,12 @@ static int ds5_chrdev_init(struct i2c_client *c, struct ds5 *state) 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); @@ -4454,8 +5107,15 @@ 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. */ +#ifndef CONFIG_OF + if (state->aggregated) + suffix += 4; snprintf(dev_name, sizeof(dev_name), "%s-%c", - DS5_DRIVER_NAME_DFU, pdata->suffix); + DS5_DRIVER_NAME_DFU, suffix); +#else + snprintf (dev_name, sizeof(dev_name), "%s-%d-%04x", + DS5_DRIVER_NAME_DFU, i2c_adapter_id(c->adapter), c->addr); +#endif chr_dev = device_create(*ds5_class, NULL, *dev_num, NULL, dev_name); if (IS_ERR(chr_dev)) { ret = PTR_ERR(chr_dev); @@ -4465,6 +5125,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; }; @@ -4478,15 +5139,45 @@ static int ds5_chrdev_remove(struct ds5 *state) 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; } -static void ds5_substream_init(void) +#ifdef CONFIG_VIDEO_INTEL_IPU6 +static void ds5_substream_init(struct ds5 *state) { int i; - + state->pad_to_vc[DS5_MUX_PAD_EXTERNAL]= -1; + if (!state->aggregated) { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[0]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[1]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[2]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[3]; + } else { + state->pad_to_vc[DS5_MUX_PAD_DEPTH] = sensor_vc[4]; + state->pad_to_vc[DS5_MUX_PAD_RGB] = sensor_vc[5]; + state->pad_to_vc[DS5_MUX_PAD_IR] = sensor_vc[6]; + state->pad_to_vc[DS5_MUX_PAD_IMU] = sensor_vc[7]; + } + + for (i = 0; i < ARRAY_SIZE(state->pad_to_substream); i++) + state->pad_to_substream[i] = -1; + /* match for IPU6 CSI2 BE SOC video capture pads */ + if (!state->aggregated) { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 0; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 2; + state->pad_to_substream[DS5_MUX_PAD_IR] = 4; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 5; + } + else { + state->pad_to_substream[DS5_MUX_PAD_DEPTH] = 6; + state->pad_to_substream[DS5_MUX_PAD_RGB] = 8; + state->pad_to_substream[DS5_MUX_PAD_IR] = 10; + state->pad_to_substream[DS5_MUX_PAD_IMU] = 11; + } /* * 0, vc 0, depth * 1, vc 0, meta data @@ -4495,51 +5186,54 @@ static void ds5_substream_init(void) * 4, vc 2, IR * 5, vc 3, IMU */ - set_sub_stream_fmt(0, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(0, 480); - set_sub_stream_w(0, 640); - set_sub_stream_dt(0, mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); - set_sub_stream_vc_id(0, 0); - - set_sub_stream_fmt(1, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(1, 1); - set_sub_stream_w(1, 68); - set_sub_stream_dt(1, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(1, 0); + /* aggreagated */ + /* + * 6, vc 2, depth + * 7, vc 2, meta data + * 8, vc 3, RGB + * 9, vc 3, meta data + * 10, vc 0, IR + * 11, vc 1, IMU + */ + /*DEPTH*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_DEPTH], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_DEPTH], 480); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_DEPTH], 640); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_DEPTH], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_DEPTH], state->pad_to_vc[DS5_MUX_PAD_DEPTH]); + /*DEPTH MD*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, 1); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, 68); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_DEPTH] + 1, state->pad_to_vc[DS5_MUX_PAD_DEPTH]); /*RGB*/ - set_sub_stream_fmt(2, MEDIA_BUS_FMT_YUYV8_1X16); - set_sub_stream_h(2, 640); - set_sub_stream_w(2, 480); - set_sub_stream_dt(2, mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); - set_sub_stream_vc_id(2, 1); - - set_sub_stream_fmt(3, MEDIA_BUS_FMT_SGRBG8_1X8); - set_sub_stream_h(3, 1); - set_sub_stream_w(3, 68); - set_sub_stream_dt(3, MIPI_CSI2_TYPE_EMBEDDED8); - set_sub_stream_vc_id(3, 1); + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_RGB], MEDIA_BUS_FMT_YUYV8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_RGB], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_RGB], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_RGB], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_RGB], state->pad_to_vc[DS5_MUX_PAD_RGB]); + /*RGB MD*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, MEDIA_BUS_FMT_SGRBG8_1X8); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, 1); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, 68); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, MIPI_CSI2_TYPE_EMBEDDED8); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_RGB] + 1, state->pad_to_vc[DS5_MUX_PAD_RGB]); /*IR*/ - set_sub_stream_fmt(4, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(4, 640); - set_sub_stream_w(4, 480); - set_sub_stream_dt(4, mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); - set_sub_stream_vc_id(4, 2); - - set_sub_stream_fmt(5, MEDIA_BUS_FMT_UYVY8_1X16); - set_sub_stream_h(5, 640); - set_sub_stream_w(5, 480); - set_sub_stream_dt(5, mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); - set_sub_stream_vc_id(5, 3); - - for (i = 0; i < DS5_MUX_PAD_COUNT; i++) - pad_to_substream[i] = -1; - - pad_to_substream[DS5_MUX_PAD_DEPTH_A] = 0; - pad_to_substream[DS5_MUX_PAD_RGB_A] = 2; - pad_to_substream[DS5_MUX_PAD_MOTION_T_A] = 4; - pad_to_substream[DS5_MUX_PAD_IMU_A] = 5; + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_IR], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_IR], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_IR], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_IR], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_IR], state->pad_to_vc[DS5_MUX_PAD_IR]); + /*IMU*/ + set_sub_stream_fmt (state->pad_to_substream[DS5_MUX_PAD_IMU], MEDIA_BUS_FMT_UYVY8_1X16); + set_sub_stream_h (state->pad_to_substream[DS5_MUX_PAD_IMU], 640); + set_sub_stream_w (state->pad_to_substream[DS5_MUX_PAD_IMU], 480); + set_sub_stream_dt (state->pad_to_substream[DS5_MUX_PAD_IMU], mbus_code_to_mipi(MEDIA_BUS_FMT_UYVY8_1X16)); + set_sub_stream_vc_id(state->pad_to_substream[DS5_MUX_PAD_IMU], state->pad_to_vc[DS5_MUX_PAD_IMU]); } +#endif /* SYSFS attributes */ #ifdef CONFIG_SYSFS @@ -4609,7 +5303,6 @@ static ssize_t ds5_read_reg_store(struct device *dev, struct dev_ds5_reg_attribute, attr); int rc = -1; u32 reg; - ds5_rw_attr->valid = 0; /* Decode input */ rc = sscanf(buf, "0x%04x", ®); @@ -4663,8 +5356,10 @@ static const struct attribute_group ds5_attr_group = { }; #endif +#ifdef CONFIG_VIDEO_INTEL_IPU6 #define NR_DESER 4 +#ifndef CONFIG_VIDEO_D4XX_SERDES static const struct regmap_config ds5_regmap_max9296 = { .reg_bits = 16, .val_bits = 8, @@ -4758,26 +5453,26 @@ static int ds5_i2c_addr_setting(struct i2c_client *c, struct ds5 *state) return 0; } - +#endif +#endif //CONFIG_VIDEO_INTEL_IPU6 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; mutex_init(&state->lock); - dev_warn(&c->dev, "Driver addr 0x%x\n", c->addr); - state->client = c; - dev_warn(&c->dev, "Probing new driver for D45x\n"); - dev_warn(&c->dev, "Driver data NAEL %d\n", (int)id->driver_data); - state->variant = ds5_variants + id->driver_data; + dev_warn(&c->dev, "Probing driver for D45x\n"); + state->variant = ds5_variants + id->driver_data; +#ifdef CONFIG_OF state->vcc = devm_regulator_get(&c->dev, "vcc"); if (IS_ERR(state->vcc)) { ret = PTR_ERR(state->vcc); @@ -4792,6 +5487,7 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) return ret; } } +#endif state->regmap = devm_regmap_init_i2c(c, &ds5_regmap_config); if (IS_ERR(state->regmap)) { ret = PTR_ERR(state->regmap); @@ -4799,6 +5495,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 +#ifdef CONFIG_VIDEO_INTEL_IPU6 +#ifndef CONFIG_VIDEO_D4XX_SERDES state->regmap_max9296 = devm_regmap_init_i2c(c, &ds5_regmap_max9296); if (IS_ERR(state->regmap_max9296)) { ret = PTR_ERR(state->regmap_max9296); @@ -4818,11 +5521,12 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) dev_err(&c->dev, "failed apply i2c addr setting\n"); return ret; } - +#endif +#endif //CONFIG_VIDEO_INTEL_IPU6 // Verify communication - retry = 10; + retry = 5; do { - ret = ds5_read(state, 0x5020, &rec_state); + ret = ds5_read(state, 0x5020, &rec_state); } while (retry-- && ret < 0); if (ret < 0) { dev_err(&c->dev, @@ -4899,7 +5603,9 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) /* create the sysfs file group */ err = sysfs_create_group(&state->client->dev.kobj, &ds5_attr_group); #endif - ds5_substream_init(); +#ifdef CONFIG_VIDEO_INTEL_IPU6 + ds5_substream_init(state); +#endif return 0; e_chardev: @@ -4908,6 +5614,12 @@ static int ds5_probe(struct i2c_client *c, const struct i2c_device_id *id) e_regulator: if (state->vcc) regulator_disable(state->vcc); +#ifdef CONFIG_VIDEO_D4XX_SERDES + if (state->ser_i2c) + i2c_unregister_device(state->ser_i2c); + if (state->dser_i2c && !state->aggregated) + i2c_unregister_device(state->dser_i2c); +#endif return ret; } @@ -4915,21 +5627,62 @@ 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 +#ifndef CONFIG_TEGRA_CAMERA_PLATFORM + state->is_depth = 1; +#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); - if (state->is_depth) { - 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; } @@ -4964,4 +5717,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.15"); diff --git a/drivers/media/i2c/max9295.c b/drivers/media/i2c/max9295.c new file mode 100644 index 000000000000..0a2038b25a93 --- /dev/null +++ b/drivers/media/i2c/max9295.c @@ -0,0 +1,771 @@ +/* + * 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, + 0x86, 0x00, 0x01, + 0x88, 0x00, 0x01, + 0x8A, 0x00, 0x01, + 0xC0, 0x02, 0x02, + 0xC4, 0x02, 0x03, + 0xC6, 0x02, 0x03, + 0xC8, 0x02, 0x03, + 0xCA, 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..8bd4ac040dd2 --- /dev/null +++ b/drivers/media/i2c/max9296.c @@ -0,0 +1,1184 @@ +/* + * 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) +{ + struct max9296_pdata *pdata = client->dev.platform_data; + if (pdata) { + if (pdata->csi_mode == GMSL_CSI_2X4_MODE) { + priv->csi_mode = MAX9296_CSI_MODE_2X4; + priv->lane_mp1 = MAX9296_LANE_MAP1_2X4; + priv->lane_mp2 = MAX9296_LANE_MAP2_2X4; + } else if (pdata->csi_mode == GMSL_CSI_4X2_MODE) { + 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; + } + priv->max_src = pdata->max_src; + } else { + 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..59b33cab7d02 --- /dev/null +++ b/include/media/max9296.h @@ -0,0 +1,177 @@ +/* + * 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 + * @{ + */ +struct max9296_pdata { + bool splitter_enabled; + __u32 max_src; + __u32 csi_mode; +}; + +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__ */