diff options
Diffstat (limited to 'drivers/media')
85 files changed, 5295 insertions, 670 deletions
diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h index f423db8855d9..fb101f15865c 100644 --- a/drivers/media/cec/core/cec-pin-priv.h +++ b/drivers/media/cec/core/cec-pin-priv.h @@ -209,8 +209,8 @@ struct cec_pin { u32 work_pin_events_dropped_cnt; ktime_t timer_ts; u32 timer_cnt; - u32 timer_100ms_overruns; - u32 timer_300ms_overruns; + u32 timer_100us_overruns; + u32 timer_300us_overruns; u32 timer_max_overrun; u32 timer_sum_overrun; diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index f006bd8eec63..8c613aa649c6 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -854,9 +854,9 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) if (delta > 100 && pin->state != CEC_ST_IDLE) { /* Keep track of timer overruns */ pin->timer_sum_overrun += delta; - pin->timer_100ms_overruns++; + pin->timer_100us_overruns++; if (delta > 300) - pin->timer_300ms_overruns++; + pin->timer_300us_overruns++; if (delta > pin->timer_max_overrun) pin->timer_max_overrun = delta; } @@ -1207,15 +1207,15 @@ static void cec_pin_adap_status(struct cec_adapter *adap, seq_printf(file, "cec pin events dropped: %u\n", pin->work_pin_events_dropped_cnt); seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed); - if (pin->timer_100ms_overruns) { - seq_printf(file, "timer overruns > 100ms: %u of %u\n", - pin->timer_100ms_overruns, pin->timer_cnt); - seq_printf(file, "timer overruns > 300ms: %u of %u\n", - pin->timer_300ms_overruns, pin->timer_cnt); + if (pin->timer_100us_overruns) { + seq_printf(file, "timer overruns > 100us: %u of %u\n", + pin->timer_100us_overruns, pin->timer_cnt); + seq_printf(file, "timer overruns > 300us: %u of %u\n", + pin->timer_300us_overruns, pin->timer_cnt); seq_printf(file, "max timer overrun: %u usecs\n", pin->timer_max_overrun); seq_printf(file, "avg timer overrun: %u usecs\n", - pin->timer_sum_overrun / pin->timer_100ms_overruns); + pin->timer_sum_overrun / pin->timer_100us_overruns); } if (pin->rx_start_bit_low_too_short_cnt) seq_printf(file, @@ -1245,8 +1245,8 @@ static void cec_pin_adap_status(struct cec_adapter *adap, seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt); pin->work_pin_events_dropped_cnt = 0; pin->timer_cnt = 0; - pin->timer_100ms_overruns = 0; - pin->timer_300ms_overruns = 0; + pin->timer_100us_overruns = 0; + pin->timer_300us_overruns = 0; pin->timer_max_overrun = 0; pin->timer_sum_overrun = 0; pin->rx_start_bit_low_too_short_cnt = 0; diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index ea4b1ebfca99..0ffd89712536 100644 --- a/drivers/media/cec/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c @@ -305,14 +305,16 @@ static int stm32_cec_probe(struct platform_device *pdev) cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec"); if (IS_ERR(cec->clk_hdmi_cec) && - PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) - return -EPROBE_DEFER; + PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_unprepare_cec_clk; + } if (!IS_ERR(cec->clk_hdmi_cec)) { ret = clk_prepare(cec->clk_hdmi_cec); if (ret) { dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n"); - return ret; + goto err_unprepare_cec_clk; } } @@ -324,19 +326,27 @@ static int stm32_cec_probe(struct platform_device *pdev) CEC_NAME, caps, CEC_MAX_LOG_ADDRS); ret = PTR_ERR_OR_ZERO(cec->adap); if (ret) - return ret; + goto err_unprepare_hdmi_cec_clk; ret = cec_register_adapter(cec->adap, &pdev->dev); - if (ret) { - cec_delete_adapter(cec->adap); - return ret; - } + if (ret) + goto err_delete_adapter; cec_hw_init(cec); platform_set_drvdata(pdev, cec); return 0; + +err_delete_adapter: + cec_delete_adapter(cec->adap); + +err_unprepare_hdmi_cec_clk: + clk_unprepare(cec->clk_hdmi_cec); + +err_unprepare_cec_clk: + clk_unprepare(cec->clk_cec); + return ret; } static int stm32_cec_remove(struct platform_device *pdev) diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index 1ac0c70a5981..5e907395ca2e 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -366,7 +366,11 @@ static int tegra_cec_probe(struct platform_device *pdev) return -ENOENT; } - clk_prepare_enable(cec->clk); + ret = clk_prepare_enable(cec->clk); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare clock for CEC\n"); + return ret; + } /* set context info. */ cec->dev = &pdev->dev; @@ -446,9 +450,7 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); - clk_prepare_enable(cec->clk); - - return 0; + return clk_prepare_enable(cec->clk); } #endif diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index 9fccc906d85a..ac6e47d81b9e 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -1172,7 +1172,6 @@ struct dvb_frontend *cx24117_attach(const struct cx24117_config *config, "%s: Error attaching frontend %d\n", KBUILD_MODNAME, demod); goto error1; - break; case 1: /* new priv instance */ priv->i2c = i2c; diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 082796534b0a..bb02354a48b8 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2107,32 +2107,55 @@ static void dib8000_load_ana_fe_coefs(struct dib8000_state *state, const s16 *an dib8000_write_word(state, 117 + mode, ana_fe[mode]); } -static const u16 lut_prbs_2k[14] = { - 0, 0x423, 0x009, 0x5C7, 0x7A6, 0x3D8, 0x527, 0x7FF, 0x79B, 0x3D6, 0x3A2, 0x53B, 0x2F4, 0x213 +static const u16 lut_prbs_2k[13] = { + 0x423, 0x009, 0x5C7, + 0x7A6, 0x3D8, 0x527, + 0x7FF, 0x79B, 0x3D6, + 0x3A2, 0x53B, 0x2F4, + 0x213 }; -static const u16 lut_prbs_4k[14] = { - 0, 0x208, 0x0C3, 0x7B9, 0x423, 0x5C7, 0x3D8, 0x7FF, 0x3D6, 0x53B, 0x213, 0x029, 0x0D0, 0x48E + +static const u16 lut_prbs_4k[13] = { + 0x208, 0x0C3, 0x7B9, + 0x423, 0x5C7, 0x3D8, + 0x7FF, 0x3D6, 0x53B, + 0x213, 0x029, 0x0D0, + 0x48E }; -static const u16 lut_prbs_8k[14] = { - 0, 0x740, 0x069, 0x7DD, 0x208, 0x7B9, 0x5C7, 0x7FF, 0x53B, 0x029, 0x48E, 0x4C4, 0x367, 0x684 + +static const u16 lut_prbs_8k[13] = { + 0x740, 0x069, 0x7DD, + 0x208, 0x7B9, 0x5C7, + 0x7FF, 0x53B, 0x029, + 0x48E, 0x4C4, 0x367, + 0x684 }; static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel) { int sub_channel_prbs_group = 0; + int prbs_group; - sub_channel_prbs_group = (subchannel / 3) + 1; - dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n", sub_channel_prbs_group, subchannel, lut_prbs_8k[sub_channel_prbs_group]); + sub_channel_prbs_group = subchannel / 3; + if (sub_channel_prbs_group >= ARRAY_SIZE(lut_prbs_2k)) + return 0; switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: - return lut_prbs_2k[sub_channel_prbs_group]; + prbs_group = lut_prbs_2k[sub_channel_prbs_group]; + break; case TRANSMISSION_MODE_4K: - return lut_prbs_4k[sub_channel_prbs_group]; + prbs_group = lut_prbs_4k[sub_channel_prbs_group]; + break; default: case TRANSMISSION_MODE_8K: - return lut_prbs_8k[sub_channel_prbs_group]; + prbs_group = lut_prbs_8k[sub_channel_prbs_group]; } + + dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n", + sub_channel_prbs_group, subchannel, prbs_group); + + return prbs_group; } static void dib8000_set_13seg_channel(struct dib8000_state *state) @@ -2409,10 +2432,8 @@ static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq /* TSB or ISDBT ? apply it now */ if (c->isdbt_sb_mode) { dib8000_set_sb_channel(state); - if (c->isdbt_sb_subchannel < 14) - init_prbs = dib8000_get_init_prbs(state, c->isdbt_sb_subchannel); - else - init_prbs = 0; + init_prbs = dib8000_get_init_prbs(state, + c->isdbt_sb_subchannel); } else { dib8000_set_13seg_channel(state); init_prbs = 0xfff; @@ -3004,6 +3025,7 @@ static int dib8000_tune(struct dvb_frontend *fe) unsigned long *timeout = &state->timeout; unsigned long now = jiffies; + u16 init_prbs; #ifdef DIB8000_AGC_FREEZE u16 agc1, agc2; #endif @@ -3302,8 +3324,10 @@ static int dib8000_tune(struct dvb_frontend *fe) break; case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */ - if (state->subchannel <= 41) { - dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel)); + init_prbs = dib8000_get_init_prbs(state, state->subchannel); + + if (init_prbs) { + dib8000_set_subchannel_prbs(state, init_prbs); *tune_state = CT_DEMOD_STEP_9; } else { *tune_state = CT_DEMOD_STOP; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 588f8eb95984..08feb3e8c1bf 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -842,6 +842,20 @@ config VIDEO_IMX334 To compile this driver as a module, choose M here: the module will be called imx334. +config VIDEO_IMX335 + tristate "Sony IMX335 sensor support" + depends on OF_GPIO + depends on I2C && VIDEO_V4L2 + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX335 camera. + + To compile this driver as a module, choose M here: the + module will be called imx335. + config VIDEO_IMX355 tristate "Sony IMX355 sensor support" depends on I2C && VIDEO_V4L2 @@ -854,6 +868,20 @@ config VIDEO_IMX355 To compile this driver as a module, choose M here: the module will be called imx355. +config VIDEO_IMX412 + tristate "Sony IMX412 sensor support" + depends on OF_GPIO + depends on I2C && VIDEO_V4L2 + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX412 camera. + + To compile this driver as a module, choose M here: the + module will be called imx412. + config VIDEO_OV02A10 tristate "OmniVision OV02A10 sensor support" depends on VIDEO_V4L2 && I2C @@ -1103,6 +1131,20 @@ config VIDEO_OV8865 To compile this driver as a module, choose M here: the module will be called ov8865. +config VIDEO_OV9282 + tristate "OmniVision OV9282 sensor support" + depends on OF_GPIO + depends on I2C && VIDEO_V4L2 + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9282 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called ov9282. + config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 1168fa6b84ed..83268f20aa3a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV8865) += ov8865.o +obj-$(CONFIG_VIDEO_OV9282) += ov9282.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV9734) += ov9734.o @@ -124,11 +125,12 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX334) += imx334.o +obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o - obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index fa5bc55bc944..d9a99fcfacb1 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -94,6 +94,7 @@ #define ADV7180_REG_SHAP_FILTER_CTL_1 0x0017 #define ADV7180_REG_CTRL_2 0x001d #define ADV7180_REG_VSYNC_FIELD_CTL_1 0x0031 +#define ADV7180_VSYNC_FIELD_CTL_1_NEWAV 0x12 #define ADV7180_REG_MANUAL_WIN_CTL_1 0x003d #define ADV7180_REG_MANUAL_WIN_CTL_2 0x003e #define ADV7180_REG_MANUAL_WIN_CTL_3 0x003f @@ -205,6 +206,7 @@ struct adv7180_state { struct mutex mutex; /* mutual excl. when accessing chip */ int irq; struct gpio_desc *pwdn_gpio; + struct gpio_desc *rst_gpio; v4l2_std_id curr_norm; bool powered; bool streaming; @@ -216,6 +218,7 @@ struct adv7180_state { struct i2c_client *vpp_client; const struct adv7180_chip_info *chip_info; enum v4l2_field field; + bool force_bt656_4; }; #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ struct adv7180_state, \ @@ -484,6 +487,19 @@ static void adv7180_set_power_pin(struct adv7180_state *state, bool on) } } +static void adv7180_set_reset_pin(struct adv7180_state *state, bool on) +{ + if (!state->rst_gpio) + return; + + if (on) { + gpiod_set_value_cansleep(state->rst_gpio, 1); + } else { + gpiod_set_value_cansleep(state->rst_gpio, 0); + usleep_range(5000, 10000); + } +} + static int adv7180_set_power(struct adv7180_state *state, bool on) { u8 val; @@ -963,10 +979,26 @@ static int adv7182_init(struct adv7180_state *state) adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57); adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0); } else { - if (state->chip_info->flags & ADV7180_FLAG_V2) - adv7180_write(state, - ADV7180_REG_EXTENDED_OUTPUT_CONTROL, - 0x17); + if (state->chip_info->flags & ADV7180_FLAG_V2) { + if (state->force_bt656_4) { + /* ITU-R BT.656-4 compatible */ + adv7180_write(state, + ADV7180_REG_EXTENDED_OUTPUT_CONTROL, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + /* Manually set NEWAVMODE */ + adv7180_write(state, + ADV7180_REG_VSYNC_FIELD_CTL_1, + ADV7180_VSYNC_FIELD_CTL_1_NEWAV); + /* Manually set V bit end position in NTSC mode */ + adv7180_write(state, + ADV7180_REG_NTSC_V_BIT_END, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + } else { + adv7180_write(state, + ADV7180_REG_EXTENDED_OUTPUT_CONTROL, + 0x17); + } + } else adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, @@ -1263,6 +1295,7 @@ static int init_device(struct adv7180_state *state) mutex_lock(&state->mutex); adv7180_set_power_pin(state, true); + adv7180_set_reset_pin(state, false); adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); usleep_range(5000, 10000); @@ -1314,6 +1347,7 @@ out_unlock: static int adv7180_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device_node *np = client->dev.of_node; struct adv7180_state *state; struct v4l2_subdev *sd; int ret; @@ -1338,6 +1372,17 @@ static int adv7180_probe(struct i2c_client *client, return ret; } + state->rst_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(state->rst_gpio)) { + ret = PTR_ERR(state->rst_gpio); + v4l_err(client, "request for reset pin failed: %d\n", ret); + return ret; + } + + if (of_property_read_bool(np, "adv,force-bt656-4")) + state->force_bt656_4 = true; + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { state->csi_client = i2c_new_dummy_device(client->adapter, ADV7180_DEFAULT_CSI_I2C_ADDR); @@ -1392,11 +1437,19 @@ static int adv7180_probe(struct i2c_client *client, if (ret) goto err_free_irq; - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); + mutex_lock(&state->mutex); + ret = adv7180_read(state, ADV7180_REG_IDENT); + mutex_unlock(&state->mutex); + if (ret < 0) + goto err_v4l2_async_unregister; + + v4l_info(client, "chip id 0x%x found @ 0x%02x (%s)\n", + ret, client->addr, client->adapter->name); return 0; +err_v4l2_async_unregister: + v4l2_async_unregister_subdev(sd); err_free_irq: if (state->irq > 0) free_irq(client->irq, state); @@ -1428,6 +1481,7 @@ static int adv7180_remove(struct i2c_client *client) i2c_unregister_device(state->vpp_client); i2c_unregister_device(state->csi_client); + adv7180_set_reset_pin(state, true); adv7180_set_power_pin(state, false); mutex_destroy(&state->mutex); diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index a9403a227c6b..5363f3bcafe3 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1943,6 +1943,51 @@ static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) return rval; } +static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + if (flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP) { + switch (sensor->hwcfg.csi_signalling_mode) { + case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY: + if (!(CCS_LIM(sensor, PHY_CTRL_CAPABILITY_2) & + CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_DPHY)) + return -EACCES; + break; + case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY: + if (!(CCS_LIM(sensor, PHY_CTRL_CAPABILITY_2) & + CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_CPHY)) + return -EACCES; + break; + default: + return -EACCES; + } + } + + rval = ccs_pm_get_init(sensor); + if (rval) + return rval; + + if (flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP) { + rval = ccs_write(sensor, MANUAL_LP_CTRL, + CCS_MANUAL_LP_CTRL_ENABLE); + if (rval) + pm_runtime_put(&client->dev); + } + + return rval; +} + +static int ccs_post_streamoff(struct v4l2_subdev *subdev) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + return pm_runtime_put(&client->dev); +} + static int ccs_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -2673,8 +2718,7 @@ static int ccs_get_skip_top_lines(struct v4l2_subdev *subdev, u32 *lines) */ static ssize_t -ccs_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, - char *buf) +nvm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); struct i2c_client *client = v4l2_get_subdevdata(subdev); @@ -2704,11 +2748,10 @@ ccs_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, */ return rval; } -static DEVICE_ATTR(nvm, S_IRUGO, ccs_sysfs_nvm_read, NULL); +static DEVICE_ATTR_RO(nvm); static ssize_t -ccs_sysfs_ident_read(struct device *dev, struct device_attribute *attr, - char *buf) +ident_show(struct device *dev, struct device_attribute *attr, char *buf) { struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); struct ccs_sensor *sensor = to_ccs_sensor(subdev); @@ -2723,8 +2766,7 @@ ccs_sysfs_ident_read(struct device *dev, struct device_attribute *attr, minfo->smia_manufacturer_id, minfo->model_id, minfo->revision_number) + 1; } - -static DEVICE_ATTR(ident, S_IRUGO, ccs_sysfs_ident_read, NULL); +static DEVICE_ATTR_RO(ident); /* ----------------------------------------------------------------------------- * V4L2 subdev core operations @@ -3058,6 +3100,8 @@ static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_video_ops ccs_video_ops = { .s_stream = ccs_set_stream, + .pre_streamon = ccs_pre_streamon, + .post_streamoff = ccs_post_streamoff, }; static const struct v4l2_subdev_pad_ops ccs_pad_ops = { diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index c7b91c0c03b5..873d614339bb 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1237,8 +1237,7 @@ out_poweroff: * sysfs attributes */ static ssize_t -et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr, - char *buf) +priv_mem_show(struct device *dev, struct device_attribute *attr, char *buf) { struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); @@ -1251,7 +1250,7 @@ et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr, return ET8EK8_PRIV_MEM_SIZE; } -static DEVICE_ATTR(priv_mem, 0444, et8ek8_priv_mem_read, NULL); +static DEVICE_ATTR_RO(priv_mem); /* -------------------------------------------------------------------------- * V4L2 subdev core operations diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 7ab9e5f9f267..81cdf37216ca 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -23,7 +23,7 @@ #define IMX258_CHIP_ID 0x0258 /* V_TIMING internal */ -#define IMX258_VTS_30FPS 0x0c98 +#define IMX258_VTS_30FPS 0x0c50 #define IMX258_VTS_30FPS_2K 0x0638 #define IMX258_VTS_30FPS_VGA 0x034c #define IMX258_VTS_MAX 0xffff @@ -47,7 +47,7 @@ /* Analog gain control */ #define IMX258_REG_ANALOG_GAIN 0x0204 #define IMX258_ANA_GAIN_MIN 0 -#define IMX258_ANA_GAIN_MAX 0x1fff +#define IMX258_ANA_GAIN_MAX 480 #define IMX258_ANA_GAIN_STEP 1 #define IMX258_ANA_GAIN_DEFAULT 0x0 diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c new file mode 100644 index 000000000000..410d6b86feb5 --- /dev/null +++ b/drivers/media/i2c/imx335.c @@ -0,0 +1,1129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sony imx335 Camera Sensor Driver + * + * Copyright (C) 2021 Intel Corporation + */ +#include <asm/unaligned.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Streaming Mode */ +#define IMX335_REG_MODE_SELECT 0x3000 +#define IMX335_MODE_STANDBY 0x01 +#define IMX335_MODE_STREAMING 0x00 + +/* Lines per frame */ +#define IMX335_REG_LPFR 0x3030 + +/* Chip ID */ +#define IMX335_REG_ID 0x3912 +#define IMX335_ID 0x00 + +/* Exposure control */ +#define IMX335_REG_SHUTTER 0x3058 +#define IMX335_EXPOSURE_MIN 1 +#define IMX335_EXPOSURE_OFFSET 9 +#define IMX335_EXPOSURE_STEP 1 +#define IMX335_EXPOSURE_DEFAULT 0x0648 + +/* Analog gain control */ +#define IMX335_REG_AGAIN 0x30e8 +#define IMX335_AGAIN_MIN 0 +#define IMX335_AGAIN_MAX 240 +#define IMX335_AGAIN_STEP 1 +#define IMX335_AGAIN_DEFAULT 0 + +/* Group hold register */ +#define IMX335_REG_HOLD 0x3001 + +/* Input clock rate */ +#define IMX335_INCLK_RATE 24000000 + +/* CSI2 HW configuration */ +#define IMX335_LINK_FREQ 594000000 +#define IMX335_NUM_DATA_LANES 4 + +#define IMX335_REG_MIN 0x00 +#define IMX335_REG_MAX 0xfffff + +/** + * struct imx335_reg - imx335 sensor register + * @address: Register address + * @val: Register value + */ +struct imx335_reg { + u16 address; + u8 val; +}; + +/** + * struct imx335_reg_list - imx335 sensor register list + * @num_of_regs: Number of registers in the list + * @regs: Pointer to register list + */ +struct imx335_reg_list { + u32 num_of_regs; + const struct imx335_reg *regs; +}; + +/** + * struct imx335_mode - imx335 sensor mode structure + * @width: Frame width + * @height: Frame height + * @code: Format code + * @hblank: Horizontal blanking in lines + * @vblank: Vertical blanking in lines + * @vblank_min: Minimum vertical blanking in lines + * @vblank_max: Maximum vertical blanking in lines + * @pclk: Sensor pixel clock + * @link_freq_idx: Link frequency index + * @reg_list: Register list for sensor mode + */ +struct imx335_mode { + u32 width; + u32 height; + u32 code; + u32 hblank; + u32 vblank; + u32 vblank_min; + u32 vblank_max; + u64 pclk; + u32 link_freq_idx; + struct imx335_reg_list reg_list; +}; + +/** + * struct imx335 - imx335 sensor device structure + * @dev: Pointer to generic device + * @client: Pointer to i2c client + * @sd: V4L2 sub-device + * @pad: Media pad. Only one pad supported + * @reset_gpio: Sensor reset gpio + * @inclk: Sensor input clock + * @ctrl_handler: V4L2 control handler + * @link_freq_ctrl: Pointer to link frequency control + * @pclk_ctrl: Pointer to pixel clock control + * @hblank_ctrl: Pointer to horizontal blanking control + * @vblank_ctrl: Pointer to vertical blanking control + * @exp_ctrl: Pointer to exposure control + * @again_ctrl: Pointer to analog gain control + * @vblank: Vertical blanking in lines + * @cur_mode: Pointer to current selected sensor mode + * @mutex: Mutex for serializing sensor controls + * @streaming: Flag indicating streaming state + */ +struct imx335 { + struct device *dev; + struct i2c_client *client; + struct v4l2_subdev sd; + struct media_pad pad; + struct gpio_desc *reset_gpio; + struct clk *inclk; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq_ctrl; + struct v4l2_ctrl *pclk_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *exp_ctrl; + struct v4l2_ctrl *again_ctrl; + }; + u32 vblank; + const struct imx335_mode *cur_mode; + struct mutex mutex; + bool streaming; +}; + +static const s64 link_freq[] = { + IMX335_LINK_FREQ, +}; + +/* Sensor mode registers */ +static const struct imx335_reg mode_2592x1940_regs[] = { + {0x3000, 0x01}, + {0x3002, 0x00}, + {0x300c, 0x3b}, + {0x300d, 0x2a}, + {0x3018, 0x04}, + {0x302c, 0x3c}, + {0x302e, 0x20}, + {0x3056, 0x94}, + {0x3074, 0xc8}, + {0x3076, 0x28}, + {0x304c, 0x00}, + {0x314c, 0xc6}, + {0x315a, 0x02}, + {0x3168, 0xa0}, + {0x316a, 0x7e}, + {0x31a1, 0x00}, + {0x3288, 0x21}, + {0x328a, 0x02}, + {0x3414, 0x05}, + {0x3416, 0x18}, + {0x3648, 0x01}, + {0x364a, 0x04}, + {0x364c, 0x04}, + {0x3678, 0x01}, + {0x367c, 0x31}, + {0x367e, 0x31}, + {0x3706, 0x10}, + {0x3708, 0x03}, + {0x3714, 0x02}, + {0x3715, 0x02}, + {0x3716, 0x01}, + {0x3717, 0x03}, + {0x371c, 0x3d}, + {0x371d, 0x3f}, + {0x372c, 0x00}, + {0x372d, 0x00}, + {0x372e, 0x46}, + {0x372f, 0x00}, + {0x3730, 0x89}, + {0x3731, 0x00}, + {0x3732, 0x08}, + {0x3733, 0x01}, + {0x3734, 0xfe}, + {0x3735, 0x05}, + {0x3740, 0x02}, + {0x375d, 0x00}, + {0x375e, 0x00}, + {0x375f, 0x11}, + {0x3760, 0x01}, + {0x3768, 0x1b}, + {0x3769, 0x1b}, + {0x376a, 0x1b}, + {0x376b, 0x1b}, + {0x376c, 0x1a}, + {0x376d, 0x17}, + {0x376e, 0x0f}, + {0x3776, 0x00}, + {0x3777, 0x00}, + {0x3778, 0x46}, + {0x3779, 0x00}, + {0x377a, 0x89}, + {0x377b, 0x00}, + {0x377c, 0x08}, + {0x377d, 0x01}, + {0x377e, 0x23}, + {0x377f, 0x02}, + {0x3780, 0xd9}, + {0x3781, 0x03}, + {0x3782, 0xf5}, + {0x3783, 0x06}, + {0x3784, 0xa5}, + {0x3788, 0x0f}, + {0x378a, 0xd9}, + {0x378b, 0x03}, + {0x378c, 0xeb}, + {0x378d, 0x05}, + {0x378e, 0x87}, + {0x378f, 0x06}, + {0x3790, 0xf5}, + {0x3792, 0x43}, + {0x3794, 0x7a}, + {0x3796, 0xa1}, + {0x37b0, 0x36}, + {0x3a00, 0x01}, +}; + +/* Supported sensor mode configurations */ +static const struct imx335_mode supported_mode = { + .width = 2592, + .height = 1940, + .hblank = 342, + .vblank = 2560, + .vblank_min = 2560, + .vblank_max = 133060, + .pclk = 396000000, + .link_freq_idx = 0, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs), + .regs = mode_2592x1940_regs, + }, +}; + +/** + * to_imx335() - imx335 V4L2 sub-device to imx335 device. + * @subdev: pointer to imx335 V4L2 sub-device + * + * Return: pointer to imx335 device + */ +static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct imx335, sd); +} + +/** + * imx335_read_reg() - Read registers. + * @imx335: pointer to imx335 device + * @reg: register address + * @len: length of bytes to read. Max supported bytes is 4 + * @val: pointer to register value to be filled. + * + * Big endian register addresses with little endian values. + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd); + struct i2c_msg msgs[2] = {0}; + u8 addr_buf[2] = {0}; + u8 data_buf[4] = {0}; + int ret; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = data_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_le32(data_buf); + + return 0; +} + +/** + * imx335_write_reg() - Write register + * @imx335: pointer to imx335 device + * @reg: register address + * @len: length of bytes. Max supported bytes is 4 + * @val: register value + * + * Big endian register addresses with little endian values. + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd); + u8 buf[6] = {0}; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_le32(val, buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/** + * imx335_write_regs() - Write a list of registers + * @imx335: pointer to imx335 device + * @regs: list of registers to be written + * @len: length of registers array + * + * Return: 0 if successful. error code otherwise. + */ +static int imx335_write_regs(struct imx335 *imx335, + const struct imx335_reg *regs, u32 len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/** + * imx335_update_controls() - Update control ranges based on streaming mode + * @imx335: pointer to imx335 device + * @mode: pointer to imx335_mode sensor mode + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_update_controls(struct imx335 *imx335, + const struct imx335_mode *mode) +{ + int ret; + + ret = __v4l2_ctrl_s_ctrl(imx335->link_freq_ctrl, mode->link_freq_idx); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(imx335->hblank_ctrl, mode->hblank); + if (ret) + return ret; + + return __v4l2_ctrl_modify_range(imx335->vblank_ctrl, mode->vblank_min, + mode->vblank_max, 1, mode->vblank); +} + +/** + * imx335_update_exp_gain() - Set updated exposure and gain + * @imx335: pointer to imx335 device + * @exposure: updated exposure value + * @gain: updated analog gain value + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) +{ + u32 lpfr, shutter; + int ret; + + lpfr = imx335->vblank + imx335->cur_mode->height; + shutter = lpfr - exposure; + + dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u", + exposure, gain, shutter, lpfr); + + ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1); + if (ret) + return ret; + + ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr); + if (ret) + goto error_release_group_hold; + + ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter); + if (ret) + goto error_release_group_hold; + + ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain); + +error_release_group_hold: + imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0); + + return ret; +} + +/** + * imx335_set_ctrl() - Set subdevice control + * @ctrl: pointer to v4l2_ctrl structure + * + * Supported controls: + * - V4L2_CID_VBLANK + * - cluster controls: + * - V4L2_CID_ANALOGUE_GAIN + * - V4L2_CID_EXPOSURE + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx335 *imx335 = + container_of(ctrl->handler, struct imx335, ctrl_handler); + u32 analog_gain; + u32 exposure; + int ret; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + imx335->vblank = imx335->vblank_ctrl->val; + + dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u", + imx335->vblank, + imx335->vblank + imx335->cur_mode->height); + + ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl, + IMX335_EXPOSURE_MIN, + imx335->vblank + + imx335->cur_mode->height - + IMX335_EXPOSURE_OFFSET, + 1, IMX335_EXPOSURE_DEFAULT); + break; + case V4L2_CID_EXPOSURE: + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx335->dev)) + return 0; + + exposure = ctrl->val; + analog_gain = imx335->again_ctrl->val; + + dev_dbg(imx335->dev, "Received exp %u, analog gain %u", + exposure, analog_gain); + + ret = imx335_update_exp_gain(imx335, exposure, analog_gain); + + pm_runtime_put(imx335->dev); + + break; + default: + dev_err(imx335->dev, "Invalid control %d", ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +/* V4l2 subdevice control ops*/ +static const struct v4l2_ctrl_ops imx335_ctrl_ops = { + .s_ctrl = imx335_set_ctrl, +}; + +/** + * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @code: V4L2 sub-device code enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = supported_mode.code; + + return 0; +} + +/** + * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fsize: V4L2 sub-device size enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fsize) +{ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->code != supported_mode.code) + return -EINVAL; + + fsize->min_width = supported_mode.width; + fsize->max_width = fsize->min_width; + fsize->min_height = supported_mode.height; + fsize->max_height = fsize->min_height; + + return 0; +} + +/** + * imx335_fill_pad_format() - Fill subdevice pad format + * from selected sensor mode + * @imx335: pointer to imx335 device + * @mode: pointer to imx335_mode sensor mode + * @fmt: V4L2 sub-device format need to be filled + */ +static void imx335_fill_pad_format(struct imx335 *imx335, + const struct imx335_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->code; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; +} + +/** + * imx335_get_pad_format() - Get subdevice pad format + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx335 *imx335 = to_imx335(sd); + + mutex_lock(&imx335->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *framefmt; + } else { + imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); + } + + mutex_unlock(&imx335->mutex); + + return 0; +} + +/** + * imx335_set_pad_format() - Set subdevice pad format + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx335 *imx335 = to_imx335(sd); + const struct imx335_mode *mode; + int ret = 0; + + mutex_lock(&imx335->mutex); + + mode = &supported_mode; + imx335_fill_pad_format(imx335, mode, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *framefmt = fmt->format; + } else { + ret = imx335_update_controls(imx335, mode); + if (!ret) + imx335->cur_mode = mode; + } + + mutex_unlock(&imx335->mutex); + + return ret; +} + +/** + * imx335_init_pad_cfg() - Initialize sub-device pad configuration + * @sd: pointer to imx335 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_init_pad_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct imx335 *imx335 = to_imx335(sd); + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + imx335_fill_pad_format(imx335, &supported_mode, &fmt); + + return imx335_set_pad_format(sd, sd_state, &fmt); +} + +/** + * imx335_start_streaming() - Start sensor stream + * @imx335: pointer to imx335 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_start_streaming(struct imx335 *imx335) +{ + const struct imx335_reg_list *reg_list; + int ret; + + /* Write sensor mode registers */ + reg_list = &imx335->cur_mode->reg_list; + ret = imx335_write_regs(imx335, reg_list->regs, + reg_list->num_of_regs); + if (ret) { + dev_err(imx335->dev, "fail to write initial registers"); + return ret; + } + + /* Setup handler will write actual exposure and gain */ + ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); + if (ret) { + dev_err(imx335->dev, "fail to setup handler"); + return ret; + } + + /* Start streaming */ + ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, + 1, IMX335_MODE_STREAMING); + if (ret) { + dev_err(imx335->dev, "fail to start streaming"); + return ret; + } + + /* Initial regulator stabilization period */ + usleep_range(18000, 20000); + + return 0; +} + +/** + * imx335_stop_streaming() - Stop sensor stream + * @imx335: pointer to imx335 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_stop_streaming(struct imx335 *imx335) +{ + return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT, + 1, IMX335_MODE_STANDBY); +} + +/** + * imx335_set_stream() - Enable sensor streaming + * @sd: pointer to imx335 subdevice + * @enable: set to enable sensor streaming + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx335 *imx335 = to_imx335(sd); + int ret; + + mutex_lock(&imx335->mutex); + + if (imx335->streaming == enable) { + mutex_unlock(&imx335->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(imx335->dev); + if (ret) + goto error_unlock; + + ret = imx335_start_streaming(imx335); + if (ret) + goto error_power_off; + } else { + imx335_stop_streaming(imx335); + pm_runtime_put(imx335->dev); + } + + imx335->streaming = enable; + + mutex_unlock(&imx335->mutex); + + return 0; + +error_power_off: + pm_runtime_put(imx335->dev); +error_unlock: + mutex_unlock(&imx335->mutex); + + return ret; +} + +/** + * imx335_detect() - Detect imx335 sensor + * @imx335: pointer to imx335 device + * + * Return: 0 if successful, -EIO if sensor id does not match + */ +static int imx335_detect(struct imx335 *imx335) +{ + int ret; + u32 val; + + ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val); + if (ret) + return ret; + + if (val != IMX335_ID) { + dev_err(imx335->dev, "chip id mismatch: %x!=%x", + IMX335_ID, val); + return -ENXIO; + } + + return 0; +} + +/** + * imx335_parse_hw_config() - Parse HW configuration and check if supported + * @imx335: pointer to imx335 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_parse_hw_config(struct imx335 *imx335) +{ + struct fwnode_handle *fwnode = dev_fwnode(imx335->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + unsigned long rate; + unsigned int i; + int ret; + + if (!fwnode) + return -ENXIO; + + /* Request optional reset pin */ + imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(imx335->reset_gpio)) { + dev_err(imx335->dev, "failed to get reset gpio %ld", + PTR_ERR(imx335->reset_gpio)); + return PTR_ERR(imx335->reset_gpio); + } + + /* Get sensor input clock */ + imx335->inclk = devm_clk_get(imx335->dev, NULL); + if (IS_ERR(imx335->inclk)) { + dev_err(imx335->dev, "could not get inclk"); + return PTR_ERR(imx335->inclk); + } + + rate = clk_get_rate(imx335->inclk); + if (rate != IMX335_INCLK_RATE) { + dev_err(imx335->dev, "inclk frequency mismatch"); + return -EINVAL; + } + + 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 != IMX335_NUM_DATA_LANES) { + dev_err(imx335->dev, + "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(imx335->dev, "no link frequencies defined"); + ret = -EINVAL; + goto done_endpoint_free; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == IMX335_LINK_FREQ) + goto done_endpoint_free; + + ret = -EINVAL; + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +/* V4l2 subdevice ops */ +static const struct v4l2_subdev_video_ops imx335_video_ops = { + .s_stream = imx335_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx335_pad_ops = { + .init_cfg = imx335_init_pad_cfg, + .enum_mbus_code = imx335_enum_mbus_code, + .enum_frame_size = imx335_enum_frame_size, + .get_fmt = imx335_get_pad_format, + .set_fmt = imx335_set_pad_format, +}; + +static const struct v4l2_subdev_ops imx335_subdev_ops = { + .video = &imx335_video_ops, + .pad = &imx335_pad_ops, +}; + +/** + * imx335_power_on() - Sensor power on sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx335 *imx335 = to_imx335(sd); + int ret; + + gpiod_set_value_cansleep(imx335->reset_gpio, 1); + + ret = clk_prepare_enable(imx335->inclk); + if (ret) { + dev_err(imx335->dev, "fail to enable inclk"); + goto error_reset; + } + + usleep_range(20, 22); + + return 0; + +error_reset: + gpiod_set_value_cansleep(imx335->reset_gpio, 0); + + return ret; +} + +/** + * imx335_power_off() - Sensor power off sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx335 *imx335 = to_imx335(sd); + + gpiod_set_value_cansleep(imx335->reset_gpio, 0); + + clk_disable_unprepare(imx335->inclk); + + return 0; +} + +/** + * imx335_init_controls() - Initialize sensor subdevice controls + * @imx335: pointer to imx335 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_init_controls(struct imx335 *imx335) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; + const struct imx335_mode *mode = imx335->cur_mode; + u32 lpfr; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + if (ret) + return ret; + + /* Serialize controls with sensor device */ + ctrl_hdlr->lock = &imx335->mutex; + + /* Initialize exposure and gain */ + lpfr = mode->vblank + mode->height; + imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX335_EXPOSURE_MIN, + lpfr - IMX335_EXPOSURE_OFFSET, + IMX335_EXPOSURE_STEP, + IMX335_EXPOSURE_DEFAULT); + + imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + IMX335_AGAIN_MIN, + IMX335_AGAIN_MAX, + IMX335_AGAIN_STEP, + IMX335_AGAIN_DEFAULT); + + v4l2_ctrl_cluster(2, &imx335->exp_ctrl); + + imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_VBLANK, + mode->vblank_min, + mode->vblank_max, + 1, mode->vblank); + + /* Read only controls */ + imx335->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_PIXEL_RATE, + mode->pclk, mode->pclk, + 1, mode->pclk); + + imx335->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - + 1, + mode->link_freq_idx, + link_freq); + if (imx335->link_freq_ctrl) + imx335->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx335->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_HBLANK, + IMX335_REG_MIN, + IMX335_REG_MAX, + 1, mode->hblank); + if (imx335->hblank_ctrl) + imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl_hdlr->error) { + dev_err(imx335->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + imx335->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +/** + * imx335_probe() - I2C client device binding + * @client: pointer to i2c client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_probe(struct i2c_client *client) +{ + struct imx335 *imx335; + int ret; + + imx335 = devm_kzalloc(&client->dev, sizeof(*imx335), GFP_KERNEL); + if (!imx335) + return -ENOMEM; + + imx335->dev = &client->dev; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops); + + ret = imx335_parse_hw_config(imx335); + if (ret) { + dev_err(imx335->dev, "HW configuration is not supported"); + return ret; + } + + mutex_init(&imx335->mutex); + + ret = imx335_power_on(imx335->dev); + if (ret) { + dev_err(imx335->dev, "failed to power-on the sensor"); + goto error_mutex_destroy; + } + + /* Check module identity */ + ret = imx335_detect(imx335); + if (ret) { + dev_err(imx335->dev, "failed to find sensor: %d", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + imx335->cur_mode = &supported_mode; + imx335->vblank = imx335->cur_mode->vblank; + + ret = imx335_init_controls(imx335); + if (ret) { + dev_err(imx335->dev, "failed to init controls: %d", ret); + goto error_power_off; + } + + /* Initialize subdev */ + imx335->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx335->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx335->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx335->sd.entity, 1, &imx335->pad); + if (ret) { + dev_err(imx335->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&imx335->sd); + if (ret < 0) { + dev_err(imx335->dev, + "failed to register async subdev: %d", ret); + goto error_media_entity; + } + + pm_runtime_set_active(imx335->dev); + pm_runtime_enable(imx335->dev); + pm_runtime_idle(imx335->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx335->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(imx335->sd.ctrl_handler); +error_power_off: + imx335_power_off(imx335->dev); +error_mutex_destroy: + mutex_destroy(&imx335->mutex); + + return ret; +} + +/** + * imx335_remove() - I2C client device unbinding + * @client: pointer to I2C client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx335_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx335 *imx335 = to_imx335(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx335_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&imx335->mutex); + + return 0; +} + +static const struct dev_pm_ops imx335_pm_ops = { + SET_RUNTIME_PM_OPS(imx335_power_off, imx335_power_on, NULL) +}; + +static const struct of_device_id imx335_of_match[] = { + { .compatible = "sony,imx335" }, + { } +}; + +MODULE_DEVICE_TABLE(of, imx335_of_match); + +static struct i2c_driver imx335_driver = { + .probe_new = imx335_probe, + .remove = imx335_remove, + .driver = { + .name = "imx335", + .pm = &imx335_pm_ops, + .of_match_table = imx335_of_match, + }, +}; + +module_i2c_driver(imx335_driver); + +MODULE_DESCRIPTION("Sony imx335 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c new file mode 100644 index 000000000000..be3f6ea55559 --- /dev/null +++ b/drivers/media/i2c/imx412.c @@ -0,0 +1,1272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sony imx412 Camera Sensor Driver + * + * Copyright (C) 2021 Intel Corporation + */ +#include <asm/unaligned.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Streaming Mode */ +#define IMX412_REG_MODE_SELECT 0x0100 +#define IMX412_MODE_STANDBY 0x00 +#define IMX412_MODE_STREAMING 0x01 + +/* Lines per frame */ +#define IMX412_REG_LPFR 0x0340 + +/* Chip ID */ +#define IMX412_REG_ID 0x0016 +#define IMX412_ID 0x577 + +/* Exposure control */ +#define IMX412_REG_EXPOSURE_CIT 0x0202 +#define IMX412_EXPOSURE_MIN 8 +#define IMX412_EXPOSURE_OFFSET 22 +#define IMX412_EXPOSURE_STEP 1 +#define IMX412_EXPOSURE_DEFAULT 0x0648 + +/* Analog gain control */ +#define IMX412_REG_AGAIN 0x0204 +#define IMX412_AGAIN_MIN 0 +#define IMX412_AGAIN_MAX 978 +#define IMX412_AGAIN_STEP 1 +#define IMX412_AGAIN_DEFAULT 0 + +/* Group hold register */ +#define IMX412_REG_HOLD 0x0104 + +/* Input clock rate */ +#define IMX412_INCLK_RATE 24000000 + +/* CSI2 HW configuration */ +#define IMX412_LINK_FREQ 600000000 +#define IMX412_NUM_DATA_LANES 4 + +#define IMX412_REG_MIN 0x00 +#define IMX412_REG_MAX 0xffff + +/** + * struct imx412_reg - imx412 sensor register + * @address: Register address + * @val: Register value + */ +struct imx412_reg { + u16 address; + u8 val; +}; + +/** + * struct imx412_reg_list - imx412 sensor register list + * @num_of_regs: Number of registers in the list + * @regs: Pointer to register list + */ +struct imx412_reg_list { + u32 num_of_regs; + const struct imx412_reg *regs; +}; + +/** + * struct imx412_mode - imx412 sensor mode structure + * @width: Frame width + * @height: Frame height + * @code: Format code + * @hblank: Horizontal blanking in lines + * @vblank: Vertical blanking in lines + * @vblank_min: Minimum vertical blanking in lines + * @vblank_max: Maximum vertical blanking in lines + * @pclk: Sensor pixel clock + * @link_freq_idx: Link frequency index + * @reg_list: Register list for sensor mode + */ +struct imx412_mode { + u32 width; + u32 height; + u32 code; + u32 hblank; + u32 vblank; + u32 vblank_min; + u32 vblank_max; + u64 pclk; + u32 link_freq_idx; + struct imx412_reg_list reg_list; +}; + +/** + * struct imx412 - imx412 sensor device structure + * @dev: Pointer to generic device + * @client: Pointer to i2c client + * @sd: V4L2 sub-device + * @pad: Media pad. Only one pad supported + * @reset_gpio: Sensor reset gpio + * @inclk: Sensor input clock + * @ctrl_handler: V4L2 control handler + * @link_freq_ctrl: Pointer to link frequency control + * @pclk_ctrl: Pointer to pixel clock control + * @hblank_ctrl: Pointer to horizontal blanking control + * @vblank_ctrl: Pointer to vertical blanking control + * @exp_ctrl: Pointer to exposure control + * @again_ctrl: Pointer to analog gain control + * @vblank: Vertical blanking in lines + * @cur_mode: Pointer to current selected sensor mode + * @mutex: Mutex for serializing sensor controls + * @streaming: Flag indicating streaming state + */ +struct imx412 { + struct device *dev; + struct i2c_client *client; + struct v4l2_subdev sd; + struct media_pad pad; + struct gpio_desc *reset_gpio; + struct clk *inclk; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq_ctrl; + struct v4l2_ctrl *pclk_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *exp_ctrl; + struct v4l2_ctrl *again_ctrl; + }; + u32 vblank; + const struct imx412_mode *cur_mode; + struct mutex mutex; + bool streaming; +}; + +static const s64 link_freq[] = { + IMX412_LINK_FREQ, +}; + +/* Sensor mode registers */ +static const struct imx412_reg mode_4056x3040_regs[] = { + {0x0136, 0x18}, + {0x0137, 0x00}, + {0x3c7e, 0x08}, + {0x3c7f, 0x02}, + {0x38a8, 0x1f}, + {0x38a9, 0xff}, + {0x38aa, 0x1f}, + {0x38ab, 0xff}, + {0x55d4, 0x00}, + {0x55d5, 0x00}, + {0x55d6, 0x07}, + {0x55d7, 0xff}, + {0x55e8, 0x07}, + {0x55e9, 0xff}, + {0x55ea, 0x00}, + {0x55eb, 0x00}, + {0x575c, 0x07}, + {0x575d, 0xff}, + {0x575e, 0x00}, + {0x575f, 0x00}, + {0x5764, 0x00}, + {0x5765, 0x00}, + {0x5766, 0x07}, + {0x5767, 0xff}, + {0x5974, 0x04}, + {0x5975, 0x01}, + {0x5f10, 0x09}, + {0x5f11, 0x92}, + {0x5f12, 0x32}, + {0x5f13, 0x72}, + {0x5f14, 0x16}, + {0x5f15, 0xba}, + {0x5f17, 0x13}, + {0x5f18, 0x24}, + {0x5f19, 0x60}, + {0x5f1a, 0xe3}, + {0x5f1b, 0xad}, + {0x5f1c, 0x74}, + {0x5f2d, 0x25}, + {0x5f5c, 0xd0}, + {0x6a22, 0x00}, + {0x6a23, 0x1d}, + {0x7ba8, 0x00}, + {0x7ba9, 0x00}, + {0x886b, 0x00}, + {0x9002, 0x0a}, + {0x9004, 0x1a}, + {0x9214, 0x93}, + {0x9215, 0x69}, + {0x9216, 0x93}, + {0x9217, 0x6b}, + {0x9218, 0x93}, + {0x9219, 0x6d}, + {0x921a, 0x57}, + {0x921b, 0x58}, + {0x921c, 0x57}, + {0x921d, 0x59}, + {0x921e, 0x57}, + {0x921f, 0x5a}, + {0x9220, 0x57}, + {0x9221, 0x5b}, + {0x9222, 0x93}, + {0x9223, 0x02}, + {0x9224, 0x93}, + {0x9225, 0x03}, + {0x9226, 0x93}, + {0x9227, 0x04}, + {0x9228, 0x93}, + {0x9229, 0x05}, + {0x922a, 0x98}, + {0x922b, 0x21}, + {0x922c, 0xb2}, + {0x922d, 0xdb}, + {0x922e, 0xb2}, + {0x922f, 0xdc}, + {0x9230, 0xb2}, + {0x9231, 0xdd}, + {0x9232, 0xe2}, + {0x9233, 0xe1}, + {0x9234, 0xb2}, + {0x9235, 0xe2}, + {0x9236, 0xb2}, + {0x9237, 0xe3}, + {0x9238, 0xb7}, + {0x9239, 0xb9}, + {0x923a, 0xb7}, + {0x923b, 0xbb}, + {0x923c, 0xb7}, + {0x923d, 0xbc}, + {0x923e, 0xb7}, + {0x923f, 0xc5}, + {0x9240, 0xb7}, + {0x9241, 0xc7}, + {0x9242, 0xb7}, + {0x9243, 0xc9}, + {0x9244, 0x98}, + {0x9245, 0x56}, + {0x9246, 0x98}, + {0x9247, 0x55}, + {0x9380, 0x00}, + {0x9381, 0x62}, + {0x9382, 0x00}, + {0x9383, 0x56}, + {0x9384, 0x00}, + {0x9385, 0x52}, + {0x9388, 0x00}, + {0x9389, 0x55}, + {0x938a, 0x00}, + {0x938b, 0x55}, + {0x938c, 0x00}, + {0x938d, 0x41}, + {0x5078, 0x01}, + {0x0112, 0x0a}, + {0x0113, 0x0a}, + {0x0114, 0x03}, + {0x0342, 0x11}, + {0x0343, 0xa0}, + {0x0340, 0x0d}, + {0x0341, 0xda}, + {0x3210, 0x00}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x0f}, + {0x0349, 0xd7}, + {0x034a, 0x0b}, + {0x034b, 0xdf}, + {0x00e3, 0x00}, + {0x00e4, 0x00}, + {0x00e5, 0x01}, + {0x00fc, 0x0a}, + {0x00fd, 0x0a}, + {0x00fe, 0x0a}, + {0x00ff, 0x0a}, + {0xe013, 0x00}, + {0x0220, 0x00}, + {0x0221, 0x11}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x00}, + {0x3140, 0x02}, + {0x3241, 0x11}, + {0x3250, 0x03}, + {0x3e10, 0x00}, + {0x3e11, 0x00}, + {0x3f0d, 0x00}, + {0x3f42, 0x00}, + {0x3f43, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040a, 0x00}, + {0x040b, 0x00}, + {0x040c, 0x0f}, + {0x040d, 0xd8}, + {0x040e, 0x0b}, + {0x040f, 0xe0}, + {0x034c, 0x0f}, + {0x034d, 0xd8}, + {0x034e, 0x0b}, + {0x034f, 0xe0}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x04}, + {0x0306, 0x00}, + {0x0307, 0xc8}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030d, 0x02}, + {0x030e, 0x01}, + {0x030f, 0x5e}, + {0x0310, 0x00}, + {0x0820, 0x12}, + {0x0821, 0xc0}, + {0x0822, 0x00}, + {0x0823, 0x00}, + {0x3e20, 0x01}, + {0x3e37, 0x00}, + {0x3f50, 0x00}, + {0x3f56, 0x00}, + {0x3f57, 0xe2}, + {0x3c0a, 0x5a}, + {0x3c0b, 0x55}, + {0x3c0c, 0x28}, + {0x3c0d, 0x07}, + {0x3c0e, 0xff}, + {0x3c0f, 0x00}, + {0x3c10, 0x00}, + {0x3c11, 0x02}, + {0x3c12, 0x00}, + {0x3c13, 0x03}, + {0x3c14, 0x00}, + {0x3c15, 0x00}, + {0x3c16, 0x0c}, + {0x3c17, 0x0c}, + {0x3c18, 0x0c}, + {0x3c19, 0x0a}, + {0x3c1a, 0x0a}, + {0x3c1b, 0x0a}, + {0x3c1c, 0x00}, + {0x3c1d, 0x00}, + {0x3c1e, 0x00}, + {0x3c1f, 0x00}, + {0x3c20, 0x00}, + {0x3c21, 0x00}, + {0x3c22, 0x3f}, + {0x3c23, 0x0a}, + {0x3e35, 0x01}, + {0x3f4a, 0x03}, + {0x3f4b, 0xbf}, + {0x3f26, 0x00}, + {0x0202, 0x0d}, + {0x0203, 0xc4}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x020e, 0x01}, + {0x020f, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + {0xbcf1, 0x00}, +}; + +/* Supported sensor mode configurations */ +static const struct imx412_mode supported_mode = { + .width = 4056, + .height = 3040, + .hblank = 456, + .vblank = 506, + .vblank_min = 506, + .vblank_max = 32420, + .pclk = 480000000, + .link_freq_idx = 0, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs), + .regs = mode_4056x3040_regs, + }, +}; + +/** + * to_imx412() - imx412 V4L2 sub-device to imx412 device. + * @subdev: pointer to imx412 V4L2 sub-device + * + * Return: pointer to imx412 device + */ +static inline struct imx412 *to_imx412(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct imx412, sd); +} + +/** + * imx412_read_reg() - Read registers. + * @imx412: pointer to imx412 device + * @reg: register address + * @len: length of bytes to read. Max supported bytes is 4 + * @val: pointer to register value to be filled. + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_read_reg(struct imx412 *imx412, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd); + struct i2c_msg msgs[2] = {0}; + u8 addr_buf[2] = {0}; + u8 data_buf[4] = {0}; + int ret; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/** + * imx412_write_reg() - Write register + * @imx412: pointer to imx412 device + * @reg: register address + * @len: length of bytes. Max supported bytes is 4 + * @val: register value + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd); + u8 buf[6] = {0}; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/** + * imx412_write_regs() - Write a list of registers + * @imx412: pointer to imx412 device + * @regs: list of registers to be written + * @len: length of registers array + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_write_regs(struct imx412 *imx412, + const struct imx412_reg *regs, u32 len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/** + * imx412_update_controls() - Update control ranges based on streaming mode + * @imx412: pointer to imx412 device + * @mode: pointer to imx412_mode sensor mode + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_update_controls(struct imx412 *imx412, + const struct imx412_mode *mode) +{ + int ret; + + ret = __v4l2_ctrl_s_ctrl(imx412->link_freq_ctrl, mode->link_freq_idx); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(imx412->hblank_ctrl, mode->hblank); + if (ret) + return ret; + + return __v4l2_ctrl_modify_range(imx412->vblank_ctrl, mode->vblank_min, + mode->vblank_max, 1, mode->vblank); +} + +/** + * imx412_update_exp_gain() - Set updated exposure and gain + * @imx412: pointer to imx412 device + * @exposure: updated exposure value + * @gain: updated analog gain value + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) +{ + u32 lpfr, shutter; + int ret; + + lpfr = imx412->vblank + imx412->cur_mode->height; + shutter = lpfr - exposure; + + dev_dbg(imx412->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u", + exposure, gain, shutter, lpfr); + + ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); + if (ret) + return ret; + + ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr); + if (ret) + goto error_release_group_hold; + + ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, shutter); + if (ret) + goto error_release_group_hold; + + ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain); + +error_release_group_hold: + imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 0); + + return ret; +} + +/** + * imx412_set_ctrl() - Set subdevice control + * @ctrl: pointer to v4l2_ctrl structure + * + * Supported controls: + * - V4L2_CID_VBLANK + * - cluster controls: + * - V4L2_CID_ANALOGUE_GAIN + * - V4L2_CID_EXPOSURE + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx412 *imx412 = + container_of(ctrl->handler, struct imx412, ctrl_handler); + u32 analog_gain; + u32 exposure; + int ret; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + imx412->vblank = imx412->vblank_ctrl->val; + + dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u", + imx412->vblank, + imx412->vblank + imx412->cur_mode->height); + + ret = __v4l2_ctrl_modify_range(imx412->exp_ctrl, + IMX412_EXPOSURE_MIN, + imx412->vblank + + imx412->cur_mode->height - + IMX412_EXPOSURE_OFFSET, + 1, IMX412_EXPOSURE_DEFAULT); + break; + case V4L2_CID_EXPOSURE: + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx412->dev)) + return 0; + + exposure = ctrl->val; + analog_gain = imx412->again_ctrl->val; + + dev_dbg(imx412->dev, "Received exp %u, analog gain %u", + exposure, analog_gain); + + ret = imx412_update_exp_gain(imx412, exposure, analog_gain); + + pm_runtime_put(imx412->dev); + + break; + default: + dev_err(imx412->dev, "Invalid control %d", ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +/* V4l2 subdevice control ops*/ +static const struct v4l2_ctrl_ops imx412_ctrl_ops = { + .s_ctrl = imx412_set_ctrl, +}; + +/** + * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes + * @sd: pointer to imx412 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @code: V4L2 sub-device code enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = supported_mode.code; + + return 0; +} + +/** + * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes + * @sd: pointer to imx412 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fsize: V4L2 sub-device size enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fsize) +{ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->code != supported_mode.code) + return -EINVAL; + + fsize->min_width = supported_mode.width; + fsize->max_width = fsize->min_width; + fsize->min_height = supported_mode.height; + fsize->max_height = fsize->min_height; + + return 0; +} + +/** + * imx412_fill_pad_format() - Fill subdevice pad format + * from selected sensor mode + * @imx412: pointer to imx412 device + * @mode: pointer to imx412_mode sensor mode + * @fmt: V4L2 sub-device format need to be filled + */ +static void imx412_fill_pad_format(struct imx412 *imx412, + const struct imx412_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->code; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; +} + +/** + * imx412_get_pad_format() - Get subdevice pad format + * @sd: pointer to imx412 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx412 *imx412 = to_imx412(sd); + + mutex_lock(&imx412->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *framefmt; + } else { + imx412_fill_pad_format(imx412, imx412->cur_mode, fmt); + } + + mutex_unlock(&imx412->mutex); + + return 0; +} + +/** + * imx412_set_pad_format() - Set subdevice pad format + * @sd: pointer to imx412 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx412 *imx412 = to_imx412(sd); + const struct imx412_mode *mode; + int ret = 0; + + mutex_lock(&imx412->mutex); + + mode = &supported_mode; + imx412_fill_pad_format(imx412, mode, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *framefmt = fmt->format; + } else { + ret = imx412_update_controls(imx412, mode); + if (!ret) + imx412->cur_mode = mode; + } + + mutex_unlock(&imx412->mutex); + + return ret; +} + +/** + * imx412_init_pad_cfg() - Initialize sub-device pad configuration + * @sd: pointer to imx412 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_init_pad_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct imx412 *imx412 = to_imx412(sd); + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + imx412_fill_pad_format(imx412, &supported_mode, &fmt); + + return imx412_set_pad_format(sd, sd_state, &fmt); +} + +/** + * imx412_start_streaming() - Start sensor stream + * @imx412: pointer to imx412 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_start_streaming(struct imx412 *imx412) +{ + const struct imx412_reg_list *reg_list; + int ret; + + /* Write sensor mode registers */ + reg_list = &imx412->cur_mode->reg_list; + ret = imx412_write_regs(imx412, reg_list->regs, + reg_list->num_of_regs); + if (ret) { + dev_err(imx412->dev, "fail to write initial registers"); + return ret; + } + + /* Setup handler will write actual exposure and gain */ + ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler); + if (ret) { + dev_err(imx412->dev, "fail to setup handler"); + return ret; + } + + /* Delay is required before streaming*/ + usleep_range(7400, 8000); + + /* Start streaming */ + ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, + 1, IMX412_MODE_STREAMING); + if (ret) { + dev_err(imx412->dev, "fail to start streaming"); + return ret; + } + + return 0; +} + +/** + * imx412_stop_streaming() - Stop sensor stream + * @imx412: pointer to imx412 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_stop_streaming(struct imx412 *imx412) +{ + return imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, + 1, IMX412_MODE_STANDBY); +} + +/** + * imx412_set_stream() - Enable sensor streaming + * @sd: pointer to imx412 subdevice + * @enable: set to enable sensor streaming + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx412 *imx412 = to_imx412(sd); + int ret; + + mutex_lock(&imx412->mutex); + + if (imx412->streaming == enable) { + mutex_unlock(&imx412->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(imx412->dev); + if (ret) + goto error_unlock; + + ret = imx412_start_streaming(imx412); + if (ret) + goto error_power_off; + } else { + imx412_stop_streaming(imx412); + pm_runtime_put(imx412->dev); + } + + imx412->streaming = enable; + + mutex_unlock(&imx412->mutex); + + return 0; + +error_power_off: + pm_runtime_put(imx412->dev); +error_unlock: + mutex_unlock(&imx412->mutex); + + return ret; +} + +/** + * imx412_detect() - Detect imx412 sensor + * @imx412: pointer to imx412 device + * + * Return: 0 if successful, -EIO if sensor id does not match + */ +static int imx412_detect(struct imx412 *imx412) +{ + int ret; + u32 val; + + ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val); + if (ret) + return ret; + + if (val != IMX412_ID) { + dev_err(imx412->dev, "chip id mismatch: %x!=%x", + IMX412_ID, val); + return -ENXIO; + } + + return 0; +} + +/** + * imx412_parse_hw_config() - Parse HW configuration and check if supported + * @imx412: pointer to imx412 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_parse_hw_config(struct imx412 *imx412) +{ + struct fwnode_handle *fwnode = dev_fwnode(imx412->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + unsigned long rate; + unsigned int i; + int ret; + + if (!fwnode) + return -ENXIO; + + /* Request optional reset pin */ + imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(imx412->reset_gpio)) { + dev_err(imx412->dev, "failed to get reset gpio %ld", + PTR_ERR(imx412->reset_gpio)); + return PTR_ERR(imx412->reset_gpio); + } + + /* Get sensor input clock */ + imx412->inclk = devm_clk_get(imx412->dev, NULL); + if (IS_ERR(imx412->inclk)) { + dev_err(imx412->dev, "could not get inclk"); + return PTR_ERR(imx412->inclk); + } + + rate = clk_get_rate(imx412->inclk); + if (rate != IMX412_INCLK_RATE) { + dev_err(imx412->dev, "inclk frequency mismatch"); + return -EINVAL; + } + + 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 != IMX412_NUM_DATA_LANES) { + dev_err(imx412->dev, + "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(imx412->dev, "no link frequencies defined"); + ret = -EINVAL; + goto done_endpoint_free; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == IMX412_LINK_FREQ) + goto done_endpoint_free; + + ret = -EINVAL; + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +/* V4l2 subdevice ops */ +static const struct v4l2_subdev_video_ops imx412_video_ops = { + .s_stream = imx412_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx412_pad_ops = { + .init_cfg = imx412_init_pad_cfg, + .enum_mbus_code = imx412_enum_mbus_code, + .enum_frame_size = imx412_enum_frame_size, + .get_fmt = imx412_get_pad_format, + .set_fmt = imx412_set_pad_format, +}; + +static const struct v4l2_subdev_ops imx412_subdev_ops = { + .video = &imx412_video_ops, + .pad = &imx412_pad_ops, +}; + +/** + * imx412_power_on() - Sensor power on sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx412 *imx412 = to_imx412(sd); + int ret; + + gpiod_set_value_cansleep(imx412->reset_gpio, 1); + + ret = clk_prepare_enable(imx412->inclk); + if (ret) { + dev_err(imx412->dev, "fail to enable inclk"); + goto error_reset; + } + + usleep_range(1000, 1200); + + return 0; + +error_reset: + gpiod_set_value_cansleep(imx412->reset_gpio, 0); + + return ret; +} + +/** + * imx412_power_off() - Sensor power off sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx412 *imx412 = to_imx412(sd); + + gpiod_set_value_cansleep(imx412->reset_gpio, 0); + + clk_disable_unprepare(imx412->inclk); + + return 0; +} + +/** + * imx412_init_controls() - Initialize sensor subdevice controls + * @imx412: pointer to imx412 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_init_controls(struct imx412 *imx412) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &imx412->ctrl_handler; + const struct imx412_mode *mode = imx412->cur_mode; + u32 lpfr; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + if (ret) + return ret; + + /* Serialize controls with sensor device */ + ctrl_hdlr->lock = &imx412->mutex; + + /* Initialize exposure and gain */ + lpfr = mode->vblank + mode->height; + imx412->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX412_EXPOSURE_MIN, + lpfr - IMX412_EXPOSURE_OFFSET, + IMX412_EXPOSURE_STEP, + IMX412_EXPOSURE_DEFAULT); + + imx412->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + IMX412_AGAIN_MIN, + IMX412_AGAIN_MAX, + IMX412_AGAIN_STEP, + IMX412_AGAIN_DEFAULT); + + v4l2_ctrl_cluster(2, &imx412->exp_ctrl); + + imx412->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_VBLANK, + mode->vblank_min, + mode->vblank_max, + 1, mode->vblank); + + /* Read only controls */ + imx412->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_PIXEL_RATE, + mode->pclk, mode->pclk, + 1, mode->pclk); + + imx412->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - + 1, + mode->link_freq_idx, + link_freq); + if (imx412->link_freq_ctrl) + imx412->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx412->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx412_ctrl_ops, + V4L2_CID_HBLANK, + IMX412_REG_MIN, + IMX412_REG_MAX, + 1, mode->hblank); + if (imx412->hblank_ctrl) + imx412->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl_hdlr->error) { + dev_err(imx412->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + imx412->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +/** + * imx412_probe() - I2C client device binding + * @client: pointer to i2c client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_probe(struct i2c_client *client) +{ + struct imx412 *imx412; + int ret; + + imx412 = devm_kzalloc(&client->dev, sizeof(*imx412), GFP_KERNEL); + if (!imx412) + return -ENOMEM; + + imx412->dev = &client->dev; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); + + ret = imx412_parse_hw_config(imx412); + if (ret) { + dev_err(imx412->dev, "HW configuration is not supported"); + return ret; + } + + mutex_init(&imx412->mutex); + + ret = imx412_power_on(imx412->dev); + if (ret) { + dev_err(imx412->dev, "failed to power-on the sensor"); + goto error_mutex_destroy; + } + + /* Check module identity */ + ret = imx412_detect(imx412); + if (ret) { + dev_err(imx412->dev, "failed to find sensor: %d", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + imx412->cur_mode = &supported_mode; + imx412->vblank = imx412->cur_mode->vblank; + + ret = imx412_init_controls(imx412); + if (ret) { + dev_err(imx412->dev, "failed to init controls: %d", ret); + goto error_power_off; + } + + /* Initialize subdev */ + imx412->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx412->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx412->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad); + if (ret) { + dev_err(imx412->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&imx412->sd); + if (ret < 0) { + dev_err(imx412->dev, + "failed to register async subdev: %d", ret); + goto error_media_entity; + } + + pm_runtime_set_active(imx412->dev); + pm_runtime_enable(imx412->dev); + pm_runtime_idle(imx412->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx412->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(imx412->sd.ctrl_handler); +error_power_off: + imx412_power_off(imx412->dev); +error_mutex_destroy: + mutex_destroy(&imx412->mutex); + + return ret; +} + +/** + * imx412_remove() - I2C client device unbinding + * @client: pointer to I2C client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx412_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx412 *imx412 = to_imx412(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx412_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&imx412->mutex); + + return 0; +} + +static const struct dev_pm_ops imx412_pm_ops = { + SET_RUNTIME_PM_OPS(imx412_power_off, imx412_power_on, NULL) +}; + +static const struct of_device_id imx412_of_match[] = { + { .compatible = "sony,imx412" }, + { } +}; + +MODULE_DEVICE_TABLE(of, imx412_of_match); + +static struct i2c_driver imx412_driver = { + .probe_new = imx412_probe, + .remove = imx412_remove, + .driver = { + .name = "imx412", + .pm = &imx412_pm_ops, + .of_match_table = imx412_of_match, + }, +}; + +module_i2c_driver(imx412_driver); + +MODULE_DESCRIPTION("Sony imx412 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 599369a3d192..934c9d65cb09 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -51,7 +51,7 @@ #define OV2740_REG_MWB_R_GAIN 0x500a #define OV2740_REG_MWB_G_GAIN 0x500c #define OV2740_REG_MWB_B_GAIN 0x500e -#define OV2740_DGTL_GAIN_MIN 0 +#define OV2740_DGTL_GAIN_MIN 1024 #define OV2740_DGTL_GAIN_MAX 4095 #define OV2740_DGTL_GAIN_STEP 1 #define OV2740_DGTL_GAIN_DEFAULT 1024 @@ -61,6 +61,12 @@ #define OV2740_TEST_PATTERN_ENABLE BIT(7) #define OV2740_TEST_PATTERN_BAR_SHIFT 2 +/* Group Access */ +#define OV2740_REG_GROUP_ACCESS 0x3208 +#define OV2740_GROUP_HOLD_START 0x0 +#define OV2740_GROUP_HOLD_END 0x10 +#define OV2740_GROUP_HOLD_LAUNCH 0xa0 + /* ISP CTRL00 */ #define OV2740_REG_ISP_CTRL00 0x5000 /* ISP CTRL01 */ @@ -438,6 +444,11 @@ static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain) { int ret = 0; + ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1, + OV2740_GROUP_HOLD_START); + if (ret) + return ret; + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain); if (ret) return ret; @@ -446,7 +457,18 @@ static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain) if (ret) return ret; - return ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain); + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain); + if (ret) + return ret; + + ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1, + OV2740_GROUP_HOLD_END); + if (ret) + return ret; + + ret = ov2740_write_reg(ov2740, OV2740_REG_GROUP_ACCESS, 1, + OV2740_GROUP_HOLD_LAUNCH); + return ret; } static int ov2740_test_pattern(struct ov2740 *ov2740, u32 pattern) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index f6e1e51e0375..ddbd71394db3 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -135,7 +135,9 @@ struct ov5640_pixfmt { static const struct ov5640_pixfmt ov5640_formats[] = { { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, }, { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, @@ -2338,11 +2340,13 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, u8 fmt, mux; switch (format->code) { + case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422, UYVY */ fmt = 0x3f; mux = OV5640_FMT_MUX_YUV422; break; + case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: /* YUV422, YUYV */ fmt = 0x30; diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index 88e19f30d376..aa74744b91c7 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -2304,25 +2304,26 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) clk_set_rate(ov8856->xvclk, xvclk_rate); xvclk_rate = clk_get_rate(ov8856->xvclk); + + ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov8856->reset_gpio)) + return PTR_ERR(ov8856->reset_gpio); + + for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++) + ov8856->supplies[i].supply = ov8856_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(ov8856_supply_names), + ov8856->supplies); + if (ret) + return ret; } if (xvclk_rate != OV8856_XVCLK_19_2) dev_warn(dev, "external clock rate %u is unsupported", xvclk_rate); - ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ov8856->reset_gpio)) - return PTR_ERR(ov8856->reset_gpio); - - for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++) - ov8856->supplies[i].supply = ov8856_supply_names[i]; - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names), - ov8856->supplies); - if (ret) - return ret; - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c new file mode 100644 index 000000000000..2e0b315801e5 --- /dev/null +++ b/drivers/media/i2c/ov9282.c @@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OmniVision ov9282 Camera Sensor Driver + * + * Copyright (C) 2021 Intel Corporation + */ +#include <asm/unaligned.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Streaming Mode */ +#define OV9282_REG_MODE_SELECT 0x0100 +#define OV9282_MODE_STANDBY 0x00 +#define OV9282_MODE_STREAMING 0x01 + +/* Lines per frame */ +#define OV9282_REG_LPFR 0x380e + +/* Chip ID */ +#define OV9282_REG_ID 0x300a +#define OV9282_ID 0x9281 + +/* Exposure control */ +#define OV9282_REG_EXPOSURE 0x3500 +#define OV9282_EXPOSURE_MIN 1 +#define OV9282_EXPOSURE_OFFSET 12 +#define OV9282_EXPOSURE_STEP 1 +#define OV9282_EXPOSURE_DEFAULT 0x0282 + +/* Analog gain control */ +#define OV9282_REG_AGAIN 0x3509 +#define OV9282_AGAIN_MIN 0x10 +#define OV9282_AGAIN_MAX 0xff +#define OV9282_AGAIN_STEP 1 +#define OV9282_AGAIN_DEFAULT 0x10 + +/* Group hold register */ +#define OV9282_REG_HOLD 0x3308 + +/* Input clock rate */ +#define OV9282_INCLK_RATE 24000000 + +/* CSI2 HW configuration */ +#define OV9282_LINK_FREQ 400000000 +#define OV9282_NUM_DATA_LANES 2 + +#define OV9282_REG_MIN 0x00 +#define OV9282_REG_MAX 0xfffff + +/** + * struct ov9282_reg - ov9282 sensor register + * @address: Register address + * @val: Register value + */ +struct ov9282_reg { + u16 address; + u8 val; +}; + +/** + * struct ov9282_reg_list - ov9282 sensor register list + * @num_of_regs: Number of registers in the list + * @regs: Pointer to register list + */ +struct ov9282_reg_list { + u32 num_of_regs; + const struct ov9282_reg *regs; +}; + +/** + * struct ov9282_mode - ov9282 sensor mode structure + * @width: Frame width + * @height: Frame height + * @code: Format code + * @hblank: Horizontal blanking in lines + * @vblank: Vertical blanking in lines + * @vblank_min: Minimum vertical blanking in lines + * @vblank_max: Maximum vertical blanking in lines + * @pclk: Sensor pixel clock + * @link_freq_idx: Link frequency index + * @reg_list: Register list for sensor mode + */ +struct ov9282_mode { + u32 width; + u32 height; + u32 code; + u32 hblank; + u32 vblank; + u32 vblank_min; + u32 vblank_max; + u64 pclk; + u32 link_freq_idx; + struct ov9282_reg_list reg_list; +}; + +/** + * struct ov9282 - ov9282 sensor device structure + * @dev: Pointer to generic device + * @client: Pointer to i2c client + * @sd: V4L2 sub-device + * @pad: Media pad. Only one pad supported + * @reset_gpio: Sensor reset gpio + * @inclk: Sensor input clock + * @ctrl_handler: V4L2 control handler + * @link_freq_ctrl: Pointer to link frequency control + * @pclk_ctrl: Pointer to pixel clock control + * @hblank_ctrl: Pointer to horizontal blanking control + * @vblank_ctrl: Pointer to vertical blanking control + * @exp_ctrl: Pointer to exposure control + * @again_ctrl: Pointer to analog gain control + * @vblank: Vertical blanking in lines + * @cur_mode: Pointer to current selected sensor mode + * @mutex: Mutex for serializing sensor controls + * @streaming: Flag indicating streaming state + */ +struct ov9282 { + struct device *dev; + struct i2c_client *client; + struct v4l2_subdev sd; + struct media_pad pad; + struct gpio_desc *reset_gpio; + struct clk *inclk; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq_ctrl; + struct v4l2_ctrl *pclk_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *exp_ctrl; + struct v4l2_ctrl *again_ctrl; + }; + u32 vblank; + const struct ov9282_mode *cur_mode; + struct mutex mutex; + bool streaming; +}; + +static const s64 link_freq[] = { + OV9282_LINK_FREQ, +}; + +/* Sensor mode registers */ +static const struct ov9282_reg mode_1280x720_regs[] = { + {0x0302, 0x32}, + {0x030d, 0x50}, + {0x030e, 0x02}, + {0x3001, 0x00}, + {0x3004, 0x00}, + {0x3005, 0x00}, + {0x3006, 0x04}, + {0x3011, 0x0a}, + {0x3013, 0x18}, + {0x301c, 0xf0}, + {0x3022, 0x01}, + {0x3030, 0x10}, + {0x3039, 0x32}, + {0x303a, 0x00}, + {0x3500, 0x00}, + {0x3501, 0x5f}, + {0x3502, 0x1e}, + {0x3503, 0x08}, + {0x3505, 0x8c}, + {0x3507, 0x03}, + {0x3508, 0x00}, + {0x3509, 0x10}, + {0x3610, 0x80}, + {0x3611, 0xa0}, + {0x3620, 0x6e}, + {0x3632, 0x56}, + {0x3633, 0x78}, + {0x3666, 0x00}, + {0x366f, 0x5a}, + {0x3680, 0x84}, + {0x3712, 0x80}, + {0x372d, 0x22}, + {0x3731, 0x80}, + {0x3732, 0x30}, + {0x3778, 0x00}, + {0x377d, 0x22}, + {0x3788, 0x02}, + {0x3789, 0xa4}, + {0x378a, 0x00}, + {0x378b, 0x4a}, + {0x3799, 0x20}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x02}, + {0x3807, 0xdf}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x02}, + {0x380b, 0xd0}, + {0x380c, 0x05}, + {0x380d, 0xfa}, + {0x380e, 0x06}, + {0x380f, 0xce}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x3c}, + {0x3821, 0x84}, + {0x3881, 0x42}, + {0x38a8, 0x02}, + {0x38a9, 0x80}, + {0x38b1, 0x00}, + {0x38c4, 0x00}, + {0x38c5, 0xc0}, + {0x38c6, 0x04}, + {0x38c7, 0x80}, + {0x3920, 0xff}, + {0x4003, 0x40}, + {0x4008, 0x02}, + {0x4009, 0x05}, + {0x400c, 0x00}, + {0x400d, 0x03}, + {0x4010, 0x40}, + {0x4043, 0x40}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4501, 0x00}, + {0x4507, 0x00}, + {0x4509, 0x80}, + {0x450a, 0x08}, + {0x4601, 0x04}, + {0x470f, 0x00}, + {0x4f07, 0x00}, + {0x4800, 0x20}, + {0x5000, 0x9f}, + {0x5001, 0x00}, + {0x5e00, 0x00}, + {0x5d00, 0x07}, + {0x5d01, 0x00}, + {0x0101, 0x01}, + {0x1000, 0x03}, + {0x5a08, 0x84}, +}; + +/* Supported sensor mode configurations */ +static const struct ov9282_mode supported_mode = { + .width = 1280, + .height = 720, + .hblank = 250, + .vblank = 1022, + .vblank_min = 151, + .vblank_max = 51540, + .pclk = 160000000, + .link_freq_idx = 0, + .code = MEDIA_BUS_FMT_Y10_1X10, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, +}; + +/** + * to_ov9282() - ov9282 V4L2 sub-device to ov9282 device. + * @subdev: pointer to ov9282 V4L2 sub-device + * + * Return: pointer to ov9282 device + */ +static inline struct ov9282 *to_ov9282(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov9282, sd); +} + +/** + * ov9282_read_reg() - Read registers. + * @ov9282: pointer to ov9282 device + * @reg: register address + * @len: length of bytes to read. Max supported bytes is 4 + * @val: pointer to register value to be filled. + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_read_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd); + struct i2c_msg msgs[2] = {0}; + u8 addr_buf[2] = {0}; + u8 data_buf[4] = {0}; + int ret; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/** + * ov9282_write_reg() - Write register + * @ov9282: pointer to ov9282 device + * @reg: register address + * @len: length of bytes. Max supported bytes is 4 + * @val: register value + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_write_reg(struct ov9282 *ov9282, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9282->sd); + u8 buf[6] = {0}; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/** + * ov9282_write_regs() - Write a list of registers + * @ov9282: pointer to ov9282 device + * @regs: list of registers to be written + * @len: length of registers array + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_write_regs(struct ov9282 *ov9282, + const struct ov9282_reg *regs, u32 len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = ov9282_write_reg(ov9282, regs[i].address, 1, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/** + * ov9282_update_controls() - Update control ranges based on streaming mode + * @ov9282: pointer to ov9282 device + * @mode: pointer to ov9282_mode sensor mode + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_update_controls(struct ov9282 *ov9282, + const struct ov9282_mode *mode) +{ + int ret; + + ret = __v4l2_ctrl_s_ctrl(ov9282->link_freq_ctrl, mode->link_freq_idx); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(ov9282->hblank_ctrl, mode->hblank); + if (ret) + return ret; + + return __v4l2_ctrl_modify_range(ov9282->vblank_ctrl, mode->vblank_min, + mode->vblank_max, 1, mode->vblank); +} + +/** + * ov9282_update_exp_gain() - Set updated exposure and gain + * @ov9282: pointer to ov9282 device + * @exposure: updated exposure value + * @gain: updated analog gain value + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) +{ + u32 lpfr; + int ret; + + lpfr = ov9282->vblank + ov9282->cur_mode->height; + + dev_dbg(ov9282->dev, "Set exp %u, analog gain %u, lpfr %u", + exposure, gain, lpfr); + + ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1); + if (ret) + return ret; + + ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr); + if (ret) + goto error_release_group_hold; + + ret = ov9282_write_reg(ov9282, OV9282_REG_EXPOSURE, 3, exposure << 4); + if (ret) + goto error_release_group_hold; + + ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain); + +error_release_group_hold: + ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0); + + return ret; +} + +/** + * ov9282_set_ctrl() - Set subdevice control + * @ctrl: pointer to v4l2_ctrl structure + * + * Supported controls: + * - V4L2_CID_VBLANK + * - cluster controls: + * - V4L2_CID_ANALOGUE_GAIN + * - V4L2_CID_EXPOSURE + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9282 *ov9282 = + container_of(ctrl->handler, struct ov9282, ctrl_handler); + u32 analog_gain; + u32 exposure; + int ret; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + ov9282->vblank = ov9282->vblank_ctrl->val; + + dev_dbg(ov9282->dev, "Received vblank %u, new lpfr %u", + ov9282->vblank, + ov9282->vblank + ov9282->cur_mode->height); + + ret = __v4l2_ctrl_modify_range(ov9282->exp_ctrl, + OV9282_EXPOSURE_MIN, + ov9282->vblank + + ov9282->cur_mode->height - + OV9282_EXPOSURE_OFFSET, + 1, OV9282_EXPOSURE_DEFAULT); + break; + case V4L2_CID_EXPOSURE: + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(ov9282->dev)) + return 0; + + exposure = ctrl->val; + analog_gain = ov9282->again_ctrl->val; + + dev_dbg(ov9282->dev, "Received exp %u, analog gain %u", + exposure, analog_gain); + + ret = ov9282_update_exp_gain(ov9282, exposure, analog_gain); + + pm_runtime_put(ov9282->dev); + + break; + default: + dev_err(ov9282->dev, "Invalid control %d", ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +/* V4l2 subdevice control ops*/ +static const struct v4l2_ctrl_ops ov9282_ctrl_ops = { + .s_ctrl = ov9282_set_ctrl, +}; + +/** + * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes + * @sd: pointer to ov9282 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @code: V4L2 sub-device code enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = supported_mode.code; + + return 0; +} + +/** + * ov9282_enum_frame_size() - Enumerate V4L2 sub-device frame sizes + * @sd: pointer to ov9282 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fsize: V4L2 sub-device size enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fsize) +{ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->code != supported_mode.code) + return -EINVAL; + + fsize->min_width = supported_mode.width; + fsize->max_width = fsize->min_width; + fsize->min_height = supported_mode.height; + fsize->max_height = fsize->min_height; + + return 0; +} + +/** + * ov9282_fill_pad_format() - Fill subdevice pad format + * from selected sensor mode + * @ov9282: pointer to ov9282 device + * @mode: pointer to ov9282_mode sensor mode + * @fmt: V4L2 sub-device format need to be filled + */ +static void ov9282_fill_pad_format(struct ov9282 *ov9282, + const struct ov9282_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->code; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; +} + +/** + * ov9282_get_pad_format() - Get subdevice pad format + * @sd: pointer to ov9282 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov9282 *ov9282 = to_ov9282(sd); + + mutex_lock(&ov9282->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + fmt->format = *framefmt; + } else { + ov9282_fill_pad_format(ov9282, ov9282->cur_mode, fmt); + } + + mutex_unlock(&ov9282->mutex); + + return 0; +} + +/** + * ov9282_set_pad_format() - Set subdevice pad format + * @sd: pointer to ov9282 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov9282 *ov9282 = to_ov9282(sd); + const struct ov9282_mode *mode; + int ret = 0; + + mutex_lock(&ov9282->mutex); + + mode = &supported_mode; + ov9282_fill_pad_format(ov9282, mode, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + *framefmt = fmt->format; + } else { + ret = ov9282_update_controls(ov9282, mode); + if (!ret) + ov9282->cur_mode = mode; + } + + mutex_unlock(&ov9282->mutex); + + return ret; +} + +/** + * ov9282_init_pad_cfg() - Initialize sub-device pad configuration + * @sd: pointer to ov9282 V4L2 sub-device structure + * @sd_state: V4L2 sub-device configuration + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct ov9282 *ov9282 = to_ov9282(sd); + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + ov9282_fill_pad_format(ov9282, &supported_mode, &fmt); + + return ov9282_set_pad_format(sd, sd_state, &fmt); +} + +/** + * ov9282_start_streaming() - Start sensor stream + * @ov9282: pointer to ov9282 device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_start_streaming(struct ov9282 *ov9282) +{ + const struct ov9282_reg_list *reg_list; + int ret; + + /* Write sensor mode registers */ + reg_list = &ov9282->cur_mode->reg_list; + ret = ov9282_write_regs(ov9282, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(ov9282->dev, "fail to write initial registers"); + return ret; + } + + /* Setup handler will write actual exposure and gain */ + ret = __v4l2_ctrl_handler_setup(ov9282->sd.ctrl_handler); + if (ret) { + dev_err(ov9282->dev, "fail to setup handler"); + return ret; + } + + /* Start streaming */ + ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, + 1, OV9282_MODE_STREAMING); + if (ret) { + dev_err(ov9282->dev, "fail to start streaming"); + return ret; + } + + return 0; +} + +/** + * ov9282_stop_streaming() - Stop sensor stream + * @ov9282: pointer to ov9282 device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_stop_streaming(struct ov9282 *ov9282) +{ + return ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT, + 1, OV9282_MODE_STANDBY); +} + +/** + * ov9282_set_stream() - Enable sensor streaming + * @sd: pointer to ov9282 subdevice + * @enable: set to enable sensor streaming + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov9282 *ov9282 = to_ov9282(sd); + int ret; + + mutex_lock(&ov9282->mutex); + + if (ov9282->streaming == enable) { + mutex_unlock(&ov9282->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_resume_and_get(ov9282->dev); + if (ret) + goto error_unlock; + + ret = ov9282_start_streaming(ov9282); + if (ret) + goto error_power_off; + } else { + ov9282_stop_streaming(ov9282); + pm_runtime_put(ov9282->dev); + } + + ov9282->streaming = enable; + + mutex_unlock(&ov9282->mutex); + + return 0; + +error_power_off: + pm_runtime_put(ov9282->dev); +error_unlock: + mutex_unlock(&ov9282->mutex); + + return ret; +} + +/** + * ov9282_detect() - Detect ov9282 sensor + * @ov9282: pointer to ov9282 device + * + * Return: 0 if successful, -EIO if sensor id does not match + */ +static int ov9282_detect(struct ov9282 *ov9282) +{ + int ret; + u32 val; + + ret = ov9282_read_reg(ov9282, OV9282_REG_ID, 2, &val); + if (ret) + return ret; + + if (val != OV9282_ID) { + dev_err(ov9282->dev, "chip id mismatch: %x!=%x", + OV9282_ID, val); + return -ENXIO; + } + + return 0; +} + +/** + * ov9282_parse_hw_config() - Parse HW configuration and check if supported + * @ov9282: pointer to ov9282 device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_parse_hw_config(struct ov9282 *ov9282) +{ + struct fwnode_handle *fwnode = dev_fwnode(ov9282->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + unsigned long rate; + unsigned int i; + int ret; + + if (!fwnode) + return -ENXIO; + + /* Request optional reset pin */ + ov9282->reset_gpio = devm_gpiod_get_optional(ov9282->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov9282->reset_gpio)) { + dev_err(ov9282->dev, "failed to get reset gpio %ld", + PTR_ERR(ov9282->reset_gpio)); + return PTR_ERR(ov9282->reset_gpio); + } + + /* Get sensor input clock */ + ov9282->inclk = devm_clk_get(ov9282->dev, NULL); + if (IS_ERR(ov9282->inclk)) { + dev_err(ov9282->dev, "could not get inclk"); + return PTR_ERR(ov9282->inclk); + } + + rate = clk_get_rate(ov9282->inclk); + if (rate != OV9282_INCLK_RATE) { + dev_err(ov9282->dev, "inclk frequency mismatch"); + return -EINVAL; + } + + 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 != OV9282_NUM_DATA_LANES) { + dev_err(ov9282->dev, + "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(ov9282->dev, "no link frequencies defined"); + ret = -EINVAL; + goto done_endpoint_free; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == OV9282_LINK_FREQ) + goto done_endpoint_free; + + ret = -EINVAL; + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +/* V4l2 subdevice ops */ +static const struct v4l2_subdev_video_ops ov9282_video_ops = { + .s_stream = ov9282_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { + .init_cfg = ov9282_init_pad_cfg, + .enum_mbus_code = ov9282_enum_mbus_code, + .enum_frame_size = ov9282_enum_frame_size, + .get_fmt = ov9282_get_pad_format, + .set_fmt = ov9282_set_pad_format, +}; + +static const struct v4l2_subdev_ops ov9282_subdev_ops = { + .video = &ov9282_video_ops, + .pad = &ov9282_pad_ops, +}; + +/** + * ov9282_power_on() - Sensor power on sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov9282 *ov9282 = to_ov9282(sd); + int ret; + + usleep_range(400, 600); + + gpiod_set_value_cansleep(ov9282->reset_gpio, 1); + + ret = clk_prepare_enable(ov9282->inclk); + if (ret) { + dev_err(ov9282->dev, "fail to enable inclk"); + goto error_reset; + } + + usleep_range(400, 600); + + return 0; + +error_reset: + gpiod_set_value_cansleep(ov9282->reset_gpio, 0); + + return ret; +} + +/** + * ov9282_power_off() - Sensor power off sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov9282 *ov9282 = to_ov9282(sd); + + gpiod_set_value_cansleep(ov9282->reset_gpio, 0); + + clk_disable_unprepare(ov9282->inclk); + + return 0; +} + +/** + * ov9282_init_controls() - Initialize sensor subdevice controls + * @ov9282: pointer to ov9282 device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_init_controls(struct ov9282 *ov9282) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &ov9282->ctrl_handler; + const struct ov9282_mode *mode = ov9282->cur_mode; + u32 lpfr; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + if (ret) + return ret; + + /* Serialize controls with sensor device */ + ctrl_hdlr->lock = &ov9282->mutex; + + /* Initialize exposure and gain */ + lpfr = mode->vblank + mode->height; + ov9282->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_EXPOSURE, + OV9282_EXPOSURE_MIN, + lpfr - OV9282_EXPOSURE_OFFSET, + OV9282_EXPOSURE_STEP, + OV9282_EXPOSURE_DEFAULT); + + ov9282->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + OV9282_AGAIN_MIN, + OV9282_AGAIN_MAX, + OV9282_AGAIN_STEP, + OV9282_AGAIN_DEFAULT); + + v4l2_ctrl_cluster(2, &ov9282->exp_ctrl); + + ov9282->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_VBLANK, + mode->vblank_min, + mode->vblank_max, + 1, mode->vblank); + + /* Read only controls */ + ov9282->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_PIXEL_RATE, + mode->pclk, mode->pclk, + 1, mode->pclk); + + ov9282->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - + 1, + mode->link_freq_idx, + link_freq); + if (ov9282->link_freq_ctrl) + ov9282->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov9282->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &ov9282_ctrl_ops, + V4L2_CID_HBLANK, + OV9282_REG_MIN, + OV9282_REG_MAX, + 1, mode->hblank); + if (ov9282->hblank_ctrl) + ov9282->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl_hdlr->error) { + dev_err(ov9282->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + ov9282->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +/** + * ov9282_probe() - I2C client device binding + * @client: pointer to i2c client device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_probe(struct i2c_client *client) +{ + struct ov9282 *ov9282; + int ret; + + ov9282 = devm_kzalloc(&client->dev, sizeof(*ov9282), GFP_KERNEL); + if (!ov9282) + return -ENOMEM; + + ov9282->dev = &client->dev; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&ov9282->sd, client, &ov9282_subdev_ops); + + ret = ov9282_parse_hw_config(ov9282); + if (ret) { + dev_err(ov9282->dev, "HW configuration is not supported"); + return ret; + } + + mutex_init(&ov9282->mutex); + + ret = ov9282_power_on(ov9282->dev); + if (ret) { + dev_err(ov9282->dev, "failed to power-on the sensor"); + goto error_mutex_destroy; + } + + /* Check module identity */ + ret = ov9282_detect(ov9282); + if (ret) { + dev_err(ov9282->dev, "failed to find sensor: %d", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + ov9282->cur_mode = &supported_mode; + ov9282->vblank = ov9282->cur_mode->vblank; + + ret = ov9282_init_controls(ov9282); + if (ret) { + dev_err(ov9282->dev, "failed to init controls: %d", ret); + goto error_power_off; + } + + /* Initialize subdev */ + ov9282->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov9282->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov9282->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov9282->sd.entity, 1, &ov9282->pad); + if (ret) { + dev_err(ov9282->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&ov9282->sd); + if (ret < 0) { + dev_err(ov9282->dev, + "failed to register async subdev: %d", ret); + goto error_media_entity; + } + + pm_runtime_set_active(ov9282->dev); + pm_runtime_enable(ov9282->dev); + pm_runtime_idle(ov9282->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&ov9282->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(ov9282->sd.ctrl_handler); +error_power_off: + ov9282_power_off(ov9282->dev); +error_mutex_destroy: + mutex_destroy(&ov9282->mutex); + + return ret; +} + +/** + * ov9282_remove() - I2C client device unbinding + * @client: pointer to I2C client device + * + * Return: 0 if successful, error code otherwise. + */ +static int ov9282_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9282 *ov9282 = to_ov9282(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov9282_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&ov9282->mutex); + + return 0; +} + +static const struct dev_pm_ops ov9282_pm_ops = { + SET_RUNTIME_PM_OPS(ov9282_power_off, ov9282_power_on, NULL) +}; + +static const struct of_device_id ov9282_of_match[] = { + { .compatible = "ovti,ov9282" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ov9282_of_match); + +static struct i2c_driver ov9282_driver = { + .probe_new = ov9282_probe, + .remove = ov9282_remove, + .driver = { + .name = "ov9282", + .pm = &ov9282_pm_ops, + .of_match_table = ov9282_of_match, + }, +}; + +module_i2c_driver(ov9282_driver); + +MODULE_DESCRIPTION("OmniVision ov9282 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c index af50c66cf5ce..df538ceb71c3 100644 --- a/drivers/media/i2c/ov9734.c +++ b/drivers/media/i2c/ov9734.c @@ -60,6 +60,12 @@ #define OV9734_TEST_PATTERN_ENABLE BIT(7) #define OV9734_TEST_PATTERN_BAR_SHIFT 2 +/* Group Access */ +#define OV9734_REG_GROUP_ACCESS 0x3208 +#define OV9734_GROUP_HOLD_START 0x0 +#define OV9734_GROUP_HOLD_END 0x10 +#define OV9734_GROUP_HOLD_LAUNCH 0xa0 + enum { OV9734_LINK_FREQ_180MHZ_INDEX, }; @@ -433,6 +439,11 @@ static int ov9734_update_digital_gain(struct ov9734 *ov9734, u32 d_gain) { int ret; + ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1, + OV9734_GROUP_HOLD_START); + if (ret) + return ret; + ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_R_GAIN, 2, d_gain); if (ret) return ret; @@ -441,7 +452,18 @@ static int ov9734_update_digital_gain(struct ov9734 *ov9734, u32 d_gain) if (ret) return ret; - return ov9734_write_reg(ov9734, OV9734_REG_MWB_B_GAIN, 2, d_gain); + ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_B_GAIN, 2, d_gain); + if (ret) + return ret; + + ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1, + OV9734_GROUP_HOLD_END); + if (ret) + return ret; + + ret = ov9734_write_reg(ov9734, OV9734_REG_GROUP_ACCESS, 1, + OV9734_GROUP_HOLD_LAUNCH); + return ret; } static int ov9734_test_pattern(struct ov9734 *ov9734, u32 pattern) diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 91e6db847bb5..6070aaf0b32e 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -563,7 +563,7 @@ static void tda1997x_delayed_work_enable_hpd(struct work_struct *work) delayed_work_enable_hpd); struct v4l2_subdev *sd = &state->sd; - v4l2_dbg(2, debug, sd, "%s:\n", __func__); + v4l2_dbg(2, debug, sd, "%s\n", __func__); /* Set HPD high */ tda1997x_manual_hpd(sd, HPD_HIGH_OTHER); @@ -1107,7 +1107,8 @@ tda1997x_detect_std(struct tda1997x_state *state, hper = io_read16(sd, REG_H_PER) & MASK_HPER; hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH; v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper); - if (!vper || !hper || !hsper) + + if (!state->input_detect[0] && !state->input_detect[1]) return -ENOLINK; for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { @@ -1695,14 +1696,15 @@ static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct tda1997x_state *state = to_state(sd); + int ret; v4l_dbg(1, debug, state->client, "%s\n", __func__); memset(timings, 0, sizeof(struct v4l2_dv_timings)); mutex_lock(&state->lock); - tda1997x_detect_std(state, timings); + ret = tda1997x_detect_std(state, timings); mutex_unlock(&state->lock); - return 0; + return ret; } static const struct v4l2_subdev_video_ops tda1997x_video_ops = { @@ -2233,6 +2235,7 @@ static int tda1997x_core_init(struct v4l2_subdev *sd) /* get initial HDMI status */ state->hdmi_status = io_read(sd, REG_HDMI_FLAGS); + io_write(sd, REG_EDID_ENABLE, EDID_ENABLE_A_EN | EDID_ENABLE_B_EN); return 0; } diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 30c63552556d..4b16ffcaef98 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -964,7 +964,7 @@ static int tvp5150_enable(struct v4l2_subdev *sd) /* * Enable the YCbCr and clock outputs. In discrete sync mode - * (non-BT.656) additionally enable the the sync outputs. + * (non-BT.656) additionally enable the sync outputs. */ switch (decoder->mbus_type) { case V4L2_MBUS_PARALLEL: diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index 9e56d2ad6b94..cf5e459b1d96 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -556,7 +556,7 @@ static const struct media_file_operations media_device_fops = { * sysfs */ -static ssize_t show_model(struct device *cd, +static ssize_t model_show(struct device *cd, struct device_attribute *attr, char *buf) { struct media_devnode *devnode = to_media_devnode(cd); @@ -565,7 +565,7 @@ static ssize_t show_model(struct device *cd, return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); } -static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); +static DEVICE_ATTR_RO(model); /* ----------------------------------------------------------------------------- * Registration/unregistration diff --git a/drivers/media/pci/ivtv/ivtv-cards.h b/drivers/media/pci/ivtv/ivtv-cards.h index f3e2c5634962..c252733df340 100644 --- a/drivers/media/pci/ivtv/ivtv-cards.h +++ b/drivers/media/pci/ivtv/ivtv-cards.h @@ -78,27 +78,53 @@ #define IVTV_PCI_ID_SONY 0x104d /* hardware flags, no gaps allowed */ -#define IVTV_HW_CX25840 (1 << 0) -#define IVTV_HW_SAA7115 (1 << 1) -#define IVTV_HW_SAA7127 (1 << 2) -#define IVTV_HW_MSP34XX (1 << 3) -#define IVTV_HW_TUNER (1 << 4) -#define IVTV_HW_WM8775 (1 << 5) -#define IVTV_HW_CS53L32A (1 << 6) -#define IVTV_HW_TVEEPROM (1 << 7) -#define IVTV_HW_SAA7114 (1 << 8) -#define IVTV_HW_UPD64031A (1 << 9) -#define IVTV_HW_UPD6408X (1 << 10) -#define IVTV_HW_SAA717X (1 << 11) -#define IVTV_HW_WM8739 (1 << 12) -#define IVTV_HW_VP27SMPX (1 << 13) -#define IVTV_HW_M52790 (1 << 14) -#define IVTV_HW_GPIO (1 << 15) -#define IVTV_HW_I2C_IR_RX_AVER (1 << 16) -#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */ -#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18) -#define IVTV_HW_Z8F0811_IR_HAUP (1 << 19) -#define IVTV_HW_I2C_IR_RX_ADAPTEC (1 << 20) +enum ivtv_hw_bits { + IVTV_HW_BIT_CX25840, + IVTV_HW_BIT_SAA7115, + IVTV_HW_BIT_SAA7127, + IVTV_HW_BIT_MSP34XX, + IVTV_HW_BIT_TUNER, + IVTV_HW_BIT_WM8775, + IVTV_HW_BIT_CS53L32A, + IVTV_HW_BIT_TVEEPROM, + IVTV_HW_BIT_SAA7114, + IVTV_HW_BIT_UPD64031A, + IVTV_HW_BIT_UPD6408X, + IVTV_HW_BIT_SAA717X, + IVTV_HW_BIT_WM8739, + IVTV_HW_BIT_VP27SMPX, + IVTV_HW_BIT_M52790, + IVTV_HW_BIT_GPIO, + IVTV_HW_BIT_I2C_IR_RX_AVER, + IVTV_HW_BIT_I2C_IR_RX_HAUP_EXT, /* External before internal */ + IVTV_HW_BIT_I2C_IR_RX_HAUP_INT, + IVTV_HW_BIT_Z8F0811_IR_HAUP, + IVTV_HW_BIT_I2C_IR_RX_ADAPTEC, + + IVTV_HW_MAX_BITS /* Should be the last one */ +}; + +#define IVTV_HW_CX25840 BIT(IVTV_HW_BIT_CX25840) +#define IVTV_HW_SAA7115 BIT(IVTV_HW_BIT_SAA7115) +#define IVTV_HW_SAA7127 BIT(IVTV_HW_BIT_SAA7127) +#define IVTV_HW_MSP34XX BIT(IVTV_HW_BIT_MSP34XX) +#define IVTV_HW_TUNER BIT(IVTV_HW_BIT_TUNER) +#define IVTV_HW_WM8775 BIT(IVTV_HW_BIT_WM8775) +#define IVTV_HW_CS53L32A BIT(IVTV_HW_BIT_CS53L32A) +#define IVTV_HW_TVEEPROM BIT(IVTV_HW_BIT_TVEEPROM) +#define IVTV_HW_SAA7114 BIT(IVTV_HW_BIT_SAA7114) +#define IVTV_HW_UPD64031A BIT(IVTV_HW_BIT_UPD64031A) +#define IVTV_HW_UPD6408X BIT(IVTV_HW_BIT_UPD6408X) +#define IVTV_HW_SAA717X BIT(IVTV_HW_BIT_SAA717X) +#define IVTV_HW_WM8739 BIT(IVTV_HW_BIT_WM8739) +#define IVTV_HW_VP27SMPX BIT(IVTV_HW_BIT_VP27SMPX) +#define IVTV_HW_M52790 BIT(IVTV_HW_BIT_M52790) +#define IVTV_HW_GPIO BIT(IVTV_HW_BIT_GPIO) +#define IVTV_HW_I2C_IR_RX_AVER BIT(IVTV_HW_BIT_I2C_IR_RX_AVER) +#define IVTV_HW_I2C_IR_RX_HAUP_EXT BIT(IVTV_HW_BIT_I2C_IR_RX_HAUP_EXT) +#define IVTV_HW_I2C_IR_RX_HAUP_INT BIT(IVTV_HW_BIT_I2C_IR_RX_HAUP_INT) +#define IVTV_HW_Z8F0811_IR_HAUP BIT(IVTV_HW_BIT_Z8F0811_IR_HAUP) +#define IVTV_HW_I2C_IR_RX_ADAPTEC BIT(IVTV_HW_BIT_I2C_IR_RX_ADAPTEC) #define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 982045c4eea8..c052c57c6dce 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -85,7 +85,7 @@ #define IVTV_ADAPTEC_IR_ADDR 0x6b /* This array should match the IVTV_HW_ defines */ -static const u8 hw_addrs[] = { +static const u8 hw_addrs[IVTV_HW_MAX_BITS] = { IVTV_CX25840_I2C_ADDR, IVTV_SAA7115_I2C_ADDR, IVTV_SAA7127_I2C_ADDR, @@ -110,7 +110,7 @@ static const u8 hw_addrs[] = { }; /* This array should match the IVTV_HW_ defines */ -static const char * const hw_devicenames[] = { +static const char * const hw_devicenames[IVTV_HW_MAX_BITS] = { "cx25840", "saa7115", "saa7127_auto", /* saa7127 or saa7129 */ @@ -240,10 +240,16 @@ void ivtv_i2c_new_ir_legacy(struct ivtv *itv) int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { - struct v4l2_subdev *sd; struct i2c_adapter *adap = &itv->i2c_adap; - const char *type = hw_devicenames[idx]; - u32 hw = 1 << idx; + struct v4l2_subdev *sd; + const char *type; + u32 hw; + + if (idx >= IVTV_HW_MAX_BITS) + return -ENODEV; + + type = hw_devicenames[idx]; + hw = 1 << idx; if (hw == IVTV_HW_TUNER) { /* special tuner handling */ diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index 7a1fb067b0e0..fb24d2ed3621 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -1215,15 +1215,13 @@ static int alsa_device_exit(struct saa7134_dev *dev) static int saa7134_alsa_init(void) { struct saa7134_dev *dev = NULL; - struct list_head *list; saa7134_dmasound_init = alsa_device_init; saa7134_dmasound_exit = alsa_device_exit; pr_info("saa7134 ALSA driver for DMA sound loaded\n"); - list_for_each(list,&saa7134_devlist) { - dev = list_entry(list, struct saa7134_dev, devlist); + list_for_each_entry(dev, &saa7134_devlist, devlist) { if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) pr_info("%s/alsa: %s doesn't support digital audio\n", dev->name, saa7134_boards[dev->board].name); diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 47158ab3956b..96328b0af164 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -223,7 +223,8 @@ int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) __le32 *cpu; dma_addr_t dma_addr = 0; - cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); + cpu = dma_alloc_coherent(&pci->dev, SAA7134_PGTABLE_SIZE, &dma_addr, + GFP_KERNEL); if (NULL == cpu) return -ENOMEM; pt->size = SAA7134_PGTABLE_SIZE; @@ -254,7 +255,7 @@ void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) { if (NULL == pt->cpu) return; - pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); + dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma); pt->cpu = NULL; } @@ -1092,7 +1093,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->pci_lat, (unsigned long long)pci_resource_start(pci_dev, 0)); pci_set_master(pci_dev); - err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32)); if (err) { pr_warn("%s: Oops: no 32bit PCI DMA ???\n", dev->name); goto err_v4l2_unregister; diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c index 716162055fb7..a65d810ce212 100644 --- a/drivers/media/pci/saa7164/saa7164-cmd.c +++ b/drivers/media/pci/saa7164/saa7164-cmd.c @@ -549,9 +549,6 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, /* See of other commands are on the bus */ if (saa7164_cmd_dequeue(dev) != SAA_OK) printk(KERN_ERR "dequeue(3) failed\n"); - - continue; - } /* (loop) */ /* Release the sequence number allocation */ diff --git a/drivers/media/pci/tw5864/tw5864-reg.h b/drivers/media/pci/tw5864/tw5864-reg.h index a26a439c4dc0..25d4a9a6cc6b 100644 --- a/drivers/media/pci/tw5864/tw5864-reg.h +++ b/drivers/media/pci/tw5864/tw5864-reg.h @@ -663,7 +663,7 @@ #define TW5864_SYNC 0x8008 /* Define controls in register TW5864_SYNC */ /* - * 0 vlc stream to syncrous port + * 0 vlc stream to synchronous port * 1 vlc stream to ddr buffers */ #define TW5864_SYNC_CFG BIT(7) diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 925aa80a139b..b66f1d174e9d 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -255,6 +255,23 @@ static void isc_sama5d2_config_rlp(struct isc_device *isc) struct regmap *regmap = isc->regmap; u32 rlp_mode = isc->config.rlp_cfg_mode; + /* + * In sama5d2, the YUV planar modes and the YUYV modes are treated + * in the same way in RLP register. + * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n) + * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n) + * but in sama5d2, the YCYC mode does not exist, and YYCC must be + * selected for both planar and interleaved modes, as in fact + * both modes are supported. + * + * Thus, if the YCYC mode is selected, replace it with the + * sama5d2-compliant mode which is YYCC . + */ + if ((rlp_mode & ISC_RLP_CFG_MODE_YCYC) == ISC_RLP_CFG_MODE_YCYC) { + rlp_mode &= ~ISC_RLP_CFG_MODE_MASK; + rlp_mode |= ISC_RLP_CFG_MODE_YYCC; + } + regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, ISC_RLP_CFG_MODE_MASK, rlp_mode); } diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 2f42808c43a4..c484c008ab02 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -2053,17 +2053,25 @@ static int __coda_start_decoding(struct coda_ctx *ctx) u32 src_fourcc, dst_fourcc; int ret; + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + if (!ctx->initialized) { ret = __coda_decoder_seq_init(ctx); if (ret < 0) return ret; + } else { + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); + if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV) + ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | + ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR); } - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - src_fourcc = q_data_src->fourcc; - dst_fourcc = q_data_dst->fourcc; - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index f9f7dd17c57c..0a2226b321d7 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -189,7 +189,7 @@ int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev) if (!ccdc_cfg) { /* * TODO. Will this ever happen? if so, we need to fix it. - * Proabably we need to add the request to a linked list and + * Probably we need to add the request to a linked list and * walk through it during vpfe probe */ printk(KERN_ERR "vpfe capture not initialized\n"); diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 3b8a24bb724c..fa648721eaab 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1238,8 +1238,8 @@ static const struct media_device_ops fimc_md_ops = { .link_notify = fimc_md_link_notify, }; -static ssize_t fimc_md_sysfs_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t subdev_conf_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct fimc_md *fmd = dev_get_drvdata(dev); @@ -1249,9 +1249,9 @@ static ssize_t fimc_md_sysfs_show(struct device *dev, return strscpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); } -static ssize_t fimc_md_sysfs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t subdev_conf_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct fimc_md *fmd = dev_get_drvdata(dev); bool subdev_api; @@ -1278,8 +1278,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, * sub-dev - for media controller API, subdevs must be configured in user * space before starting streaming. */ -static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, - fimc_md_sysfs_show, fimc_md_sysfs_store); +static DEVICE_ATTR_RW(subdev_conf_mode); static int cam_clk_prepare(struct clk_hw *hw) { diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 070a0f3fc337..58f9463f3b8c 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -692,7 +692,7 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) * Scatter/gather mode requires stopping the controller between * frames so we can put in a new DMA descriptor array. If no new * buffer exists at frame completion, the controller is left stopped; - * this function is charged with gettig things going again. + * this function is charged with getting things going again. */ static void mcam_sg_restart(struct mcam_camera *cam) { diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 53025c8c7531..20f59c59ff8a 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2037,8 +2037,10 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) mutex_lock(&isp->media_dev.graph_mutex); ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); - if (ret) + if (ret) { + mutex_unlock(&isp->media_dev.graph_mutex); return ret; + } list_for_each_entry(sd, &v4l2_dev->subdevs, list) { if (sd->notifier != &isp->notifier) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 8df2d497d706..5ec851115eca 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -234,6 +234,7 @@ struct venc_controls { u32 h264_loop_filter_mode; s32 h264_loop_filter_alpha; s32 h264_loop_filter_beta; + u32 h264_8x8_transform; u32 hevc_i_qp; u32 hevc_p_qp; @@ -256,6 +257,7 @@ struct venc_controls { u32 header_mode; bool aud_enable; + u32 intra_refresh_period; struct { u32 h264; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 1fe6d463dc99..8012f5c7bf34 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1137,6 +1137,9 @@ int venus_helper_set_format_constraints(struct venus_inst *inst) if (!IS_V6(inst->core)) return 0; + if (inst->opb_fmt == HFI_COLOR_FORMAT_NV12_UBWC) + return 0; + pconstraint.buffer_type = HFI_BUFFER_OUTPUT2; pconstraint.num_planes = 2; pconstraint.plane_format[0].stride_multiples = 128; diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index f51024786991..60f4b8e4b8d0 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1239,6 +1239,14 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, break; } + case HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8: { + struct hfi_h264_8x8_transform *in = pdata, *tm = prop_data; + + tm->enable_type = in->enable_type; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*tm); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index b0a9beb4163c..bec4feb63ceb 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -507,6 +507,7 @@ #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC 0x2005021 #define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY 0x2005023 +#define HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8 0x2005025 #define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 #define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP 0x2005027 #define HFI_PROPERTY_PARAM_VENC_INITIAL_QP 0x2005028 @@ -562,6 +563,10 @@ struct hfi_bitrate { u32 layer_id; }; +struct hfi_h264_8x8_transform { + u32 enable_type; +}; + #define HFI_CAPABILITY_FRAME_WIDTH 0x01 #define HFI_CAPABILITY_FRAME_HEIGHT 0x02 #define HFI_CAPABILITY_MBS_PER_FRAME 0x03 diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index d9fde66f6fa8..9a2bdb002edc 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -261,7 +261,7 @@ sys_get_prop_image_version(struct device *dev, smem_tbl_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMG_VER_TBL, &smem_blk_sz); - if (smem_tbl_ptr && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ) + if (!IS_ERR(smem_tbl_ptr) && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ) memcpy(smem_tbl_ptr + SMEM_IMG_OFFSET_VENUS, img_ver, VER_STR_SZ); } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 8dd49d4f124c..bc1c42dd53c0 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -183,6 +183,8 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); + if (!fmt) + return NULL; } pixmp->width = clamp(pixmp->width, frame_width_min(inst), @@ -547,6 +549,7 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_quantization_range quant_range; struct hfi_enable en; struct hfi_ltr_mode ltr_mode; + struct hfi_intra_refresh intra_refresh = {}; u32 ptype, rate_control, bitrate; u32 profile, level; int ret; @@ -567,6 +570,7 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_h264_vui_timing_info info; struct hfi_h264_entropy_control entropy; struct hfi_h264_db_control deblock; + struct hfi_h264_8x8_transform h264_transform; ptype = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO; info.enable = 1; @@ -597,6 +601,17 @@ static int venc_set_properties(struct venus_inst *inst) ret = hfi_session_set_property(inst, ptype, &deblock); if (ret) return ret; + + ptype = HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8; + h264_transform.enable_type = 0; + if (ctr->profile.h264 == HFI_H264_PROFILE_HIGH || + ctr->profile.h264 == HFI_H264_PROFILE_CONSTRAINED_HIGH) + h264_transform.enable_type = ctr->h264_8x8_transform; + + ret = hfi_session_set_property(inst, ptype, &h264_transform); + if (ret) + return ret; + } if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || @@ -802,6 +817,31 @@ static int venc_set_properties(struct venus_inst *inst) en.enable = 1; ret = hfi_session_set_property(inst, ptype, &en); + } + + if ((inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) && + (rate_control == HFI_RATE_CONTROL_CBR_VFR || + rate_control == HFI_RATE_CONTROL_CBR_CFR)) { + intra_refresh.mode = HFI_INTRA_REFRESH_NONE; + intra_refresh.cir_mbs = 0; + + if (ctr->intra_refresh_period) { + u32 mbs; + + mbs = ALIGN(inst->width, 16) * ALIGN(inst->height, 16); + mbs /= 16 * 16; + if (mbs % ctr->intra_refresh_period) + mbs++; + mbs /= ctr->intra_refresh_period; + + intra_refresh.mode = HFI_INTRA_REFRESH_RANDOM; + intra_refresh.cir_mbs = mbs; + } + + ptype = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + + ret = hfi_session_set_property(inst, ptype, &intra_refresh); if (ret) return ret; } diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 637c92f6c5be..1ada42df314d 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -17,7 +17,6 @@ #define SLICE_BYTE_SIZE_MAX 1024 #define SLICE_BYTE_SIZE_MIN 1024 #define SLICE_MB_SIZE_MAX 300 -#define INTRA_REFRESH_MBS_MAX 300 #define AT_SLICE_BOUNDARY \ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY #define MAX_LTR_FRAME_COUNT 4 @@ -227,8 +226,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) } mutex_unlock(&inst->lock); break; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: - break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ret = venc_calc_bpframes(ctrl->val, ctr->num_b_frames, &bframes, &ctr->num_p_frames); @@ -319,6 +316,28 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY: ctr->mastering = *ctrl->p_new.p_hdr10_mastering; break; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: + ctr->intra_refresh_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + if (ctr->profile.h264 != HFI_H264_PROFILE_HIGH && + ctr->profile.h264 != HFI_H264_PROFILE_CONSTRAINED_HIGH) + return -EINVAL; + + /* + * In video firmware, 8x8 transform is supported only for + * high profile(HP) and constrained high profile(CHP). + * If client wants to disable 8x8 transform for HP/CHP, + * it is better to set profile as main profile(MP). + * Because there is no difference between HP and MP + * if we disable 8x8 transform for HP. + */ + + if (ctrl->val == 0) + return -EINVAL; + + ctr->h264_8x8_transform = ctrl->val; + break; default: return -EINVAL; } @@ -334,7 +353,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 57); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 58); if (ret) return ret; @@ -438,6 +457,9 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, @@ -503,10 +525,6 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, - 0, INTRA_REFRESH_MBS_MAX, 1, 0); - - v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 30); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, @@ -564,6 +582,10 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY, v4l2_ctrl_ptr_create(NULL)); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, 0, + ((4096 * 2304) >> 8), 1, 0); + ret = inst->ctrl_handler.error; if (ret) goto err; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index cca15a10c0b3..0d141155f0e3 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -253,8 +253,8 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, int ret; sd_state = v4l2_subdev_alloc_state(sd); - if (sd_state == NULL) - return -ENOMEM; + if (IS_ERR(sd_state)) + return PTR_ERR(sd_state); if (!rvin_format_from_pixel(vin, pix->pixelformat)) pix->pixelformat = RVIN_DEFAULT_FORMAT; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index bf3fd71ec3af..6759091b15e0 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -863,12 +863,12 @@ static int rga_probe(struct platform_device *pdev) if (IS_ERR(rga->m2m_dev)) { v4l2_err(&rga->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(rga->m2m_dev); - goto unreg_video_dev; + goto rel_vdev; } ret = pm_runtime_resume_and_get(rga->dev); if (ret < 0) - goto unreg_video_dev; + goto rel_vdev; rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; @@ -882,11 +882,23 @@ static int rga_probe(struct platform_device *pdev) rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE, &rga->cmdbuf_phy, GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); + if (!rga->cmdbuf_virt) { + ret = -ENOMEM; + goto rel_vdev; + } rga->src_mmu_pages = (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); + if (!rga->src_mmu_pages) { + ret = -ENOMEM; + goto free_dma; + } rga->dst_mmu_pages = (unsigned int *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 3); + if (rga->dst_mmu_pages) { + ret = -ENOMEM; + goto free_src_pages; + } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; def_frame.size = def_frame.stride * def_frame.height; @@ -894,7 +906,7 @@ static int rga_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; + goto free_dst_pages; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -902,10 +914,15 @@ static int rga_probe(struct platform_device *pdev) return 0; +free_dst_pages: + free_pages((unsigned long)rga->dst_mmu_pages, 3); +free_src_pages: + free_pages((unsigned long)rga->src_mmu_pages, 3); +free_dma: + dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, + rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_vdev: video_device_release(vfd); -unreg_video_dev: - video_unregister_device(rga->vfd); unreg_v4l2_dev: v4l2_device_unregister(&rga->v4l2_dev); err_put_clk: diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 60cd2200e7ae..41988eb0ec0a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -750,7 +750,7 @@ static int rkisp1_vb2_queue_setup(struct vb2_queue *queue, return 0; } -static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) +static int rkisp1_vb2_buf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rkisp1_buffer *ispbuf = @@ -780,6 +780,15 @@ static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) if (cap->pix.info->comp_planes == 3 && cap->pix.cfg->uv_swap) swap(ispbuf->buff_addr[RKISP1_PLANE_CR], ispbuf->buff_addr[RKISP1_PLANE_CB]); + return 0; +} + +static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_buffer *ispbuf = + container_of(vbuf, struct rkisp1_buffer, vb); + struct rkisp1_capture *cap = vb->vb2_queue->drv_priv; spin_lock_irq(&cap->buf.lock); list_add_tail(&ispbuf->queue, &cap->buf.queue); @@ -1039,6 +1048,7 @@ err_ret_buffers: static const struct vb2_ops rkisp1_vb2_ops = { .queue_setup = rkisp1_vb2_queue_setup, + .buf_init = rkisp1_vb2_buf_init, .buf_queue = rkisp1_vb2_buf_queue, .buf_prepare = rkisp1_vb2_buf_prepare, .wait_prepare = vb2_ops_wait_prepare, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 038c303a8aed..bb73f4e17b66 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -156,15 +156,11 @@ struct rkisp1_vdev_node { * @vb: vb2 buffer * @queue: entry of the buffer in the queue * @buff_addr: dma addresses of each plane, used only by the capture devices: selfpath, mainpath - * @vaddr: virtual address for buffers used by params and stats devices */ struct rkisp1_buffer { struct vb2_v4l2_buffer vb; struct list_head queue; - union { - u32 buff_addr[VIDEO_MAX_PLANES]; - void *vaddr; - }; + u32 buff_addr[VIDEO_MAX_PLANES]; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 529c6e21815f..8fa5b0abf1f9 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -1143,7 +1143,7 @@ static void rkisp1_params_apply_params_cfg(struct rkisp1_params *params, cur_buf = list_first_entry(¶ms->params, struct rkisp1_buffer, queue); - new_params = (struct rkisp1_params_cfg *)(cur_buf->vaddr); + new_params = (struct rkisp1_params_cfg *)vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0); rkisp1_isp_isr_other_config(params, new_params); rkisp1_isp_isr_meas_config(params, new_params); @@ -1382,7 +1382,6 @@ static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) struct vb2_queue *vq = vb->vb2_queue; struct rkisp1_params *params = vq->drv_priv; - params_buf->vaddr = vb2_plane_vaddr(vb, 0); spin_lock_irq(¶ms->config_lock); list_add_tail(¶ms_buf->queue, ¶ms->params); spin_unlock_irq(¶ms->config_lock); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c index c1d07a2e8839..e88bdd612d71 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c @@ -112,7 +112,6 @@ static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb) struct vb2_queue *vq = vb->vb2_queue; struct rkisp1_stats *stats_dev = vq->drv_priv; - stats_buf->vaddr = vb2_plane_vaddr(vb, 0); spin_lock_irq(&stats_dev->lock); list_add_tail(&stats_buf->queue, &stats_dev->stat); @@ -305,9 +304,8 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris) if (!cur_buf) return; - cur_stat_buf = - (struct rkisp1_stat_buffer *)(cur_buf->vaddr); - + cur_stat_buf = (struct rkisp1_stat_buffer *) + vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0); if (isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 49503c20d320..28a06dc343fd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1418,7 +1418,7 @@ static void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev) if (test_and_clear_bit(0, &dev->hw_lock) == 0) mfc_err("Failed to unlock hardware\n"); - /* This is in deed imporant, as no operation has been + /* This is indeed important, as no operation has been * scheduled, reduce the clock count as no one will * ever do this, because no interrupt related to this try_run * will ever come from hardware. */ diff --git a/drivers/media/platform/sti/delta/delta-ipc.c b/drivers/media/platform/sti/delta/delta-ipc.c index 186d88f02ecd..21d3e08e259a 100644 --- a/drivers/media/platform/sti/delta/delta-ipc.c +++ b/drivers/media/platform/sti/delta/delta-ipc.c @@ -175,8 +175,7 @@ int delta_ipc_open(struct delta_ctx *pctx, const char *name, msg.ipc_buf_size = ipc_buf_size; msg.ipc_buf_paddr = ctx->ipc_buf->paddr; - memcpy(msg.name, name, sizeof(msg.name)); - msg.name[sizeof(msg.name) - 1] = 0; + strscpy(msg.name, name, sizeof(msg.name)); msg.param_size = param->size; memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size); diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 124a4e2bdefe..4bf7a8c2e711 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -45,22 +45,30 @@ static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) * ------------------------------------------------------------------ */ -static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) +static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) { - struct v4l2_ctrl *ctrl; - s64 rate; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + const struct cal_format_info *fmtinfo; + u32 bpp; + s64 freq; + + fmtinfo = cal_format_by_code(phy->formats[CAL_CAMERARX_PAD_SINK].code); + if (!fmtinfo) + return -EINVAL; + + bpp = fmtinfo->bpp; + + freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes); + if (freq < 0) { + phy_err(phy, "failed to get link freq for subdev '%s'\n", + phy->source->name); + return freq; } - rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); + phy_dbg(3, phy, "Source Link Freq: %llu\n", freq); - return rate; + return freq; } static void cal_camerarx_lane_config(struct cal_camerarx *phy) @@ -116,34 +124,19 @@ void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate) +static void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ - /* - * CSI-2 is DDR and we only count used lanes. - * - * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * phy->fmtinfo->bpp; - */ - csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp, - 2 * num_lanes * 1000); - - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; + ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000); phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4; phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); @@ -240,25 +233,42 @@ static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error IRQs. */ + const u32 vc_err_mask = + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3); + + /* Enable CIO & VC error IRQs. */ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), + vc_err_mask); } static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { /* Disable CIO error irqs */ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0); } static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK); + + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -270,16 +280,21 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) static int cal_camerarx_start(struct cal_camerarx *phy) { - s64 external_rate; + s64 link_freq; u32 sscounter; u32 val; int ret; - external_rate = cal_camerarx_get_external_rate(phy); - if (external_rate < 0) - return external_rate; + if (phy->enable_count > 0) { + phy->enable_count++; + return 0; + } + + link_freq = cal_camerarx_get_ext_link_freq(phy); + if (link_freq < 0) + return link_freq; - ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + ret = v4l2_subdev_call(phy->source, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { phy_err(phy, "power on failed in subdev\n"); return ret; @@ -311,7 +326,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) * 2. CSI PHY and link initialization sequence. * * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion - * at this point, as it requires the external sensor to send the + * at this point, as it requires the external source to send the * CSI-2 HS clock. */ cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), @@ -325,7 +340,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate); + cal_camerarx_config(phy, link_freq); /* * b. Assert the FORCERXMODE signal. @@ -370,12 +385,12 @@ static int cal_camerarx_start(struct cal_camerarx *phy) cal_camerarx_power(phy, true); /* - * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * Start the source to enable the CSI-2 HS clock. We can now wait for * CSI-2 PHY reset to complete. */ - ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + ret = v4l2_subdev_call(phy->source, video, s_stream, 1); if (ret) { - v4l2_subdev_call(phy->sensor, core, s_power, 0); + v4l2_subdev_call(phy->source, core, s_power, 0); cal_camerarx_disable_irqs(phy); phy_err(phy, "stream on failed in subdev\n"); return ret; @@ -399,14 +414,18 @@ static int cal_camerarx_start(struct cal_camerarx *phy) /* Finally, enable the PHY Protocol Interface (PPI). */ cal_camerarx_ppi_enable(phy); + phy->enable_count++; + return 0; } static void cal_camerarx_stop(struct cal_camerarx *phy) { - unsigned int i; int ret; + if (--phy->enable_count > 0) + return; + cal_camerarx_ppi_disable(phy); cal_camerarx_disable_irqs(phy); @@ -418,27 +437,17 @@ static void cal_camerarx_stop(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n", phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, - (i >= 10) ? "(timeout)" : ""); + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Disable the phy */ cal_camerarx_disable(phy); - if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + if (v4l2_subdev_call(phy->source, video, s_stream, 0)) phy_err(phy, "stream off failed in subdev\n"); - ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + ret = v4l2_subdev_call(phy->source, core, s_power, 0); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) phy_err(phy, "power off failed in subdev\n"); } @@ -558,16 +567,16 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) endpoint->bus.mipi_csi2.flags); /* Retrieve the connected device and store it for later use. */ - phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node); - phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node); - if (!phy->sensor_node) { + phy->source_ep_node = of_graph_get_remote_endpoint(ep_node); + phy->source_node = of_graph_get_port_parent(phy->source_ep_node); + if (!phy->source_node) { phy_dbg(3, phy, "Can't get remote parent\n"); - of_node_put(phy->sensor_ep_node); + of_node_put(phy->source_ep_node); ret = -EINVAL; goto done; } - phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node); done: of_node_put(ep_node); @@ -602,12 +611,18 @@ cal_camerarx_get_pad_format(struct cal_camerarx *phy, static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) { struct cal_camerarx *phy = to_cal_camerarx(sd); + int ret = 0; + + mutex_lock(&phy->mutex); if (enable) - return cal_camerarx_start(phy); + ret = cal_camerarx_start(phy); + else + cal_camerarx_stop(phy); - cal_camerarx_stop(phy); - return 0; + mutex_unlock(&phy->mutex); + + return ret; } static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, @@ -615,27 +630,36 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct cal_camerarx *phy = to_cal_camerarx(sd); + int ret = 0; + + mutex_lock(&phy->mutex); /* No transcoding, source and sink codes must match. */ - if (code->pad == CAL_CAMERARX_PAD_SOURCE) { + if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; - if (code->index > 0) - return -EINVAL; + if (code->index > 0) { + ret = -EINVAL; + goto out; + } fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, code->which); code->code = fmt->code; - return 0; - } + } else { + if (code->index >= cal_num_formats) { + ret = -EINVAL; + goto out; + } - if (code->index >= cal_num_formats) - return -EINVAL; + code->code = cal_formats[code->index].code; + } - code->code = cal_formats[code->index].code; +out: + mutex_unlock(&phy->mutex); - return 0; + return ret; } static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, @@ -644,38 +668,46 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, { struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; + int ret = 0; if (fse->index > 0) return -EINVAL; + mutex_lock(&phy->mutex); + /* No transcoding, source and sink formats must match. */ - if (fse->pad == CAL_CAMERARX_PAD_SOURCE) { + if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, fse->which); - if (fse->code != fmt->code) - return -EINVAL; + if (fse->code != fmt->code) { + ret = -EINVAL; + goto out; + } fse->min_width = fmt->width; fse->max_width = fmt->width; fse->min_height = fmt->height; fse->max_height = fmt->height; + } else { + fmtinfo = cal_format_by_code(fse->code); + if (!fmtinfo) { + ret = -EINVAL; + goto out; + } - return 0; + fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_height = CAL_MIN_HEIGHT_LINES; + fse->max_height = CAL_MAX_HEIGHT_LINES; } - fmtinfo = cal_format_by_code(fse->code); - if (!fmtinfo) - return -EINVAL; - - fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); - fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); - fse->min_height = CAL_MIN_HEIGHT_LINES; - fse->max_height = CAL_MAX_HEIGHT_LINES; +out: + mutex_unlock(&phy->mutex); - return 0; + return ret; } static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, @@ -685,10 +717,14 @@ static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, struct cal_camerarx *phy = to_cal_camerarx(sd); struct v4l2_mbus_framefmt *fmt; + mutex_lock(&phy->mutex); + fmt = cal_camerarx_get_pad_format(phy, sd_state, format->pad, format->which); format->format = *fmt; + mutex_unlock(&phy->mutex); + return 0; } @@ -702,21 +738,18 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, unsigned int bpp; /* No transcoding, source and sink formats must match. */ - if (format->pad == CAL_CAMERARX_PAD_SOURCE) + if (cal_rx_pad_is_source(format->pad)) return cal_camerarx_sd_get_fmt(sd, sd_state, format); /* - * Default to the first format is the requested media bus code isn't + * Default to the first format if the requested media bus code isn't * supported. */ fmtinfo = cal_format_by_code(format->format.code); if (!fmtinfo) fmtinfo = &cal_formats[0]; - /* - * Clamp the size, update the code. The field and colorspace are - * accepted as-is. - */ + /* Clamp the size, update the code. The colorspace is accepted as-is. */ bpp = ALIGN(fmtinfo->bpp, 8); format->format.width = clamp_t(unsigned int, format->format.width, @@ -726,20 +759,23 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, CAL_MIN_HEIGHT_LINES, CAL_MAX_HEIGHT_LINES); format->format.code = fmtinfo->code; + format->format.field = V4L2_FIELD_NONE; /* Store the format and propagate it to the source pad. */ + + mutex_lock(&phy->mutex); + fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, format->which); *fmt = format->format; fmt = cal_camerarx_get_pad_format(phy, sd_state, - CAL_CAMERARX_PAD_SOURCE, + CAL_CAMERARX_PAD_FIRST_SOURCE, format->which); *fmt = format->format; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - phy->fmtinfo = fmtinfo; + mutex_unlock(&phy->mutex); return 0; } @@ -798,6 +834,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; struct v4l2_subdev *sd; + unsigned int i; int ret; phy = kzalloc(sizeof(*phy), GFP_KERNEL); @@ -807,6 +844,8 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + mutex_init(&phy->mutex); + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? "cal_rx_core0" : @@ -838,14 +877,17 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, sd->dev = cal->dev; phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i) + phy->pads[i].flags = MEDIA_PAD_FL_SOURCE; sd->entity.ops = &cal_camerarx_media_ops; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), phy->pads); if (ret) goto error; - cal_camerarx_sd_init_cfg(sd, NULL); + ret = cal_camerarx_sd_init_cfg(sd, NULL); + if (ret) + goto error; ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); if (ret) @@ -866,7 +908,8 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) v4l2_device_unregister_subdev(&phy->subdev); media_entity_cleanup(&phy->subdev.entity); - of_node_put(phy->sensor_ep_node); - of_node_put(phy->sensor_node); + of_node_put(phy->source_ep_node); + of_node_put(phy->source_node); + mutex_destroy(&phy->mutex); kfree(phy); } diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 15fb5360cf13..7799da1cc261 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -102,8 +102,8 @@ static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx, return NULL; } -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int cal_legacy_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; @@ -128,7 +128,7 @@ static int __subdev_get_format(struct cal_ctx *ctx, sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -151,7 +151,7 @@ static int __subdev_set_format(struct cal_ctx *ctx, sd_fmt.pad = 0; *mbus_fmt = *fmt; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -189,8 +189,8 @@ static void cal_calc_format_size(struct cal_ctx *ctx, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); } -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; @@ -216,7 +216,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, &fse); if (ret) break; @@ -249,8 +249,8 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); struct vb2_queue *q = &ctx->vb_vidq; @@ -266,7 +266,7 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, return -EBUSY; } - ret = cal_try_fmt_vid_cap(file, priv, f); + ret = cal_legacy_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; @@ -300,8 +300,8 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) +static int cal_legacy_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; @@ -321,7 +321,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -337,8 +337,8 @@ static int cal_enum_framesizes(struct file *file, void *fh, return 0; } -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) +static int cal_legacy_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) { if (inp->index > 0) return -EINVAL; @@ -348,20 +348,20 @@ static int cal_enum_input(struct file *file, void *priv, return 0; } -static int cal_g_input(struct file *file, void *priv, unsigned int *i) +static int cal_legacy_g_input(struct file *file, void *priv, unsigned int *i) { *i = 0; return 0; } -static int cal_s_input(struct file *file, void *priv, unsigned int i) +static int cal_legacy_s_input(struct file *file, void *priv, unsigned int i) { return i > 0 ? -EINVAL : 0; } /* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) +static int cal_legacy_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) { struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; @@ -378,7 +378,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fie.code = fmtinfo->code; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -388,13 +388,27 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return 0; } -static const struct v4l2_ioctl_ops cal_ioctl_video_ops = { +static int cal_legacy_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct cal_ctx *ctx = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), ctx->phy->source, a); +} + +static int cal_legacy_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct cal_ctx *ctx = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), ctx->phy->source, a); +} + +static const struct v4l2_ioctl_ops cal_ioctl_legacy_ops = { .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = cal_legacy_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_try_fmt_vid_cap = cal_legacy_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_legacy_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_legacy_enum_framesizes, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, @@ -402,15 +416,17 @@ static const struct v4l2_ioctl_ops cal_ioctl_video_ops = { .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_enum_input = cal_legacy_enum_input, + .vidioc_g_input = cal_legacy_g_input, + .vidioc_s_input = cal_legacy_s_input, + .vidioc_enum_frameintervals = cal_legacy_enum_frameintervals, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_parm = cal_legacy_g_parm, + .vidioc_s_parm = cal_legacy_s_parm, }; /* ------------------------------------------------------------------ @@ -421,13 +437,28 @@ static const struct v4l2_ioctl_ops cal_ioctl_video_ops = { static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { + unsigned int i; + unsigned int idx; + if (f->index >= cal_num_formats) return -EINVAL; - f->pixelformat = cal_formats[f->index].fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + idx = 0; - return 0; + for (i = 0; i < cal_num_formats; ++i) { + if (f->mbus_code && cal_formats[i].code != f->mbus_code) + continue; + + if (idx == f->index) { + f->pixelformat = cal_formats[i].fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + + idx++; + } + + return -EINVAL; } static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f, @@ -656,8 +687,13 @@ static void cal_release_buffers(struct cal_ctx *ctx, static int cal_video_check_format(struct cal_ctx *ctx) { const struct v4l2_mbus_framefmt *format; + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_pad(&ctx->pad); + if (!remote_pad) + return -ENODEV; - format = &ctx->phy->formats[CAL_CAMERARX_PAD_SOURCE]; + format = &ctx->phy->formats[remote_pad->index]; if (ctx->fmtinfo->code != format->code || ctx->v_fmt.fmt.pix.height != format->height || @@ -692,9 +728,15 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto error_pipeline; } + ret = cal_ctx_prepare(ctx); + if (ret) { + ctx_err(ctx, "Failed to prepare context: %d\n", ret); + goto error_pipeline; + } + spin_lock_irq(&ctx->dma.lock); buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); - ctx->dma.pending = buf; + ctx->dma.active = buf; list_del(&buf->list); spin_unlock_irq(&ctx->dma.lock); @@ -719,6 +761,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) error_stop: cal_ctx_stop(ctx); pm_runtime_put_sync(ctx->cal->dev); + cal_ctx_unprepare(ctx); error_pipeline: media_pipeline_stop(&ctx->vdev.entity); @@ -738,6 +781,8 @@ static void cal_stop_streaming(struct vb2_queue *vq) pm_runtime_put_sync(ctx->cal->dev); + cal_ctx_unprepare(ctx); + cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); media_pipeline_stop(&ctx->vdev.entity); @@ -785,20 +830,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, NULL, &mbus_code); if (ret == -EINVAL) break; if (ret) { ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n", - ctx->phy->sensor->name, ret); + ctx->phy->source->name, ret); return ret; } ctx_dbg(2, ctx, "subdev %s: code: %04x idx: %u\n", - ctx->phy->sensor->name, mbus_code.code, j); + ctx->phy->source->name, mbus_code.code, j); for (k = 0; k < cal_num_formats; k++) { fmtinfo = &cal_formats[k]; @@ -816,7 +861,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (i == 0) { ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->sensor->name); + ctx->phy->source->name); return -EINVAL; } @@ -841,22 +886,57 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) return 0; } +static int cal_ctx_v4l2_init_mc_format(struct cal_ctx *ctx) +{ + const struct cal_format_info *fmtinfo; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; + + fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_2X8); + if (!fmtinfo) + return -EINVAL; + + pix_fmt->width = 640; + pix_fmt->height = 480; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + pix_fmt->pixelformat = fmtinfo->fourcc; + + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* Save current format */ + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + ctx->fmtinfo = fmtinfo; + + return 0; +} + int cal_ctx_v4l2_register(struct cal_ctx *ctx) { struct video_device *vfd = &ctx->vdev; int ret; - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - return ret; - if (!cal_mc_api) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) { + ctx_err(ctx, "Failed to init formats: %d\n", ret); + return ret; + } + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->source->ctrl_handler, NULL, true); if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + ctx_err(ctx, "Failed to add source ctrl handler\n"); + return ret; + } + } else { + ret = cal_ctx_v4l2_init_mc_format(ctx); + if (ret) { + ctx_err(ctx, "Failed to init format: %d\n", ret); return ret; } } @@ -868,13 +948,13 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx) } ret = media_create_pad_link(&ctx->phy->subdev.entity, - CAL_CAMERARX_PAD_SOURCE, + CAL_CAMERARX_PAD_FIRST_SOURCE, &vfd->entity, 0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret) { ctx_err(ctx, "Failed to create media link for context %u\n", - ctx->index); + ctx->dma_ctx); video_unregister_device(vfd); return ret; } @@ -926,9 +1006,9 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) | (cal_mc_api ? V4L2_CAP_IO_MC : 0); vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; - snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->dma_ctx); vfd->release = video_device_release_empty; - vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_video_ops; + vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_legacy_ops; vfd->lock = &ctx->mutex; video_set_drvdata(vfd, ctx); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 76fe7a8b33f6..8e469d518a74 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -290,11 +290,42 @@ void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ +#define CAL_MAX_PIX_PROC 4 + +static int cal_reserve_pix_proc(struct cal_dev *cal) +{ + unsigned long ret; + + spin_lock(&cal->v4l2_dev.lock); + + ret = find_first_zero_bit(&cal->reserved_pix_proc_mask, CAL_MAX_PIX_PROC); + + if (ret == CAL_MAX_PIX_PROC) { + spin_unlock(&cal->v4l2_dev.lock); + return -ENOSPC; + } + + cal->reserved_pix_proc_mask |= BIT(ret); + + spin_unlock(&cal->v4l2_dev.lock); + + return ret; +} + +static void cal_release_pix_proc(struct cal_dev *cal, unsigned int pix_proc_num) +{ + spin_lock(&cal->v4l2_dev.lock); + + cal->reserved_pix_proc_mask &= ~BIT(pix_proc_num); + + spin_unlock(&cal->v4l2_dev.lock); +} + static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); + val = cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx)); cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs @@ -304,15 +335,16 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) * 0x2A: RAW8 1 pixel = 1 byte * 0x1E: YUV422 2 pixels = 4 bytes */ - cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->datatype, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, ctx->vc, CAL_CSI2_CTX_VC_MASK); cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); - cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); + cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX(%u, %u) = 0x%08x\n", + ctx->phy->instance, ctx->csi2_ctx, + cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx))); } static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) @@ -354,16 +386,16 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) break; } - val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc)); cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%u) = 0x%08x\n", ctx->pix_proc, + cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc))); } static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) @@ -371,27 +403,25 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; u32 val; - val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_WR_DMA_CTRL_YSIZE_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, CAL_WR_DMA_CTRL_DTAG_MASK); - cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx))); - cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index), + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx), stride / 16, CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx))); - val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx)); /* 64 bit word means no skipping */ cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* @@ -400,34 +430,32 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) * written per line. */ cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK); - cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, - cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); - - val = cal_read(ctx->cal, CAL_CTRL); - cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, - CAL_CTRL_BURSTSIZE_MASK); - cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - cal_write(ctx->cal, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->dma_ctx, + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx))); } void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) { - cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->dma_ctx), addr); +} + +static void cal_ctx_wr_dma_enable(struct cal_ctx *ctx) +{ + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); + + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); } static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) { - u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS, CAL_WR_DMA_CTRL_MODE_MASK); - cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); } static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) @@ -441,6 +469,31 @@ static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) return stopped; } +int cal_ctx_prepare(struct cal_ctx *ctx) +{ + int ret; + + ctx->use_pix_proc = !ctx->fmtinfo->meta; + + if (ctx->use_pix_proc) { + ret = cal_reserve_pix_proc(ctx->cal); + if (ret < 0) { + ctx_err(ctx, "Failed to reserve pix proc: %d\n", ret); + return ret; + } + + ctx->pix_proc = ret; + } + + return 0; +} + +void cal_ctx_unprepare(struct cal_ctx *ctx) +{ + if (ctx->use_pix_proc) + cal_release_pix_proc(ctx->cal, ctx->pix_proc); +} + void cal_ctx_start(struct cal_ctx *ctx) { ctx->sequence = 0; @@ -448,14 +501,17 @@ void cal_ctx_start(struct cal_ctx *ctx) /* Configure the CSI-2, pixel processing and write DMA contexts. */ cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); + if (ctx->use_pix_proc) + cal_ctx_pix_proc_config(ctx); cal_ctx_wr_dma_config(ctx); /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), - CAL_HL_IRQ_MASK(ctx->index)); + CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), - CAL_HL_IRQ_MASK(ctx->index)); + CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); + + cal_ctx_wr_dma_enable(ctx); } void cal_ctx_stop(struct cal_ctx *ctx) @@ -479,11 +535,18 @@ void cal_ctx_stop(struct cal_ctx *ctx) /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), - CAL_HL_IRQ_MASK(ctx->index)); + CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), - CAL_HL_IRQ_MASK(ctx->index)); + CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); ctx->dma.state = CAL_DMA_STOPPED; + + /* Disable CSI2 context */ + cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), 0); + + /* Disable pix proc */ + if (ctx->use_pix_proc) + cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), 0); } /* ------------------------------------------------------------------ @@ -577,6 +640,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } + + if (status & CAL_HL_IRQ_VC_MASK(i)) { + u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i)); + + dev_err_ratelimited(cal->dev, + "CIO%u VC error: %#08x\n", + i, vc_stat); + + cal_write(cal, CAL_CSI2_VC_IRQSTATUS(i), vc_stat); + } } } @@ -588,8 +661,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ cal_write(cal, CAL_HL_IRQSTATUS(1), status); - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) + for (i = 0; i < cal->num_contexts; ++i) { + if (status & CAL_HL_IRQ_WDMA_END_MASK(i)) cal_irq_wdma_end(cal->ctx[i]); } } @@ -602,8 +675,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ cal_write(cal, CAL_HL_IRQSTATUS(2), status); - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) + for (i = 0; i < cal->num_contexts; ++i) { + if (status & CAL_HL_IRQ_WDMA_START_MASK(i)) cal_irq_wdma_start(cal->ctx[i]); } } @@ -635,20 +708,20 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, int pad; int ret; - if (phy->sensor) { + if (phy->source) { phy_info(phy, "Rejecting subdev %s (Already set!!)", subdev->name); return 0; } - phy->sensor = subdev; - phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); + phy->source = subdev; + phy_dbg(1, phy, "Using source %s for capture\n", subdev->name); pad = media_entity_get_fwnode_pad(&subdev->entity, - of_fwnode_handle(phy->sensor_ep_node), + of_fwnode_handle(phy->source_ep_node), MEDIA_PAD_FL_SOURCE); if (pad < 0) { - phy_err(phy, "Sensor %s has no connected source pad\n", + phy_err(phy, "Source %s has no connected source pad\n", subdev->name); return pad; } @@ -658,7 +731,7 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret) { - phy_err(phy, "Failed to create media link for sensor %s\n", + phy_err(phy, "Failed to create media link for source %s\n", subdev->name); return ret; } @@ -670,15 +743,30 @@ static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); unsigned int i; - int ret = 0; + int ret; - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (cal->ctx[i]) - cal_ctx_v4l2_register(cal->ctx[i]); + for (i = 0; i < cal->num_contexts; ++i) { + ret = cal_ctx_v4l2_register(cal->ctx[i]); + if (ret) + goto err_ctx_unreg; } - if (cal_mc_api) - ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); + if (!cal_mc_api) + return 0; + + ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); + if (ret) + goto err_ctx_unreg; + + return 0; + +err_ctx_unreg: + for (; i > 0; --i) { + if (!cal->ctx[i - 1]) + continue; + + cal_ctx_v4l2_unregister(cal->ctx[i - 1]); + } return ret; } @@ -701,10 +789,10 @@ static int cal_async_notifier_register(struct cal_dev *cal) struct cal_v4l2_async_subdev *casd; struct fwnode_handle *fwnode; - if (!phy->sensor_node) + if (!phy->source_node) continue; - fwnode = of_fwnode_handle(phy->sensor_node); + fwnode = of_fwnode_handle(phy->source_node); casd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, fwnode, struct cal_v4l2_async_subdev); @@ -777,10 +865,8 @@ static void cal_media_unregister(struct cal_dev *cal) unsigned int i; /* Unregister all the V4L2 video devices. */ - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_unregister(cal->ctx[i]); - } + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_v4l2_unregister(cal->ctx[i]); cal_async_notifier_unregister(cal); media_device_unregister(&cal->mdev); @@ -825,13 +911,6 @@ static int cal_media_init(struct cal_dev *cal) */ static void cal_media_cleanup(struct cal_dev *cal) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_cleanup(cal->ctx[i]); - } - v4l2_device_unregister(&cal->v4l2_dev); media_device_cleanup(&cal->mdev); @@ -848,14 +927,17 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) struct cal_ctx *ctx; int ret; - ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; ctx->cal = cal; ctx->phy = cal->phy[inst]; - ctx->index = inst; + ctx->dma_ctx = inst; + ctx->csi2_ctx = inst; ctx->cport = inst; + ctx->vc = 0; + ctx->datatype = CAL_CSI2_CTX_DT_ANY; ret = cal_ctx_v4l2_init(ctx); if (ret) @@ -864,6 +946,13 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) return ctx; } +static void cal_ctx_destroy(struct cal_ctx *ctx) +{ + cal_ctx_v4l2_cleanup(ctx); + + kfree(ctx); +} + static const struct of_device_id cal_of_match[] = { { .compatible = "ti,dra72-cal", @@ -976,7 +1065,6 @@ static int cal_init_camerarx_regmap(struct cal_dev *cal) static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; - struct cal_ctx *ctx; bool connected = false; unsigned int i; int ret; @@ -1045,7 +1133,7 @@ static int cal_probe(struct platform_device *pdev) goto error_camerarx; } - if (cal->phy[i]->sensor_node) + if (cal->phy[i]->source_node) connected = true; } @@ -1057,15 +1145,17 @@ static int cal_probe(struct platform_device *pdev) /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { - if (!cal->phy[i]->sensor_node) + if (!cal->phy[i]->source_node) continue; - cal->ctx[i] = cal_ctx_create(cal, i); - if (!cal->ctx[i]) { - cal_err(cal, "Failed to create context %u\n", i); + cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i); + if (!cal->ctx[cal->num_contexts]) { + cal_err(cal, "Failed to create context %u\n", cal->num_contexts); ret = -ENODEV; goto error_context; } + + cal->num_contexts++; } /* Register the media device. */ @@ -1076,11 +1166,8 @@ static int cal_probe(struct platform_device *pdev) return 0; error_context: - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - ctx = cal->ctx[i]; - if (ctx) - cal_ctx_v4l2_cleanup(ctx); - } + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_destroy(cal->ctx[i]); error_camerarx: for (i = 0; i < cal->data->num_csi2_phy; i++) @@ -1106,16 +1193,17 @@ static int cal_remove(struct platform_device *pdev) cal_media_unregister(cal); - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { - if (cal->phy[i]) - cal_camerarx_disable(cal->phy[i]); - } + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_disable(cal->phy[i]); - cal_media_cleanup(cal); + for (i = 0; i < cal->num_contexts; i++) + cal_ctx_destroy(cal->ctx[i]); for (i = 0; i < cal->data->num_csi2_phy; i++) cal_camerarx_destroy(cal->phy[i]); + cal_media_cleanup(cal); + if (ret >= 0) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1127,6 +1215,7 @@ static int cal_runtime_resume(struct device *dev) { struct cal_dev *cal = dev_get_drvdata(dev); unsigned int i; + u32 val; if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* @@ -1143,6 +1232,17 @@ static int cal_runtime_resume(struct device *dev) */ cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + val = cal_read(cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xf, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xff, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xff, CAL_CTRL_MFLAGH_MASK); + cal_write(cal, CAL_CTRL, val); + cal_dbg(3, cal, "CAL_CTRL = 0x%08x\n", cal_read(cal, CAL_CTRL)); + return 0; } diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index db0e408eaa94..527e22d022f3 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -29,7 +29,7 @@ #include <media/videobuf2-v4l2.h> #define CAL_MODULE_NAME "cal" -#define CAL_NUM_CONTEXT 2 +#define CAL_MAX_NUM_CONTEXT 8 #define CAL_NUM_CSI2_PORTS 2 /* @@ -44,7 +44,22 @@ #define CAL_MAX_HEIGHT_LINES 16383 #define CAL_CAMERARX_PAD_SINK 0 -#define CAL_CAMERARX_PAD_SOURCE 1 +#define CAL_CAMERARX_PAD_FIRST_SOURCE 1 +#define CAL_CAMERARX_NUM_SOURCE_PADS 1 +#define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS) + +static inline bool cal_rx_pad_is_sink(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad == 0; +} + +static inline bool cal_rx_pad_is_source(u32 pad) +{ + /* Camera RX has 1 sink pad, and N source pads */ + return pad >= CAL_CAMERARX_PAD_FIRST_SOURCE && + pad <= CAL_CAMERARX_NUM_SOURCE_PADS; +} struct device; struct device_node; @@ -73,6 +88,7 @@ struct cal_format_info { u32 code; /* Bits per pixel */ u8 bpp; + bool meta; }; /* buffer for one video frame */ @@ -149,22 +165,29 @@ struct cal_data { struct cal_camerarx { void __iomem *base; struct resource *res; - struct device *dev; struct regmap_field *fields[F_MAX_FIELDS]; struct cal_dev *cal; unsigned int instance; struct v4l2_fwnode_endpoint endpoint; - struct device_node *sensor_ep_node; - struct device_node *sensor_node; - struct v4l2_subdev *sensor; + struct device_node *source_ep_node; + struct device_node *source_node; + struct v4l2_subdev *source; struct media_pipeline pipe; struct v4l2_subdev subdev; - struct media_pad pads[2]; - struct v4l2_mbus_framefmt formats[2]; - const struct cal_format_info *fmtinfo; + struct media_pad pads[CAL_CAMERARX_NUM_PADS]; + struct v4l2_mbus_framefmt formats[CAL_CAMERARX_NUM_PADS]; + + /* + * Lock for camerarx ops. Protects: + * - formats + * - enable_count + */ + struct mutex mutex; + + unsigned int enable_count; }; struct cal_dev { @@ -184,11 +207,14 @@ struct cal_dev { /* Camera Core Module handle */ struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + u32 num_contexts; + struct cal_ctx *ctx[CAL_MAX_NUM_CONTEXT]; struct media_device mdev; struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; + + unsigned long reserved_pix_proc_mask; }; /* @@ -212,14 +238,20 @@ struct cal_ctx { /* Used to store current pixel format */ struct v4l2_format v_fmt; - /* Current subdev enumerated format */ + /* Current subdev enumerated format (legacy) */ const struct cal_format_info **active_fmt; unsigned int num_active_fmt; unsigned int sequence; struct vb2_queue vb_vidq; - unsigned int index; - unsigned int cport; + u8 dma_ctx; + u8 cport; + u8 csi2_ctx; + u8 pix_proc; + u8 vc; + u8 datatype; + + bool use_pix_proc; }; extern unsigned int cal_debug; @@ -237,11 +269,11 @@ extern bool cal_mc_api; dev_err((cal)->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) #define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) #define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->dma_ctx, ##arg) #define phy_dbg(level, phy, fmt, arg...) \ cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) @@ -297,6 +329,8 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance); void cal_camerarx_destroy(struct cal_camerarx *phy); +int cal_ctx_prepare(struct cal_ctx *ctx); +void cal_ctx_unprepare(struct cal_ctx *ctx); void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); void cal_ctx_start(struct cal_ctx *ctx); void cal_ctx_stop(struct cal_ctx *ctx); diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index f752096dcf7f..40e4f972fcb7 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -72,22 +72,8 @@ #define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) #define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) #define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) -#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) -#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) -#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) -#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) -#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) -#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) -#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) -#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) -#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) -#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) -#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) -#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) -#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) -#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) -#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) -#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) +#define CAL_CSI2_CTX(phy, csi2_ctx) (0x330U + (phy) * 0x80U + (csi2_ctx) * 4) +#define CAL_CSI2_STATUS(phy, csi2_ctx) (0x350U + (phy) * 0x80U + (csi2_ctx) * 4) /* CAL CSI2 PHY register offsets */ #define CAL_CSI2_PHY_REG0 0x000 @@ -139,7 +125,8 @@ #define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 -#define CAL_HL_IRQ_MASK(m) BIT(m) +#define CAL_HL_IRQ_WDMA_END_MASK(m) BIT(m) +#define CAL_HL_IRQ_WDMA_START_MASK(m) BIT(m) #define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) @@ -419,32 +406,16 @@ #define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT(14) #define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT(15) -#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT(0) -#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT(1) -#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT(2) -#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT(3) -#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT(4) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT(5) -#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT(8) -#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT(9) -#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT(10) -#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT(11) -#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT(12) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT(13) -#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT(16) -#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT(17) -#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT(18) -#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT(19) -#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT(20) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT(21) -#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT(24) -#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT(25) -#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT(26) -#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT(27) -#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT(28) -#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT(29) +#define CAL_CSI2_VC_IRQ_FS_IRQ_MASK(n) BIT(0 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_FE_IRQ_MASK(n) BIT(1 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_LS_IRQ_MASK(n) BIT(2 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_LE_IRQ_MASK(n) BIT(3 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_CS_IRQ_MASK(n) BIT(4 + ((n) * 8)) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(n) BIT(5 + ((n) * 8)) #define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0) +#define CAL_CSI2_CTX_DT_DISABLED 0 +#define CAL_CSI2_CTX_DT_ANY 1 #define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6) #define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8) #define CAL_CSI2_CTX_ATT_MASK BIT(13) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 6f51e5c75543..823c15facd1b 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -676,9 +676,9 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, * rectangles. */ entity->config = v4l2_subdev_alloc_state(&entity->subdev); - if (entity->config == NULL) { + if (IS_ERR(entity->config)) { media_entity_cleanup(&entity->subdev.entity); - return -ENOMEM; + return PTR_ERR(entity->config); } return 0; diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index d0a8326b75c2..fd5a7a058714 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -246,6 +246,16 @@ config IR_MESON To compile this driver as a module, choose M here: the module will be called meson-ir. +config IR_MESON_TX + tristate "Amlogic Meson IR TX" + depends on ARCH_MESON || COMPILE_TEST + help + Say Y if you want to use the IR transmitter available on + Amlogic Meson SoCs. + + To compile this driver as a module, choose M here: the + module will be called meson-ir-tx. + config IR_MTK tristate "Mediatek IR remote receiver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 692e9b6b203f..0db51fad27d6 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o obj-$(CONFIG_IR_FINTEK) += fintek-cir.o obj-$(CONFIG_IR_MESON) += meson-ir.o +obj-$(CONFIG_IR_MESON_TX) += meson-ir-tx.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o obj-$(CONFIG_IR_REDRAT3) += redrat3.o diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 6049e5c95394..e09270916fbc 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1052,7 +1052,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) rdev->device_name = "ENE eHome Infrared Remote Receiver"; if (dev->hw_learning_and_tx_capable) { - rdev->s_learning_mode = ene_set_learning_mode; + rdev->s_wideband_receiver = ene_set_learning_mode; init_completion(&dev->tx_complete); rdev->tx_ir = ene_transmit; rdev->s_tx_mask = ene_set_tx_mask; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 116daf90c858..7f591ff5269d 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -412,7 +412,7 @@ static long lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) val |= LIRC_CAN_SET_REC_CARRIER | LIRC_CAN_SET_REC_CARRIER_RANGE; - if (dev->s_learning_mode) + if (dev->s_wideband_receiver) val |= LIRC_CAN_USE_WIDEBAND_RECEIVER; if (dev->s_carrier_report) @@ -519,10 +519,10 @@ static long lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case LIRC_SET_WIDEBAND_RECEIVER: - if (!dev->s_learning_mode) + if (!dev->s_wideband_receiver) ret = -ENOTTY; else - ret = dev->s_learning_mode(dev, !!val); + ret = dev->s_wideband_receiver(dev, !!val); break; case LIRC_SET_MEASURE_CARRIER_MODE: diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 5642595a057e..e03dd1f0144f 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1630,7 +1630,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->tx_ir = mceusb_tx_ir; } if (ir->flags.rx2 > 0) { - rc->s_learning_mode = mceusb_set_rx_wideband; + rc->s_wideband_receiver = mceusb_set_rx_wideband; rc->s_carrier_report = mceusb_set_rx_carrier_report; } rc->driver_name = DRIVER_NAME; diff --git a/drivers/media/rc/meson-ir-tx.c b/drivers/media/rc/meson-ir-tx.c new file mode 100644 index 000000000000..3055f8e1b6ff --- /dev/null +++ b/drivers/media/rc/meson-ir-tx.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * meson-ir-tx.c - Amlogic Meson IR TX driver + * + * Copyright (c) 2021, SberDevices. All Rights Reserved. + * + * Author: Viktor Prutyanov <viktor.prutyanov@phystech.edu> + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/of_irq.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <media/rc-core.h> + +#define DEVICE_NAME "Meson IR TX" +#define DRIVER_NAME "meson-ir-tx" + +#define MIRTX_DEFAULT_CARRIER 38000 +#define MIRTX_DEFAULT_DUTY_CYCLE 50 +#define MIRTX_FIFO_THD 32 + +#define IRB_MOD_1US_CLK_RATE 1000000 + +#define IRB_FIFO_LEN 128 + +#define IRB_ADDR0 0x0 +#define IRB_ADDR1 0x4 +#define IRB_ADDR2 0x8 +#define IRB_ADDR3 0xc + +#define IRB_MAX_DELAY (1 << 10) +#define IRB_DELAY_MASK (IRB_MAX_DELAY - 1) + +/* IRCTRL_IR_BLASTER_ADDR0 */ +#define IRB_MOD_CLK(x) ((x) << 12) +#define IRB_MOD_SYS_CLK 0 +#define IRB_MOD_XTAL3_CLK 1 +#define IRB_MOD_1US_CLK 2 +#define IRB_MOD_10US_CLK 3 +#define IRB_INIT_HIGH BIT(2) +#define IRB_ENABLE BIT(0) + +/* IRCTRL_IR_BLASTER_ADDR2 */ +#define IRB_MOD_COUNT(lo, hi) ((((lo) - 1) << 16) | ((hi) - 1)) + +/* IRCTRL_IR_BLASTER_ADDR2 */ +#define IRB_WRITE_FIFO BIT(16) +#define IRB_MOD_ENABLE BIT(12) +#define IRB_TB_1US (0x0 << 10) +#define IRB_TB_10US (0x1 << 10) +#define IRB_TB_100US (0x2 << 10) +#define IRB_TB_MOD_CLK (0x3 << 10) + +/* IRCTRL_IR_BLASTER_ADDR3 */ +#define IRB_FIFO_THD_PENDING BIT(16) +#define IRB_FIFO_IRQ_ENABLE BIT(8) + +struct meson_irtx { + struct device *dev; + void __iomem *reg_base; + u32 *buf; + unsigned int buf_len; + unsigned int buf_head; + unsigned int carrier; + unsigned int duty_cycle; + /* Locks buf */ + spinlock_t lock; + struct completion completion; + unsigned long clk_rate; +}; + +static void meson_irtx_set_mod(struct meson_irtx *ir) +{ + unsigned int cnt = DIV_ROUND_CLOSEST(ir->clk_rate, ir->carrier); + unsigned int pulse_cnt = DIV_ROUND_CLOSEST(cnt * ir->duty_cycle, 100); + unsigned int space_cnt = cnt - pulse_cnt; + + dev_dbg(ir->dev, "F_mod = %uHz, T_mod = %luns, duty_cycle = %u%%\n", + ir->carrier, NSEC_PER_SEC / ir->clk_rate * cnt, + 100 * pulse_cnt / cnt); + + writel(IRB_MOD_COUNT(pulse_cnt, space_cnt), + ir->reg_base + IRB_ADDR1); +} + +static void meson_irtx_setup(struct meson_irtx *ir, unsigned int clk_nr) +{ + /* + * Disable the TX, set modulator clock tick and set initialize + * output to be high. Set up carrier frequency and duty cycle. Then + * unset initialize output. Enable FIFO interrupt, set FIFO interrupt + * threshold. Finally, enable the transmitter back. + */ + writel(~IRB_ENABLE & (IRB_MOD_CLK(clk_nr) | IRB_INIT_HIGH), + ir->reg_base + IRB_ADDR0); + meson_irtx_set_mod(ir); + writel(readl(ir->reg_base + IRB_ADDR0) & ~IRB_INIT_HIGH, + ir->reg_base + IRB_ADDR0); + writel(IRB_FIFO_IRQ_ENABLE | MIRTX_FIFO_THD, + ir->reg_base + IRB_ADDR3); + writel(readl(ir->reg_base + IRB_ADDR0) | IRB_ENABLE, + ir->reg_base + IRB_ADDR0); +} + +static u32 meson_irtx_prepare_pulse(struct meson_irtx *ir, unsigned int time) +{ + unsigned int delay; + unsigned int tb = IRB_TB_MOD_CLK; + unsigned int tb_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, ir->carrier); + + delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK; + + return ((IRB_WRITE_FIFO | IRB_MOD_ENABLE) | tb | delay); +} + +static u32 meson_irtx_prepare_space(struct meson_irtx *ir, unsigned int time) +{ + unsigned int delay; + unsigned int tb = IRB_TB_100US; + unsigned int tb_us = 100; + + if (time <= IRB_MAX_DELAY) { + tb = IRB_TB_1US; + tb_us = 1; + } else if (time <= 10 * IRB_MAX_DELAY) { + tb = IRB_TB_10US; + tb_us = 10; + } else if (time <= 100 * IRB_MAX_DELAY) { + tb = IRB_TB_100US; + tb_us = 100; + } + + delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK; + + return ((IRB_WRITE_FIFO & ~IRB_MOD_ENABLE) | tb | delay); +} + +static void meson_irtx_send_buffer(struct meson_irtx *ir) +{ + unsigned int nr = 0; + unsigned int max_fifo_level = IRB_FIFO_LEN - MIRTX_FIFO_THD; + + while (ir->buf_head < ir->buf_len && nr < max_fifo_level) { + writel(ir->buf[ir->buf_head], ir->reg_base + IRB_ADDR2); + + ir->buf_head++; + nr++; + } +} + +static bool meson_irtx_check_buf(struct meson_irtx *ir, + unsigned int *buf, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + unsigned int max_tb_us; + /* + * Max space timebase is 100 us. + * Pulse timebase equals to carrier period. + */ + if (i % 2 == 0) + max_tb_us = USEC_PER_SEC / ir->carrier; + else + max_tb_us = 100; + + if (buf[i] >= max_tb_us * IRB_MAX_DELAY) + return false; + } + + return true; +} + +static void meson_irtx_fill_buf(struct meson_irtx *ir, u32 *dst_buf, + unsigned int *src_buf, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + if (i % 2 == 0) + dst_buf[i] = meson_irtx_prepare_pulse(ir, src_buf[i]); + else + dst_buf[i] = meson_irtx_prepare_space(ir, src_buf[i]); + } +} + +static irqreturn_t meson_irtx_irqhandler(int irq, void *data) +{ + unsigned long flags; + struct meson_irtx *ir = data; + + writel(readl(ir->reg_base + IRB_ADDR3) & ~IRB_FIFO_THD_PENDING, + ir->reg_base + IRB_ADDR3); + + if (completion_done(&ir->completion)) + return IRQ_HANDLED; + + spin_lock_irqsave(&ir->lock, flags); + if (ir->buf_head < ir->buf_len) + meson_irtx_send_buffer(ir); + else + complete(&ir->completion); + spin_unlock_irqrestore(&ir->lock, flags); + + return IRQ_HANDLED; +} + +static int meson_irtx_set_carrier(struct rc_dev *rc, u32 carrier) +{ + struct meson_irtx *ir = rc->priv; + + if (carrier == 0) + return -EINVAL; + + ir->carrier = carrier; + meson_irtx_set_mod(ir); + + return 0; +} + +static int meson_irtx_set_duty_cycle(struct rc_dev *rc, u32 duty_cycle) +{ + struct meson_irtx *ir = rc->priv; + + ir->duty_cycle = duty_cycle; + meson_irtx_set_mod(ir); + + return 0; +} + +static void meson_irtx_update_buf(struct meson_irtx *ir, u32 *buf, + unsigned int len, unsigned int head) +{ + ir->buf = buf; + ir->buf_len = len; + ir->buf_head = head; +} + +static int meson_irtx_transmit(struct rc_dev *rc, unsigned int *buf, + unsigned int len) +{ + unsigned long flags; + struct meson_irtx *ir = rc->priv; + u32 *tx_buf; + int ret = len; + + if (!meson_irtx_check_buf(ir, buf, len)) + return -EINVAL; + + tx_buf = kmalloc_array(len, sizeof(u32), GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + meson_irtx_fill_buf(ir, tx_buf, buf, len); + dev_dbg(ir->dev, "TX buffer filled, length = %u\n", len); + + spin_lock_irqsave(&ir->lock, flags); + meson_irtx_update_buf(ir, tx_buf, len, 0); + reinit_completion(&ir->completion); + meson_irtx_send_buffer(ir); + spin_unlock_irqrestore(&ir->lock, flags); + + if (!wait_for_completion_timeout(&ir->completion, + usecs_to_jiffies(IR_MAX_DURATION))) + ret = -ETIMEDOUT; + + spin_lock_irqsave(&ir->lock, flags); + kfree(ir->buf); + meson_irtx_update_buf(ir, NULL, 0, 0); + spin_unlock_irqrestore(&ir->lock, flags); + + return ret; +} + +static int meson_irtx_mod_clock_probe(struct meson_irtx *ir, + unsigned int *clk_nr) +{ + struct device_node *np = ir->dev->of_node; + struct clk *clock; + + if (!np) + return -ENODEV; + + clock = devm_clk_get(ir->dev, "xtal"); + if (IS_ERR(clock) || clk_prepare_enable(clock)) + return -ENODEV; + + *clk_nr = IRB_MOD_XTAL3_CLK; + ir->clk_rate = clk_get_rate(clock) / 3; + + if (ir->clk_rate < IRB_MOD_1US_CLK_RATE) { + *clk_nr = IRB_MOD_1US_CLK; + ir->clk_rate = IRB_MOD_1US_CLK_RATE; + } + + dev_info(ir->dev, "F_clk = %luHz\n", ir->clk_rate); + + return 0; +} + +static int __init meson_irtx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct meson_irtx *ir; + struct rc_dev *rc; + int irq; + unsigned int clk_nr; + int ret; + + ir = devm_kzalloc(dev, sizeof(*ir), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + ir->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ir->reg_base)) + return PTR_ERR(ir->reg_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq resource found\n"); + return -ENODEV; + } + + ir->dev = dev; + ir->carrier = MIRTX_DEFAULT_CARRIER; + ir->duty_cycle = MIRTX_DEFAULT_DUTY_CYCLE; + init_completion(&ir->completion); + spin_lock_init(&ir->lock); + + ret = meson_irtx_mod_clock_probe(ir, &clk_nr); + if (ret) { + dev_err(dev, "modulator clock setup failed\n"); + return ret; + } + meson_irtx_setup(ir, clk_nr); + + ret = devm_request_irq(dev, irq, + meson_irtx_irqhandler, + IRQF_TRIGGER_RISING, + DRIVER_NAME, ir); + if (ret) { + dev_err(dev, "irq request failed\n"); + return ret; + } + + rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX); + if (!rc) + return -ENOMEM; + + rc->driver_name = DRIVER_NAME; + rc->device_name = DEVICE_NAME; + rc->priv = ir; + + rc->tx_ir = meson_irtx_transmit; + rc->s_tx_carrier = meson_irtx_set_carrier; + rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle; + + ret = rc_register_device(rc); + if (ret < 0) { + dev_err(dev, "rc_dev registration failed\n"); + rc_free_device(rc); + return ret; + } + + platform_set_drvdata(pdev, rc); + + return 0; +} + +static int meson_irtx_remove(struct platform_device *pdev) +{ + struct rc_dev *rc = platform_get_drvdata(pdev); + + rc_unregister_device(rc); + + return 0; +} + +static const struct of_device_id meson_irtx_dt_match[] = { + { + .compatible = "amlogic,meson-g12a-ir-tx", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, meson_irtx_dt_match); + +static struct platform_driver meson_irtx_pd = { + .remove = meson_irtx_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = meson_irtx_dt_match, + }, +}; + +module_platform_driver_probe(meson_irtx_pd, meson_irtx_probe); + +MODULE_DESCRIPTION("Meson IR TX driver"); +MODULE_AUTHOR("Viktor Prutyanov <viktor.prutyanov@phystech.edu>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 1ba3f96ffa7d..6441879fcba1 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -15,12 +15,9 @@ #include <linux/slab.h> #include <media/rc-core.h> -#define DRIVER_NAME "rc-loopback" -#define dprintk(x...) if (debug) printk(KERN_INFO DRIVER_NAME ": " x) -#define RXMASK_REGULAR 0x1 -#define RXMASK_LEARNING 0x2 - -static bool debug; +#define DRIVER_NAME "rc-loopback" +#define RXMASK_NARROWBAND 0x1 +#define RXMASK_WIDEBAND 0x2 struct loopback_dev { struct rc_dev *dev; @@ -28,7 +25,7 @@ struct loopback_dev { u32 txcarrier; u32 txduty; bool idle; - bool learning; + bool wideband; bool carrierreport; u32 rxcarriermin; u32 rxcarriermax; @@ -40,12 +37,12 @@ static int loop_set_tx_mask(struct rc_dev *dev, u32 mask) { struct loopback_dev *lodev = dev->priv; - if ((mask & (RXMASK_REGULAR | RXMASK_LEARNING)) != mask) { - dprintk("invalid tx mask: %u\n", mask); - return -EINVAL; + if ((mask & (RXMASK_NARROWBAND | RXMASK_WIDEBAND)) != mask) { + dev_dbg(&dev->dev, "invalid tx mask: %u\n", mask); + return 2; } - dprintk("setting tx mask: %u\n", mask); + dev_dbg(&dev->dev, "setting tx mask: %u\n", mask); lodev->txmask = mask; return 0; } @@ -54,7 +51,7 @@ static int loop_set_tx_carrier(struct rc_dev *dev, u32 carrier) { struct loopback_dev *lodev = dev->priv; - dprintk("setting tx carrier: %u\n", carrier); + dev_dbg(&dev->dev, "setting tx carrier: %u\n", carrier); lodev->txcarrier = carrier; return 0; } @@ -64,11 +61,11 @@ static int loop_set_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle) struct loopback_dev *lodev = dev->priv; if (duty_cycle < 1 || duty_cycle > 99) { - dprintk("invalid duty cycle: %u\n", duty_cycle); + dev_dbg(&dev->dev, "invalid duty cycle: %u\n", duty_cycle); return -EINVAL; } - dprintk("setting duty cycle: %u\n", duty_cycle); + dev_dbg(&dev->dev, "setting duty cycle: %u\n", duty_cycle); lodev->txduty = duty_cycle; return 0; } @@ -78,11 +75,11 @@ static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max) struct loopback_dev *lodev = dev->priv; if (min < 1 || min > max) { - dprintk("invalid rx carrier range %u to %u\n", min, max); + dev_dbg(&dev->dev, "invalid rx carrier range %u to %u\n", min, max); return -EINVAL; } - dprintk("setting rx carrier range %u to %u\n", min, max); + dev_dbg(&dev->dev, "setting rx carrier range %u to %u\n", min, max); lodev->rxcarriermin = min; lodev->rxcarriermax = max; return 0; @@ -97,25 +94,33 @@ static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) if (lodev->txcarrier < lodev->rxcarriermin || lodev->txcarrier > lodev->rxcarriermax) { - dprintk("ignoring tx, carrier out of range\n"); + dev_dbg(&dev->dev, "ignoring tx, carrier out of range\n"); goto out; } - if (lodev->learning) - rxmask = RXMASK_LEARNING; + if (lodev->wideband) + rxmask = RXMASK_WIDEBAND; else - rxmask = RXMASK_REGULAR; + rxmask = RXMASK_NARROWBAND; if (!(rxmask & lodev->txmask)) { - dprintk("ignoring tx, rx mask mismatch\n"); + dev_dbg(&dev->dev, "ignoring tx, rx mask mismatch\n"); goto out; } for (i = 0; i < count; i++) { rawir.pulse = i % 2 ? false : true; rawir.duration = txbuf[i]; - if (rawir.duration) - ir_raw_event_store_with_filter(dev, &rawir); + + ir_raw_event_store_with_filter(dev, &rawir); + } + + if (lodev->carrierreport) { + rawir.pulse = false; + rawir.carrier_report = true; + rawir.carrier = lodev->txcarrier; + + ir_raw_event_store(dev, &rawir); } /* Fake a silence long enough to cause us to go idle */ @@ -134,18 +139,18 @@ static void loop_set_idle(struct rc_dev *dev, bool enable) struct loopback_dev *lodev = dev->priv; if (lodev->idle != enable) { - dprintk("%sing idle mode\n", enable ? "enter" : "exit"); + dev_dbg(&dev->dev, "%sing idle mode\n", enable ? "enter" : "exit"); lodev->idle = enable; } } -static int loop_set_learning_mode(struct rc_dev *dev, int enable) +static int loop_set_wideband_receiver(struct rc_dev *dev, int enable) { struct loopback_dev *lodev = dev->priv; - if (lodev->learning != enable) { - dprintk("%sing learning mode\n", enable ? "enter" : "exit"); - lodev->learning = !!enable; + if (lodev->wideband != enable) { + dev_dbg(&dev->dev, "using %sband receiver\n", enable ? "wide" : "narrow"); + lodev->wideband = !!enable; } return 0; @@ -156,7 +161,7 @@ static int loop_set_carrier_report(struct rc_dev *dev, int enable) struct loopback_dev *lodev = dev->priv; if (lodev->carrierreport != enable) { - dprintk("%sabling carrier reports\n", enable ? "en" : "dis"); + dev_dbg(&dev->dev, "%sabling carrier reports\n", enable ? "en" : "dis"); lodev->carrierreport = !!enable; } @@ -204,10 +209,8 @@ static int __init loop_init(void) int ret; rc = rc_allocate_device(RC_DRIVER_IR_RAW); - if (!rc) { - printk(KERN_ERR DRIVER_NAME ": rc_dev allocation failed\n"); + if (!rc) return -ENOMEM; - } rc->device_name = "rc-core loopback device"; rc->input_phys = "rc-core/virtual"; @@ -219,9 +222,9 @@ static int __init loop_init(void) rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; rc->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER; rc->encode_wakeup = true; - rc->timeout = MS_TO_US(100); /* 100 ms */ + rc->timeout = IR_DEFAULT_TIMEOUT; rc->min_timeout = 1; - rc->max_timeout = UINT_MAX; + rc->max_timeout = IR_MAX_TIMEOUT; rc->rx_resolution = 1; rc->tx_resolution = 1; rc->s_tx_mask = loop_set_tx_mask; @@ -230,22 +233,22 @@ static int __init loop_init(void) rc->s_rx_carrier_range = loop_set_rx_carrier_range; rc->tx_ir = loop_tx_ir; rc->s_idle = loop_set_idle; - rc->s_learning_mode = loop_set_learning_mode; + rc->s_wideband_receiver = loop_set_wideband_receiver; rc->s_carrier_report = loop_set_carrier_report; rc->s_wakeup_filter = loop_set_wakeup_filter; - loopdev.txmask = RXMASK_REGULAR; + loopdev.txmask = RXMASK_NARROWBAND; loopdev.txcarrier = 36000; loopdev.txduty = 50; loopdev.rxcarriermin = 1; loopdev.rxcarriermax = ~0; loopdev.idle = true; - loopdev.learning = false; + loopdev.wideband = false; loopdev.carrierreport = false; ret = rc_register_device(rc); if (ret < 0) { - printk(KERN_ERR DRIVER_NAME ": rc_dev registration failed\n"); + dev_err(&rc->dev, "rc_dev registration failed\n"); rc_free_device(rc); return ret; } @@ -262,9 +265,6 @@ static void __exit loop_exit(void) module_init(loop_init); module_exit(loop_exit); -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debug messages"); - MODULE_DESCRIPTION("Loopback device for rc-core debugging"); MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 8e88dc8ea6c5..b90438a71c80 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1940,7 +1940,7 @@ int rc_register_device(struct rc_dev *dev) kfree(path); /* - * once the the input device is registered in rc_setup_rx_device, + * once the input device is registered in rc_setup_rx_device, * userspace can open the input device and rc_open() will be called * as a result. This results in driver code being allowed to submit * keycodes with rc_keydown, so lirc must be registered first. diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 2cf3377ec63a..ac85464864b9 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -6,7 +6,7 @@ * based heavily on the work of Stephen Cox, with additional * help from RedRat Ltd. * - * This driver began life based an an old version of the first-generation + * This driver began life based on an old version of the first-generation * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's * Chris Dodge. diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 9f3cd9fb6b6e..9cd765e31c49 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -265,8 +265,6 @@ static void streamzap_callback(struct urb *urb) ir_raw_event_handle(sz->rdev); usb_submit_urb(urb, GFP_ATOMIC); - - return; } static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz) diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c index e5094fff04c5..b91a1e845b97 100644 --- a/drivers/media/spi/cxd2880-spi.c +++ b/drivers/media/spi/cxd2880-spi.c @@ -524,13 +524,13 @@ cxd2880_spi_probe(struct spi_device *spi) if (IS_ERR(dvb_spi->vcc_supply)) { if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto fail_adapter; + goto fail_regulator; } dvb_spi->vcc_supply = NULL; } else { ret = regulator_enable(dvb_spi->vcc_supply); if (ret) - goto fail_adapter; + goto fail_regulator; } dvb_spi->spi = spi; @@ -618,6 +618,9 @@ fail_frontend: fail_attach: dvb_unregister_adapter(&dvb_spi->adapter); fail_adapter: + if (!dvb_spi->vcc_supply) + regulator_disable(dvb_spi->vcc_supply); +fail_regulator: kfree(dvb_spi); return ret; } diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 4d2413e87730..55ea039fe5b2 100644 --- a/drivers/media/test-drivers/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c @@ -282,5 +282,5 @@ struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev, snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d", dev->inst, is_source ? "out" : "cap", idx); return cec_allocate_adapter(&vivid_cec_adap_ops, dev, - name, caps, 1); + name, caps, CEC_MAX_LOG_ADDRS); } diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 5c75303fba9d..60ca8b9d070b 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -34,6 +34,8 @@ config DVB_USB_AF9035 tristate "Afatech AF9035 DVB-T USB2.0 support" depends on DVB_USB_V2 select DVB_AF9033 + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index b1f69c11c839..5eef37b00a52 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -210,6 +210,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, /* register I2C device */ client = i2c_new_client_device(adapter, &board_info); if (!i2c_client_has_driver(client)) { + dev_err(&intf->dev, "failed to bind i2c device to %s driver\n", type); ret = -ENODEV; goto err; } diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 689829f1b52a..1221c924312a 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -541,7 +541,9 @@ static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) si2168_config.i2c_adapter = &i2c_adapter; si2168_config.fe = &adap->fe[0]; si2168_config.ts_mode = SI2168_TS_PARALLEL; - if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2) + if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2 || + le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230C2_LITE || + le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230A) si2168_config.ts_mode |= SI2168_TS_CLK_MANUAL; si2168_config.ts_clock_inv = 1; @@ -577,15 +579,24 @@ static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) { - dvbsky_gpio_ctrl(d, 0x04, 1); - msleep(20); - dvbsky_gpio_ctrl(d, 0x83, 0); - dvbsky_gpio_ctrl(d, 0xc0, 1); - msleep(100); - dvbsky_gpio_ctrl(d, 0x83, 1); - dvbsky_gpio_ctrl(d, 0xc0, 0); - msleep(50); - + if (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_MYGICA_T230A) { + dvbsky_gpio_ctrl(d, 0x87, 0); + msleep(20); + dvbsky_gpio_ctrl(d, 0x86, 1); + dvbsky_gpio_ctrl(d, 0x80, 0); + msleep(100); + dvbsky_gpio_ctrl(d, 0x80, 1); + msleep(50); + } else { + dvbsky_gpio_ctrl(d, 0x04, 1); + msleep(20); + dvbsky_gpio_ctrl(d, 0x83, 0); + dvbsky_gpio_ctrl(d, 0xc0, 1); + msleep(100); + dvbsky_gpio_ctrl(d, 0x83, 1); + dvbsky_gpio_ctrl(d, 0xc0, 0); + msleep(50); + } return WARM; } @@ -789,6 +800,12 @@ static const struct usb_device_id dvbsky_id_table[] = { { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, + { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2_LITE, + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2 Lite", + NULL) }, + { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230A, + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230A", + NULL) }, { } }; MODULE_DEVICE_TABLE(usb, dvbsky_id_table); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-i2c.c b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c index 2e07106f4680..bc4b2abdde1a 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-i2c.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c @@ -17,7 +17,8 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d) if (d->props.i2c_algo == NULL) { err("no i2c algorithm specified"); - return -EINVAL; + ret = -EINVAL; + goto err; } strscpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); @@ -27,11 +28,15 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d) i2c_set_adapdata(&d->i2c_adap, d); - if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0) + ret = i2c_add_adapter(&d->i2c_adap); + if (ret < 0) { err("could not add i2c adapter"); + goto err; + } d->state |= DVB_USB_STATE_I2C; +err: return ret; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 28e1fd64dd3c..61439c8f33ca 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -194,8 +194,8 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) err_adapter_init: dvb_usb_adapter_exit(d); -err_i2c_init: dvb_usb_i2c_exit(d); +err_i2c_init: if (d->priv && d->props.priv_destroy) d->props.priv_destroy(d); err_priv_init: diff --git a/drivers/media/usb/dvb-usb/nova-t-usb2.c b/drivers/media/usb/dvb-usb/nova-t-usb2.c index e7b290552b66..9c0eb0d40822 100644 --- a/drivers/media/usb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/usb/dvb-usb/nova-t-usb2.c @@ -130,7 +130,7 @@ ret: static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) { - int i; + int i, ret; u8 b; mac[0] = 0x00; @@ -139,7 +139,9 @@ static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) /* this is a complete guess, but works for my box */ for (i = 136; i < 139; i++) { - dibusb_read_eeprom_byte(d,i, &b); + ret = dibusb_read_eeprom_byte(d, i, &b); + if (ret) + return ret; mac[5 - (i - 136)] = b; } diff --git a/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c index bf54747e2e01..a1d9e4801a2b 100644 --- a/drivers/media/usb/dvb-usb/vp702x.c +++ b/drivers/media/usb/dvb-usb/vp702x.c @@ -291,16 +291,22 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) { u8 i, *buf; + int ret; struct vp702x_device_state *st = d->priv; mutex_lock(&st->buf_mutex); buf = st->buf; - for (i = 6; i < 12; i++) - vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &buf[i - 6], 1); + for (i = 6; i < 12; i++) { + ret = vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, + &buf[i - 6], 1); + if (ret < 0) + goto err; + } memcpy(mac, buf, 6); +err: mutex_unlock(&st->buf_mutex); - return 0; + return ret; } static int vp702x_frontend_attach(struct dvb_usb_adapter *adap) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 59529cbf9cd0..0b6d77c3bec8 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -842,7 +842,6 @@ error: kfree(ir); ref_put: em28xx_shutdown_buttons(dev); - kref_put(&dev->ref, em28xx_free_device); return err; } diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index f1767be9d868..6650eab913d8 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -691,49 +691,23 @@ struct go7007 *go7007_alloc(const struct go7007_board_info *board, struct device *dev) { struct go7007 *go; - int i; go = kzalloc(sizeof(struct go7007), GFP_KERNEL); if (go == NULL) return NULL; go->dev = dev; go->board_info = board; - go->board_id = 0; go->tuner_type = -1; - go->channel_number = 0; - go->name[0] = 0; mutex_init(&go->hw_lock); init_waitqueue_head(&go->frame_waitq); spin_lock_init(&go->spinlock); go->status = STATUS_INIT; - memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); - go->i2c_adapter_online = 0; - go->interrupt_available = 0; init_waitqueue_head(&go->interrupt_waitq); - go->input = 0; go7007_update_board(go); - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; go->format = V4L2_PIX_FMT_MJPEG; go->bitrate = 1500000; go->fps_scale = 1; - go->pali = 0; go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - go->interlace_coding = 0; - for (i = 0; i < 4; ++i) - go->modet[i].enable = 0; - for (i = 0; i < 1624; ++i) - go->modet_map[i] = 0; - go->audio_deliver = NULL; - go->audio_enabled = 0; return go; } diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index dbf0455d5d50..eeb85981e02b 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1134,7 +1134,7 @@ static int go7007_usb_probe(struct usb_interface *intf, ep = usb->usbdev->ep_in[4]; if (!ep) - return -ENODEV; + goto allocfail; /* Allocate the URB and buffer for receiving incoming interrupts */ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index 0283e3b908e4..dca4e16ed133 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -4,7 +4,6 @@ menuconfig USB_GSPCA depends on VIDEO_V4L2 depends on INPUT || INPUT=n select VIDEOBUF2_VMALLOC - default m help Say Y here if you want to enable selecting webcams based on the GSPCA framework. diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c index 4cb7c92ea132..e7a534be061d 100644 --- a/drivers/media/usb/gspca/vc032x.c +++ b/drivers/media/usb/gspca/vc032x.c @@ -1796,7 +1796,7 @@ static const u8 ov7660_60HZ[][4] = { {} }; -static const u8 ov7660_NoFliker[][4] = { +static const u8 ov7660_NoFlicker[][4] = { {0x00, 0x13, 0x87, 0xaa}, {} }; @@ -3319,8 +3319,8 @@ static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip) static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; - static const u8 (*ov7660_freq_tb[3])[4] = - {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; + static const u8 (*ov7660_freq_tb[3])[4] = { + ov7660_NoFlicker, ov7660_50HZ, ov7660_60HZ}; if (sd->sensor != SENSOR_OV7660) return; diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index aa285d5d6c0d..5bcbf0d40147 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -323,7 +323,7 @@ static const struct usb_action adcm2700_60HZ[] = { {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ {} }; -static const struct usb_action adcm2700_NoFliker[] = { +static const struct usb_action adcm2700_NoFlicker[] = { {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ @@ -525,7 +525,7 @@ static const struct usb_action cs2102_60HZ[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_NoFlikerScale[] = { +static const struct usb_action cs2102_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0001}, {0xaa, 0x24, 0x005f}, @@ -547,7 +547,7 @@ static const struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_NoFliker[] = { +static const struct usb_action cs2102_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00af}, @@ -1385,7 +1385,7 @@ static const struct usb_action gc0305_60HZ[] = { {} }; -static const struct usb_action gc0305_NoFliker[] = { +static const struct usb_action gc0305_NoFlicker[] = { {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ @@ -1710,7 +1710,7 @@ static const struct usb_action hdcs2020_60HZ[] = { {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */ {} }; -static const struct usb_action hdcs2020_NoFliker[] = { +static const struct usb_action hdcs2020_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ @@ -1925,7 +1925,7 @@ static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */ {} }; -static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ +static const struct usb_action hv7131b_NoFlicker[] = { /* 640x480*/ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ @@ -1950,7 +1950,7 @@ static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */ {} }; -static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ +static const struct usb_action hv7131b_NoFlickerScale[] = { /* 320x240 */ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */ @@ -2141,7 +2141,7 @@ static const struct usb_action hv7131r_60HZScale[] = { {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action hv7131r_NoFliker[] = { +static const struct usb_action hv7131r_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, @@ -2159,7 +2159,7 @@ static const struct usb_action hv7131r_NoFliker[] = { {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action hv7131r_NoFlikerScale[] = { +static const struct usb_action hv7131r_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, @@ -2662,7 +2662,7 @@ static const struct usb_action icm105a_60HZ[] = { {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ {} }; -static const struct usb_action icm105a_NoFlikerScale[] = { +static const struct usb_action icm105a_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ @@ -2693,7 +2693,7 @@ static const struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {} }; -static const struct usb_action icm105a_NoFliker[] = { +static const struct usb_action icm105a_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ @@ -3009,7 +3009,7 @@ static const struct usb_action mc501cb_60HZScale[] = { {} }; -static const struct usb_action mc501cb_NoFliker[] = { +static const struct usb_action mc501cb_NoFlicker[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ @@ -3021,7 +3021,7 @@ static const struct usb_action mc501cb_NoFliker[] = { {} }; -static const struct usb_action mc501cb_NoFlikerScale[] = { +static const struct usb_action mc501cb_NoFlickerScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ @@ -3211,7 +3211,7 @@ static const struct usb_action ov7620_60HZ[] = { {0xa1, 0x01, 0x0037}, */ {} }; -static const struct usb_action ov7620_NoFliker[] = { +static const struct usb_action ov7620_NoFlicker[] = { {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ /* disable 1/120s & 1/100s exposures for banding filter */ @@ -3827,7 +3827,7 @@ static const struct usb_action pas106b_60HZ[] = { {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ {} }; -static const struct usb_action pas106b_NoFliker[] = { +static const struct usb_action pas106b_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */ @@ -4051,7 +4051,7 @@ static const struct usb_action pas202b_60HZScale[] = { {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ {} }; -static const struct usb_action pas202b_NoFliker[] = { +static const struct usb_action pas202b_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ @@ -4080,7 +4080,7 @@ static const struct usb_action pas202b_NoFliker[] = { {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ {} }; -static const struct usb_action pas202b_NoFlikerScale[] = { +static const struct usb_action pas202b_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ @@ -4309,7 +4309,7 @@ static const struct usb_action mt9v111_1_AE60HZScale[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mt9v111_1_AENoFliker[] = { +static const struct usb_action mt9v111_1_AENoFlicker[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0509}, @@ -4332,7 +4332,7 @@ static const struct usb_action mt9v111_1_AENoFliker[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mt9v111_1_AENoFlikerScale[] = { +static const struct usb_action mt9v111_1_AENoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0534}, @@ -4554,7 +4554,7 @@ static const struct usb_action mt9v111_3_AE60HZScale[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mt9v111_3_AENoFliker[] = { +static const struct usb_action mt9v111_3_AENoFlicker[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x05, 0x0034}, @@ -4577,7 +4577,7 @@ static const struct usb_action mt9v111_3_AENoFliker[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mt9v111_3_AENoFlikerScale[] = { +static const struct usb_action mt9v111_3_AENoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x05, 0x0034}, @@ -4787,7 +4787,7 @@ static const struct usb_action pb0330_60HZScale[] = { {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action pb0330_NoFliker[] = { +static const struct usb_action pb0330_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0509}, {0xbb, 0x02, 0x0940}, @@ -4809,7 +4809,7 @@ static const struct usb_action pb0330_NoFliker[] = { {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action pb0330_NoFlikerScale[] = { +static const struct usb_action pb0330_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0535}, {0xbb, 0x01, 0x0980}, @@ -5031,7 +5031,7 @@ static const struct usb_action po2030_60HZ[] = { {} }; -static const struct usb_action po2030_NoFliker[] = { +static const struct usb_action po2030_NoFlicker[] = { {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ @@ -5215,7 +5215,7 @@ static const struct usb_action tas5130c_60HZScale[] = { {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; -static const struct usb_action tas5130c_NoFliker[] = { +static const struct usb_action tas5130c_NoFlicker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */ @@ -5241,7 +5241,7 @@ static const struct usb_action tas5130c_NoFliker[] = { {} }; -static const struct usb_action tas5130c_NoFlikerScale[] = { +static const struct usb_action tas5130c_NoFlickerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */ @@ -5482,7 +5482,7 @@ static const struct usb_action gc0303_60HZScale[] = { {} }; -static const struct usb_action gc0303_NoFliker[] = { +static const struct usb_action gc0303_NoFlicker[] = { {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ @@ -5504,7 +5504,7 @@ static const struct usb_action gc0303_NoFliker[] = { {} }; -static const struct usb_action gc0303_NoFlikerScale[] = { +static const struct usb_action gc0303_NoFlickerScale[] = { {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ @@ -5806,7 +5806,7 @@ static void setquality(struct gspca_dev *gspca_dev) * Valid frequencies are: * 50Hz, for European and Asian lighting (default) * 60Hz, for American lighting - * 0 = No Fliker (for outdoore usage) + * 0 = No Flicker (for outdoor usage) */ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) { @@ -5814,80 +5814,80 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) int i, mode; const struct usb_action *zc3_freq; static const struct usb_action *freq_tb[SENSOR_MAX][6] = { - [SENSOR_ADCM2700] = - {adcm2700_NoFliker, adcm2700_NoFliker, + [SENSOR_ADCM2700] = { + adcm2700_NoFlicker, adcm2700_NoFlicker, adcm2700_50HZ, adcm2700_50HZ, adcm2700_60HZ, adcm2700_60HZ}, - [SENSOR_CS2102] = - {cs2102_NoFliker, cs2102_NoFlikerScale, + [SENSOR_CS2102] = { + cs2102_NoFlicker, cs2102_NoFlickerScale, cs2102_50HZ, cs2102_50HZScale, cs2102_60HZ, cs2102_60HZScale}, - [SENSOR_CS2102K] = - {cs2102_NoFliker, cs2102_NoFlikerScale, + [SENSOR_CS2102K] = { + cs2102_NoFlicker, cs2102_NoFlickerScale, NULL, NULL, /* currently disabled */ NULL, NULL}, - [SENSOR_GC0303] = - {gc0303_NoFliker, gc0303_NoFlikerScale, + [SENSOR_GC0303] = { + gc0303_NoFlicker, gc0303_NoFlickerScale, gc0303_50HZ, gc0303_50HZScale, gc0303_60HZ, gc0303_60HZScale}, - [SENSOR_GC0305] = - {gc0305_NoFliker, gc0305_NoFliker, + [SENSOR_GC0305] = { + gc0305_NoFlicker, gc0305_NoFlicker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, - [SENSOR_HDCS2020] = - {hdcs2020_NoFliker, hdcs2020_NoFliker, + [SENSOR_HDCS2020] = { + hdcs2020_NoFlicker, hdcs2020_NoFlicker, hdcs2020_50HZ, hdcs2020_50HZ, hdcs2020_60HZ, hdcs2020_60HZ}, - [SENSOR_HV7131B] = - {hv7131b_NoFliker, hv7131b_NoFlikerScale, + [SENSOR_HV7131B] = { + hv7131b_NoFlicker, hv7131b_NoFlickerScale, hv7131b_50HZ, hv7131b_50HZScale, hv7131b_60HZ, hv7131b_60HZScale}, - [SENSOR_HV7131R] = - {hv7131r_NoFliker, hv7131r_NoFlikerScale, + [SENSOR_HV7131R] = { + hv7131r_NoFlicker, hv7131r_NoFlickerScale, hv7131r_50HZ, hv7131r_50HZScale, hv7131r_60HZ, hv7131r_60HZScale}, - [SENSOR_ICM105A] = - {icm105a_NoFliker, icm105a_NoFlikerScale, + [SENSOR_ICM105A] = { + icm105a_NoFlicker, icm105a_NoFlickerScale, icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, - [SENSOR_MC501CB] = - {mc501cb_NoFliker, mc501cb_NoFlikerScale, + [SENSOR_MC501CB] = { + mc501cb_NoFlicker, mc501cb_NoFlickerScale, mc501cb_50HZ, mc501cb_50HZScale, mc501cb_60HZ, mc501cb_60HZScale}, - [SENSOR_MT9V111_1] = - {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale, + [SENSOR_MT9V111_1] = { + mt9v111_1_AENoFlicker, mt9v111_1_AENoFlickerScale, mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale, mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale}, - [SENSOR_MT9V111_3] = - {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale, + [SENSOR_MT9V111_3] = { + mt9v111_3_AENoFlicker, mt9v111_3_AENoFlickerScale, mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale, mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale}, - [SENSOR_OV7620] = - {ov7620_NoFliker, ov7620_NoFliker, + [SENSOR_OV7620] = { + ov7620_NoFlicker, ov7620_NoFlicker, ov7620_50HZ, ov7620_50HZ, ov7620_60HZ, ov7620_60HZ}, - [SENSOR_OV7630C] = - {NULL, NULL, + [SENSOR_OV7630C] = { + NULL, NULL, NULL, NULL, NULL, NULL}, - [SENSOR_PAS106] = - {pas106b_NoFliker, pas106b_NoFliker, + [SENSOR_PAS106] = { + pas106b_NoFlicker, pas106b_NoFlicker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, - [SENSOR_PAS202B] = - {pas202b_NoFliker, pas202b_NoFlikerScale, + [SENSOR_PAS202B] = { + pas202b_NoFlicker, pas202b_NoFlickerScale, pas202b_50HZ, pas202b_50HZScale, pas202b_60HZ, pas202b_60HZScale}, - [SENSOR_PB0330] = - {pb0330_NoFliker, pb0330_NoFlikerScale, + [SENSOR_PB0330] = { + pb0330_NoFlicker, pb0330_NoFlickerScale, pb0330_50HZ, pb0330_50HZScale, pb0330_60HZ, pb0330_60HZScale}, - [SENSOR_PO2030] = - {po2030_NoFliker, po2030_NoFliker, + [SENSOR_PO2030] = { + po2030_NoFlicker, po2030_NoFlicker, po2030_50HZ, po2030_50HZ, po2030_60HZ, po2030_60HZ}, - [SENSOR_TAS5130C] = - {tas5130c_NoFliker, tas5130c_NoFlikerScale, + [SENSOR_TAS5130C] = { + tas5130c_NoFlicker, tas5130c_NoFlickerScale, tas5130c_50HZ, tas5130c_50HZScale, tas5130c_60HZ, tas5130c_60HZScale}, }; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index a45d464427c4..0e231e576dc3 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1346,7 +1346,7 @@ static int stk_camera_probe(struct usb_interface *interface, if (!dev->isoc_ep) { pr_err("Could not find isoc-in endpoint\n"); err = -ENODEV; - goto error; + goto error_put; } dev->vsettings.palette = V4L2_PIX_FMT_RGB565; dev->vsettings.mode = MODE_VGA; @@ -1359,10 +1359,12 @@ static int stk_camera_probe(struct usb_interface *interface, err = stk_register_video_device(dev); if (err) - goto error; + goto error_put; return 0; +error_put: + usb_put_intf(interface); error: v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(&dev->v4l2_dev); diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 252136cc885c..6acb8013de08 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -899,8 +899,8 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input) { struct uvc_fh *handle = fh; struct uvc_video_chain *chain = handle->chain; + u8 *buf; int ret; - u8 i; if (chain->selector == NULL || (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { @@ -908,22 +908,27 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input) return 0; } + buf = kmalloc(1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id, chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, - &i, 1); - if (ret < 0) - return ret; + buf, 1); + if (!ret) + *input = *buf - 1; - *input = i - 1; - return 0; + kfree(buf); + + return ret; } static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input) { struct uvc_fh *handle = fh; struct uvc_video_chain *chain = handle->chain; + u8 *buf; int ret; - u32 i; ret = uvc_acquire_privileges(handle); if (ret < 0) @@ -939,10 +944,17 @@ static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input) if (input >= chain->selector->bNrInPins) return -EINVAL; - i = input + 1; - return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id, - chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, - &i, 1); + buf = kmalloc(1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *buf = input + 1; + ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id, + chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, + buf, 1); + kfree(buf); + + return ret; } static int uvc_ioctl_queryctrl(struct file *file, void *fh, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index b6344bbf1e00..421300e13a41 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -833,6 +833,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface"; case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable"; case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs"; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return "Intra Refresh Period"; case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable"; case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control"; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode"; @@ -1258,6 +1259,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE: case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: *type = V4L2_CTRL_TYPE_INTEGER; break; case V4L2_CID_MPEG_VIDEO_LTR_COUNT: diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 230d65a64217..af48705c704f 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -196,7 +196,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, if (!v4l2_valid_dv_timings(t, cap, fnc, fnc_handle)) return false; - for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, fnc, fnc_handle) && v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, @@ -218,7 +218,7 @@ bool v4l2_find_dv_timings_cea861_vic(struct v4l2_dv_timings *t, u8 vic) { unsigned int i; - for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index 10ddcc48aa17..e70e128ccc9c 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -76,10 +76,11 @@ static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, return (brightness * ctrl->step) + ctrl->minimum; } -static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, - struct v4l2_ctrl *ctrl) +static int v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, + struct v4l2_ctrl *ctrl) { struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; + struct led_classdev *led_cdev; enum led_brightness brightness; if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) @@ -102,14 +103,20 @@ static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, if (ctrl == ctrls[TORCH_INTENSITY]) { if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) - return; + return 0; - led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev, - brightness); + if (WARN_ON_ONCE(!v4l2_flash->fled_cdev)) + return -EINVAL; + + led_cdev = &v4l2_flash->fled_cdev->led_cdev; } else { - led_set_brightness_sync(v4l2_flash->iled_cdev, - brightness); + if (WARN_ON_ONCE(!v4l2_flash->iled_cdev)) + return -EINVAL; + + led_cdev = v4l2_flash->iled_cdev; } + + return led_set_brightness_sync(led_cdev, brightness); } static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, @@ -128,8 +135,15 @@ static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, */ if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) return 0; + + if (WARN_ON_ONCE(!v4l2_flash->fled_cdev)) + return -EINVAL; + led_cdev = &v4l2_flash->fled_cdev->led_cdev; } else { + if (WARN_ON_ONCE(!v4l2_flash->iled_cdev)) + return -EINVAL; + led_cdev = v4l2_flash->iled_cdev; } @@ -159,6 +173,12 @@ static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) case V4L2_CID_FLASH_TORCH_INTENSITY: case V4L2_CID_FLASH_INDICATOR_INTENSITY: return v4l2_flash_update_led_brightness(v4l2_flash, c); + } + + if (!fled_cdev) + return -EINVAL; + + switch (c->id) { case V4L2_CID_FLASH_INTENSITY: ret = led_update_flash_brightness(fled_cdev); if (ret < 0) @@ -194,12 +214,23 @@ static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) { struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; - struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; + struct led_classdev *led_cdev; struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; bool external_strobe; int ret = 0; switch (c->id) { + case V4L2_CID_FLASH_TORCH_INTENSITY: + case V4L2_CID_FLASH_INDICATOR_INTENSITY: + return v4l2_flash_set_led_brightness(v4l2_flash, c); + } + + if (!fled_cdev) + return -EINVAL; + + led_cdev = &fled_cdev->led_cdev; + + switch (c->id) { case V4L2_CID_FLASH_LED_MODE: switch (c->val) { case V4L2_FLASH_LED_MODE_NONE: @@ -230,9 +261,8 @@ static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) if (ret < 0) return ret; - v4l2_flash_set_led_brightness(v4l2_flash, - ctrls[TORCH_INTENSITY]); - return 0; + return v4l2_flash_set_led_brightness(v4l2_flash, + ctrls[TORCH_INTENSITY]); } break; case V4L2_CID_FLASH_STROBE_SOURCE: @@ -268,10 +298,6 @@ static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) * microamperes for flash intensity units. */ return led_set_flash_brightness(fled_cdev, c->val); - case V4L2_CID_FLASH_TORCH_INTENSITY: - case V4L2_CID_FLASH_INDICATOR_INTENSITY: - v4l2_flash_set_led_brightness(v4l2_flash, c); - return 0; } return -EINVAL; @@ -483,15 +509,24 @@ static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; int ret = 0; - if (ctrls[TORCH_INTENSITY]) - v4l2_flash_set_led_brightness(v4l2_flash, - ctrls[TORCH_INTENSITY]); + if (ctrls[TORCH_INTENSITY]) { + ret = v4l2_flash_set_led_brightness(v4l2_flash, + ctrls[TORCH_INTENSITY]); + if (ret < 0) + return ret; + } - if (ctrls[INDICATOR_INTENSITY]) - v4l2_flash_set_led_brightness(v4l2_flash, - ctrls[INDICATOR_INTENSITY]); + if (ctrls[INDICATOR_INTENSITY]) { + ret = v4l2_flash_set_led_brightness(v4l2_flash, + ctrls[INDICATOR_INTENSITY]); + if (ret < 0) + return ret; + } if (ctrls[FLASH_TIMEOUT]) { + if (WARN_ON_ONCE(!fled_cdev)) + return -EINVAL; + ret = led_set_flash_timeout(fled_cdev, ctrls[FLASH_TIMEOUT]->val); if (ret < 0) @@ -499,6 +534,9 @@ static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) } if (ctrls[FLASH_INTENSITY]) { + if (WARN_ON_ONCE(!fled_cdev)) + return -EINVAL; + ret = led_set_flash_brightness(fled_cdev, ctrls[FLASH_INTENSITY]->val); if (ret < 0) |