diff --git a/drivers/media/i2c/gc8034.c b/drivers/media/i2c/gc8034.c index a40d7894468b..a55682dcd2fa 100644 --- a/drivers/media/i2c/gc8034.c +++ b/drivers/media/i2c/gc8034.c @@ -9,6 +9,10 @@ * V0.0X01.0X03 add enum_frame_interval function. * V0.0X01.0X04 add quick stream on/off * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc8034_g_mbus_config function. */ #include @@ -18,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,11 +31,16 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06) #ifndef V4L2_CID_DIGITAL_GAIN #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN @@ -37,9 +48,10 @@ #define GC8034_LANES 4 #define GC8034_BITS_PER_SAMPLE 10 -#define GC8034_LINK_FREQ_MHZ 336000000 +#define GC8034_MIPI_FREQ_336MHZ 336000000U + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ -#define GC8034_PIXEL_RATE 80000000 +#define GC8034_PIXEL_RATE 288000000 #define GC8034_XVCLK_FREQ 24000000 #define CHIP_ID 0x8044 @@ -78,6 +90,7 @@ #define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" #define GC8034_NAME "gc8034" +#define GC8034_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 static const char * const gc8034_supply_names[] = { "avdd", /* Analog power */ @@ -85,6 +98,7 @@ static const char * const gc8034_supply_names[] = { "dvdd", /* Digital core power */ }; +static const struct regval *gc8034_global_regs; #define GC8034_NUM_SUPPLIES ARRAY_SIZE(gc8034_supply_names) struct gc8034_dd { @@ -150,6 +164,7 @@ struct gc8034_mode { struct gc8034 { struct i2c_client *client; struct clk *xvclk; + struct gpio_desc *power_gpio; struct gpio_desc *reset_gpio; struct gpio_desc *pwdn_gpio; struct regulator_bulk_data supplies[GC8034_NUM_SUPPLIES]; @@ -164,8 +179,12 @@ struct gc8034 { struct v4l2_ctrl *digi_gain; struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *link_freq; struct mutex mutex; bool streaming; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; bool power_on; const struct gc8034_mode *cur_mode; u32 module_index; @@ -180,10 +199,384 @@ struct gc8034 { #define to_gc8034(sd) container_of(sd, struct gc8034, subdev) +#undef GC8034_MIRROR_NORMAL +#undef GC8034_MIRROR_H +#undef GC8034_MIRROR_V +#undef GC8034_MIRROR_HV +/* If you use the otp function, keep the otp_drv -> + * gc8034_common_otp_drv.h consistent. + */ +#define GC8034_MIRROR_NORMAL + +#if defined(GC8034_MIRROR_NORMAL) + #define GC8034_MIRROR 0xc0 + #define BINNING_STARTY 0x04 + #define BINNING_STARTX 0x05 + #define FULL_STARTY 0x08 + #define FULL_STARTX 0x09 +#elif defined(GC8034_MIRROR_H) + #define GC8034_MIRROR 0xc1 + #define BINNING_STARTY 0x04 + #define BINNING_STARTX 0x04 + #define FULL_STARTY 0x08 + #define FULL_STARTX 0x08 +#elif defined(GC8034_MIRROR_V) + #define GC8034_MIRROR 0xc2 + #define BINNING_STARTY 0x05 + #define BINNING_STARTX 0x05 + #define FULL_STARTY 0x09 + #define FULL_STARTX 0x09 +#elif defined(GC8034_MIRROR_HV) + #define GC8034_MIRROR 0xc3 + #define BINNING_STARTY 0x05 + #define BINNING_STARTX 0x04 + #define FULL_STARTY 0x09 + #define FULL_STARTX 0x08 +#else + #define GC8034_MIRROR 0xc0 + #define BINNING_STARTY 0x04 + #define BINNING_STARTX 0x05 + #define FULL_STARTY 0x08 + #define FULL_STARTX 0x09 +#endif + /* * Xclk 24Mhz */ -static const struct regval gc8034_global_regs[] = { +static const struct regval gc8034_global_regs_2lane[] = { + /*SYS*/ + {0xf2, 0x00}, + {0xf4, 0x80}, + {0xf5, 0x19}, + {0xf6, 0x44}, + {0xf7, 0x95}, //pll enable + {0xf8, 0x63}, //pll mode + {0xf9, 0x00}, + {0xfa, 0x45}, + {0xfc, 0xfe}, + + /*Cisctl&Analog*/ + {0xfe, 0x00}, + {0x03, 0x08}, + {0x04, 0xc6}, + {0x05, 0x02}, + {0x06, 0x16}, + {0x07, 0x00}, + {0x08, 0x10}, + {0x0a, 0x3a}, //row start + {0x0b, 0x00}, + {0x0c, 0x04}, //col start + {0x0d, 0x09}, + {0x0e, 0xa0}, //win_height 2464 + {0x0f, 0x0c}, + {0x10, 0xd4}, //win_width 3284 + {0x17, GC8034_MIRROR}, + {0x18, 0x02}, + {0x19, 0x17}, + {0x1e, 0x50}, + {0x1f, 0x80}, + {0x21, 0x4c}, + {0x25, 0x00}, + {0x28, 0x4a}, + {0x2d, 0x89}, + {0xca, 0x02}, + {0xcb, 0x00}, + {0xcc, 0x39}, + {0xce, 0xd0}, + {0xcf, 0x93}, + {0xd0, 0x1b}, + {0xd1, 0xaa}, + {0xd2, 0xcb}, + {0xd8, 0x40}, + {0xd9, 0xff}, + {0xda, 0x0e}, + {0xdb, 0xb0}, + {0xdc, 0x0e}, + {0xde, 0x08}, + {0xe4, 0xc6}, + {0xe5, 0x08}, + {0xe6, 0x10}, + {0xed, 0x2a}, + {0xfe, 0x02}, + {0x59, 0x02}, + {0x5a, 0x04}, + {0x5b, 0x08}, + {0x5c, 0x20}, + {0xfe, 0x00}, + {0x1a, 0x09}, + {0x1d, 0x13}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfe, 0x10}, + {0xfe, 0x00}, + + /* Gamma */ + {0xfe, 0x00}, + {0x20, 0x54}, + {0x33, 0x82}, + {0xfe, 0x01}, + {0xdf, 0x06}, + {0xe7, 0x18}, + {0xe8, 0x20}, + {0xe9, 0x16}, + {0xea, 0x17}, + {0xeb, 0x50}, + {0xec, 0x6c}, + {0xed, 0x9b}, + {0xee, 0xd8}, + + /*ISP*/ + {0xfe, 0x00}, + {0x80, 0x13}, + {0x84, 0x01}, + {0x89, 0x03}, + {0x8d, 0x03}, + {0x8f, 0x14}, + {0xad, 0x00}, + + /*Crop window*/ + {0x90, 0x01}, + {0x92, FULL_STARTY}, + {0x94, FULL_STARTX}, + {0x95, 0x09}, + {0x96, 0x90}, + {0x97, 0x0c}, + {0x98, 0xc0}, + + /*Gain*/ + {0xb0, 0x90}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb6, 0x00}, + + /*BLK*/ + {0xfe, 0x00}, + {0x40, 0x22}, + {0x43, 0x03}, //add_offset + {0x4e, 0x00}, //row_bits[15:8] + {0x4f, 0x3c}, //row_bits[7:0] + {0x58, 0x80}, //dark current ratio + {0x59, 0x80}, + {0x5a, 0x80}, + {0x5b, 0x80}, + {0x5c, 0x00}, + {0x5d, 0x00}, + {0x5e, 0x00}, + {0x5f, 0x00}, + + /*WB offset*/ + {0xfe, 0x01}, + {0xbf, 0x40}, + + /*Dark Sun*/ + {0xfe, 0x01}, + {0x68, 0x77}, + + /*DPC*/ + {0xfe, 0x01}, + {0x60, 0x15}, + {0x61, 0x10}, + {0x62, 0x60}, + {0x63, 0x48}, + {0x64, 0x02}, + + /*LSC*/ + {0xfe, 0x01}, + {0xa0, 0x10}, //[6]segment_width[8], 0x[5:4]segment_height[9:8] + {0xa8, 0x60}, //segment_height[7:0] + {0xa2, 0xd1}, //height_ratio[7:0] + {0xc8, 0x5b}, //[7:4]height_ratio[11:8] + {0xa1, 0xb8}, //segment_width[7:0] + {0xa3, 0x91}, //width_ratio[7:0] + {0xc0, 0x50}, //[7:4]width_ratio[11:8] + {0xd0, 0x05}, //segment_width_end[11:8] + {0xd1, 0xb2}, //segment_width_end[7:0] + {0xd2, 0x1f}, //col_segment + {0xd3, 0x00}, //row_num_start[7:0] + {0xd4, 0x00}, //[5:4]row_num_start[9:8] [3:0]col_seg_start + {0xd5, 0x00}, //[7:2]col_num_start[7:2] + {0xd6, 0x00}, //[2:0]col_num_start[10:8] + {0xd7, 0x00}, //row_seg_start + {0xd8, 0x00}, //col_cal_start[7:0] + {0xd9, 0x00}, //[2:0]col_cal_start[10:8] + + /*ABB*/ + {0xfe, 0x01}, + {0x20, 0x02}, + {0x21, 0x02}, + {0x23, 0x43}, + + /*MIPI*/ + {0xfe, 0x03}, + {0x01, 0x07}, + {0x02, 0x07}, + {0x03, 0x92}, + {0x04, 0x80}, + {0x11, 0x2b}, + {0x12, 0xf0}, //lwc 3264*5/4 + {0x13, 0x0f}, + {0x15, 0x10}, //LP + {0x16, 0x29}, + {0x17, 0xff}, + {0x18, 0x01}, + {0x19, 0xaa}, + {0x1a, 0x02}, + {0x21, 0x05}, + {0x22, 0x05}, + {0x23, 0x16}, + {0x24, 0x00}, + {0x25, 0x12}, + {0x26, 0x07}, + {0x29, 0x07}, + {0x2a, 0x08}, + {0x2b, 0x07}, + {0xfe, 0x00}, + //{0x3f, 0x91}, + {0x3f, 0x00}, + + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 672Mbps + */ +static const struct regval gc8034_1632x1224_regs_2lane[] = { + /*SYS*/ + {0xf2, 0x00}, + {0xf4, 0x80}, + {0xf5, 0x19}, + {0xf6, 0x44}, + {0xf8, 0x63}, + {0xfa, 0x45}, + {0xf9, 0x00}, + {0xf7, 0x95}, + {0xfc, 0x00}, + {0xfc, 0x00}, + {0xfc, 0xea}, + {0xfe, 0x03}, + {0x03, 0x9a}, + {0xfc, 0xee}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfe, 0x10}, + {0xfe, 0x00}, + + /*ISP*/ + {0xfe, 0x00}, + {0x80, 0x10}, + {0xad, 0x30}, + {0x66, 0x2c}, + {0xbc, 0x49}, + + /*Crop window*/ + {0x90, 0x01}, + {0x92, BINNING_STARTY}, //crop y + {0x94, BINNING_STARTX}, //crop x + {0x95, 0x04}, + {0x96, 0xc8}, + {0x97, 0x06}, + {0x98, 0x60}, + + /*MIPI*/ + {0xfe, 0x03}, + {0x01, 0x07}, + {0x02, 0x03}, + {0x04, 0x80}, + {0x11, 0x2b}, + {0x12, 0xf8}, + {0x13, 0x07}, + {0x15, 0x10}, //LP mode + {0x16, 0x29}, + {0x17, 0xff}, + {0x18, 0x01}, + {0x19, 0xaa}, + {0x1a, 0x02}, + {0x21, 0x05}, + {0x22, 0x06}, + {0x23, 0x16}, + {0x24, 0x00}, + {0x25, 0x12}, + {0x26, 0x07}, + {0x29, 0x07}, + {0x2a, 0x08}, + {0x2b, 0x07}, + {0xfe, 0x00}, + {0x3f, 0x00}, + + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 15fps + * mipi_datarate per lane 672Mbps + */ +static const struct regval gc8034_3264x2448_regs_2lane[] = { + /*SYS*/ + {0xf2, 0x00}, + {0xf4, 0x80}, + {0xf5, 0x19}, + {0xf6, 0x44}, + {0xf7, 0x95}, //pll enable + {0xf8, 0x63}, //pll mode + {0xf9, 0x00}, + {0xfa, 0x45}, + {0xfc, 0x00}, + {0xfc, 0x00}, + {0xfc, 0xfe}, + + /* ISP */ + {0xfe, 0x00}, + {0x80, 0x13}, + {0xad, 0x00}, + {0x66, 0x0c}, + {0xbc, 0x09}, + + /* Crop window */ + {0x90, 0x01}, + {0x92, FULL_STARTY}, + {0x94, FULL_STARTX}, + {0x95, 0x09}, + {0x96, 0x90}, + {0x97, 0x0c}, + {0x98, 0xc0}, + + /* MIPI */ + {0xfe, 0x03}, + {0x01, 0x07}, + {0x02, 0x03}, + {0x03, 0x92}, + {0x04, 0x80}, + {0x11, 0x2b}, + {0x12, 0xf0}, //lwc 3264*5/4 + {0x13, 0x0f}, + {0x15, 0x10}, //LP + {0x16, 0x29}, + {0x17, 0xff}, + {0x18, 0x01}, + {0x19, 0xaa}, + {0x1a, 0x02}, + {0x21, 0x05}, + {0x22, 0x05}, + {0x23, 0x16}, + {0x24, 0x00}, + {0x25, 0x12}, + {0x26, 0x07}, + {0x29, 0x07}, + {0x2a, 0x08}, + {0x2b, 0x07}, + {0xfe, 0x00}, + //{0x3f, 0x91}, + {0x3f, 0x00}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + */ +static const struct regval gc8034_global_regs_4lane[] = { /*SYS*/ {0xf2, 0x00}, {0xf4, 0x80}, @@ -445,11 +838,38 @@ static const struct regval gc8034_global_regs[] = { * max_framerate 30fps * mipi_datarate per lane 656Mbps */ -static const struct regval gc8034_3264x2448_regs[] = { +static const struct regval gc8034_3264x2448_regs_4lane[] = { {REG_NULL, 0x00}, }; -static const struct gc8034_mode supported_modes[] = { +static const struct gc8034_mode supported_modes_2lane[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x09a0, + .hts_def = 0x0858 * 2, + .vts_def = 0x09c4, + .reg_list = gc8034_3264x2448_regs_2lane, + }, + { + .width = 1632, + .height = 1224, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x09a0, + .hts_def = 0x0858 * 2, + .vts_def = 0x09c4, + .reg_list = gc8034_1632x1224_regs_2lane, + }, +}; + +static const struct gc8034_mode supported_modes_4lane[] = { { .width = 3264, .height = 2448, @@ -460,12 +880,14 @@ static const struct gc8034_mode supported_modes[] = { .exp_def = 0x08c6, .hts_def = 0x10b0, .vts_def = 0x09c0, - .reg_list = gc8034_3264x2448_regs, + .reg_list = gc8034_3264x2448_regs_4lane, }, }; +static const struct gc8034_mode *supported_modes; + static const s64 link_freq_menu_items[] = { - GC8034_LINK_FREQ_MHZ + GC8034_MIPI_FREQ_336MHZ, }; /* Write registers up to 4 at a time */ @@ -544,7 +966,8 @@ static int gc8034_get_reso_dist(const struct gc8034_mode *mode, } static const struct gc8034_mode * -gc8034_find_best_fit(struct v4l2_subdev_format *fmt) +gc8034_find_best_fit(struct gc8034 *gc8034, + struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt = &fmt->format; int dist; @@ -552,7 +975,7 @@ gc8034_find_best_fit(struct v4l2_subdev_format *fmt) int cur_best_fit_dist = -1; unsigned int i; - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + for (i = 0; i < gc8034->cfg_num; i++) { dist = gc8034_get_reso_dist(&supported_modes[i], framefmt); if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { cur_best_fit_dist = dist; @@ -573,8 +996,8 @@ static int gc8034_set_fmt(struct v4l2_subdev *sd, mutex_lock(&gc8034->mutex); - mode = gc8034_find_best_fit(fmt); - fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + mode = gc8034_find_best_fit(gc8034, fmt); + fmt->format.code = GC8034_MEDIA_BUS_FMT; fmt->format.width = mode->width; fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; @@ -594,6 +1017,8 @@ static int gc8034_set_fmt(struct v4l2_subdev *sd, __v4l2_ctrl_modify_range(gc8034->vblank, vblank_def, GC8034_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(gc8034->link_freq, + link_freq_menu_items[0]); } mutex_unlock(&gc8034->mutex); @@ -619,7 +1044,7 @@ static int gc8034_get_fmt(struct v4l2_subdev *sd, } else { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.code = GC8034_MEDIA_BUS_FMT; fmt->format.field = V4L2_FIELD_NONE; } mutex_unlock(&gc8034->mutex); @@ -633,7 +1058,7 @@ static int gc8034_enum_mbus_code(struct v4l2_subdev *sd, { if (code->index != 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + code->code = GC8034_MEDIA_BUS_FMT; return 0; } @@ -642,10 +1067,12 @@ static int gc8034_enum_frame_sizes(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct gc8034 *gc8034 = to_gc8034(sd); + + if (fse->index >= gc8034->cfg_num) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + if (fse->code != GC8034_MEDIA_BUS_FMT) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -669,7 +1096,6 @@ static int gc8034_g_frame_interval(struct v4l2_subdev *sd, return 0; } -#define GC8034_MIRROR_NORMAL #define DD_WIDTH 3284 #define DD_HEIGHT 2464 @@ -1226,9 +1652,15 @@ static long gc8034_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ret = gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); - ret |= gc8034_write_reg(gc8034->client, - GC8034_REG_CTRL_MODE, - GC8034_MODE_STREAMING); + if (2 == gc8034->lane_num) { + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_CTRL_MODE, + 0x91); + } else { + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_CTRL_MODE, + GC8034_MODE_STREAMING); + } } else { ret = gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, @@ -1497,13 +1929,18 @@ static int __gc8034_start_stream(struct gc8034 *gc8034) mutex_unlock(&gc8034->mutex); ret = v4l2_ctrl_handler_setup(&gc8034->ctrl_handler); mutex_lock(&gc8034->mutex); - ret = gc8034_write_reg(gc8034->client, + ret |= gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); - ret |= gc8034_write_reg(gc8034->client, - GC8034_REG_CTRL_MODE, - GC8034_MODE_STREAMING); - + if (2 == gc8034->lane_num) { + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_CTRL_MODE, + 0x91); + } else { + ret |= gc8034_write_reg(gc8034->client, + GC8034_REG_CTRL_MODE, + GC8034_MODE_STREAMING); + } return ret; } @@ -1518,7 +1955,7 @@ static int __gc8034_stop_stream(struct gc8034 *gc8034) GC8034_REG_CTRL_MODE, GC8034_MODE_SW_STANDBY); - return 0; + return ret; } static int gc8034_s_stream(struct v4l2_subdev *sd, int on) @@ -1527,6 +1964,12 @@ static int gc8034_s_stream(struct v4l2_subdev *sd, int on) struct i2c_client *client = gc8034->client; int ret = 0; + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + gc8034->cur_mode->width, + gc8034->cur_mode->height, + DIV_ROUND_CLOSEST(gc8034->cur_mode->max_fps.denominator, + gc8034->cur_mode->max_fps.numerator)); + mutex_lock(&gc8034->mutex); on = !!on; if (on == gc8034->streaming) @@ -1564,6 +2007,7 @@ static int gc8034_s_power(struct v4l2_subdev *sd, int on) struct i2c_client *client = gc8034->client; int ret = 0; + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); mutex_lock(&gc8034->mutex); /* If the power state is not modified - no work to do. */ @@ -1608,6 +2052,11 @@ static int __gc8034_power_on(struct gc8034 *gc8034) u32 delay_us; struct device *dev = &gc8034->client->dev; + if (!IS_ERR(gc8034->power_gpio)) + gpiod_set_value_cansleep(gc8034->power_gpio, 1); + + usleep_range(1000, 2000); + if (!IS_ERR_OR_NULL(gc8034->pins_default)) { ret = pinctrl_select_state(gc8034->pinctrl, gc8034->pins_default); @@ -1668,6 +2117,9 @@ static void __gc8034_power_off(struct gc8034 *gc8034) if (ret < 0) dev_dbg(&gc8034->client->dev, "could not set pins\n"); } + if (!IS_ERR(gc8034->power_gpio)) + gpiod_set_value_cansleep(gc8034->power_gpio, 0); + regulator_bulk_disable(GC8034_NUM_SUPPLIES, gc8034->supplies); } @@ -1703,7 +2155,7 @@ static int gc8034_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ try_fmt->width = def_mode->width; try_fmt->height = def_mode->height; - try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->code = GC8034_MEDIA_BUS_FMT; try_fmt->field = V4L2_FIELD_NONE; mutex_unlock(&gc8034->mutex); @@ -1717,10 +2169,12 @@ static int gc8034_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { - if (fie->index >= ARRAY_SIZE(supported_modes)) + struct gc8034 *gc8034 = to_gc8034(sd); + + if (fie->index >= gc8034->cfg_num) return -EINVAL; - if (fie->code != MEDIA_BUS_FMT_SRGGB10_1X10) + if (fie->code != GC8034_MEDIA_BUS_FMT) return -EINVAL; fie->width = supported_modes[fie->index].width; @@ -1732,14 +2186,25 @@ static int gc8034_enum_frame_interval(struct v4l2_subdev *sd, static int gc8034_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *config) { - u32 val = 0; + struct gc8034 *sensor = to_gc8034(sd); + struct device *dev = &sensor->client->dev; - val = 1 << (GC8034_LANES - 1) | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - config->type = V4L2_MBUS_CSI2; - config->flags = val; + dev_info(dev, "%s(%d) enter!\n", __func__, __LINE__); + if (2 == sensor->lane_num) { + config->type = V4L2_MBUS_CSI2; + config->flags = V4L2_MBUS_CSI2_2_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else if (4 == sensor->lane_num) { + config->type = V4L2_MBUS_CSI2; + config->flags = V4L2_MBUS_CSI2_4_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else { + dev_err(&sensor->client->dev, + "unsupported lane_num(%d)\n", sensor->lane_num); + } return 0; } @@ -1902,6 +2367,7 @@ static int gc8034_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = gc8034->client; s64 max; int ret = 0; + s32 temp; /* Propagate change of current control to all related controls */ switch (ctrl->id) { @@ -1921,21 +2387,26 @@ static int gc8034_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_EXPOSURE: /* 4 least significant bits of expsoure are fractional part */ + dev_info(&client->dev, "set exposure value 0x%x\n", ctrl->val); ret = gc8034_set_exposure_reg(gc8034, ctrl->val); break; case V4L2_CID_ANALOGUE_GAIN: + dev_info(&client->dev, "set analog gain value 0x%x\n", ctrl->val); ret = gc8034_set_gain_reg(gc8034, ctrl->val); break; case V4L2_CID_VBLANK: + dev_info(&client->dev, "set vb value 0x%x\n", ctrl->val); + /* VB = VTS - 2448 -36, according android8.1 driver */ + temp = ctrl->val + gc8034->cur_mode->height - 2448 - 36; ret = gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); ret |= gc8034_write_reg(gc8034->client, GC8034_REG_VTS_H, - ((ctrl->val - 36) >> 8) & 0xff); + (temp >> 8) & 0xff); ret |= gc8034_write_reg(gc8034->client, GC8034_REG_VTS_L, - (ctrl->val - 36) & 0xff); + temp & 0xff); break; default: dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", @@ -1956,7 +2427,6 @@ static int gc8034_initialize_controls(struct gc8034 *gc8034) { const struct gc8034_mode *mode; struct v4l2_ctrl_handler *handler; - struct v4l2_ctrl *ctrl; s64 exposure_max, vblank_def; u32 h_blank; int ret; @@ -1968,13 +2438,12 @@ static int gc8034_initialize_controls(struct gc8034 *gc8034) return ret; handler->lock = &gc8034->mutex; - ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, - 0, 0, link_freq_menu_items); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + gc8034->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, 0, 0, + link_freq_menu_items); v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, - 0, GC8034_PIXEL_RATE, 1, GC8034_PIXEL_RATE); + 0, gc8034->pixel_rate, 1, gc8034->pixel_rate); h_blank = mode->hts_def - mode->width; gc8034->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, @@ -2031,7 +2500,7 @@ static int gc8034_check_sensor_id(struct gc8034 *gc8034, dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); return -ENODEV; } - + dev_info(dev, "detected gc%04x sensor\n", id); return ret; } @@ -2047,6 +2516,60 @@ static int gc8034_configure_regulators(struct gc8034 *gc8034) gc8034->supplies); } +static int gc8034_parse_of(struct gc8034 *gc8034) +{ + struct device *dev = &gc8034->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + unsigned int fps; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc8034->lane_num = rval; + if (4 == gc8034->lane_num) { + gc8034->cur_mode = &supported_modes_4lane[0]; + supported_modes = supported_modes_4lane; + gc8034->cfg_num = ARRAY_SIZE(supported_modes_4lane); + gc8034_global_regs = gc8034_global_regs_4lane; + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + fps = DIV_ROUND_CLOSEST(gc8034->cur_mode->max_fps.denominator, + gc8034->cur_mode->max_fps.numerator); + gc8034->pixel_rate = gc8034->cur_mode->vts_def * + gc8034->cur_mode->hts_def * fps; + + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc8034->lane_num, gc8034->pixel_rate); + } else if (2 == gc8034->lane_num) { + gc8034->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + gc8034->cfg_num = ARRAY_SIZE(supported_modes_2lane); + gc8034_global_regs = gc8034_global_regs_2lane; + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + fps = DIV_ROUND_CLOSEST(gc8034->cur_mode->max_fps.denominator, + gc8034->cur_mode->max_fps.numerator); + gc8034->pixel_rate = gc8034->cur_mode->vts_def * + gc8034->cur_mode->hts_def * fps; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc8034->lane_num, gc8034->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", gc8034->lane_num); + return -1; + } + + return 0; +} + static int gc8034_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2079,7 +2602,6 @@ static int gc8034_probe(struct i2c_client *client, return -EINVAL; } gc8034->client = client; - gc8034->cur_mode = &supported_modes[0]; gc8034->xvclk = devm_clk_get(dev, "xvclk"); if (IS_ERR(gc8034->xvclk)) { @@ -2087,6 +2609,9 @@ static int gc8034_probe(struct i2c_client *client, return -EINVAL; } + gc8034->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(gc8034->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); gc8034->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gc8034->reset_gpio)) dev_warn(dev, "Failed to get reset-gpios\n"); @@ -2101,6 +2626,10 @@ static int gc8034_probe(struct i2c_client *client, return ret; } + ret = gc8034_parse_of(gc8034); + if (ret != 0) + return -EINVAL; + gc8034->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(gc8034->pinctrl)) { gc8034->pins_default =