diff options
Diffstat (limited to 'drivers/media/i2c')
27 files changed, 2209 insertions, 1084 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8ba096b8ebca..e576b213084d 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -140,6 +140,7 @@ config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB select REGMAP_I2C + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX214 camera. @@ -1146,6 +1147,17 @@ config VIDEO_ISL7998X Support for Intersil ISL7998x analog to MIPI-CSI2 or BT.656 decoder. +config VIDEO_LT6911UXE + tristate "Lontium LT6911UXE decoder" + depends on ACPI && VIDEO_DEV + select V4L2_FWNODE + help + This is a Video4Linux2 sensor-level driver for the Lontium + LT6911UXE HDMI to MIPI CSI-2 bridge. + + To compile this driver as a module, choose M here: the + module will be called lt6911uxe. + config VIDEO_KS0127 tristate "KS0127 video decoder" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index fbb988bd067a..6c23a4463527 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o +obj-$(CONFIG_VIDEO_LT6911UXE) += lt6911uxe.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index ff7dfa0278a7..6e50b14f888f 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -195,6 +195,7 @@ struct adv7180_state; #define ADV7180_FLAG_V2 BIT(1) #define ADV7180_FLAG_MIPI_CSI2 BIT(2) #define ADV7180_FLAG_I2P BIT(3) +#define ADV7180_FLAG_TEST_PATTERN BIT(4) struct adv7180_chip_info { unsigned int flags; @@ -682,11 +683,15 @@ static int adv7180_init_controls(struct adv7180_state *state) ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL); - v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, &adv7180_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(test_pattern_menu) - 1, - 0, ARRAY_SIZE(test_pattern_menu) - 1, - test_pattern_menu); + if (state->chip_info->flags & ADV7180_FLAG_TEST_PATTERN) { + v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, + &adv7180_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, + ARRAY_SIZE(test_pattern_menu) - 1, + test_pattern_menu); + } state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { @@ -1221,7 +1226,7 @@ static const struct adv7180_chip_info adv7182_info = { }; static const struct adv7180_chip_info adv7280_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P | ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1235,7 +1240,8 @@ static const struct adv7180_chip_info adv7280_info = { }; static const struct adv7180_chip_info adv7280_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1256,7 +1262,8 @@ static const struct adv7180_chip_info adv7280_m_info = { }; static const struct adv7180_chip_info adv7281_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN7) | @@ -1271,7 +1278,8 @@ static const struct adv7180_chip_info adv7281_info = { }; static const struct adv7180_chip_info adv7281_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1291,7 +1299,8 @@ static const struct adv7180_chip_info adv7281_m_info = { }; static const struct adv7180_chip_info adv7281_ma_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | @@ -1316,7 +1325,7 @@ static const struct adv7180_chip_info adv7281_ma_info = { }; static const struct adv7180_chip_info adv7282_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P | ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN7) | @@ -1331,7 +1340,8 @@ static const struct adv7180_chip_info adv7282_info = { }; static const struct adv7180_chip_info adv7282_m_info = { - .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P | + ADV7180_FLAG_TEST_PATTERN, .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN3) | diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 9bc0121d0eff..2c1db5968af8 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -320,7 +320,7 @@ struct adv748x_state { /* Free run pattern select */ #define ADV748X_SDP_FRP 0x14 -#define ADV748X_SDP_FRP_MASK GENMASK(3, 1) +#define ADV748X_SDP_FRP_MASK GENMASK(2, 0) /* Saturation */ #define ADV748X_SDP_SD_SAT_U 0xe3 /* user_map_rw_reg_e3 */ diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 4036972af3a6..f95a99d85360 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1664,7 +1664,9 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) if (!err) { adv7511_dbg_dump_edid(2, debug, sd, segment, &state->edid.data[segment * 256]); if (segment == 0) { - state->edid.blocks = state->edid.data[0x7e] + 1; + state->edid.blocks = + v4l2_num_edid_blocks(state->edid.data, + EDID_MAX_SEGM * 2); v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); } @@ -1682,7 +1684,7 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) /* one more segment read ok */ state->edid.segments = segment + 1; v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1); - if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { + if (state->edid.blocks > state->edid.segments * 2) { /* Request next EDID segment */ v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); adv7511_wr(sd, 0xc9, 0xf); diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index cf8858cb13d4..34ccda666524 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -75,11 +75,11 @@ static const char *pll_string(unsigned int which) #define PLL_FL(f) CCS_PLL_FLAG_##f -static void print_pll(struct device *dev, struct ccs_pll *pll) +static void print_pll(struct device *dev, const struct ccs_pll *pll) { const struct { - struct ccs_pll_branch_fr *fr; - struct ccs_pll_branch_bk *bk; + const struct ccs_pll_branch_fr *fr; + const struct ccs_pll_branch_bk *bk; unsigned int which; } branches[] = { { &pll->vt_fr, &pll->vt_bk, PLL_VT }, @@ -150,10 +150,10 @@ static u32 op_pix_ddr(u32 flags) static int check_fr_bounds(struct device *dev, const struct ccs_pll_limits *lim, - struct ccs_pll *pll, unsigned int which) + const struct ccs_pll *pll, unsigned int which) { const struct ccs_pll_branch_limits_fr *lim_fr; - struct ccs_pll_branch_fr *pll_fr; + const struct ccs_pll_branch_fr *pll_fr; const char *s = pll_string(which); int rval; @@ -190,10 +190,10 @@ static int check_fr_bounds(struct device *dev, static int check_bk_bounds(struct device *dev, const struct ccs_pll_limits *lim, - struct ccs_pll *pll, unsigned int which) + const struct ccs_pll *pll, unsigned int which) { const struct ccs_pll_branch_limits_bk *lim_bk; - struct ccs_pll_branch_bk *pll_bk; + const struct ccs_pll_branch_bk *pll_bk; const char *s = pll_string(which); int rval; @@ -230,7 +230,7 @@ static int check_bk_bounds(struct device *dev, return rval; } -static int check_ext_bounds(struct device *dev, struct ccs_pll *pll) +static int check_ext_bounds(struct device *dev, const struct ccs_pll *pll) { if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) && pll->pixel_rate_pixel_array > pll->pixel_rate_csi) { diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 2cdab2f3d9dc..004d28c33287 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3566,6 +3566,7 @@ static int ccs_probe(struct i2c_client *client) out_disable_runtime_pm: pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); out_cleanup: ccs_cleanup(sensor); @@ -3595,9 +3596,10 @@ static void ccs_remove(struct i2c_client *client) v4l2_async_unregister_subdev(subdev); pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) + if (!pm_runtime_status_suspended(&client->dev)) { ccs_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_set_suspended(&client->dev); + } for (i = 0; i < sensor->ssds_used; i++) v4l2_device_unregister_subdev(&sensor->ssds[i].sd); diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index c626ed845928..032fbcb981f2 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -2,8 +2,10 @@ // Copyright (c) 2012 Intel Corporation /* - * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo: - * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 + * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c from: + * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 and + * latte-l-oss/drivers/external_drivers/camera/drivers/media/i2c/micam/dw9761.c + * from: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/ */ #include <linux/delay.h> @@ -23,26 +25,45 @@ #define DW9719_INFO CCI_REG8(0) #define DW9719_ID 0xF1 +#define DW9761_ID 0xF4 #define DW9719_CONTROL CCI_REG8(2) +#define DW9719_STANDBY 0x00 +#define DW9719_SHUTDOWN 0x01 #define DW9719_ENABLE_RINGING 0x02 #define DW9719_VCM_CURRENT CCI_REG16(3) +#define DW9719_STATUS CCI_REG16(5) +#define DW9719_STATUS_BUSY BIT(0) + #define DW9719_MODE CCI_REG8(6) #define DW9719_MODE_SAC_SHIFT 4 -#define DW9719_MODE_SAC3 4 +#define DW9719_DEFAULT_SAC 4 +#define DW9761_DEFAULT_SAC 6 #define DW9719_VCM_FREQ CCI_REG8(7) #define DW9719_DEFAULT_VCM_FREQ 0x60 +#define DW9761_DEFAULT_VCM_FREQ 0x3E + +#define DW9761_VCM_PRELOAD CCI_REG8(8) +#define DW9761_DEFAULT_VCM_PRELOAD 0x73 + #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd) +enum dw9719_model { + DW9719, + DW9761, +}; + struct dw9719_device { struct v4l2_subdev sd; struct device *dev; struct regmap *regmap; struct regulator *regulator; + enum dw9719_model model; + u32 mode_low_bits; u32 sac_mode; u32 vcm_freq; @@ -52,30 +73,14 @@ struct dw9719_device { } ctrls; }; -static int dw9719_detect(struct dw9719_device *dw9719) -{ - int ret; - u64 val; - - ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); - if (ret < 0) - return ret; - - if (val != DW9719_ID) { - dev_err(dw9719->dev, "Failed to detect correct id\n"); - return -ENXIO; - } - - return 0; -} - static int dw9719_power_down(struct dw9719_device *dw9719) { return regulator_disable(dw9719->regulator); } -static int dw9719_power_up(struct dw9719_device *dw9719) +static int dw9719_power_up(struct dw9719_device *dw9719, bool detect) { + u64 val; int ret; ret = regulator_enable(dw9719->regulator); @@ -83,16 +88,54 @@ static int dw9719_power_up(struct dw9719_device *dw9719) return ret; /* Jiggle SCL pin to wake up device */ - cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret); - + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_SHUTDOWN, &ret); + fsleep(100); + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_STANDBY, &ret); /* Need 100us to transit from SHUTDOWN to STANDBY */ fsleep(100); + if (detect) { + ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); + if (ret < 0) + return ret; + + switch (val) { + case DW9719_ID: + dw9719->model = DW9719; + dw9719->mode_low_bits = 0x00; + dw9719->sac_mode = DW9719_DEFAULT_SAC; + dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; + break; + case DW9761_ID: + dw9719->model = DW9761; + dw9719->mode_low_bits = 0x01; + dw9719->sac_mode = DW9761_DEFAULT_SAC; + dw9719->vcm_freq = DW9761_DEFAULT_VCM_FREQ; + break; + default: + dev_err(dw9719->dev, + "Error unknown device id 0x%02llx\n", val); + return -ENXIO; + } + + /* Optional indication of SAC mode select */ + device_property_read_u32(dw9719->dev, "dongwoon,sac-mode", + &dw9719->sac_mode); + + /* Optional indication of VCM frequency */ + device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq", + &dw9719->vcm_freq); + } + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); - cci_write(dw9719->regmap, DW9719_MODE, - dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret); + cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits | + (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret); cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + if (dw9719->model == DW9761) + cci_write(dw9719->regmap, DW9761_VCM_PRELOAD, + DW9761_DEFAULT_VCM_PRELOAD, &ret); + if (ret) dw9719_power_down(dw9719); @@ -159,7 +202,7 @@ static int dw9719_resume(struct device *dev) int ret; int val; - ret = dw9719_power_up(dw9719); + ret = dw9719_power_up(dw9719, false); if (ret) return ret; @@ -237,16 +280,6 @@ static int dw9719_probe(struct i2c_client *client) return PTR_ERR(dw9719->regmap); dw9719->dev = &client->dev; - dw9719->sac_mode = DW9719_MODE_SAC3; - dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; - - /* Optional indication of SAC mode select */ - device_property_read_u32(&client->dev, "dongwoon,sac-mode", - &dw9719->sac_mode); - - /* Optional indication of VCM frequency */ - device_property_read_u32(&client->dev, "dongwoon,vcm-freq", - &dw9719->vcm_freq); dw9719->regulator = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(dw9719->regulator)) @@ -274,14 +307,10 @@ static int dw9719_probe(struct i2c_client *client) * will work. */ - ret = dw9719_power_up(dw9719); + ret = dw9719_power_up(dw9719, true); if (ret) goto err_cleanup_media; - ret = dw9719_detect(dw9719); - if (ret) - goto err_powerdown; - pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); pm_runtime_enable(&client->dev); @@ -299,7 +328,6 @@ static int dw9719_probe(struct i2c_client *client) err_pm_runtime: pm_runtime_disable(&client->dev); pm_runtime_put_noidle(&client->dev); -err_powerdown: dw9719_power_down(dw9719); err_cleanup_media: media_entity_cleanup(&dw9719->sd.entity); @@ -327,6 +355,7 @@ static void dw9719_remove(struct i2c_client *client) static const struct i2c_device_id dw9719_id_table[] = { { "dw9719" }, + { "dw9761" }, { } }; MODULE_DEVICE_TABLE(i2c, dw9719_id_table); diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index 3ac42d1ab8b4..aed258211b8a 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -719,7 +719,7 @@ static int hi556_write_reg_list(struct hi556 *hi556, r_list->regs[i].val); if (ret) { dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", + "failed to write reg 0x%4.4x. error = %d\n", r_list->regs[i].address, ret); return ret; } @@ -926,7 +926,7 @@ static int hi556_identify_module(struct hi556 *hi556) return ret; if (val != HI556_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", HI556_CHIP_ID, val); return -ENXIO; } @@ -1002,14 +1002,14 @@ static int hi556_start_streaming(struct hi556 *hi556) reg_list = &link_freq_configs[link_freq_index].reg_list; ret = hi556_write_reg_list(hi556, reg_list); if (ret) { - dev_err(&client->dev, "failed to set plls"); + dev_err(&client->dev, "failed to set plls\n"); return ret; } reg_list = &hi556->cur_mode->reg_list; ret = hi556_write_reg_list(hi556, reg_list); if (ret) { - dev_err(&client->dev, "failed to set mode"); + dev_err(&client->dev, "failed to set mode\n"); return ret; } @@ -1021,7 +1021,7 @@ static int hi556_start_streaming(struct hi556 *hi556) HI556_REG_VALUE_16BIT, HI556_MODE_STREAMING); if (ret) { - dev_err(&client->dev, "failed to set stream"); + dev_err(&client->dev, "failed to set stream\n"); return ret; } @@ -1034,7 +1034,7 @@ static void hi556_stop_streaming(struct hi556 *hi556) if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); + dev_err(&client->dev, "failed to set stream\n"); } static int hi556_set_stream(struct v4l2_subdev *sd, int enable) @@ -1053,7 +1053,6 @@ static int hi556_set_stream(struct v4l2_subdev *sd, int enable) ret = hi556_start_streaming(hi556); if (ret) { - enable = 0; hi556_stop_streaming(hi556); pm_runtime_put(&client->dev); } @@ -1220,33 +1219,35 @@ static int hi556_check_hwcfg(struct device *dev) */ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) - return ret; + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; + dev_err(dev, "can't get clock frequency\n"); + goto check_hwcfg_error; } if (mclk != HI556_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; + dev_err(dev, "external clock %d is not supported\n", mclk); + ret = -EINVAL; + goto check_hwcfg_error; } if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto check_hwcfg_error; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); + dev_err(dev, "no link frequencies defined\n"); ret = -EINVAL; goto check_hwcfg_error; } @@ -1259,7 +1260,7 @@ static int hi556_check_hwcfg(struct device *dev) } if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", + dev_err(dev, "no link frequency %lld supported\n", link_freq_menu_items[i]); ret = -EINVAL; goto check_hwcfg_error; @@ -1332,11 +1333,8 @@ static int hi556_probe(struct i2c_client *client) int ret; ret = hi556_check_hwcfg(&client->dev); - if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", - ret); + if (ret) return ret; - } hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); if (!hi556) @@ -1371,7 +1369,7 @@ static int hi556_probe(struct i2c_client *client) ret = hi556_identify_module(hi556); if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); + dev_err(&client->dev, "failed to find sensor: %d\n", ret); goto probe_error_power_off; } } @@ -1380,7 +1378,7 @@ static int hi556_probe(struct i2c_client *client) hi556->cur_mode = &supported_modes[0]; ret = hi556_init_controls(hi556); if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); + dev_err(&client->dev, "failed to init controls: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } @@ -1391,13 +1389,13 @@ static int hi556_probe(struct i2c_client *client) hi556->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&hi556->sd.entity, 1, &hi556->pad); if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); + dev_err(&client->dev, "failed to init entity pads: %d\n", ret); goto probe_error_v4l2_ctrl_handler_free; } ret = v4l2_async_register_subdev_sensor(&hi556->sd); if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", + dev_err(&client->dev, "failed to register V4L2 subdev: %d\n", ret); goto probe_error_media_entity_cleanup; } diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 4962cfe7c83d..dd7bc45523d8 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -15,26 +15,186 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <media/media-entity.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define IMX214_REG_MODE_SELECT 0x0100 +/* Chip ID */ +#define IMX214_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX214_CHIP_ID 0x0214 + +#define IMX214_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX214_MODE_STANDBY 0x00 #define IMX214_MODE_STREAMING 0x01 +#define IMX214_REG_FAST_STANDBY_CTRL CCI_REG8(0x0106) + #define IMX214_DEFAULT_CLK_FREQ 24000000 -#define IMX214_DEFAULT_LINK_FREQ 480000000 +#define IMX214_DEFAULT_LINK_FREQ 600000000 +/* Keep wrong link frequency for backward compatibility */ +#define IMX214_DEFAULT_LINK_FREQ_LEGACY 480000000 #define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10) #define IMX214_FPS 30 -#define IMX214_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 + +/* V-TIMING internal */ +#define IMX214_REG_FRM_LENGTH_LINES CCI_REG16(0x0340) +#define IMX214_VTS_MAX 0xffff + +#define IMX214_VBLANK_MIN 890 + +/* HBLANK control - read only */ +#define IMX214_PPL_DEFAULT 5008 /* Exposure control */ -#define IMX214_REG_EXPOSURE 0x0202 -#define IMX214_EXPOSURE_MIN 0 -#define IMX214_EXPOSURE_MAX 3184 +#define IMX214_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX214_EXPOSURE_OFFSET 10 +#define IMX214_EXPOSURE_MIN 1 #define IMX214_EXPOSURE_STEP 1 #define IMX214_EXPOSURE_DEFAULT 3184 +#define IMX214_REG_EXPOSURE_RATIO CCI_REG8(0x0222) +#define IMX214_REG_SHORT_EXPOSURE CCI_REG16(0x0224) + +/* Analog gain control */ +#define IMX214_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX214_REG_SHORT_ANALOG_GAIN CCI_REG16(0x0216) +#define IMX214_ANA_GAIN_MIN 0 +#define IMX214_ANA_GAIN_MAX 448 +#define IMX214_ANA_GAIN_STEP 1 +#define IMX214_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX214_REG_DIG_GAIN_GREENR CCI_REG16(0x020e) +#define IMX214_REG_DIG_GAIN_RED CCI_REG16(0x0210) +#define IMX214_REG_DIG_GAIN_BLUE CCI_REG16(0x0212) +#define IMX214_REG_DIG_GAIN_GREENB CCI_REG16(0x0214) +#define IMX214_DGTL_GAIN_MIN 0x0100 +#define IMX214_DGTL_GAIN_MAX 0x0fff +#define IMX214_DGTL_GAIN_DEFAULT 0x0100 +#define IMX214_DGTL_GAIN_STEP 1 + +#define IMX214_REG_ORIENTATION CCI_REG8(0x0101) + +#define IMX214_REG_MASK_CORR_FRAMES CCI_REG8(0x0105) +#define IMX214_CORR_FRAMES_TRANSMIT 0 +#define IMX214_CORR_FRAMES_MASK 1 + +#define IMX214_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define IMX214_CSI_DATA_FORMAT_RAW8 0x0808 +#define IMX214_CSI_DATA_FORMAT_RAW10 0x0A0A +#define IMX214_CSI_DATA_FORMAT_COMP6 0x0A06 +#define IMX214_CSI_DATA_FORMAT_COMP8 0x0A08 + +#define IMX214_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX214_CSI_2_LANE_MODE 1 +#define IMX214_CSI_4_LANE_MODE 3 + +#define IMX214_REG_EXCK_FREQ CCI_REG16(0x0136) +#define IMX214_EXCK_FREQ(n) ((n) * 256) /* n expressed in MHz */ + +#define IMX214_REG_TEMP_SENSOR_CONTROL CCI_REG8(0x0138) + +#define IMX214_REG_HDR_MODE CCI_REG8(0x0220) +#define IMX214_HDR_MODE_OFF 0 +#define IMX214_HDR_MODE_ON 1 + +#define IMX214_REG_HDR_RES_REDUCTION CCI_REG8(0x0221) +#define IMX214_HDR_RES_REDU_THROUGH 0x11 +#define IMX214_HDR_RES_REDU_2_BINNING 0x22 + +/* PLL settings */ +#define IMX214_REG_VTPXCK_DIV CCI_REG8(0x0301) +#define IMX214_REG_VTSYCK_DIV CCI_REG8(0x0303) +#define IMX214_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX214_REG_PLL_VT_MPY CCI_REG16(0x0306) +#define IMX214_REG_OPPXCK_DIV CCI_REG8(0x0309) +#define IMX214_REG_OPSYCK_DIV CCI_REG8(0x030b) +#define IMX214_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX214_PLL_SINGLE 0 +#define IMX214_PLL_DUAL 1 + +#define IMX214_REG_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define IMX214_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX214_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX214_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX214_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX214_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX214_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define IMX214_REG_X_EVEN_INC CCI_REG8(0x0381) +#define IMX214_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX214_REG_Y_EVEN_INC CCI_REG8(0x0385) +#define IMX214_REG_Y_ODD_INC CCI_REG8(0x0387) + +#define IMX214_REG_SCALE_MODE CCI_REG8(0x0401) +#define IMX214_SCALE_NONE 0 +#define IMX214_SCALE_HORIZONTAL 1 +#define IMX214_SCALE_FULL 2 +#define IMX214_REG_SCALE_M CCI_REG16(0x0404) + +#define IMX214_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX214_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX214_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX214_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) + +#define IMX214_REG_REQ_LINK_BIT_RATE CCI_REG32(0x0820) +#define IMX214_LINK_BIT_RATE_MBPS(n) ((n) << 16) + +/* Binning mode */ +#define IMX214_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX214_BINNING_NONE 0 +#define IMX214_BINNING_ENABLE 1 +#define IMX214_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX214_REG_BINNING_WEIGHTING CCI_REG8(0x0902) +#define IMX214_BINNING_AVERAGE 0x00 +#define IMX214_BINNING_SUMMED 0x01 +#define IMX214_BINNING_BAYER 0x02 + +#define IMX214_REG_SING_DEF_CORR_EN CCI_REG8(0x0b06) +#define IMX214_SING_DEF_CORR_OFF 0 +#define IMX214_SING_DEF_CORR_ON 1 + +/* AWB control */ +#define IMX214_REG_ABS_GAIN_GREENR CCI_REG16(0x0b8e) +#define IMX214_REG_ABS_GAIN_RED CCI_REG16(0x0b90) +#define IMX214_REG_ABS_GAIN_BLUE CCI_REG16(0x0b92) +#define IMX214_REG_ABS_GAIN_GREENB CCI_REG16(0x0b94) + +#define IMX214_REG_RMSC_NR_MODE CCI_REG8(0x3001) +#define IMX214_REG_STATS_OUT_EN CCI_REG8(0x3013) +#define IMX214_STATS_OUT_OFF 0 +#define IMX214_STATS_OUT_ON 1 + +/* Chroma noise reduction */ +#define IMX214_REG_NML_NR_EN CCI_REG8(0x30a2) +#define IMX214_NML_NR_OFF 0 +#define IMX214_NML_NR_ON 1 + +#define IMX214_REG_EBD_SIZE_V CCI_REG8(0x5041) +#define IMX214_EBD_NO 0 +#define IMX214_EBD_4_LINE 4 + +#define IMX214_REG_RG_STATS_LMT CCI_REG16(0x6d12) +#define IMX214_RG_STATS_LMT_10_BIT 0x03FF +#define IMX214_RG_STATS_LMT_14_BIT 0x3FFF + +#define IMX214_REG_ATR_FAST_MOVE CCI_REG8(0x9300) + +/* Test Pattern Control */ +#define IMX214_REG_TEST_PATTERN CCI_REG16(0x0600) +#define IMX214_TEST_PATTERN_DISABLE 0 +#define IMX214_TEST_PATTERN_SOLID_COLOR 1 +#define IMX214_TEST_PATTERN_COLOR_BARS 2 +#define IMX214_TEST_PATTERN_GREY_COLOR 3 +#define IMX214_TEST_PATTERN_PN9 4 + +/* Test pattern colour components */ +#define IMX214_REG_TESTP_RED CCI_REG16(0x0602) +#define IMX214_REG_TESTP_GREENR CCI_REG16(0x0604) +#define IMX214_REG_TESTP_BLUE CCI_REG16(0x0606) +#define IMX214_REG_TESTP_GREENB CCI_REG16(0x0608) +#define IMX214_TESTP_COLOUR_MIN 0 +#define IMX214_TESTP_COLOUR_MAX 0x03ff +#define IMX214_TESTP_COLOUR_STEP 1 /* IMX214 native and active pixel array size */ #define IMX214_NATIVE_WIDTH 4224U @@ -52,6 +212,38 @@ static const char * const imx214_supply_name[] = { #define IMX214_NUM_SUPPLIES ARRAY_SIZE(imx214_supply_name) +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 imx214_mbus_formats[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const char * const imx214_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx214_test_pattern_val[] = { + IMX214_TEST_PATTERN_DISABLE, + IMX214_TEST_PATTERN_COLOR_BARS, + IMX214_TEST_PATTERN_SOLID_COLOR, + IMX214_TEST_PATTERN_GREY_COLOR, + IMX214_TEST_PATTERN_PN9, +}; + struct imx214 { struct device *dev; struct clk *xclk; @@ -59,365 +251,262 @@ struct imx214 { struct v4l2_subdev sd; struct media_pad pad; - struct v4l2_mbus_framefmt fmt; - struct v4l2_rect crop; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; struct v4l2_ctrl *unit_size; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; struct regulator_bulk_data supplies[IMX214_NUM_SUPPLIES]; struct gpio_desc *enable_gpio; - - /* - * Serialize control access, get/set format, get selection - * and start streaming. - */ - struct mutex mutex; -}; - -struct reg_8 { - u16 addr; - u8 val; -}; - -enum { - IMX214_TABLE_WAIT_MS = 0, - IMX214_TABLE_END, - IMX214_MAX_RETRIES, - IMX214_WAIT_MS }; /*From imx214_mode_tbls.h*/ -static const struct reg_8 mode_4096x2304[] = { - {0x0114, 0x03}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0222, 0x01}, - {0x0340, 0x0C}, - {0x0341, 0x7A}, - {0x0342, 0x13}, - {0x0343, 0x90}, - {0x0344, 0x00}, - {0x0345, 0x38}, - {0x0346, 0x01}, - {0x0347, 0x98}, - {0x0348, 0x10}, - {0x0349, 0x37}, - {0x034A, 0x0A}, - {0x034B, 0x97}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x00}, - {0x0902, 0x00}, - {0x3000, 0x35}, - {0x3054, 0x01}, - {0x305C, 0x11}, - - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x034C, 0x10}, - {0x034D, 0x00}, - {0x034E, 0x09}, - {0x034F, 0x00}, - {0x0401, 0x00}, - {0x0404, 0x00}, - {0x0405, 0x10}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x10}, - {0x040D, 0x00}, - {0x040E, 0x09}, - {0x040F, 0x00}, - - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x03}, - {0x0306, 0x00}, - {0x0307, 0x96}, - {0x0309, 0x0A}, - {0x030B, 0x01}, - {0x0310, 0x00}, - - {0x0820, 0x12}, - {0x0821, 0xC0}, - {0x0822, 0x00}, - {0x0823, 0x00}, - - {0x3A03, 0x09}, - {0x3A04, 0x50}, - {0x3A05, 0x01}, - - {0x0B06, 0x01}, - {0x30A2, 0x00}, - - {0x30B4, 0x00}, - - {0x3A02, 0xFF}, - - {0x3011, 0x00}, - {0x3013, 0x01}, - - {0x0202, 0x0C}, - {0x0203, 0x70}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x0210, 0x01}, - {0x0211, 0x00}, - {0x0212, 0x01}, - {0x0213, 0x00}, - {0x0214, 0x01}, - {0x0215, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - - {0x4170, 0x00}, - {0x4171, 0x10}, - {0x4176, 0x00}, - {0x4177, 0x3C}, - {0xAE20, 0x04}, - {0xAE21, 0x5C}, - - {IMX214_TABLE_WAIT_MS, 10}, - {0x0138, 0x01}, - {IMX214_TABLE_END, 0x00} +static const struct cci_reg_sequence mode_4096x2304[] = { + { IMX214_REG_HDR_MODE, IMX214_HDR_MODE_OFF }, + { IMX214_REG_HDR_RES_REDUCTION, IMX214_HDR_RES_REDU_THROUGH }, + { IMX214_REG_EXPOSURE_RATIO, 1 }, + { IMX214_REG_X_ADD_STA, 56 }, + { IMX214_REG_Y_ADD_STA, 408 }, + { IMX214_REG_X_ADD_END, 4151 }, + { IMX214_REG_Y_ADD_END, 2711 }, + { IMX214_REG_X_EVEN_INC, 1 }, + { IMX214_REG_X_ODD_INC, 1 }, + { IMX214_REG_Y_EVEN_INC, 1 }, + { IMX214_REG_Y_ODD_INC, 1 }, + { IMX214_REG_BINNING_MODE, IMX214_BINNING_NONE }, + { IMX214_REG_BINNING_TYPE, 0 }, + { IMX214_REG_BINNING_WEIGHTING, IMX214_BINNING_AVERAGE }, + { CCI_REG8(0x3000), 0x35 }, + { CCI_REG8(0x3054), 0x01 }, + { CCI_REG8(0x305C), 0x11 }, + + { IMX214_REG_CSI_DATA_FORMAT, IMX214_CSI_DATA_FORMAT_RAW10 }, + { IMX214_REG_X_OUTPUT_SIZE, 4096 }, + { IMX214_REG_Y_OUTPUT_SIZE, 2304 }, + { IMX214_REG_SCALE_MODE, IMX214_SCALE_NONE }, + { IMX214_REG_SCALE_M, 2 }, + { IMX214_REG_DIG_CROP_X_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_Y_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_WIDTH, 4096 }, + { IMX214_REG_DIG_CROP_HEIGHT, 2304 }, + + { IMX214_REG_VTPXCK_DIV, 5 }, + { IMX214_REG_VTSYCK_DIV, 2 }, + { IMX214_REG_PREPLLCK_VT_DIV, 3 }, + { IMX214_REG_PLL_VT_MPY, 150 }, + { IMX214_REG_OPPXCK_DIV, 10 }, + { IMX214_REG_OPSYCK_DIV, 1 }, + { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE }, + + { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) }, + + { CCI_REG8(0x3A03), 0x09 }, + { CCI_REG8(0x3A04), 0x50 }, + { CCI_REG8(0x3A05), 0x01 }, + + { IMX214_REG_SING_DEF_CORR_EN, IMX214_SING_DEF_CORR_ON }, + { IMX214_REG_NML_NR_EN, IMX214_NML_NR_OFF }, + + { CCI_REG8(0x30B4), 0x00 }, + + { CCI_REG8(0x3A02), 0xFF }, + + { CCI_REG8(0x3011), 0x00 }, + { IMX214_REG_STATS_OUT_EN, IMX214_STATS_OUT_ON }, + + { IMX214_REG_SHORT_EXPOSURE, 500 }, + + { CCI_REG8(0x4170), 0x00 }, + { CCI_REG8(0x4171), 0x10 }, + { CCI_REG8(0x4176), 0x00 }, + { CCI_REG8(0x4177), 0x3C }, + { CCI_REG8(0xAE20), 0x04 }, + { CCI_REG8(0xAE21), 0x5C }, }; -static const struct reg_8 mode_1920x1080[] = { - {0x0114, 0x03}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0222, 0x01}, - {0x0340, 0x0C}, - {0x0341, 0x7A}, - {0x0342, 0x13}, - {0x0343, 0x90}, - {0x0344, 0x04}, - {0x0345, 0x78}, - {0x0346, 0x03}, - {0x0347, 0xFC}, - {0x0348, 0x0B}, - {0x0349, 0xF7}, - {0x034A, 0x08}, - {0x034B, 0x33}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x00}, - {0x0902, 0x00}, - {0x3000, 0x35}, - {0x3054, 0x01}, - {0x305C, 0x11}, - - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x034C, 0x07}, - {0x034D, 0x80}, - {0x034E, 0x04}, - {0x034F, 0x38}, - {0x0401, 0x00}, - {0x0404, 0x00}, - {0x0405, 0x10}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040A, 0x00}, - {0x040B, 0x00}, - {0x040C, 0x07}, - {0x040D, 0x80}, - {0x040E, 0x04}, - {0x040F, 0x38}, - - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x03}, - {0x0306, 0x00}, - {0x0307, 0x96}, - {0x0309, 0x0A}, - {0x030B, 0x01}, - {0x0310, 0x00}, - - {0x0820, 0x12}, - {0x0821, 0xC0}, - {0x0822, 0x00}, - {0x0823, 0x00}, - - {0x3A03, 0x04}, - {0x3A04, 0xF8}, - {0x3A05, 0x02}, - - {0x0B06, 0x01}, - {0x30A2, 0x00}, - - {0x30B4, 0x00}, - - {0x3A02, 0xFF}, - - {0x3011, 0x00}, - {0x3013, 0x01}, - - {0x0202, 0x0C}, - {0x0203, 0x70}, - {0x0224, 0x01}, - {0x0225, 0xF4}, - - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x020E, 0x01}, - {0x020F, 0x00}, - {0x0210, 0x01}, - {0x0211, 0x00}, - {0x0212, 0x01}, - {0x0213, 0x00}, - {0x0214, 0x01}, - {0x0215, 0x00}, - {0x0216, 0x00}, - {0x0217, 0x00}, - - {0x4170, 0x00}, - {0x4171, 0x10}, - {0x4176, 0x00}, - {0x4177, 0x3C}, - {0xAE20, 0x04}, - {0xAE21, 0x5C}, - - {IMX214_TABLE_WAIT_MS, 10}, - {0x0138, 0x01}, - {IMX214_TABLE_END, 0x00} +static const struct cci_reg_sequence mode_1920x1080[] = { + { IMX214_REG_HDR_MODE, IMX214_HDR_MODE_OFF }, + { IMX214_REG_HDR_RES_REDUCTION, IMX214_HDR_RES_REDU_THROUGH }, + { IMX214_REG_EXPOSURE_RATIO, 1 }, + { IMX214_REG_X_ADD_STA, 1144 }, + { IMX214_REG_Y_ADD_STA, 1020 }, + { IMX214_REG_X_ADD_END, 3063 }, + { IMX214_REG_Y_ADD_END, 2099 }, + { IMX214_REG_X_EVEN_INC, 1 }, + { IMX214_REG_X_ODD_INC, 1 }, + { IMX214_REG_Y_EVEN_INC, 1 }, + { IMX214_REG_Y_ODD_INC, 1 }, + { IMX214_REG_BINNING_MODE, IMX214_BINNING_NONE }, + { IMX214_REG_BINNING_TYPE, 0 }, + { IMX214_REG_BINNING_WEIGHTING, IMX214_BINNING_AVERAGE }, + { CCI_REG8(0x3000), 0x35 }, + { CCI_REG8(0x3054), 0x01 }, + { CCI_REG8(0x305C), 0x11 }, + + { IMX214_REG_CSI_DATA_FORMAT, IMX214_CSI_DATA_FORMAT_RAW10 }, + { IMX214_REG_X_OUTPUT_SIZE, 1920 }, + { IMX214_REG_Y_OUTPUT_SIZE, 1080 }, + { IMX214_REG_SCALE_MODE, IMX214_SCALE_NONE }, + { IMX214_REG_SCALE_M, 2 }, + { IMX214_REG_DIG_CROP_X_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_Y_OFFSET, 0 }, + { IMX214_REG_DIG_CROP_WIDTH, 1920 }, + { IMX214_REG_DIG_CROP_HEIGHT, 1080 }, + + { IMX214_REG_VTPXCK_DIV, 5 }, + { IMX214_REG_VTSYCK_DIV, 2 }, + { IMX214_REG_PREPLLCK_VT_DIV, 3 }, + { IMX214_REG_PLL_VT_MPY, 150 }, + { IMX214_REG_OPPXCK_DIV, 10 }, + { IMX214_REG_OPSYCK_DIV, 1 }, + { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE }, + + { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) }, + + { CCI_REG8(0x3A03), 0x04 }, + { CCI_REG8(0x3A04), 0xF8 }, + { CCI_REG8(0x3A05), 0x02 }, + + { IMX214_REG_SING_DEF_CORR_EN, IMX214_SING_DEF_CORR_ON }, + { IMX214_REG_NML_NR_EN, IMX214_NML_NR_OFF }, + + { CCI_REG8(0x30B4), 0x00 }, + + { CCI_REG8(0x3A02), 0xFF }, + + { CCI_REG8(0x3011), 0x00 }, + { IMX214_REG_STATS_OUT_EN, IMX214_STATS_OUT_ON }, + + { IMX214_REG_SHORT_EXPOSURE, 500 }, + + { CCI_REG8(0x4170), 0x00 }, + { CCI_REG8(0x4171), 0x10 }, + { CCI_REG8(0x4176), 0x00 }, + { CCI_REG8(0x4177), 0x3C }, + { CCI_REG8(0xAE20), 0x04 }, + { CCI_REG8(0xAE21), 0x5C }, }; -static const struct reg_8 mode_table_common[] = { +static const struct cci_reg_sequence mode_table_common[] = { /* software reset */ /* software standby settings */ - {0x0100, 0x00}, + { IMX214_REG_MODE_SELECT, IMX214_MODE_STANDBY }, /* ATR setting */ - {0x9300, 0x02}, + { IMX214_REG_ATR_FAST_MOVE, 2 }, /* external clock setting */ - {0x0136, 0x18}, - {0x0137, 0x00}, + { IMX214_REG_EXCK_FREQ, IMX214_EXCK_FREQ(IMX214_DEFAULT_CLK_FREQ / 1000000) }, /* global setting */ /* basic config */ - {0x0101, 0x00}, - {0x0105, 0x01}, - {0x0106, 0x01}, - {0x4550, 0x02}, - {0x4601, 0x00}, - {0x4642, 0x05}, - {0x6227, 0x11}, - {0x6276, 0x00}, - {0x900E, 0x06}, - {0xA802, 0x90}, - {0xA803, 0x11}, - {0xA804, 0x62}, - {0xA805, 0x77}, - {0xA806, 0xAE}, - {0xA807, 0x34}, - {0xA808, 0xAE}, - {0xA809, 0x35}, - {0xA80A, 0x62}, - {0xA80B, 0x83}, - {0xAE33, 0x00}, + { IMX214_REG_MASK_CORR_FRAMES, IMX214_CORR_FRAMES_MASK }, + { IMX214_REG_FAST_STANDBY_CTRL, 1 }, + { IMX214_REG_LINE_LENGTH_PCK, IMX214_PPL_DEFAULT }, + { CCI_REG8(0x4550), 0x02 }, + { CCI_REG8(0x4601), 0x00 }, + { CCI_REG8(0x4642), 0x05 }, + { CCI_REG8(0x6227), 0x11 }, + { CCI_REG8(0x6276), 0x00 }, + { CCI_REG8(0x900E), 0x06 }, + { CCI_REG8(0xA802), 0x90 }, + { CCI_REG8(0xA803), 0x11 }, + { CCI_REG8(0xA804), 0x62 }, + { CCI_REG8(0xA805), 0x77 }, + { CCI_REG8(0xA806), 0xAE }, + { CCI_REG8(0xA807), 0x34 }, + { CCI_REG8(0xA808), 0xAE }, + { CCI_REG8(0xA809), 0x35 }, + { CCI_REG8(0xA80A), 0x62 }, + { CCI_REG8(0xA80B), 0x83 }, + { CCI_REG8(0xAE33), 0x00 }, /* analog setting */ - {0x4174, 0x00}, - {0x4175, 0x11}, - {0x4612, 0x29}, - {0x461B, 0x12}, - {0x461F, 0x06}, - {0x4635, 0x07}, - {0x4637, 0x30}, - {0x463F, 0x18}, - {0x4641, 0x0D}, - {0x465B, 0x12}, - {0x465F, 0x11}, - {0x4663, 0x11}, - {0x4667, 0x0F}, - {0x466F, 0x0F}, - {0x470E, 0x09}, - {0x4909, 0xAB}, - {0x490B, 0x95}, - {0x4915, 0x5D}, - {0x4A5F, 0xFF}, - {0x4A61, 0xFF}, - {0x4A73, 0x62}, - {0x4A85, 0x00}, - {0x4A87, 0xFF}, + { CCI_REG8(0x4174), 0x00 }, + { CCI_REG8(0x4175), 0x11 }, + { CCI_REG8(0x4612), 0x29 }, + { CCI_REG8(0x461B), 0x12 }, + { CCI_REG8(0x461F), 0x06 }, + { CCI_REG8(0x4635), 0x07 }, + { CCI_REG8(0x4637), 0x30 }, + { CCI_REG8(0x463F), 0x18 }, + { CCI_REG8(0x4641), 0x0D }, + { CCI_REG8(0x465B), 0x12 }, + { CCI_REG8(0x465F), 0x11 }, + { CCI_REG8(0x4663), 0x11 }, + { CCI_REG8(0x4667), 0x0F }, + { CCI_REG8(0x466F), 0x0F }, + { CCI_REG8(0x470E), 0x09 }, + { CCI_REG8(0x4909), 0xAB }, + { CCI_REG8(0x490B), 0x95 }, + { CCI_REG8(0x4915), 0x5D }, + { CCI_REG8(0x4A5F), 0xFF }, + { CCI_REG8(0x4A61), 0xFF }, + { CCI_REG8(0x4A73), 0x62 }, + { CCI_REG8(0x4A85), 0x00 }, + { CCI_REG8(0x4A87), 0xFF }, /* embedded data */ - {0x5041, 0x04}, - {0x583C, 0x04}, - {0x620E, 0x04}, - {0x6EB2, 0x01}, - {0x6EB3, 0x00}, - {0x9300, 0x02}, + { IMX214_REG_EBD_SIZE_V, IMX214_EBD_4_LINE }, + { CCI_REG8(0x583C), 0x04 }, + { CCI_REG8(0x620E), 0x04 }, + { CCI_REG8(0x6EB2), 0x01 }, + { CCI_REG8(0x6EB3), 0x00 }, + { IMX214_REG_ATR_FAST_MOVE, 2 }, /* imagequality */ /* HDR setting */ - {0x3001, 0x07}, - {0x6D12, 0x3F}, - {0x6D13, 0xFF}, - {0x9344, 0x03}, - {0x9706, 0x10}, - {0x9707, 0x03}, - {0x9708, 0x03}, - {0x9E04, 0x01}, - {0x9E05, 0x00}, - {0x9E0C, 0x01}, - {0x9E0D, 0x02}, - {0x9E24, 0x00}, - {0x9E25, 0x8C}, - {0x9E26, 0x00}, - {0x9E27, 0x94}, - {0x9E28, 0x00}, - {0x9E29, 0x96}, + { IMX214_REG_RMSC_NR_MODE, 0x07 }, + { IMX214_REG_RG_STATS_LMT, IMX214_RG_STATS_LMT_14_BIT }, + { CCI_REG8(0x9344), 0x03 }, + { CCI_REG8(0x9706), 0x10 }, + { CCI_REG8(0x9707), 0x03 }, + { CCI_REG8(0x9708), 0x03 }, + { CCI_REG8(0x9E04), 0x01 }, + { CCI_REG8(0x9E05), 0x00 }, + { CCI_REG8(0x9E0C), 0x01 }, + { CCI_REG8(0x9E0D), 0x02 }, + { CCI_REG8(0x9E24), 0x00 }, + { CCI_REG8(0x9E25), 0x8C }, + { CCI_REG8(0x9E26), 0x00 }, + { CCI_REG8(0x9E27), 0x94 }, + { CCI_REG8(0x9E28), 0x00 }, + { CCI_REG8(0x9E29), 0x96 }, /* CNR parameter setting */ - {0x69DB, 0x01}, + { CCI_REG8(0x69DB), 0x01 }, /* Moire reduction */ - {0x6957, 0x01}, + { CCI_REG8(0x6957), 0x01 }, /* image enhancement */ - {0x6987, 0x17}, - {0x698A, 0x03}, - {0x698B, 0x03}, + { CCI_REG8(0x6987), 0x17 }, + { CCI_REG8(0x698A), 0x03 }, + { CCI_REG8(0x698B), 0x03 }, /* white balanace */ - {0x0B8E, 0x01}, - {0x0B8F, 0x00}, - {0x0B90, 0x01}, - {0x0B91, 0x00}, - {0x0B92, 0x01}, - {0x0B93, 0x00}, - {0x0B94, 0x01}, - {0x0B95, 0x00}, + { IMX214_REG_ABS_GAIN_GREENR, 0x0100 }, + { IMX214_REG_ABS_GAIN_RED, 0x0100 }, + { IMX214_REG_ABS_GAIN_BLUE, 0x0100 }, + { IMX214_REG_ABS_GAIN_GREENB, 0x0100 }, /* ATR setting */ - {0x6E50, 0x00}, - {0x6E51, 0x32}, - {0x9340, 0x00}, - {0x9341, 0x3C}, - {0x9342, 0x03}, - {0x9343, 0xFF}, - {IMX214_TABLE_END, 0x00} + { CCI_REG8(0x6E50), 0x00 }, + { CCI_REG8(0x6E51), 0x32 }, + { CCI_REG8(0x9340), 0x00 }, + { CCI_REG8(0x9341), 0x3C }, + { CCI_REG8(0x9342), 0x03 }, + { CCI_REG8(0x9343), 0xFF }, }; /* @@ -427,16 +516,25 @@ static const struct reg_8 mode_table_common[] = { static const struct imx214_mode { u32 width; u32 height; - const struct reg_8 *reg_table; + + /* V-timing */ + unsigned int vts_def; + + unsigned int num_of_regs; + const struct cci_reg_sequence *reg_table; } imx214_modes[] = { { .width = 4096, .height = 2304, + .vts_def = 3194, + .num_of_regs = ARRAY_SIZE(mode_4096x2304), .reg_table = mode_4096x2304, }, { .width = 1920, .height = 1080, + .vts_def = 3194, + .num_of_regs = ARRAY_SIZE(mode_1920x1080), .reg_table = mode_1920x1080, }, }; @@ -490,14 +588,42 @@ static int __maybe_unused imx214_power_off(struct device *dev) return 0; } +/* Get bayer order based on flip setting. */ +static u32 imx214_get_format_code(struct imx214 *imx214) +{ + unsigned int i; + + i = (imx214->vflip->val ? 2 : 0) | (imx214->hflip->val ? 1 : 0); + + return imx214_mbus_formats[i]; +} + +static void imx214_update_pad_format(struct imx214 *imx214, + const struct imx214_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = imx214_get_format_code(imx214); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +} + static int imx214_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + struct imx214 *imx214 = to_imx214(sd); + + if (code->index >= (ARRAY_SIZE(imx214_mbus_formats) / 4)) return -EINVAL; - code->code = IMX214_MBUS_CODE; + code->code = imx214_get_format_code(imx214); return 0; } @@ -506,7 +632,11 @@ static int imx214_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->code != IMX214_MBUS_CODE) + struct imx214 *imx214 = to_imx214(subdev); + u32 code; + + code = imx214_get_format_code(imx214); + if (fse->code != code) return -EINVAL; if (fse->index >= ARRAY_SIZE(imx214_modes)) @@ -549,52 +679,6 @@ static const struct v4l2_subdev_core_ops imx214_core_ops = { #endif }; -static struct v4l2_mbus_framefmt * -__imx214_get_pad_format(struct imx214 *imx214, - struct v4l2_subdev_state *sd_state, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_format(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx214->fmt; - default: - return NULL; - } -} - -static int imx214_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct imx214 *imx214 = to_imx214(sd); - - mutex_lock(&imx214->mutex); - format->format = *__imx214_get_pad_format(imx214, sd_state, - format->pad, - format->which); - mutex_unlock(&imx214->mutex); - - return 0; -} - -static struct v4l2_rect * -__imx214_get_pad_crop(struct imx214 *imx214, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_crop(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx214->crop; - default: - return NULL; - } -} - static int imx214_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -604,34 +688,48 @@ static int imx214_set_format(struct v4l2_subdev *sd, struct v4l2_rect *__crop; const struct imx214_mode *mode; - mutex_lock(&imx214->mutex); - - __crop = __imx214_get_pad_crop(imx214, sd_state, format->pad, - format->which); - mode = v4l2_find_nearest_size(imx214_modes, ARRAY_SIZE(imx214_modes), width, height, format->format.width, format->format.height); - __crop->width = mode->width; - __crop->height = mode->height; + imx214_update_pad_format(imx214, mode, &format->format, + format->format.code); + __format = v4l2_subdev_state_get_format(sd_state, 0); - __format = __imx214_get_pad_format(imx214, sd_state, format->pad, - format->which); - __format->width = __crop->width; - __format->height = __crop->height; - __format->code = IMX214_MBUS_CODE; - __format->field = V4L2_FIELD_NONE; - __format->colorspace = V4L2_COLORSPACE_SRGB; - __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); - __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - __format->colorspace, __format->ycbcr_enc); - __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); + *__format = format->format; - format->format = *__format; + __crop = v4l2_subdev_state_get_crop(sd_state, 0); + __crop->width = mode->width; + __crop->height = mode->height; - mutex_unlock(&imx214->mutex); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + int exposure_max; + int exposure_def; + int hblank; + + /* Update blank limits */ + __v4l2_ctrl_modify_range(imx214->vblank, IMX214_VBLANK_MIN, + IMX214_VTS_MAX - mode->height, 2, + mode->vts_def - mode->height); + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(imx214->exposure, + imx214->exposure->minimum, + exposure_max, imx214->exposure->step, + exposure_def); + + /* + * Currently PPL is fixed to IMX214_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeable in any + * way other than changing the mode. + */ + hblank = IMX214_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx214->hblank, hblank, hblank, 1, + hblank); + } return 0; } @@ -640,14 +738,9 @@ static int imx214_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct imx214 *imx214 = to_imx214(sd); - switch (sel->target) { case V4L2_SEL_TGT_CROP: - mutex_lock(&imx214->mutex); - sel->r = *__imx214_get_pad_crop(imx214, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx214->mutex); + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); return 0; case V4L2_SEL_TGT_NATIVE_SIZE: @@ -675,6 +768,7 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_format fmt = { }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; fmt.format.width = imx214_modes[0].width; fmt.format.height = imx214_modes[0].height; @@ -683,12 +777,41 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev, return 0; } +static int imx214_update_digital_gain(struct imx214 *imx214, u32 val) +{ + int ret = 0; + + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_GREENR, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_RED, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_BLUE, val, &ret); + cci_write(imx214->regmap, IMX214_REG_DIG_GAIN_GREENB, val, &ret); + + return ret; +} + static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx214 *imx214 = container_of(ctrl->handler, struct imx214, ctrls); - u8 vals[2]; - int ret; + const struct v4l2_mbus_framefmt *format = NULL; + struct v4l2_subdev_state *state; + int ret = 0; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + state = v4l2_subdev_get_locked_active_state(&imx214->sd); + format = v4l2_subdev_state_get_format(state, 0); + + /* Update max exposure while meeting expected vblanking */ + exposure_max = + format->height + ctrl->val - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(imx214->exposure, + imx214->exposure->minimum, + exposure_max, imx214->exposure->step, + exposure_def); + } /* * Applying V4L2 control value only happens @@ -698,15 +821,47 @@ static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(imx214->regmap, IMX214_REG_ANALOG_GAIN, + ctrl->val, &ret); + cci_write(imx214->regmap, IMX214_REG_SHORT_ANALOG_GAIN, + ctrl->val, &ret); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx214_update_digital_gain(imx214, ctrl->val); + break; case V4L2_CID_EXPOSURE: - vals[1] = ctrl->val; - vals[0] = ctrl->val >> 8; - ret = regmap_bulk_write(imx214->regmap, IMX214_REG_EXPOSURE, vals, 2); - if (ret < 0) - dev_err(imx214->dev, "Error %d\n", ret); - ret = 0; + cci_write(imx214->regmap, IMX214_REG_EXPOSURE, ctrl->val, &ret); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(imx214->regmap, IMX214_REG_ORIENTATION, + imx214->hflip->val | imx214->vflip->val << 1, &ret); + break; + case V4L2_CID_VBLANK: + cci_write(imx214->regmap, IMX214_REG_FRM_LENGTH_LINES, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN: + cci_write(imx214->regmap, IMX214_REG_TEST_PATTERN, + imx214_test_pattern_val[ctrl->val], &ret); + break; + case V4L2_CID_TEST_PATTERN_RED: + cci_write(imx214->regmap, IMX214_REG_TESTP_RED, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + cci_write(imx214->regmap, IMX214_REG_TESTP_GREENR, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + cci_write(imx214->regmap, IMX214_REG_TESTP_BLUE, + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + cci_write(imx214->regmap, IMX214_REG_TESTP_GREENB, + ctrl->val, &ret); break; - default: ret = -EINVAL; } @@ -729,16 +884,19 @@ static int imx214_ctrls_init(struct imx214 *imx214) .width = 1120, .height = 1120, }; + const struct imx214_mode *mode = &imx214_modes[0]; struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; - int ret; + int exposure_max, exposure_def; + int hblank; + int i, ret; ret = v4l2_fwnode_device_parse(imx214->dev, &props); if (ret < 0) return ret; ctrl_hdlr = &imx214->ctrls; - ret = v4l2_ctrl_handler_init(&imx214->ctrls, 6); + ret = v4l2_ctrl_handler_init(&imx214->ctrls, 13); if (ret) return ret; @@ -764,17 +922,75 @@ static int imx214_ctrls_init(struct imx214 *imx214) * * Yours sincerely, Ricardo. */ + + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx214->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_VBLANK, IMX214_VBLANK_MIN, + IMX214_VTS_MAX - mode->height, 2, + mode->vts_def - mode->height); + + hblank = IMX214_PPL_DEFAULT - mode->width; + imx214->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx214->hblank) + imx214->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + exposure_max = mode->vts_def - IMX214_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT); imx214->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_EXPOSURE, IMX214_EXPOSURE_MIN, - IMX214_EXPOSURE_MAX, + exposure_max, IMX214_EXPOSURE_STEP, - IMX214_EXPOSURE_DEFAULT); + exposure_def); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX214_ANA_GAIN_MIN, IMX214_ANA_GAIN_MAX, + IMX214_ANA_GAIN_STEP, IMX214_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX214_DGTL_GAIN_MIN, IMX214_DGTL_GAIN_MAX, + IMX214_DGTL_GAIN_STEP, IMX214_DGTL_GAIN_DEFAULT); + + imx214->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx214->hflip) + imx214->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx214->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx214->vflip) + imx214->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_cluster(2, &imx214->hflip); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx214_test_pattern_menu) - 1, + 0, 0, imx214_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX214_TESTP_COLOUR_MIN, + IMX214_TESTP_COLOUR_MAX, + IMX214_TESTP_COLOUR_STEP, + IMX214_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } imx214->unit_size = v4l2_ctrl_new_std_compound(ctrl_hdlr, NULL, V4L2_CID_UNIT_CELL_SIZE, - v4l2_ctrl_ptr_create((void *)&unit_size)); + v4l2_ctrl_ptr_create((void *)&unit_size), + v4l2_ctrl_ptr_create(NULL), + v4l2_ctrl_ptr_create(NULL)); v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx214_ctrl_ops, &props); @@ -790,76 +1006,52 @@ static int imx214_ctrls_init(struct imx214 *imx214) return 0; }; -#define MAX_CMD 4 -static int imx214_write_table(struct imx214 *imx214, - const struct reg_8 table[]) -{ - u8 vals[MAX_CMD]; - int i; - int ret; - - for (; table->addr != IMX214_TABLE_END ; table++) { - if (table->addr == IMX214_TABLE_WAIT_MS) { - usleep_range(table->val * 1000, - table->val * 1000 + 500); - continue; - } - - for (i = 0; i < MAX_CMD; i++) { - if (table[i].addr != (table[0].addr + i)) - break; - vals[i] = table[i].val; - } - - ret = regmap_bulk_write(imx214->regmap, table->addr, vals, i); - - if (ret) { - dev_err(imx214->dev, "write_table error: %d\n", ret); - return ret; - } - - table += i - 1; - } - - return 0; -} - static int imx214_start_streaming(struct imx214 *imx214) { + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; const struct imx214_mode *mode; int ret; - mutex_lock(&imx214->mutex); - ret = imx214_write_table(imx214, mode_table_common); + ret = cci_multi_reg_write(imx214->regmap, mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); if (ret < 0) { dev_err(imx214->dev, "could not sent common table %d\n", ret); - goto error; + return ret; } - mode = v4l2_find_nearest_size(imx214_modes, - ARRAY_SIZE(imx214_modes), width, height, - imx214->fmt.width, imx214->fmt.height); - ret = imx214_write_table(imx214, mode->reg_table); + ret = cci_write(imx214->regmap, IMX214_REG_CSI_LANE_MODE, + IMX214_CSI_4_LANE_MODE, NULL); + if (ret) { + dev_err(imx214->dev, "failed to configure lanes\n"); + return ret; + } + + state = v4l2_subdev_get_locked_active_state(&imx214->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + mode = v4l2_find_nearest_size(imx214_modes, ARRAY_SIZE(imx214_modes), + width, height, fmt->width, fmt->height); + ret = cci_multi_reg_write(imx214->regmap, mode->reg_table, + mode->num_of_regs, NULL); if (ret < 0) { dev_err(imx214->dev, "could not sent mode table %d\n", ret); - goto error; + return ret; } + + usleep_range(10000, 10500); + + cci_write(imx214->regmap, IMX214_REG_TEMP_SENSOR_CONTROL, 0x01, NULL); + ret = __v4l2_ctrl_handler_setup(&imx214->ctrls); if (ret < 0) { dev_err(imx214->dev, "could not sync v4l2 controls\n"); - goto error; + return ret; } - ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STREAMING); - if (ret < 0) { + ret = cci_write(imx214->regmap, IMX214_REG_MODE_SELECT, + IMX214_MODE_STREAMING, NULL); + if (ret < 0) dev_err(imx214->dev, "could not sent start table %d\n", ret); - goto error; - } - - mutex_unlock(&imx214->mutex); - return 0; -error: - mutex_unlock(&imx214->mutex); return ret; } @@ -867,7 +1059,8 @@ static int imx214_stop_streaming(struct imx214 *imx214) { int ret; - ret = regmap_write(imx214->regmap, IMX214_REG_MODE_SELECT, IMX214_MODE_STANDBY); + ret = cci_write(imx214->regmap, IMX214_REG_MODE_SELECT, + IMX214_MODE_STANDBY, NULL); if (ret < 0) dev_err(imx214->dev, "could not sent stop table %d\n", ret); @@ -877,6 +1070,7 @@ static int imx214_stop_streaming(struct imx214 *imx214) static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) { struct imx214 *imx214 = to_imx214(subdev); + struct v4l2_subdev_state *state; int ret; if (enable) { @@ -884,7 +1078,9 @@ static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) if (ret < 0) return ret; + state = v4l2_subdev_lock_and_get_active_state(subdev); ret = imx214_start_streaming(imx214); + v4l2_subdev_unlock_state(state); if (ret < 0) goto err_rpm_put; } else { @@ -918,12 +1114,22 @@ static int imx214_get_frame_interval(struct v4l2_subdev *subdev, return 0; } +/* + * Raw sensors should be using the VBLANK and HBLANK controls to determine + * the frame rate. However this driver was initially added using the + * [S|G|ENUM]_FRAME_INTERVAL ioctls with a fixed rate of 30fps. + * Retain the frame_interval ops for backwards compatibility, but they do + * nothing. + */ static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { + struct imx214 *imx214 = to_imx214(subdev); const struct imx214_mode *mode; + dev_warn_once(imx214->dev, "frame_interval functions return an unreliable value for compatibility reasons. Use the VBLANK and HBLANK controls to determine the correct frame rate.\n"); + if (fie->index != 0) return -EINVAL; @@ -931,7 +1137,7 @@ static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, ARRAY_SIZE(imx214_modes), width, height, fie->width, fie->height); - fie->code = IMX214_MBUS_CODE; + fie->code = imx214_get_format_code(imx214); fie->width = mode->width; fie->height = mode->height; fie->interval.numerator = 1; @@ -948,7 +1154,7 @@ static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { .enum_mbus_code = imx214_enum_mbus_code, .enum_frame_size = imx214_enum_frame_size, .enum_frame_interval = imx214_enum_frame_interval, - .get_fmt = imx214_get_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx214_set_format, .get_selection = imx214_get_selection, .get_frame_interval = imx214_get_frame_interval, @@ -965,12 +1171,6 @@ static const struct v4l2_subdev_internal_ops imx214_internal_ops = { .init_state = imx214_entity_init_state, }; -static const struct regmap_config sensor_regmap_config = { - .reg_bits = 16, - .val_bits = 8, - .cache_type = REGCACHE_MAPLE, -}; - static int imx214_get_regulators(struct device *dev, struct imx214 *imx214) { unsigned int i; @@ -982,6 +1182,27 @@ static int imx214_get_regulators(struct device *dev, struct imx214 *imx214) imx214->supplies); } +/* Verify chip ID */ +static int imx214_identify_module(struct imx214 *imx214) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx214->sd); + int ret; + u64 val; + + ret = cci_read(imx214->regmap, IMX214_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to read chip id %x\n", + IMX214_CHIP_ID); + + if (val != IMX214_CHIP_ID) + return dev_err_probe(&client->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + IMX214_CHIP_ID, val); + + return 0; +} + static int imx214_parse_fwnode(struct device *dev) { struct fwnode_handle *endpoint; @@ -992,28 +1213,42 @@ static int imx214_parse_fwnode(struct device *dev) int ret; endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!endpoint) { - dev_err(dev, "endpoint node not found\n"); - return -EINVAL; - } + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "endpoint node not found\n"); ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); if (ret) { - dev_err(dev, "parsing endpoint node failed\n"); + dev_err_probe(dev, ret, "parsing endpoint node failed\n"); goto done; } - for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + /* Check the number of MIPI CSI2 data lanes */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(dev, -EINVAL, + "only 4 data lanes are currently supported\n"); + goto done; + } + + if (bus_cfg.nr_of_link_frequencies != 1) + dev_warn(dev, "Only one link-frequency supported, please review your DT. Continuing anyway\n"); + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { if (bus_cfg.link_frequencies[i] == IMX214_DEFAULT_LINK_FREQ) break; - - if (i == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "link-frequencies %d not supported, Please review your DT\n", - IMX214_DEFAULT_LINK_FREQ); - ret = -EINVAL; - goto done; + if (bus_cfg.link_frequencies[i] == + IMX214_DEFAULT_LINK_FREQ_LEGACY) { + dev_warn(dev, + "link-frequencies %d not supported, please review your DT. Continuing anyway\n", + IMX214_DEFAULT_LINK_FREQ); + break; + } } + if (i == bus_cfg.nr_of_link_frequencies) + ret = dev_err_probe(dev, -EINVAL, + "link-frequencies %d not supported, please review your DT\n", + IMX214_DEFAULT_LINK_FREQ); + done: v4l2_fwnode_endpoint_free(&bus_cfg); fwnode_handle_put(endpoint); @@ -1037,34 +1272,28 @@ static int imx214_probe(struct i2c_client *client) imx214->dev = dev; imx214->xclk = devm_clk_get(dev, NULL); - if (IS_ERR(imx214->xclk)) { - dev_err(dev, "could not get xclk"); - return PTR_ERR(imx214->xclk); - } + if (IS_ERR(imx214->xclk)) + return dev_err_probe(dev, PTR_ERR(imx214->xclk), + "failed to get xclk\n"); ret = clk_set_rate(imx214->xclk, IMX214_DEFAULT_CLK_FREQ); - if (ret) { - dev_err(dev, "could not set xclk frequency\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to set xclk frequency\n"); ret = imx214_get_regulators(dev, imx214); - if (ret < 0) { - dev_err(dev, "cannot get regulators\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get regulators\n"); imx214->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(imx214->enable_gpio)) { - dev_err(dev, "cannot get enable gpio\n"); - return PTR_ERR(imx214->enable_gpio); - } + if (IS_ERR(imx214->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(imx214->enable_gpio), + "failed to get enable gpio\n"); - imx214->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config); - if (IS_ERR(imx214->regmap)) { - dev_err(dev, "regmap init failed\n"); - return PTR_ERR(imx214->regmap); - } + imx214->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx214->regmap)) + return dev_err_probe(dev, PTR_ERR(imx214->regmap), + "failed to initialize CCI\n"); v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); imx214->sd.internal_ops = &imx214_internal_ops; @@ -1075,17 +1304,14 @@ static int imx214_probe(struct i2c_client *client) */ imx214_power_on(imx214->dev); - pm_runtime_set_active(imx214->dev); - pm_runtime_enable(imx214->dev); - pm_runtime_idle(imx214->dev); + ret = imx214_identify_module(imx214); + if (ret) + goto error_power_off; ret = imx214_ctrls_init(imx214); if (ret < 0) goto error_power_off; - mutex_init(&imx214->mutex); - imx214->ctrls.lock = &imx214->mutex; - imx214->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; imx214->pad.flags = MEDIA_PAD_FL_SOURCE; imx214->sd.dev = &client->dev; @@ -1093,28 +1319,44 @@ static int imx214_probe(struct i2c_client *client) ret = media_entity_pads_init(&imx214->sd.entity, 1, &imx214->pad); if (ret < 0) { - dev_err(dev, "could not register media entity\n"); + dev_err_probe(dev, ret, "failed to init entity pads\n"); goto free_ctrl; } - imx214_entity_init_state(&imx214->sd, NULL); + imx214->sd.state_lock = imx214->ctrls.lock; + ret = v4l2_subdev_init_finalize(&imx214->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "subdev init error\n"); + goto free_entity; + } + + pm_runtime_set_active(imx214->dev); + pm_runtime_enable(imx214->dev); ret = v4l2_async_register_subdev_sensor(&imx214->sd); if (ret < 0) { - dev_err(dev, "could not register v4l2 device\n"); - goto free_entity; + dev_err_probe(dev, ret, + "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; } + pm_runtime_idle(imx214->dev); + return 0; +error_subdev_cleanup: + pm_runtime_disable(imx214->dev); + pm_runtime_set_suspended(&client->dev); + v4l2_subdev_cleanup(&imx214->sd); + free_entity: media_entity_cleanup(&imx214->sd.entity); + free_ctrl: - mutex_destroy(&imx214->mutex); v4l2_ctrl_handler_free(&imx214->ctrls); + error_power_off: - pm_runtime_disable(imx214->dev); - regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); + imx214_power_off(imx214->dev); return ret; } @@ -1125,13 +1367,14 @@ static void imx214_remove(struct i2c_client *client) struct imx214 *imx214 = to_imx214(sd); v4l2_async_unregister_subdev(&imx214->sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&imx214->sd.entity); v4l2_ctrl_handler_free(&imx214->ctrls); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&imx214->mutex); + if (!pm_runtime_status_suspended(&client->dev)) { + imx214_power_off(imx214->dev); + pm_runtime_set_suspended(&client->dev); + } } static const struct of_device_id imx214_of_match[] = { diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 2d54cea113e1..04262bbf6306 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -70,15 +70,14 @@ #define IMX219_EXPOSURE_MAX 65535 /* V_TIMING internal */ -#define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_MAX 0xffff - -#define IMX219_VBLANK_MIN 4 - -/* HBLANK control - read only */ -#define IMX219_PPL_DEFAULT 3448 - +#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) +#define IMX219_FLL_MAX 0xffff +#define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) +#define IMX219_LLP_MIN 0x0d78 +#define IMX219_BINNED_LLP_MIN 0x0de8 +#define IMX219_LLP_MAX 0x7ff0 + #define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164) #define IMX219_REG_X_ADD_END_A CCI_REG16(0x0166) #define IMX219_REG_Y_ADD_STA_A CCI_REG16(0x0168) @@ -133,10 +132,11 @@ /* Pixel rate is fixed for all the modes */ #define IMX219_PIXEL_RATE 182400000 -#define IMX219_PIXEL_RATE_4LANE 280800000 +#define IMX219_PIXEL_RATE_4LANE 281600000 #define IMX219_DEFAULT_LINK_FREQ 456000000 -#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 +#define IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED 363000000 +#define IMX219_DEFAULT_LINK_FREQ_4LANE 364000000 /* IMX219 native and active pixel array size. */ #define IMX219_NATIVE_WIDTH 3296U @@ -154,7 +154,7 @@ struct imx219_mode { unsigned int height; /* V-timing */ - unsigned int vts_def; + unsigned int fll_def; }; static const struct cci_reg_sequence imx219_common_regs[] = { @@ -168,15 +168,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { CCI_REG8(0x30eb), 0x05 }, { CCI_REG8(0x30eb), 0x09 }, - /* PLL Clock Table */ - { IMX219_REG_VTPXCK_DIV, 5 }, - { IMX219_REG_VTSYCK_DIV, 1 }, - { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ - { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ - { IMX219_REG_PLL_VT_MPY, 57 }, - { IMX219_REG_OPSYCK_DIV, 1 }, - { IMX219_REG_PLL_OP_MPY, 114 }, - /* Undocumented registers */ { CCI_REG8(0x455e), 0x00 }, { CCI_REG8(0x471e), 0x4b }, @@ -192,7 +183,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { CCI_REG8(0x479b), 0x0e }, /* Frame Bank Register Group "A" */ - { IMX219_REG_LINE_LENGTH_A, 3448 }, { IMX219_REG_X_ODD_INC_A, 1 }, { IMX219_REG_Y_ODD_INC_A, 1 }, @@ -201,12 +191,45 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_EXCK_FREQ, IMX219_EXCK_FREQ(IMX219_XCLK_FREQ / 1000000) }, }; +static const struct cci_reg_sequence imx219_2lane_regs[] = { + /* PLL Clock Table */ + { IMX219_REG_VTPXCK_DIV, 5 }, + { IMX219_REG_VTSYCK_DIV, 1 }, + { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PLL_VT_MPY, 57 }, + { IMX219_REG_OPSYCK_DIV, 1 }, + { IMX219_REG_PLL_OP_MPY, 114 }, + + /* 2-Lane CSI Mode */ + { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_2_LANE_MODE }, +}; + +static const struct cci_reg_sequence imx219_4lane_regs[] = { + /* PLL Clock Table */ + { IMX219_REG_VTPXCK_DIV, 5 }, + { IMX219_REG_VTSYCK_DIV, 1 }, + { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ + { IMX219_REG_PLL_VT_MPY, 88 }, + { IMX219_REG_OPSYCK_DIV, 1 }, + { IMX219_REG_PLL_OP_MPY, 91 }, + + /* 4-Lane CSI Mode */ + { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_4_LANE_MODE }, +}; + static const s64 imx219_link_freq_menu[] = { IMX219_DEFAULT_LINK_FREQ, }; static const s64 imx219_link_freq_4lane_menu[] = { IMX219_DEFAULT_LINK_FREQ_4LANE, + /* + * This will never be advertised to userspace, but will be used for + * v4l2_link_freq_to_bitmap + */ + IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED, }; static const char * const imx219_test_pattern_menu[] = { @@ -289,25 +312,25 @@ static const struct imx219_mode supported_modes[] = { /* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .vts_def = 3526, + .fll_def = 3526, }, { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .vts_def = 1763, + .fll_def = 1763, }, { - /* 2x2 binned 30fps mode */ + /* 2x2 binned 60fps mode */ .width = 1640, .height = 1232, - .vts_def = 1763, + .fll_def = 1707, }, { - /* 640x480 30fps mode */ + /* 640x480 60fps mode */ .width = 640, .height = 480, - .vts_def = 1763, + .fll_def = 1707, }, }; @@ -359,6 +382,62 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } +static u32 imx219_get_format_bpp(const struct v4l2_mbus_framefmt *format) +{ + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return 8; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + default: + return 10; + } +} + +static void imx219_get_binning(struct v4l2_subdev_state *state, u8 *bin_h, + u8 *bin_v) +{ + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); + u32 hbin = crop->width / format->width; + u32 vbin = crop->height / format->height; + + *bin_h = IMX219_BINNING_NONE; + *bin_v = IMX219_BINNING_NONE; + + /* + * Use analog binning only if both dimensions are binned, as it crops + * the other dimension. + */ + if (hbin == 2 && vbin == 2) { + *bin_h = IMX219_BINNING_X2_ANALOG; + *bin_v = IMX219_BINNING_X2_ANALOG; + + return; + } + + if (hbin == 2) + *bin_h = IMX219_BINNING_X2; + if (vbin == 2) + *bin_v = IMX219_BINNING_X2; +} + +static inline u32 imx219_get_rate_factor(struct v4l2_subdev_state *state) +{ + u8 bin_h, bin_v; + + imx219_get_binning(state, &bin_h, &bin_v); + + return (bin_h & bin_v) == IMX219_BINNING_X2_ANALOG ? 2 : 1; +} + /* ----------------------------------------------------------------------------- * Controls */ @@ -370,10 +449,12 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + u32 rate_factor; int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx219->sd); format = v4l2_subdev_state_get_format(state, 0); + rate_factor = imx219_get_rate_factor(state); if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -402,7 +483,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, - ctrl->val, &ret); + ctrl->val / rate_factor, &ret); break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, @@ -418,8 +499,12 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) imx219->hflip->val | imx219->vflip->val << 1, &ret); break; case V4L2_CID_VBLANK: - cci_write(imx219->regmap, IMX219_REG_VTS, - format->height + ctrl->val, &ret); + cci_write(imx219->regmap, IMX219_REG_FRM_LENGTH_A, + (format->height + ctrl->val) / rate_factor, &ret); + break; + case V4L2_CID_HBLANK: + cci_write(imx219->regmap, IMX219_REG_LINE_LENGTH_A, + format->width + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, @@ -466,7 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219) const struct imx219_mode *mode = &supported_modes[0]; struct v4l2_ctrl_handler *ctrl_hdlr; struct v4l2_fwnode_device_properties props; - int exposure_max, exposure_def, hblank; + int exposure_max, exposure_def; int i, ret; ctrl_hdlr = &imx219->ctrl_handler; @@ -490,18 +575,17 @@ static int imx219_init_controls(struct imx219 *imx219) if (imx219->link_freq) imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* Initial vblank/hblank/exposure parameters based on current mode */ + /* Initial blanking and exposure. Limits are updated during set_fmt */ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_VBLANK, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); - hblank = IMX219_PPL_DEFAULT - mode->width; + IMX219_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, - 1, hblank); - if (imx219->hblank) - imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - exposure_max = mode->vts_def - 4; + V4L2_CID_HBLANK, + IMX219_LLP_MIN - mode->width, + IMX219_LLP_MAX - mode->width, 1, + IMX219_LLP_MIN - mode->width); + exposure_max = mode->fll_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, @@ -587,29 +671,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, { const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; - unsigned int bpp; - u64 bin_h, bin_v; + u8 bin_h, bin_v; + u32 bpp; int ret = 0; format = v4l2_subdev_state_get_format(state, 0); crop = v4l2_subdev_state_get_crop(state, 0); - - switch (format->code) { - case MEDIA_BUS_FMT_SRGGB8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - bpp = 8; - break; - - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - default: - bpp = 10; - break; - } + bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); @@ -620,26 +688,7 @@ static int imx219_set_framefmt(struct imx219 *imx219, cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); - switch (crop->width / format->width) { - case 1: - default: - bin_h = IMX219_BINNING_NONE; - break; - case 2: - bin_h = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - - switch (crop->height / format->height) { - case 1: - default: - bin_v = IMX219_BINNING_NONE; - break; - case 2: - bin_v = bpp == 8 ? IMX219_BINNING_X2_ANALOG : IMX219_BINNING_X2; - break; - } - + imx219_get_binning(state, &bin_h, &bin_v); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_V, bin_v, &ret); @@ -662,9 +711,11 @@ static int imx219_set_framefmt(struct imx219 *imx219, static int imx219_configure_lanes(struct imx219 *imx219) { - return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, - imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE : - IMX219_CSI_4_LANE_MODE, NULL); + /* Write the appropriate PLL settings for the number of MIPI lanes */ + return cci_multi_reg_write(imx219->regmap, + imx219->lanes == 2 ? imx219_2lane_regs : imx219_4lane_regs, + imx219->lanes == 2 ? ARRAY_SIZE(imx219_2lane_regs) : + ARRAY_SIZE(imx219_4lane_regs), NULL); }; static int imx219_start_streaming(struct imx219 *imx219, @@ -815,7 +866,11 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, const struct imx219_mode *mode; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - unsigned int bin_h, bin_v; + u8 bin_h, bin_v; + u32 prev_line_len; + + format = v4l2_subdev_state_get_format(state, 0); + prev_line_len = format->width + imx219->hblank->val; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), @@ -823,8 +878,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); - - format = v4l2_subdev_state_get_format(state, 0); *format = fmt->format; /* @@ -843,30 +896,51 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; - int hblank; + int hblank, llp_min; + int pixel_rate; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, - IMX219_VTS_MAX - mode->height, 1, - mode->vts_def - mode->height); + IMX219_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); __v4l2_ctrl_s_ctrl(imx219->vblank, - mode->vts_def - mode->height); + mode->fll_def - mode->height); /* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def - 4; + exposure_max = mode->fll_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum, exposure_max, imx219->exposure->step, exposure_def); + /* - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank - * depends on mode->width only, and is not changeble in any - * way other than changing the mode. + * With analog binning the default minimum line length of 3448 + * can cause artefacts with RAW10 formats, because the ADC + * operates on two lines together. So we switch to a higher + * minimum of 3560. */ - hblank = IMX219_PPL_DEFAULT - mode->width; - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, - hblank); + imx219_get_binning(state, &bin_h, &bin_v); + llp_min = (bin_h & bin_v) == IMX219_BINNING_X2_ANALOG ? + IMX219_BINNED_LLP_MIN : IMX219_LLP_MIN; + __v4l2_ctrl_modify_range(imx219->hblank, llp_min - mode->width, + IMX219_LLP_MAX - mode->width, 1, + llp_min - mode->width); + /* + * Retain PPL setting from previous mode so that the + * line time does not change on a mode change. + * Limits have to be recomputed as the controls define + * the blanking only, so PPL values need to have the + * mode width subtracted. + */ + hblank = prev_line_len - mode->width; + __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + /* Scale the pixel rate based on the mode specific factor */ + pixel_rate = imx219_get_pixel_rate(imx219) * + imx219_get_rate_factor(state); + __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); } return 0; @@ -877,10 +951,9 @@ static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { switch (sel->target) { - case V4L2_SEL_TGT_CROP: { + case V4L2_SEL_TGT_CROP: sel->r = *v4l2_subdev_state_get_crop(state, 0); return 0; - } case V4L2_SEL_TGT_NATIVE_SIZE: sel->r.top = 0; @@ -1035,6 +1108,7 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) struct v4l2_fwnode_endpoint ep_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + unsigned long link_freq_bitmap; int ret = -EINVAL; endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); @@ -1056,23 +1130,40 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes; /* Check the link frequency set in device tree */ - if (!ep_cfg.nr_of_link_frequencies) { - dev_err_probe(dev, -EINVAL, - "link-frequency property not found in DT\n"); - goto error_out; + switch (imx219->lanes) { + case 2: + ret = v4l2_link_freq_to_bitmap(dev, + ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + imx219_link_freq_menu, + ARRAY_SIZE(imx219_link_freq_menu), + &link_freq_bitmap); + break; + case 4: + ret = v4l2_link_freq_to_bitmap(dev, + ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + imx219_link_freq_4lane_menu, + ARRAY_SIZE(imx219_link_freq_4lane_menu), + &link_freq_bitmap); + + if (!ret && (link_freq_bitmap & BIT(1))) { + dev_warn(dev, "Link frequency of %d not supported, but has been incorrectly advertised previously\n", + IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED); + dev_warn(dev, "Using link frequency of %d\n", + IMX219_DEFAULT_LINK_FREQ_4LANE); + link_freq_bitmap |= BIT(0); + } + break; } - if (ep_cfg.nr_of_link_frequencies != 1 || - (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? - IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { + if (ret || !(link_freq_bitmap & BIT(0))) { + ret = -EINVAL; dev_err_probe(dev, -EINVAL, "Link frequency not supported: %lld\n", ep_cfg.link_frequencies[0]); - goto error_out; } - ret = 0; - error_out: v4l2_fwnode_endpoint_free(&ep_cfg); fwnode_handle_put(endpoint); @@ -1178,6 +1269,9 @@ static int imx219_probe(struct i2c_client *client) goto error_media_entity; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + ret = v4l2_async_register_subdev_sensor(&imx219->sd); if (ret < 0) { dev_err_probe(dev, ret, @@ -1185,15 +1279,14 @@ static int imx219_probe(struct i2c_client *client) goto error_subdev_cleanup; } - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); pm_runtime_idle(dev); return 0; error_subdev_cleanup: v4l2_subdev_cleanup(&imx219->sd); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); error_media_entity: media_entity_cleanup(&imx219->sd.entity); @@ -1218,9 +1311,10 @@ static void imx219_remove(struct i2c_client *client) imx219_free_controls(imx219); pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) + if (!pm_runtime_status_suspended(&client->dev)) { imx219_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } static const struct of_device_id imx219_dt_ids[] = { diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index f676faf4b301..beb9169f93ad 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -1170,8 +1170,10 @@ static int imx283_disable_streams(struct v4l2_subdev *sd, } /* Power/clock management functions */ -static int imx283_power_on(struct imx283 *imx283) +static int imx283_power_on(struct device *dev) { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); int ret; ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name), @@ -1199,29 +1201,14 @@ reg_off: return ret; } -static int imx283_power_off(struct imx283 *imx283) -{ - gpiod_set_value_cansleep(imx283->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); - clk_disable_unprepare(imx283->xclk); - - return 0; -} - -static int imx283_runtime_resume(struct device *dev) +static int imx283_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx283 *imx283 = to_imx283(sd); - return imx283_power_on(imx283); -} - -static int imx283_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct imx283 *imx283 = to_imx283(sd); - - imx283_power_off(imx283); + gpiod_set_value_cansleep(imx283->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + clk_disable_unprepare(imx283->xclk); return 0; } @@ -1516,7 +1503,7 @@ static int imx283_probe(struct i2c_client *client) * The sensor must be powered for imx283_identify_module() * to be able to read the CHIP_ID register */ - ret = imx283_power_on(imx283); + ret = imx283_power_on(imx283->dev); if (ret) return ret; @@ -1589,7 +1576,7 @@ error_pm: pm_runtime_disable(imx283->dev); pm_runtime_set_suspended(imx283->dev); error_power_off: - imx283_power_off(imx283); + imx283_power_off(imx283->dev); return ret; } @@ -1606,12 +1593,12 @@ static void imx283_remove(struct i2c_client *client) pm_runtime_disable(imx283->dev); if (!pm_runtime_status_suspended(imx283->dev)) - imx283_power_off(imx283); + imx283_power_off(imx283->dev); pm_runtime_set_suspended(imx283->dev); } -static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_runtime_suspend, - imx283_runtime_resume, NULL); +static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_power_off, + imx283_power_on, NULL); static const struct of_device_id imx283_dt_ids[] = { { .compatible = "sony,imx283" }, diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index dd1b4ff983dc..701840f4a5cc 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -2442,17 +2442,19 @@ static int imx319_probe(struct i2c_client *client) if (full_power) pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); ret = v4l2_async_register_subdev_sensor(&imx319->sd); if (ret < 0) goto error_media_entity_pm; + pm_runtime_idle(&client->dev); + return 0; error_media_entity_pm: pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + if (full_power) + pm_runtime_set_suspended(&client->dev); media_entity_cleanup(&imx319->sd.entity); error_handler_free: @@ -2474,7 +2476,8 @@ static void imx319_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + pm_runtime_set_suspended(&client->dev); mutex_destroy(&imx319->mutex); } diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index fcfd1d851bd4..0beb80b8c458 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -559,12 +559,14 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) imx335->vblank, imx335->vblank + imx335->cur_mode->height); - return __v4l2_ctrl_modify_range(imx335->exp_ctrl, - IMX335_EXPOSURE_MIN, - imx335->vblank + - imx335->cur_mode->height - - IMX335_EXPOSURE_OFFSET, - 1, IMX335_EXPOSURE_DEFAULT); + ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl, + IMX335_EXPOSURE_MIN, + imx335->vblank + + imx335->cur_mode->height - + IMX335_EXPOSURE_OFFSET, + 1, IMX335_EXPOSURE_DEFAULT); + if (ret) + return ret; } /* @@ -575,6 +577,13 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { + case V4L2_CID_VBLANK: + exposure = imx335->exp_ctrl->val; + analog_gain = imx335->again_ctrl->val; + + ret = imx335_update_exp_gain(imx335, exposure, analog_gain); + + break; case V4L2_CID_EXPOSURE: exposure = ctrl->val; analog_gain = imx335->again_ctrl->val; diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3f7924aa1bd3..9f37779bd611 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -26,6 +26,10 @@ #define IMX415_PIXEL_ARRAY_WIDTH 3864 #define IMX415_PIXEL_ARRAY_HEIGHT 2192 #define IMX415_PIXEL_ARRAY_VBLANK 58 +#define IMX415_EXPOSURE_OFFSET 8 + +#define IMX415_PIXEL_RATE_74_25MHZ 891000000 +#define IMX415_PIXEL_RATE_72MHZ 864000000 #define IMX415_NUM_CLK_PARAM_REGS 11 @@ -51,7 +55,10 @@ #define IMX415_OUTSEL CCI_REG8(0x30c0) #define IMX415_DRV CCI_REG8(0x30c1) #define IMX415_VMAX CCI_REG24_LE(0x3024) +#define IMX415_VMAX_MAX 0xfffff #define IMX415_HMAX CCI_REG16_LE(0x3028) +#define IMX415_HMAX_MAX 0xffff +#define IMX415_HMAX_MULTIPLIER 12 #define IMX415_SHR0 CCI_REG24_LE(0x3050) #define IMX415_GAIN_PCG_0 CCI_REG16_LE(0x3090) #define IMX415_AGAIN_MIN 0 @@ -445,11 +452,8 @@ static const struct imx415_clk_params imx415_clk_params[] = { }, }; -/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ -static const struct cci_reg_sequence imx415_mode_2_720[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x07F0 }, - { IMX415_LANEMODE, IMX415_LANEMODE_2 }, +/* 720 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_720mbps[] = { { IMX415_TCLKPOST, 0x006F }, { IMX415_TCLKPREPARE, 0x002F }, { IMX415_TCLKTRAIL, 0x002F }, @@ -461,11 +465,8 @@ static const struct cci_reg_sequence imx415_mode_2_720[] = { { IMX415_TLPX, 0x0027 }, }; -/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ -static const struct cci_reg_sequence imx415_mode_2_1440[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x042A }, - { IMX415_LANEMODE, IMX415_LANEMODE_2 }, +/* 1440 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_1440mbps[] = { { IMX415_TCLKPOST, 0x009F }, { IMX415_TCLKPREPARE, 0x0057 }, { IMX415_TCLKTRAIL, 0x0057 }, @@ -477,11 +478,8 @@ static const struct cci_reg_sequence imx415_mode_2_1440[] = { { IMX415_TLPX, 0x004F }, }; -/* all-pixel 4-lane 891 Mbps 30 Hz mode */ -static const struct cci_reg_sequence imx415_mode_4_891[] = { - { IMX415_VMAX, 0x08CA }, - { IMX415_HMAX, 0x044C }, - { IMX415_LANEMODE, IMX415_LANEMODE_4 }, +/* 891 Mbps CSI configuration */ +static const struct cci_reg_sequence imx415_linkrate_891mbps[] = { { IMX415_TCLKPOST, 0x007F }, { IMX415_TCLKPREPARE, 0x0037 }, { IMX415_TCLKTRAIL, 0x0037 }, @@ -498,39 +496,9 @@ struct imx415_mode_reg_list { const struct cci_reg_sequence *regs; }; -/* - * Mode : number of lanes, lane rate and frame rate dependent settings - * - * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl - * interface. These values can not be found in the data sheet and should be - * treated as virtual values. Use following table when adding new modes. - * - * lane_rate lanes fps hmax_pix pixel_rate - * - * 594 2 10.000 4400 99000000 - * 891 2 15.000 4400 148500000 - * 720 2 15.748 4064 144000000 - * 1782 2 30.000 4400 297000000 - * 2079 2 30.000 4400 297000000 - * 1440 2 30.019 4510 304615385 - * - * 594 4 20.000 5500 247500000 - * 594 4 25.000 4400 247500000 - * 720 4 25.000 4400 247500000 - * 720 4 30.019 4510 304615385 - * 891 4 30.000 4400 297000000 - * 1440 4 30.019 4510 304615385 - * 1440 4 60.038 4510 609230769 - * 1485 4 60.000 4400 594000000 - * 1782 4 60.000 4400 594000000 - * 2079 4 60.000 4400 594000000 - * 2376 4 90.164 4392 891000000 - */ struct imx415_mode { u64 lane_rate; - u32 lanes; - u32 hmax_pix; - u64 pixel_rate; + u32 hmax_min[2]; struct imx415_mode_reg_list reg_list; }; @@ -538,32 +506,26 @@ struct imx415_mode { static const struct imx415_mode supported_modes[] = { { .lane_rate = 720000000, - .lanes = 2, - .hmax_pix = 4064, - .pixel_rate = 144000000, + .hmax_min = { 2032, 1066 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_2_720), - .regs = imx415_mode_2_720, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_720mbps), + .regs = imx415_linkrate_720mbps, }, }, { .lane_rate = 1440000000, - .lanes = 2, - .hmax_pix = 4510, - .pixel_rate = 304615385, + .hmax_min = { 1066, 533 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440), - .regs = imx415_mode_2_1440, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_1440mbps), + .regs = imx415_linkrate_1440mbps, }, }, { .lane_rate = 891000000, - .lanes = 4, - .hmax_pix = 4400, - .pixel_rate = 297000000, + .hmax_min = { 2200, 1100 }, .reg_list = { - .num_of_regs = ARRAY_SIZE(imx415_mode_4_891), - .regs = imx415_mode_4_891, + .num_of_regs = ARRAY_SIZE(imx415_linkrate_891mbps), + .regs = imx415_linkrate_891mbps, }, }, }; @@ -587,6 +549,7 @@ static const char *const imx415_test_pattern_menu[] = { struct imx415 { struct device *dev; struct clk *clk; + unsigned long pixel_rate; struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)]; struct gpio_desc *reset; struct regmap *regmap; @@ -598,8 +561,10 @@ struct imx415 { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; + struct v4l2_ctrl *exposure; unsigned int cur_mode; unsigned int num_data_lanes; @@ -730,17 +695,38 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ctrls); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; + u32 exposure_max; unsigned int vmax; unsigned int flip; int ret; - if (!pm_runtime_get_if_in_use(sensor->dev)) - return 0; - state = v4l2_subdev_get_locked_active_state(&sensor->subdev); format = v4l2_subdev_state_get_format(state, 0); + if (ctrl->id == V4L2_CID_VBLANK) { + exposure_max = format->height + ctrl->val - + IMX415_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + sensor->exposure->default_value); + } + + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + switch (ctrl->id) { + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, IMX415_VMAX, + format->height + ctrl->val, NULL); + if (ret) + return ret; + /* + * Exposure is set based on VMAX which has just changed, so + * program exposure register as well + */ + ctrl = sensor->exposure; + fallthrough; case V4L2_CID_EXPOSURE: /* clamp the exposure value to VMAX. */ vmax = format->height + sensor->vblank->cur.val; @@ -766,6 +752,13 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ret = imx415_set_testpattern(sensor, ctrl->val); break; + case V4L2_CID_HBLANK: + ret = cci_write(sensor->regmap, IMX415_HMAX, + (format->width + ctrl->val) / + IMX415_HMAX_MULTIPLIER, + NULL); + break; + default: ret = -EINVAL; break; @@ -784,11 +777,12 @@ static int imx415_ctrls_init(struct imx415 *sensor) { struct v4l2_fwnode_device_properties props; struct v4l2_ctrl *ctrl; - u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate; - u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate; + const struct imx415_mode *cur_mode = &supported_modes[sensor->cur_mode]; + u64 lane_rate = cur_mode->lane_rate; u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT + - IMX415_PIXEL_ARRAY_VBLANK - 8; - u32 hblank; + IMX415_PIXEL_ARRAY_VBLANK - + IMX415_EXPOSURE_OFFSET; + u32 hblank_min, hblank_max; unsigned int i; int ret; @@ -816,36 +810,33 @@ static int imx415_ctrls_init(struct imx415 *sensor) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE, - 4, exposure_max, 1, exposure_max); + sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_EXPOSURE, 4, + exposure_max, 1, exposure_max); v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN, IMX415_AGAIN_MAX, IMX415_AGAIN_STEP, IMX415_AGAIN_MIN); - hblank = supported_modes[sensor->cur_mode].hmax_pix - - IMX415_PIXEL_ARRAY_WIDTH; + hblank_min = (cur_mode->hmax_min[sensor->num_data_lanes == 2 ? 0 : 1] * + IMX415_HMAX_MULTIPLIER) - IMX415_PIXEL_ARRAY_WIDTH; + hblank_max = (IMX415_HMAX_MAX * IMX415_HMAX_MULTIPLIER) - + IMX415_PIXEL_ARRAY_WIDTH; ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, 1, hblank); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + V4L2_CID_HBLANK, hblank_min, + hblank_max, IMX415_HMAX_MULTIPLIER, + hblank_min); sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_VBLANK, IMX415_PIXEL_ARRAY_VBLANK, - IMX415_PIXEL_ARRAY_VBLANK, 1, - IMX415_PIXEL_ARRAY_VBLANK); - if (sensor->vblank) - sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + IMX415_VMAX_MAX - IMX415_PIXEL_ARRAY_HEIGHT, + 1, IMX415_PIXEL_ARRAY_VBLANK); - /* - * The pixel rate used here is a virtual value and can be used for - * calculating the frame rate together with hblank. It may not - * necessarily be the physically correct pixel clock. - */ - v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate, - pixel_rate, 1, pixel_rate); + v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, + sensor->pixel_rate, sensor->pixel_rate, 1, + sensor->pixel_rate); sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); @@ -890,7 +881,12 @@ static int imx415_set_mode(struct imx415 *sensor, int mode) IMX415_NUM_CLK_PARAM_REGS, &ret); - return 0; + ret = cci_write(sensor->regmap, IMX415_LANEMODE, + sensor->num_data_lanes == 2 ? IMX415_LANEMODE_2 : + IMX415_LANEMODE_4, + NULL); + + return ret; } static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state) @@ -1301,8 +1297,6 @@ static int imx415_parse_hw_config(struct imx415 *sensor) } for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) { - if (sensor->num_data_lanes != supported_modes[j].lanes) - continue; if (bus_cfg.link_frequencies[i] * 2 != supported_modes[j].lane_rate) continue; @@ -1317,6 +1311,17 @@ static int imx415_parse_hw_config(struct imx415 *sensor) "no valid sensor mode defined\n"); goto done_endpoint_free; } + switch (inck) { + case 27000000: + case 37125000: + case 74250000: + sensor->pixel_rate = IMX415_PIXEL_RATE_74_25MHZ; + break; + case 24000000: + case 72000000: + sensor->pixel_rate = IMX415_PIXEL_RATE_72MHZ; + break; + } lane_rate = supported_modes[sensor->cur_mode].lane_rate; for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) { diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c new file mode 100644 index 000000000000..c5b40bb58a37 --- /dev/null +++ b/drivers/media/i2c/lt6911uxe.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 - 2025 Intel Corporation. + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/v4l2-dv-timings.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-dv-timings.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> + +#define LT6911UXE_CHIP_ID 0x2102 +#define REG_CHIP_ID CCI_REG16(0xe100) + +#define REG_ENABLE_I2C CCI_REG8(0xe0ee) +#define REG_HALF_PIX_CLK CCI_REG24(0xe085) +#define REG_BYTE_CLK CCI_REG24(0xe092) +#define REG_HALF_H_TOTAL CCI_REG16(0xe088) +#define REG_V_TOTAL CCI_REG16(0xe08a) +#define REG_HALF_H_ACTIVE CCI_REG16(0xe08c) +#define REG_V_ACTIVE CCI_REG16(0xe08e) +#define REG_MIPI_FORMAT CCI_REG8(0xe096) +#define REG_MIPI_TX_CTRL CCI_REG8(0xe0b0) + +/* Interrupts */ +#define REG_INT_HDMI CCI_REG8(0xe084) +#define INT_VIDEO_DISAPPEAR 0x0 +#define INT_VIDEO_READY 0x1 + +#define LT6911UXE_DEFAULT_LANES 4 +#define LT6911_PAGE_CONTROL 0xff +#define YUV422_8_BIT 0x7 + +static const struct v4l2_dv_timings_cap lt6911uxe_timings_cap_4kp30 = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with CLANG */ + .reserved = { 0 }, + /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ + V4L2_INIT_BT_TIMINGS(160, 3840, /* min/max width */ + 120, 2160, /* min/max height */ + 50000000, 594000000, /* min/max pixelclock */ + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT, + V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_CUSTOM | + V4L2_DV_BT_CAP_REDUCED_BLANKING) +}; + +static const struct regmap_range_cfg lt6911uxe_ranges[] = { + { + .name = "register_range", + .range_min = 0, + .range_max = 0xffff, + .selector_reg = LT6911_PAGE_CONTROL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct regmap_config lt6911uxe_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xffff, + .ranges = lt6911uxe_ranges, + .num_ranges = ARRAY_SIZE(lt6911uxe_ranges), +}; + +struct lt6911uxe_mode { + u32 width; + u32 height; + u32 htotal; + u32 vtotal; + u32 code; + u32 fps; + u32 lanes; + s64 link_freq; + u64 pixel_clk; +}; + +struct lt6911uxe { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_dv_timings timings; + struct lt6911uxe_mode cur_mode; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct gpio_desc *irq_gpio; +}; + +static const struct v4l2_event lt6911uxe_ev_source_change = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + +static inline struct lt6911uxe *to_lt6911uxe(struct v4l2_subdev *sd) +{ + return container_of(sd, struct lt6911uxe, sd); +} + +static s64 get_pixel_rate(struct lt6911uxe *lt6911uxe) +{ + s64 pixel_rate; + + pixel_rate = (s64)lt6911uxe->cur_mode.width * + lt6911uxe->cur_mode.height * + lt6911uxe->cur_mode.fps * 16; + do_div(pixel_rate, lt6911uxe->cur_mode.lanes); + + return pixel_rate; +} + +static int lt6911uxe_get_detected_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_bt_timings *bt = &timings->bt; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + + timings->type = V4L2_DV_BT_656_1120; + + bt->width = lt6911uxe->cur_mode.width; + bt->height = lt6911uxe->cur_mode.height; + bt->vsync = lt6911uxe->cur_mode.vtotal - lt6911uxe->cur_mode.height; + bt->hsync = lt6911uxe->cur_mode.htotal - lt6911uxe->cur_mode.width; + bt->pixelclock = lt6911uxe->cur_mode.pixel_clk; + + return 0; +} + +static int lt6911uxe_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (v4l2_match_dv_timings(<6911uxe->timings, timings, 0, false)) { + v4l2_subdev_unlock_state(state); + return 0; + } + + if (!v4l2_valid_dv_timings(timings, <6911uxe_timings_cap_4kp30, + NULL, NULL)) { + v4l2_subdev_unlock_state(state); + return -ERANGE; + } + lt6911uxe->timings = *timings; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + *timings = lt6911uxe->timings; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_dv_timings *timings) +{ + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + ret = lt6911uxe_get_detected_timings(sd, timings); + if (ret) { + v4l2_subdev_unlock_state(state); + return ret; + } + + if (!v4l2_valid_dv_timings(timings, <6911uxe_timings_cap_4kp30, + NULL, NULL)) { + v4l2_subdev_unlock_state(state); + return -ERANGE; + } + + v4l2_subdev_unlock_state(state); + return 0; +} + +static int lt6911uxe_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, + <6911uxe_timings_cap_4kp30, NULL, NULL); +} + +static int lt6911uxe_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = lt6911uxe_timings_cap_4kp30; + return 0; +} + +static int lt6911uxe_status_update(struct lt6911uxe *lt6911uxe) +{ + struct i2c_client *client = v4l2_get_subdevdata(<6911uxe->sd); + u64 int_event; + u64 byte_clk, half_pix_clk, fps, format; + u64 half_htotal, vtotal, half_width, height; + int ret = 0; + + /* Read interrupt event */ + cci_read(lt6911uxe->regmap, REG_INT_HDMI, &int_event, &ret); + if (ret) { + dev_err(&client->dev, "failed to read interrupt event: %d\n", + ret); + return ret; + } + + switch (int_event) { + case INT_VIDEO_READY: + cci_read(lt6911uxe->regmap, REG_BYTE_CLK, &byte_clk, &ret); + byte_clk *= 1000; + cci_read(lt6911uxe->regmap, REG_HALF_PIX_CLK, + &half_pix_clk, &ret); + half_pix_clk *= 1000; + + if (ret || byte_clk == 0 || half_pix_clk == 0) { + dev_dbg(&client->dev, + "invalid ByteClock or PixelClock\n"); + return -EINVAL; + } + + cci_read(lt6911uxe->regmap, REG_HALF_H_TOTAL, + &half_htotal, &ret); + cci_read(lt6911uxe->regmap, REG_V_TOTAL, &vtotal, &ret); + if (ret || half_htotal == 0 || vtotal == 0) { + dev_dbg(&client->dev, "invalid htotal or vtotal\n"); + return -EINVAL; + } + + fps = div_u64(half_pix_clk, half_htotal * vtotal); + if (fps > 60) { + dev_dbg(&client->dev, + "max fps is 60, current fps: %llu\n", fps); + return -EINVAL; + } + + cci_read(lt6911uxe->regmap, REG_HALF_H_ACTIVE, + &half_width, &ret); + cci_read(lt6911uxe->regmap, REG_V_ACTIVE, &height, &ret); + if (ret || half_width == 0 || half_width * 2 > 3840 || + height == 0 || height > 2160) { + dev_dbg(&client->dev, "invalid width or height\n"); + return -EINVAL; + } + + /* + * Get MIPI format, YUV422_8_BIT is expected in lt6911uxe + */ + cci_read(lt6911uxe->regmap, REG_MIPI_FORMAT, &format, &ret); + if (format != YUV422_8_BIT) { + dev_dbg(&client->dev, "invalid MIPI format\n"); + return -EINVAL; + } + + lt6911uxe->cur_mode.height = height; + lt6911uxe->cur_mode.width = half_width * 2; + lt6911uxe->cur_mode.fps = fps; + /* MIPI Clock Rate = ByteClock × 4, defined in lt6911uxe spec */ + lt6911uxe->cur_mode.link_freq = byte_clk * 4; + lt6911uxe->cur_mode.pixel_clk = half_pix_clk * 2; + lt6911uxe->cur_mode.vtotal = vtotal; + lt6911uxe->cur_mode.htotal = half_htotal * 2; + break; + + case INT_VIDEO_DISAPPEAR: + cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x0, &ret); + lt6911uxe->cur_mode.height = 0; + lt6911uxe->cur_mode.width = 0; + lt6911uxe->cur_mode.fps = 0; + lt6911uxe->cur_mode.link_freq = 0; + break; + + default: + ret = -ENOLINK; + } + v4l2_subdev_notify_event(<6911uxe->sd, <6911uxe_ev_source_change); + return ret; +} + +static int lt6911uxe_init_controls(struct lt6911uxe *lt6911uxe) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 pixel_rate; + int ret; + + ctrl_hdlr = <6911uxe->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + pixel_rate = get_pixel_rate(lt6911uxe); + lt6911uxe->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, + V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, + pixel_rate); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto hdlr_free; + } + lt6911uxe->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +hdlr_free: + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; +} + +static void lt6911uxe_update_pad_format(const struct lt6911uxe_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = mode->code; + fmt->field = V4L2_FIELD_NONE; +} + +static int lt6911uxe_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x1, &ret); + if (ret) { + dev_err(&client->dev, "failed to start stream: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static int lt6911uxe_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct i2c_client *client = v4l2_get_subdevdata(<6911uxe->sd); + int ret; + + ret = cci_write(lt6911uxe->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); + if (ret) + dev_err(&client->dev, "failed to stop stream: %d\n", ret); + + pm_runtime_put(&client->dev); + return 0; +} + +static int lt6911uxe_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + u64 pixel_rate; + + lt6911uxe_update_pad_format(<6911uxe->cur_mode, &fmt->format); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + pixel_rate = get_pixel_rate(lt6911uxe); + __v4l2_ctrl_modify_range(lt6911uxe->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); + + return 0; +} + +static int lt6911uxe_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + + if (code->index) + return -EINVAL; + + code->code = lt6911uxe->cur_mode.code; + + return 0; +} + +static int lt6911uxe_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_and_get_active_state(sd); + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->link_freq = lt6911uxe->cur_mode.link_freq; + v4l2_subdev_unlock_state(state); + + return 0; +} + +static int lt6911uxe_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + return lt6911uxe_set_format(sd, sd_state, &fmt); +} + +static const struct v4l2_subdev_video_ops lt6911uxe_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +/* + * lt6911uxe provides editable EDID for customers, but only can be edited like + * updating flash. Due to this limitation, it is not possible to implement + * EDID support. + */ +static const struct v4l2_subdev_pad_ops lt6911uxe_pad_ops = { + .set_fmt = lt6911uxe_set_format, + .get_fmt = v4l2_subdev_get_fmt, + .enable_streams = lt6911uxe_enable_streams, + .disable_streams = lt6911uxe_disable_streams, + .enum_mbus_code = lt6911uxe_enum_mbus_code, + .get_frame_interval = v4l2_subdev_get_frame_interval, + .s_dv_timings = lt6911uxe_s_dv_timings, + .g_dv_timings = lt6911uxe_g_dv_timings, + .query_dv_timings = lt6911uxe_query_dv_timings, + .enum_dv_timings = lt6911uxe_enum_dv_timings, + .dv_timings_cap = lt6911uxe_dv_timings_cap, + .get_mbus_config = lt6911uxe_get_mbus_config, +}; + +static const struct v4l2_subdev_core_ops lt6911uxe_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops lt6911uxe_subdev_ops = { + .core = <6911uxe_subdev_core_ops, + .video = <6911uxe_video_ops, + .pad = <6911uxe_pad_ops, +}; + +static const struct media_entity_operations lt6911uxe_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops lt6911uxe_internal_ops = { + .init_state = lt6911uxe_init_state, +}; + +static int lt6911uxe_fwnode_parse(struct lt6911uxe *lt6911uxe, + struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) + return dev_err_probe(dev, -EPROBE_DEFER, + "endpoint node not found\n"); + + ret = v4l2_fwnode_endpoint_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "failed to parse endpoint node: %d\n", ret); + goto out_err; + } + + /* + * Check the number of MIPI CSI2 data lanes, + * lt6911uxe only support 4 lanes. + */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != LT6911UXE_DEFAULT_LANES) { + dev_err(dev, "only 4 data lanes are currently supported\n"); + ret = -EINVAL; + goto out_err; + } + lt6911uxe->cur_mode.lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + lt6911uxe->cur_mode.code = MEDIA_BUS_FMT_UYVY8_1X16; + + return 0; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static int lt6911uxe_identify_module(struct lt6911uxe *lt6911uxe, + struct device *dev) +{ + u64 val; + int ret = 0; + + /* Chip ID should be confirmed when the I2C slave is active */ + cci_write(lt6911uxe->regmap, REG_ENABLE_I2C, 0x1, &ret); + cci_read(lt6911uxe->regmap, REG_CHIP_ID, &val, &ret); + cci_write(lt6911uxe->regmap, REG_ENABLE_I2C, 0x0, &ret); + if (ret) + return dev_err_probe(dev, ret, "fail to read chip id\n"); + + if (val != LT6911UXE_CHIP_ID) { + return dev_err_probe(dev, -ENXIO, "chip id mismatch: %x!=%x\n", + LT6911UXE_CHIP_ID, (u16)val); + } + + return 0; +} + +static irqreturn_t lt6911uxe_threaded_irq_fn(int irq, void *dev_id) +{ + struct v4l2_subdev *sd = dev_id; + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + struct v4l2_subdev_state *state; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE + }; + + lt6911uxe_status_update(lt6911uxe); + state = v4l2_subdev_lock_and_get_active_state(sd); + /* + * As a HDMI to CSI2 bridge, it needs to update the format in time + * when the HDMI source changes. + */ + lt6911uxe_set_format(sd, state, &fmt); + v4l2_subdev_unlock_state(state); + + return IRQ_HANDLED; +} + +static void lt6911uxe_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd); + + free_irq(gpiod_to_irq(lt6911uxe->irq_gpio), lt6911uxe); + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(<6911uxe->ctrl_handler); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static int lt6911uxe_probe(struct i2c_client *client) +{ + struct lt6911uxe *lt6911uxe; + struct device *dev = &client->dev; + int ret; + + lt6911uxe = devm_kzalloc(dev, sizeof(*lt6911uxe), GFP_KERNEL); + if (!lt6911uxe) + return -ENOMEM; + + lt6911uxe->regmap = devm_regmap_init_i2c(client, + <6911uxe_regmap_config); + if (IS_ERR(lt6911uxe->regmap)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->regmap), + "failed to init CCI\n"); + + v4l2_i2c_subdev_init(<6911uxe->sd, client, <6911uxe_subdev_ops); + + lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN); + if (IS_ERR(lt6911uxe->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->reset_gpio), + "failed to get reset gpio\n"); + + lt6911uxe->irq_gpio = devm_gpiod_get(dev, "readystat", GPIOD_IN); + if (IS_ERR(lt6911uxe->irq_gpio)) + return dev_err_probe(dev, PTR_ERR(lt6911uxe->irq_gpio), + "failed to get ready_stat gpio\n"); + + ret = lt6911uxe_fwnode_parse(lt6911uxe, dev); + if (ret) + return ret; + + usleep_range(10000, 10500); + + ret = lt6911uxe_identify_module(lt6911uxe, dev); + if (ret) + return dev_err_probe(dev, ret, "failed to find chip\n"); + + ret = lt6911uxe_init_controls(lt6911uxe); + if (ret) + return dev_err_probe(dev, ret, "failed to init control\n"); + + lt6911uxe->sd.dev = dev; + lt6911uxe->sd.internal_ops = <6911uxe_internal_ops; + lt6911uxe->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + lt6911uxe->sd.entity.ops = <6911uxe_subdev_entity_ops; + lt6911uxe->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + lt6911uxe->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(<6911uxe->sd.entity, 1, <6911uxe->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto v4l2_ctrl_handler_free; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = v4l2_subdev_init_finalize(<6911uxe->sd); + if (ret) { + dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); + goto media_entity_cleanup; + } + + /* Setting irq */ + ret = request_threaded_irq(gpiod_to_irq(lt6911uxe->irq_gpio), NULL, + lt6911uxe_threaded_irq_fn, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, NULL, lt6911uxe); + if (ret) { + dev_err(dev, "failed to request IRQ: %d\n", ret); + goto subdev_cleanup; + } + + ret = v4l2_async_register_subdev_sensor(<6911uxe->sd); + if (ret) { + dev_err(dev, "failed to register V4L2 subdev: %d\n", ret); + goto free_irq; + } + + return 0; + +free_irq: + free_irq(gpiod_to_irq(lt6911uxe->irq_gpio), lt6911uxe); + +subdev_cleanup: + v4l2_subdev_cleanup(<6911uxe->sd); + +media_entity_cleanup: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + media_entity_cleanup(<6911uxe->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(lt6911uxe->sd.ctrl_handler); + + return ret; +} + +static const struct acpi_device_id lt6911uxe_acpi_ids[] = { + { "INTC10C5" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, lt6911uxe_acpi_ids); + +static struct i2c_driver lt6911uxe_i2c_driver = { + .driver = { + .name = "lt6911uxe", + .acpi_match_table = ACPI_PTR(lt6911uxe_acpi_ids), + }, + .probe = lt6911uxe_probe, + .remove = lt6911uxe_remove, +}; + +module_i2c_driver(lt6911uxe_i2c_driver); + +MODULE_AUTHOR("Yan Dongcheng <dongcheng.yan@intel.com>"); +MODULE_DESCRIPTION("Lontium lt6911uxe HDMI to MIPI Bridge Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index b9682264e2f5..cf0e41fc3071 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -1322,9 +1322,6 @@ static int ov08x40_power_on(struct device *dev) struct ov08x40 *ov08x = to_ov08x40(sd); int ret; - if (is_acpi_node(dev_fwnode(dev))) - return 0; - ret = clk_prepare_enable(ov08x->xvclk); if (ret < 0) { dev_err(dev, "failed to enable xvclk\n"); @@ -1360,9 +1357,6 @@ static int ov08x40_power_off(struct device *dev) struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov08x40 *ov08x = to_ov08x40(sd); - if (is_acpi_node(dev_fwnode(dev))) - return 0; - gpiod_set_value_cansleep(ov08x->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(ov08x40_supply_names), ov08x->supplies); @@ -1400,7 +1394,7 @@ static int ov08x40_read_reg(struct ov08x40 *ov08x, ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) - return -EIO; + return ret < 0 ? ret : -EIO; *val = be32_to_cpu(data_be); @@ -1469,7 +1463,7 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x, u16 reg, u32 len, u32 __val) { struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); - int buf_i, val_i; + int buf_i, val_i, ret; u8 buf[6], *val_p; __be32 val; @@ -1487,8 +1481,9 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x, while (val_i < 4) buf[buf_i++] = val_p[val_i++]; - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) + return ret < 0 ? ret : -EIO; return 0; } @@ -1937,6 +1932,35 @@ static int ov08x40_stop_streaming(struct ov08x40 *ov08x) OV08X40_REG_VALUE_08BIT, OV08X40_MODE_STANDBY); } +/* Verify chip ID */ +static int ov08x40_identify_module(struct ov08x40 *ov08x) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); + int ret; + u32 val; + + if (ov08x->identified) + return 0; + + ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, + OV08X40_REG_VALUE_24BIT, &val); + if (ret) { + dev_err(&client->dev, "error reading chip-id register: %d\n", ret); + return ret; + } + + if (val != OV08X40_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + OV08X40_CHIP_ID, val); + return -ENXIO; + } + + dev_dbg(&client->dev, "chip id 0x%x\n", val); + ov08x->identified = true; + + return 0; +} + static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) { struct ov08x40 *ov08x = to_ov08x40(sd); @@ -1950,6 +1974,10 @@ static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) if (ret < 0) goto err_unlock; + ret = ov08x40_identify_module(ov08x); + if (ret) + goto err_rpm_put; + /* * Apply default & customized values * and then start streaming. @@ -1974,32 +2002,6 @@ err_unlock: return ret; } -/* Verify chip ID */ -static int ov08x40_identify_module(struct ov08x40 *ov08x) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd); - int ret; - u32 val; - - if (ov08x->identified) - return 0; - - ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, - OV08X40_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV08X40_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - OV08X40_CHIP_ID, val); - return -ENXIO; - } - - ov08x->identified = true; - - return 0; -} - static const struct v4l2_subdev_video_ops ov08x40_video_ops = { .s_stream = ov08x40_set_stream, }; @@ -2151,65 +2153,69 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) int ret; u32 xvclk_rate; - if (!fwnode) - return -ENXIO; + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this also add sensor properties, wait for this. + */ + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); - if (!is_acpi_node(fwnode)) { - ov08x->xvclk = devm_clk_get(dev, NULL); - if (IS_ERR(ov08x->xvclk)) { - dev_err(dev, "could not get xvclk clock (%pe)\n", - ov08x->xvclk); - return PTR_ERR(ov08x->xvclk); - } + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); - xvclk_rate = clk_get_rate(ov08x->xvclk); + ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov08x->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(ov08x->reset_gpio), + "getting reset GPIO\n"); + goto out_err; + } - ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ov08x->reset_gpio)) - return PTR_ERR(ov08x->reset_gpio); + for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++) + ov08x->supplies[i].supply = ov08x40_supply_names[i]; - for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++) - ov08x->supplies[i].supply = ov08x40_supply_names[i]; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov08x40_supply_names), + ov08x->supplies); + if (ret) + goto out_err; - ret = devm_regulator_bulk_get(dev, - ARRAY_SIZE(ov08x40_supply_names), - ov08x->supplies); - if (ret) - return ret; + ov08x->xvclk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov08x->xvclk)) { + ret = dev_err_probe(dev, PTR_ERR(ov08x->xvclk), + "getting xvclk\n"); + goto out_err; + } + if (ov08x->xvclk) { + xvclk_rate = clk_get_rate(ov08x->xvclk); } else { ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &xvclk_rate); if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; + dev_err(dev, "can't get clock frequency\n"); + goto out_err; } } if (xvclk_rate != OV08X40_XVCLK) { - dev_err(dev, "external clock %d is not supported", + dev_err(dev, "external clock %d is not supported\n", xvclk_rate); - return -EINVAL; + ret = -EINVAL; + goto out_err; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto out_err; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); + dev_err(dev, "no link frequencies defined\n"); ret = -EINVAL; goto out_err; } @@ -2222,7 +2228,7 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) } if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", + dev_err(dev, "no link frequency %lld supported\n", link_freq_menu_items[i]); ret = -EINVAL; goto out_err; @@ -2246,10 +2252,8 @@ static int ov08x40_probe(struct i2c_client *client) /* Check HW config */ ret = ov08x40_check_hwcfg(ov08x, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to check hwcfg: %d", ret); + if (ret) return ret; - } /* Initialize subdev */ v4l2_i2c_subdev_init(&ov08x->sd, client, &ov08x40_subdev_ops); @@ -2264,10 +2268,8 @@ static int ov08x40_probe(struct i2c_client *client) /* Check module identity */ ret = ov08x40_identify_module(ov08x); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); + if (ret) goto probe_power_off; - } } /* Set default mode to max resolution */ @@ -2324,11 +2326,14 @@ static void ov08x40_remove(struct i2c_client *client) ov08x40_free_controls(ov08x); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov08x40_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - - ov08x40_power_off(&client->dev); } +static DEFINE_RUNTIME_DEV_PM_OPS(ov08x40_pm_ops, ov08x40_power_off, + ov08x40_power_on, NULL); + #ifdef CONFIG_ACPI static const struct acpi_device_id ov08x40_acpi_ids[] = { {"OVTI08F4"}, @@ -2349,6 +2354,7 @@ static struct i2c_driver ov08x40_i2c_driver = { .name = "ov08x40", .acpi_match_table = ACPI_PTR(ov08x40_acpi_ids), .of_match_table = ov08x40_of_match, + .pm = pm_sleep_ptr(&ov08x40_pm_ops), }, .probe = ov08x40_probe, .remove = ov08x40_remove, diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 9a5d118b87b0..80d151e8ae29 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -83,8 +83,6 @@ static const char * const ov2740_supply_name[] = { "DVDD", }; -#define OV2740_NUM_SUPPLIES ARRAY_SIZE(ov2740_supply_name) - struct nvm_data { struct nvmem_device *nvmem; struct regmap *regmap; @@ -536,7 +534,7 @@ struct ov2740 { struct gpio_desc *reset_gpio; struct gpio_desc *powerdown_gpio; struct clk *clk; - struct regulator_bulk_data supplies[OV2740_NUM_SUPPLIES]; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov2740_supply_name)]; /* Current mode */ const struct ov2740_mode *cur_mode; @@ -655,7 +653,7 @@ static int ov2740_identify_module(struct ov2740 *ov2740) return -ENXIO; } - dev_dbg(&client->dev, "chip id: %x\n", val); + dev_dbg(&client->dev, "chip id: 0x%x\n", val); ov2740->identified = true; @@ -828,8 +826,10 @@ static int ov2740_init_controls(struct ov2740 *ov2740) 0, 0, ov2740_test_pattern_menu); ret = v4l2_fwnode_device_parse(&client->dev, &props); - if (ret) + if (ret) { + v4l2_ctrl_handler_free(ctrl_hdlr); return ret; + } v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov2740_ctrl_ops, &props); @@ -1319,7 +1319,8 @@ static int ov2740_suspend(struct device *dev) gpiod_set_value_cansleep(ov2740->reset_gpio, 1); gpiod_set_value_cansleep(ov2740->powerdown_gpio, 1); clk_disable_unprepare(ov2740->clk); - regulator_bulk_disable(OV2740_NUM_SUPPLIES, ov2740->supplies); + regulator_bulk_disable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); return 0; } @@ -1329,13 +1330,15 @@ static int ov2740_resume(struct device *dev) struct ov2740 *ov2740 = to_ov2740(sd); int ret; - ret = regulator_bulk_enable(OV2740_NUM_SUPPLIES, ov2740->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); if (ret) return ret; ret = clk_prepare_enable(ov2740->clk); if (ret) { - regulator_bulk_disable(OV2740_NUM_SUPPLIES, ov2740->supplies); + regulator_bulk_disable(ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); return ret; } @@ -1351,7 +1354,8 @@ static int ov2740_probe(struct i2c_client *client) struct device *dev = &client->dev; struct ov2740 *ov2740; bool full_power; - int i, ret; + unsigned int i; + int ret; ov2740 = devm_kzalloc(&client->dev, sizeof(*ov2740), GFP_KERNEL); if (!ov2740) @@ -1389,10 +1393,11 @@ static int ov2740_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(ov2740->clk), "failed to get clock\n"); - for (i = 0; i < OV2740_NUM_SUPPLIES; i++) + for (i = 0; i < ARRAY_SIZE(ov2740_supply_name); i++) ov2740->supplies[i].supply = ov2740_supply_name[i]; - ret = devm_regulator_bulk_get(dev, OV2740_NUM_SUPPLIES, ov2740->supplies); + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov2740_supply_name), + ov2740->supplies); if (ret) return dev_err_probe(dev, ret, "failed to get regulators\n"); diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 30f61e04ecaf..3226888d77e9 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -922,6 +922,8 @@ static int ov7251_set_power_on(struct device *dev) return ret; } + usleep_range(1000, 1100); + gpiod_set_value_cansleep(ov7251->enable_gpio, 1); /* wait at least 65536 external clock cycles */ @@ -1696,7 +1698,7 @@ static int ov7251_probe(struct i2c_client *client) return PTR_ERR(ov7251->analog_regulator); } - ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ov7251->enable_gpio)) { dev_err(dev, "cannot get enable gpio\n"); return PTR_ERR(ov7251->enable_gpio); diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 87e5d7ce5a47..c882a021cf18 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -44,6 +44,15 @@ #define OV9282_EXPOSURE_STEP 1 #define OV9282_EXPOSURE_DEFAULT 0x0282 +/* AEC/AGC manual */ +#define OV9282_REG_AEC_MANUAL 0x3503 +#define OV9282_DIGFRAC_GAIN_DELAY BIT(6) +#define OV9282_GAIN_CHANGE_DELAY BIT(5) +#define OV9282_GAIN_DELAY BIT(4) +#define OV9282_GAIN_PREC16_EN BIT(3) +#define OV9282_GAIN_MANUAL_AS_SENSGAIN BIT(2) +#define OV9282_AEC_MANUAL_DEFAULT 0x00 + /* Analog gain control */ #define OV9282_REG_AGAIN 0x3509 #define OV9282_AGAIN_MIN 0x10 @@ -214,7 +223,7 @@ static const struct ov9282_reg common_regs[] = { {0x3030, 0x10}, {0x3039, 0x32}, {0x303a, 0x00}, - {0x3503, 0x08}, + {OV9282_REG_AEC_MANUAL, OV9282_GAIN_PREC16_EN}, {0x3505, 0x8c}, {0x3507, 0x03}, {0x3508, 0x00}, @@ -296,8 +305,8 @@ static const struct ov9282_reg mode_1280x800_regs[] = { {0x3813, 0x08}, {0x3814, 0x11}, {0x3815, 0x11}, - {0x3820, 0x40}, - {0x3821, 0x00}, + {OV9282_REG_TIMING_FORMAT_1, 0x40}, + {OV9282_REG_TIMING_FORMAT_2, 0x00}, {0x4003, 0x40}, {0x4008, 0x04}, {0x4009, 0x0b}, @@ -327,8 +336,8 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x3813, 0x08}, {0x3814, 0x11}, {0x3815, 0x11}, - {0x3820, 0x3c}, - {0x3821, 0x84}, + {OV9282_REG_TIMING_FORMAT_1, 0x3c}, + {OV9282_REG_TIMING_FORMAT_2, 0x84}, {0x4003, 0x40}, {0x4008, 0x02}, {0x4009, 0x05}, @@ -358,8 +367,8 @@ static const struct ov9282_reg mode_640x400_regs[] = { {0x3813, 0x04}, {0x3814, 0x31}, {0x3815, 0x22}, - {0x3820, 0x60}, - {0x3821, 0x01}, + {OV9282_REG_TIMING_FORMAT_1, 0x60}, + {OV9282_REG_TIMING_FORMAT_2, 0x01}, {0x4008, 0x02}, {0x4009, 0x05}, {0x400c, 0x00}, diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index f08db3cfe076..f4568e87f018 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -301,8 +301,9 @@ static int mipid02_detect(struct mipid02_dev *bridge) static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, struct v4l2_mbus_framefmt *fmt) { + struct media_pad *remote = + &bridge->s_subdev->entity.pads[bridge->s_subdev_pad_id]; struct i2c_client *client = bridge->i2c_client; - struct v4l2_subdev *subdev = bridge->s_subdev; struct v4l2_fwnode_endpoint *ep = &bridge->rx; u32 bpp = bpp_from_code(fmt->code); /* @@ -312,7 +313,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, u64 ui_4 = 2000000000; s64 link_freq; - link_freq = v4l2_get_link_freq(subdev->ctrl_handler, bpp, + link_freq = v4l2_get_link_freq(remote, bpp, 2 * ep->bus.mipi_csi2.num_data_lanes); if (link_freq < 0) { dev_err(&client->dev, "Failed to get link frequency"); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index c50d4e85dfd1..2d5f42f11158 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2201,7 +2201,7 @@ static int tc358743_probe(struct i2c_client *client) err_work_queues: cec_unregister_adapter(state->cec_adap); if (!state->i2c_client->irq) { - del_timer(&state->timer); + timer_delete(&state->timer); flush_work(&state->work_i2c_poll); } cancel_delayed_work(&state->delayed_work_enable_hotplug); @@ -2218,7 +2218,7 @@ static void tc358743_remove(struct i2c_client *client) struct tc358743_state *state = to_state(sd); if (!state->i2c_client->irq) { - del_timer_sync(&state->timer); + timer_delete_sync(&state->timer); flush_work(&state->work_i2c_poll); } cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 389582420ba7..143aa1359aba 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -161,10 +161,6 @@ struct tc358746 { u16 pll_pre_div; u16 pll_mul; -#define TC358746_VB_MAX_SIZE (511 * 32) -#define TC358746_VB_DEFAULT_SIZE (1 * 32) - unsigned int vb_size; /* Video buffer size in bits */ - struct phy_configure_opts_mipi_dphy dphy_cfg; }; @@ -202,6 +198,15 @@ enum { PDFORMAT_YUV444, }; +#define TC358746_FORMAT_RAW(_bpp, _code) \ +{ \ + .code = _code, \ + .bus_width = _bpp, \ + .bpp = _bpp, \ + .pdformat = PDFORMAT_RAW##_bpp, \ + .pdataf = PDATAF_MODE0, /* don't care */ \ +} + /* Check tc358746_src_mbus_code() if you add new formats */ static const struct tc358746_format tc358746_formats[] = { { @@ -230,7 +235,23 @@ static const struct tc358746_format tc358746_formats[] = { .bpp = 20, .pdformat = PDFORMAT_YUV422_10BIT, .pdataf = PDATAF_MODE0, /* don't care */ - } + }, + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SBGGR8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGBRG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGRBG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SRGGB8_1X8), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SBGGR10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGBRG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGRBG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SRGGB10_1X10), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SBGGR12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGBRG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGRBG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SRGGB12_1X12), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SBGGR14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGBRG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGRBG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SRGGB14_1X14), }; /* Get n-th format for pad */ @@ -415,6 +436,70 @@ tc358746_apply_pll_config(struct tc358746 *tc358746) return tc358746_set_bits(tc358746, PLLCTL1_REG, CKEN); } +#define TC358746_VB_PRECISION 10 +#define TC358746_VB_MAX_SIZE (511 * 32) +#define TC358746_VB_DEFAULT_SIZE (1 * 32) + +static int tc358746_calc_vb_size(struct tc358746 *tc358746, + s64 source_link_freq, + const struct v4l2_mbus_framefmt *mbusfmt, + const struct tc358746_format *fmt) +{ + unsigned long csi_bitrate, source_bitrate; + unsigned int fifo_sz, tmp, n; + int vb_size; /* Video buffer size in bits */ + + source_bitrate = source_link_freq * fmt->bus_width; + + csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; + + dev_dbg(tc358746->sd.dev, + "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", + source_bitrate, csi_bitrate); + + /* Avoid possible FIFO overflows */ + if (csi_bitrate < source_bitrate) + return -EINVAL; + + /* Best case */ + if (csi_bitrate == source_bitrate) { + fifo_sz = TC358746_VB_DEFAULT_SIZE; + vb_size = TC358746_VB_DEFAULT_SIZE; + } else { + /* + * Avoid possible FIFO underflow in case of + * csi_bitrate > source_bitrate. For such case the chip has a internal + * fifo which can be used to delay the line output. + * + * Fifo size calculation (excluding precision): + * + * fifo-sz, image-width - in bits + * sbr - source_bitrate in bits/s + * csir - csi_bitrate in bits/s + * + * image-width / csir >= (image-width - fifo-sz) / sbr + * image-width * sbr / csir >= image-width - fifo-sz + * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr + * fifo-sz >= image-width - image-width / n + */ + source_bitrate /= TC358746_VB_PRECISION; + n = csi_bitrate / source_bitrate; + tmp = (mbusfmt->width * TC358746_VB_PRECISION) / n; + fifo_sz = mbusfmt->width - tmp; + fifo_sz *= fmt->bpp; + vb_size = round_up(fifo_sz, 32); + } + + dev_dbg(tc358746->sd.dev, + "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", + fifo_sz, vb_size); + + if (vb_size > TC358746_VB_MAX_SIZE) + return -EINVAL; + + return vb_size; +} + static int tc358746_apply_misc_config(struct tc358746 *tc358746) { const struct v4l2_mbus_framefmt *mbusfmt; @@ -422,6 +507,9 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) struct v4l2_subdev_state *sink_state; const struct tc358746_format *fmt; struct device *dev = sd->dev; + struct media_pad *source_pad; + s64 source_link_freq; + int vb_size; u32 val; int err; @@ -430,6 +518,21 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); + source_pad = media_entity_remote_source_pad_unique(&sd->entity); + if (IS_ERR(source_pad)) { + dev_err(dev, "Failed to get source pad of %s\n", sd->name); + err = PTR_ERR(source_pad); + goto out; + } + source_link_freq = v4l2_get_link_freq(source_pad, 0, 0); + if (source_link_freq <= 0) { + dev_err(dev, + "Failed to query or invalid source link frequency\n"); + /* Return -EINVAL in case of source_link_freq is 0 */ + err = source_link_freq ?: -EINVAL; + goto out; + } + /* Self defined CSI user data type id's are not supported yet */ val = PDFMT(fmt->pdformat); dev_dbg(dev, "DATAFMT: 0x%x\n", val); @@ -443,7 +546,13 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) if (err) goto out; - val = tc358746->vb_size / 32; + vb_size = tc358746_calc_vb_size(tc358746, source_link_freq, mbusfmt, fmt); + if (vb_size < 0) { + err = vb_size; + goto out; + } + + val = vb_size / 32; dev_dbg(dev, "FIFOCTL: %u (0x%x)\n", val, val); err = tc358746_write(tc358746, FIFOCTL_REG, val); if (err) @@ -460,24 +569,20 @@ out: return err; } -/* Use MHz as base so the div needs no u64 */ -static u32 tc358746_cfg_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz, - unsigned int time_base) +static u32 tc358746_cfg_to_cnt(unsigned long cfg_val, unsigned long clk_hz, + unsigned long long time_base) { - return DIV_ROUND_UP(cfg_val * clk_mhz, time_base); + return div64_u64((u64)cfg_val * clk_hz + time_base - 1, time_base); } -static u32 tc358746_ps_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_ps_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, USEC_PER_SEC); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, PSEC_PER_SEC); } -static u32 tc358746_us_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_us_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, 1); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, USEC_PER_SEC); } static int tc358746_apply_dphy_config(struct tc358746 *tc358746) @@ -492,7 +597,6 @@ static int tc358746_apply_dphy_config(struct tc358746 *tc358746) /* The hs_byte_clk is also called SYSCLK in the excel sheet */ hs_byte_clk = cfg->hs_clk_rate / 8; - hs_byte_clk /= HZ_PER_MHZ; hf_clk = hs_byte_clk / 2; val = tc358746_us_to_cnt(cfg->init, hf_clk) - 1; @@ -882,97 +986,6 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, return best_freq; } -#define TC358746_PRECISION 10 - -static int -tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt) -{ - struct tc358746 *tc358746 = to_tc358746(sd); - unsigned long csi_bitrate, source_bitrate; - struct v4l2_subdev_state *sink_state; - struct v4l2_mbus_framefmt *mbusfmt; - const struct tc358746_format *fmt; - unsigned int fifo_sz, tmp, n; - struct v4l2_subdev *source; - s64 source_link_freq; - int err; - - err = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); - if (err) - return err; - - sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); - - /* Check the FIFO settings */ - fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); - - source = media_entity_to_v4l2_subdev(link->source->entity); - source_link_freq = v4l2_get_link_freq(source->ctrl_handler, 0, 0); - if (source_link_freq <= 0) { - dev_err(tc358746->sd.dev, - "Failed to query or invalid source link frequency\n"); - v4l2_subdev_unlock_state(sink_state); - /* Return -EINVAL in case of source_link_freq is 0 */ - return source_link_freq ? : -EINVAL; - } - source_bitrate = source_link_freq * fmt->bus_width; - - csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; - - dev_dbg(tc358746->sd.dev, - "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", - source_bitrate, csi_bitrate); - - /* Avoid possible FIFO overflows */ - if (csi_bitrate < source_bitrate) { - v4l2_subdev_unlock_state(sink_state); - return -EINVAL; - } - - /* Best case */ - if (csi_bitrate == source_bitrate) { - fifo_sz = TC358746_VB_DEFAULT_SIZE; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - goto out; - } - - /* - * Avoid possible FIFO underflow in case of - * csi_bitrate > source_bitrate. For such case the chip has a internal - * fifo which can be used to delay the line output. - * - * Fifo size calculation (excluding precision): - * - * fifo-sz, image-width - in bits - * sbr - source_bitrate in bits/s - * csir - csi_bitrate in bits/s - * - * image-width / csir >= (image-width - fifo-sz) / sbr - * image-width * sbr / csir >= image-width - fifo-sz - * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr - * fifo-sz >= image-width - image-width / n - */ - - source_bitrate /= TC358746_PRECISION; - n = csi_bitrate / source_bitrate; - tmp = (mbusfmt->width * TC358746_PRECISION) / n; - fifo_sz = mbusfmt->width - tmp; - fifo_sz *= fmt->bpp; - tc358746->vb_size = round_up(fifo_sz, 32); - -out: - dev_dbg(tc358746->sd.dev, - "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", - fifo_sz, tc358746->vb_size); - - v4l2_subdev_unlock_state(sink_state); - - return tc358746->vb_size > TC358746_VB_MAX_SIZE ? -EINVAL : 0; -} - static int tc358746_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config) { @@ -1040,7 +1053,7 @@ static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, - .link_validate = tc358746_link_validate, + .link_validate = v4l2_subdev_link_validate_default, .get_mbus_config = tc358746_get_mbus_config, }; @@ -1352,8 +1365,6 @@ tc358746_init_output_port(struct tc358746 *tc358746, unsigned long refclk) if (err) goto err; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - return 0; err: diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 3b7e5ff5b010..959590afc80f 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2315,11 +2315,10 @@ static int tda1997x_parse_dt(struct tda1997x_state *state) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); - if (ret) { - of_node_put(ep); - return ret; - } of_node_put(ep); + if (ret) + return ret; + pdata->vidout_bus_type = bus_cfg.bus_type; /* polarity of HS/VS/DE */ diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 654725dfafac..42115118a0bd 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1787,7 +1787,7 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd) struct CHIPSTATE *chip = to_state(sd); chip->radio = 1; - /* del_timer(&chip->wt); */ + /* timer_delete(&chip->wt); */ return 0; } @@ -2071,7 +2071,7 @@ static void tvaudio_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct CHIPSTATE *chip = to_state(sd); - del_timer_sync(&chip->wt); + timer_delete_sync(&chip->wt); if (chip->thread) { /* shutdown async thread */ kthread_stop(chip->thread); diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c index d77468c8587b..5b0479f3a3c0 100644 --- a/drivers/media/i2c/vgxy61.c +++ b/drivers/media/i2c/vgxy61.c @@ -892,8 +892,8 @@ static u32 vgxy61_get_expo_long_max(struct vgxy61_dev *sensor, third_rot_max_expo = (sensor->frame_length / 71) * short_expo_ratio; /* Take the minimum from all rules */ - return min(min(first_rot_max_expo, second_rot_max_expo), - third_rot_max_expo); + return min3(first_rot_max_expo, second_rot_max_expo, + third_rot_max_expo); } static int vgxy61_update_exposure(struct vgxy61_dev *sensor, u16 new_expo_long, diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 036a6375627a..0dd991d70d53 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -264,18 +264,8 @@ static int amg88xx_set_power(struct video_i2c_data *data, bool on) #if IS_REACHABLE(CONFIG_HWMON) -static const u32 amg88xx_temp_config[] = { - HWMON_T_INPUT, - 0 -}; - -static const struct hwmon_channel_info amg88xx_temp = { - .type = hwmon_temp, - .config = amg88xx_temp_config, -}; - static const struct hwmon_channel_info * const amg88xx_info[] = { - &amg88xx_temp, + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), NULL }; |