diff options
Diffstat (limited to 'drivers/media/i2c')
56 files changed, 4182 insertions, 809 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 79ce9ec6fc1b..c68e002d26ea 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -22,8 +22,11 @@ config VIDEO_IR_I2C # Encoder / Decoder module configuration # +comment "I2C drivers hidden by 'Autoselect ancillary drivers'" + depends on MEDIA_HIDE_ANCILLARY_SUBDRV + menu "I2C Encoders, decoders, sensors and other helper chips" - visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST || EXPERT + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV comment "Audio decoders, processors and mixers" @@ -563,10 +566,23 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +if MEDIA_CAMERA_SUPPORT + +config VIDEO_HI556 + tristate "Hynix Hi-556 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Hynix + Hi-556 camera. + + To compile this driver as a module, choose M here: the + module will be called hi556. + config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT depends on V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony @@ -578,7 +594,6 @@ config VIDEO_IMX214 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -589,16 +604,25 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX290 + tristate "Sony IMX290 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX290 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called imx290. + config VIDEO_IMX319 tristate "Sony IMX319 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX319 camera. @@ -609,7 +633,6 @@ config VIDEO_IMX319 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -620,7 +643,6 @@ config VIDEO_IMX355 config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. @@ -630,8 +652,7 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" - depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT + depends on VIDEO_V4L2 && I2C && GPIOLIB select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -643,7 +664,6 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -655,7 +675,6 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -668,7 +687,6 @@ config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Omnivision @@ -678,7 +696,6 @@ config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -690,7 +707,6 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -702,7 +718,6 @@ config VIDEO_OV5647 config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. @@ -713,7 +728,6 @@ config VIDEO_OV6650 config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT depends on MEDIA_CONTROLLER select V4L2_FWNODE help @@ -723,10 +737,22 @@ config VIDEO_OV5670 To compile this driver as a module, choose M here: the module will be called ov5670. +config VIDEO_OV5675 + tristate "OmniVision OV5675 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV5675 camera. + + To compile this driver as a module, choose M here: the + module will be called ov5675. + config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. @@ -737,7 +763,6 @@ config VIDEO_OV5695 config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -749,7 +774,6 @@ config VIDEO_OV7251 config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision @@ -761,7 +785,6 @@ config VIDEO_OV772X config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. @@ -772,7 +795,6 @@ config VIDEO_OV7640 config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -782,7 +804,6 @@ config VIDEO_OV7670 config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. @@ -790,7 +811,6 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -817,7 +837,6 @@ config VIDEO_OV9650 config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -826,7 +845,6 @@ config VIDEO_OV13858 config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the ST VS6624 camera. @@ -837,7 +855,6 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This driver supports MT9M001 cameras from Micron, monochrome and colour models. @@ -845,7 +862,6 @@ config VIDEO_MT9M001 config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL help This driver supports MT9M032 camera sensors from Aptina, monochrome @@ -862,7 +878,6 @@ config VIDEO_MT9M111 config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL help This is a Video4Linux2 sensor driver for the Aptina @@ -871,7 +886,6 @@ config VIDEO_MT9P031 config VIDEO_MT9T001 tristate "Aptina MT9T001 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina (Micron) mt0t001 3 Mpixel camera. @@ -879,7 +893,6 @@ config VIDEO_MT9T001 config VIDEO_MT9T112 tristate "Aptina MT9T111/MT9T112 support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. @@ -890,7 +903,6 @@ config VIDEO_MT9T112 config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the @@ -899,7 +911,6 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C select V4L2_FWNODE help @@ -909,7 +920,6 @@ config VIDEO_MT9V032 config VIDEO_MT9V111 tristate "Aptina MT9V111 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina/Micron MT9V111 sensor. @@ -920,14 +930,12 @@ config VIDEO_MT9V111 config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This driver supports SR030PC30 VGA camera from Siliconfile config VIDEO_NOON010PC30 tristate "Siliconfile NOON010PC30 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This driver supports NOON010PC30 CIF camera from Siliconfile @@ -936,7 +944,6 @@ source "drivers/media/i2c/m5mols/Kconfig" config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. @@ -946,7 +953,6 @@ config VIDEO_RJ54N1 config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" - depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M @@ -954,7 +960,6 @@ config VIDEO_S5K6AA config VIDEO_S5K6A3 tristate "Samsung S5K6A3 sensor support" - depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6A3 raw @@ -986,12 +991,15 @@ config VIDEO_S5C73M3 help This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. +endif comment "Lens drivers" +if MEDIA_CAMERA_SUPPORT + config VIDEO_AD5820 tristate "AD5820 lens voice coil support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on GPIOLIB && I2C && VIDEO_V4L2 && MEDIA_CONTROLLER help This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). @@ -1026,12 +1034,15 @@ config VIDEO_DW9807_VCM capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +endif + comment "Flash devices" +if MEDIA_CAMERA_SUPPORT + config VIDEO_ADP1653 tristate "ADP1653 flash support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT help This is a driver for the ADP1653 flash controller. It is used for example in Nokia N900. @@ -1039,7 +1050,6 @@ config VIDEO_ADP1653 config VIDEO_LM3560 tristate "LM3560 dual flash driver support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a driver for the lm3560 dual flash controllers. It controls @@ -1048,12 +1058,13 @@ config VIDEO_LM3560 config VIDEO_LM3646 tristate "LM3646 dual flash driver support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a driver for the lm3646 dual flash controllers. It controls flash, torch LEDs. +endif + comment "Video improvement chips" config VIDEO_UPD64031A @@ -1097,6 +1108,7 @@ comment "SDR tuner chips" config SDR_MAX2175 tristate "Maxim 2175 RF to Bits tuner" depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C + select REGMAP_I2C help Support for Maxim 2175 tuner. It is an advanced analog/digital radio receiver with RF-to-Bits front-end designed for SDR solutions. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index fd4ea86dedd5..c147bb9d28db 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o +obj-$(CONFIG_VIDEO_OV5675) += ov5675.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o obj-$(CONFIG_VIDEO_OV7251) += ov7251.o @@ -108,9 +109,11 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 925c171e7797..19c74db0649f 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -19,13 +19,12 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -#define AD5820_NAME "ad5820" - /* Register definitions */ #define AD5820_POWER_DOWN (1 << 15) #define AD5820_DAC_SHIFT 4 @@ -47,6 +46,8 @@ struct ad5820_device { u32 focus_ramp_time; u32 focus_ramp_mode; + struct gpio_desc *enable_gpio; + struct mutex power_lock; int power_count; @@ -114,6 +115,8 @@ static int ad5820_power_off(struct ad5820_device *coil, bool standby) ret = ad5820_update_hw(coil); } + gpiod_set_value_cansleep(coil->enable_gpio, 0); + ret2 = regulator_disable(coil->vana); if (ret) return ret; @@ -128,6 +131,8 @@ static int ad5820_power_on(struct ad5820_device *coil, bool restore) if (ret < 0) return ret; + gpiod_set_value_cansleep(coil->enable_gpio, 1); + if (restore) { /* Restore the hardware settings. */ coil->standby = false; @@ -138,6 +143,7 @@ static int ad5820_power_on(struct ad5820_device *coil, bool restore) return 0; fail: + gpiod_set_value_cansleep(coil->enable_gpio, 0); coil->standby = true; regulator_disable(coil->vana); @@ -304,11 +310,21 @@ static int ad5820_probe(struct i2c_client *client, return ret; } + coil->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(coil->enable_gpio)) { + ret = PTR_ERR(coil->enable_gpio); + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "could not get enable gpio\n"); + return ret; + } + mutex_init(&coil->power_lock); v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; coil->subdev.internal_ops = &ad5820_internal_ops; + coil->subdev.entity.function = MEDIA_ENT_F_LENS; strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name)); ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); @@ -341,17 +357,28 @@ static int ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { AD5820_NAME, 0 }, + { "ad5820", 0 }, + { "ad5821", 0 }, + { "ad5823", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); +static const struct of_device_id ad5820_of_table[] = { + { .compatible = "adi,ad5820" }, + { .compatible = "adi,ad5821" }, + { .compatible = "adi,ad5823" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5820_of_table); + static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); static struct i2c_driver ad5820_i2c_driver = { .driver = { - .name = AD5820_NAME, + .name = "ad5820", .pm = &ad5820_pm, + .of_match_table = ad5820_of_table, }, .probe = ad5820_probe, .remove = ad5820_remove, diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index aa8b04cfed0f..8679a44e6413 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1148,10 +1148,10 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", ad9389b_rd(sd, 0x41), state->chip_revision); - state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1)); - if (state->edid_i2c_client == NULL) { + state->edid_i2c_client = i2c_new_dummy_device(client->adapter, (0x7e >> 1)); + if (IS_ERR(state->edid_i2c_client)) { v4l2_err(sd, "failed to register edid i2c client\n"); - err = -ENOMEM; + err = PTR_ERR(state->edid_i2c_client); goto err_entity; } diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 6f3dc8862622..6528e2343fc8 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1309,9 +1309,6 @@ static int adv7180_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); - state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -1329,17 +1326,17 @@ static int adv7180_probe(struct i2c_client *client, } if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { - state->csi_client = i2c_new_dummy(client->adapter, + state->csi_client = i2c_new_dummy_device(client->adapter, ADV7180_DEFAULT_CSI_I2C_ADDR); - if (!state->csi_client) - return -ENOMEM; + if (IS_ERR(state->csi_client)) + return PTR_ERR(state->csi_client); } if (state->chip_info->flags & ADV7180_FLAG_I2P) { - state->vpp_client = i2c_new_dummy(client->adapter, + state->vpp_client = i2c_new_dummy_device(client->adapter, ADV7180_DEFAULT_VPP_I2C_ADDR); - if (!state->vpp_client) { - ret = -ENOMEM; + if (IS_ERR(state->vpp_client)) { + ret = PTR_ERR(state->vpp_client); goto err_unregister_csi_client; } } @@ -1382,6 +1379,9 @@ 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); + return 0; err_free_irq: diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 4a441ee99dd8..63e94dfcb5d3 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -428,8 +428,7 @@ done: return pdata; } -static int adv7343_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7343_probe(struct i2c_client *client) { struct adv7343_state *state; int err; @@ -524,7 +523,7 @@ static struct i2c_driver adv7343_driver = { .of_match_table = of_match_ptr(adv7343_of_match), .name = "adv7343", }, - .probe = adv7343_probe, + .probe_new = adv7343_probe, .remove = adv7343_remove, .id_table = adv7343_id, }; diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index f57cd77a32fa..23e02ff27b17 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -183,14 +183,14 @@ static int adv748x_initialise_clients(struct adv748x_state *state) int ret; for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) { - state->i2c_clients[i] = i2c_new_secondary_device( + state->i2c_clients[i] = i2c_new_ancillary_device( state->client, adv748x_default_addresses[i].name, adv748x_default_addresses[i].default_addr); - if (state->i2c_clients[i] == NULL) { + if (IS_ERR(state->i2c_clients[i])) { adv_err(state, "failed to create i2c client %u\n", i); - return -ENOMEM; + return PTR_ERR(state->i2c_clients[i]); } ret = adv748x_configure_regmap(state, i); @@ -668,8 +668,7 @@ static void adv748x_dt_cleanup(struct adv748x_state *state) of_node_put(state->endpoints[i]); } -static int adv748x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv748x_probe(struct i2c_client *client) { struct adv748x_state *state; int ret; @@ -797,13 +796,6 @@ static int adv748x_remove(struct i2c_client *client) return 0; } -static const struct i2c_device_id adv748x_id[] = { - { "adv7481", 0 }, - { "adv7482", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, adv748x_id); - static const struct of_device_id adv748x_of_table[] = { { .compatible = "adi,adv7481", }, { .compatible = "adi,adv7482", }, @@ -816,9 +808,8 @@ static struct i2c_driver adv748x_driver = { .name = "adv748x", .of_match_table = adv748x_of_table, }, - .probe = adv748x_probe, + .probe_new = adv748x_probe, .remove = adv748x_remove, - .id_table = adv748x_id, }; module_i2c_driver(adv748x_driver); diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 5042f9e94aee..fccb388ce179 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -394,10 +394,10 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, #define io_read(s, r) adv748x_read(s, ADV748X_PAGE_IO, r) #define io_write(s, r, v) adv748x_write(s, ADV748X_PAGE_IO, r, v) -#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~m) | v) +#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~(m)) | (v)) #define hdmi_read(s, r) adv748x_read(s, ADV748X_PAGE_HDMI, r) -#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, r+1)) & m) +#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, (r)+1)) & (m)) #define hdmi_write(s, r, v) adv748x_write(s, ADV748X_PAGE_HDMI, r, v) #define repeater_read(s, r) adv748x_read(s, ADV748X_PAGE_REPEATER, r) @@ -405,11 +405,11 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, #define sdp_read(s, r) adv748x_read(s, ADV748X_PAGE_SDP, r) #define sdp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_SDP, r, v) -#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~m) | v) +#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~(m)) | (v)) #define cp_read(s, r) adv748x_read(s, ADV748X_PAGE_CP, r) #define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v) -#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~m) | v) +#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~(m)) | (v)) #define tx_read(t, r) adv748x_read(t->state, t->page, r) #define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v) diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 2ad6bdf1a9fc..62763ec4cd07 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1872,11 +1872,11 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * goto err_entity; } - state->i2c_edid = i2c_new_dummy(client->adapter, + state->i2c_edid = i2c_new_dummy_device(client->adapter, state->i2c_edid_addr >> 1); - if (state->i2c_edid == NULL) { + if (IS_ERR(state->i2c_edid)) { v4l2_err(sd, "failed to register edid i2c client\n"); - err = -ENOMEM; + err = PTR_ERR(state->i2c_edid); goto err_entity; } @@ -1889,11 +1889,11 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * } if (state->pdata.cec_clk) { - state->i2c_cec = i2c_new_dummy(client->adapter, + state->i2c_cec = i2c_new_dummy_device(client->adapter, state->i2c_cec_addr >> 1); - if (state->i2c_cec == NULL) { + if (IS_ERR(state->i2c_cec)) { v4l2_err(sd, "failed to register cec i2c client\n"); - err = -ENOMEM; + err = PTR_ERR(state->i2c_cec); goto err_unreg_edid; } adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ @@ -1901,10 +1901,10 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ } - state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); - if (state->i2c_pktmem == NULL) { + state->i2c_pktmem = i2c_new_dummy_device(client->adapter, state->i2c_pktmem_addr >> 1); + if (IS_ERR(state->i2c_pktmem)) { v4l2_err(sd, "failed to register pktmem i2c client\n"); - err = -ENOMEM; + err = PTR_ERR(state->i2c_pktmem); goto err_unreg_cec; } @@ -1940,8 +1940,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * err_unreg_pktmem: i2c_unregister_device(state->i2c_pktmem); err_unreg_cec: - if (state->i2c_cec) - i2c_unregister_device(state->i2c_cec); + i2c_unregister_device(state->i2c_cec); err_unreg_edid: i2c_unregister_device(state->i2c_edid); err_entity: @@ -1967,8 +1966,7 @@ static int adv7511_remove(struct i2c_client *client) adv7511_init_setup(sd); cancel_delayed_work(&state->edid_handler); i2c_unregister_device(state->i2c_edid); - if (state->i2c_cec) - i2c_unregister_device(state->i2c_cec); + i2c_unregister_device(state->i2c_cec); i2c_unregister_device(state->i2c_pktmem); destroy_workqueue(state->work_queue); v4l2_device_unregister_subdev(sd); @@ -1980,14 +1978,14 @@ static int adv7511_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7511_id[] = { - { "adv7511", 0 }, + { "adv7511-v4l2", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_id); static struct i2c_driver adv7511_driver = { .driver = { - .name = "adv7511", + .name = "adv7511-v4l2", }, .probe = adv7511_probe, .remove = adv7511_remove, diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 28a84bf9f8a9..09004d928d11 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1503,23 +1503,14 @@ static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) { - unsigned int freq; int a, b; a = hdmi_read(sd, 0x06); b = hdmi_read(sd, 0x3b); if (a < 0 || b < 0) return 0; - freq = a * 1000000 + ((b & 0x30) >> 4) * 250000; - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; - - freq = freq * 8 / bits_per_channel; - } - - return freq; + return a * 1000000 + ((b & 0x30) >> 4) * 250000; } static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) @@ -1530,9 +1521,28 @@ static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) b = hdmi_read(sd, 0x52); if (a < 0 || b < 0) return 0; + return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; } +static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; + unsigned int freq, bits_per_channel, pixelrepetition; + + freq = info->read_hdmi_pixelclock(sd); + if (is_hdmi(sd)) { + /* adjust for deep color mode and pixel repetition */ + bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + pixelrepetition = (hdmi_read(sd, 0x05) & 0x0f) + 1; + + freq = freq * 8 / bits_per_channel / pixelrepetition; + } + + return freq; +} + static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { @@ -1579,7 +1589,7 @@ static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, bt->width = w; bt->height = h; - bt->pixelclock = info->read_hdmi_pixelclock(sd); + bt->pixelclock = adv76xx_read_hdmi_pixelclock(sd); bt->hfrontporch = hdmi_read16(sd, 0x20, info->hfrontporch_mask); bt->hsync = hdmi_read16(sd, 0x22, info->hsync_mask); bt->hbackporch = hdmi_read16(sd, 0x24, info->hbackporch_mask); @@ -2862,10 +2872,8 @@ static void adv76xx_unregister_clients(struct adv76xx_state *state) { unsigned int i; - for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) { - if (state->i2c_clients[i]) - i2c_unregister_device(state->i2c_clients[i]); - } + for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) + i2c_unregister_device(state->i2c_clients[i]); } static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, @@ -2878,14 +2886,14 @@ static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd, struct i2c_client *new_client; if (pdata && pdata->i2c_addresses[page]) - new_client = i2c_new_dummy(client->adapter, + new_client = i2c_new_dummy_device(client->adapter, pdata->i2c_addresses[page]); else - new_client = i2c_new_secondary_device(client, + new_client = i2c_new_ancillary_device(client, adv76xx_default_addresses[page].name, adv76xx_default_addresses[page].default_addr); - if (new_client) + if (!IS_ERR(new_client)) io_write(sd, io_reg, new_client->addr << 1); return new_client; @@ -3516,15 +3524,19 @@ static int adv76xx_probe(struct i2c_client *client, } for (i = 1; i < ADV76XX_PAGE_MAX; ++i) { + struct i2c_client *dummy_client; + if (!(BIT(i) & state->info->page_mask)) continue; - state->i2c_clients[i] = adv76xx_dummy_client(sd, i); - if (!state->i2c_clients[i]) { - err = -EINVAL; + dummy_client = adv76xx_dummy_client(sd, i); + if (IS_ERR(dummy_client)) { + err = PTR_ERR(dummy_client); v4l2_err(sd, "failed to create i2c client %u\n", i); goto err_i2c; } + + state->i2c_clients[i] = dummy_client; } INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 11ab2df02dc7..0855f648416d 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2547,7 +2547,7 @@ struct adv7842_cfg_read_infoframe { u8 payload_addr; }; -static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infoframe *cri) +static void log_infoframe(struct v4l2_subdev *sd, const struct adv7842_cfg_read_infoframe *cri) { int i; u8 buffer[32]; @@ -2585,7 +2585,7 @@ static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infofr static void adv7842_log_infoframes(struct v4l2_subdev *sd) { int i; - struct adv7842_cfg_read_infoframe cri[] = { + static const struct adv7842_cfg_read_infoframe cri[] = { { "AVI", 0x01, 0xe0, 0x00 }, { "Audio", 0x02, 0xe3, 0x1c }, { "SDP", 0x04, 0xe6, 0x2a }, @@ -3351,28 +3351,17 @@ static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = { static void adv7842_unregister_clients(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); - if (state->i2c_avlink) - i2c_unregister_device(state->i2c_avlink); - if (state->i2c_cec) - i2c_unregister_device(state->i2c_cec); - if (state->i2c_infoframe) - i2c_unregister_device(state->i2c_infoframe); - if (state->i2c_sdp_io) - i2c_unregister_device(state->i2c_sdp_io); - if (state->i2c_sdp) - i2c_unregister_device(state->i2c_sdp); - if (state->i2c_afe) - i2c_unregister_device(state->i2c_afe); - if (state->i2c_repeater) - i2c_unregister_device(state->i2c_repeater); - if (state->i2c_edid) - i2c_unregister_device(state->i2c_edid); - if (state->i2c_hdmi) - i2c_unregister_device(state->i2c_hdmi); - if (state->i2c_cp) - i2c_unregister_device(state->i2c_cp); - if (state->i2c_vdp) - i2c_unregister_device(state->i2c_vdp); + i2c_unregister_device(state->i2c_avlink); + i2c_unregister_device(state->i2c_cec); + i2c_unregister_device(state->i2c_infoframe); + i2c_unregister_device(state->i2c_sdp_io); + i2c_unregister_device(state->i2c_sdp); + i2c_unregister_device(state->i2c_afe); + i2c_unregister_device(state->i2c_repeater); + i2c_unregister_device(state->i2c_edid); + i2c_unregister_device(state->i2c_hdmi); + i2c_unregister_device(state->i2c_cp); + i2c_unregister_device(state->i2c_vdp); state->i2c_avlink = NULL; state->i2c_cec = NULL; @@ -3400,9 +3389,12 @@ static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const cha return NULL; } - cp = i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); - if (!cp) - v4l2_err(sd, "register %s on i2c addr 0x%x failed\n", desc, addr); + cp = i2c_new_dummy_device(client->adapter, io_read(sd, io_reg) >> 1); + if (IS_ERR(cp)) { + v4l2_err(sd, "register %s on i2c addr 0x%x failed with %ld\n", + desc, addr, PTR_ERR(cp)); + cp = NULL; + } return cp; } diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 43336175c7d9..73bc50c919d7 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -157,7 +157,7 @@ static int bt819_init(struct v4l2_subdev *sd) 0x12, 0x04, /* 0x12 Output Format */ 0x13, 0x20, /* 0x13 Vertical Scaling msb 0x00 chroma comb OFF, line drop scaling, interlace scaling - BUG? Why does turning the chroma comb on fuck up color? + BUG? Why does turning the chroma comb on screw up color? Bug in the bt819 stepping on my board? */ 0x14, 0x00, /* 0x14 Vertical Scaling lsb */ diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index e6c06cb75d33..256acf73d5ea 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1396,8 +1396,7 @@ static int __maybe_unused et8ek8_resume(struct device *dev) return __et8ek8_set_power(sensor, true); } -static int et8ek8_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int et8ek8_probe(struct i2c_client *client) { struct et8ek8_sensor *sensor; struct device *dev = &client->dev; @@ -1504,7 +1503,7 @@ static struct i2c_driver et8ek8_i2c_driver = { .pm = &et8ek8_pm_ops, .of_match_table = et8ek8_of_table, }, - .probe = et8ek8_probe, + .probe_new = et8ek8_probe, .remove = __exit_p(et8ek8_remove), .id_table = et8ek8_id_table, }; diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c new file mode 100644 index 000000000000..c66cd1446c0f --- /dev/null +++ b/drivers/media/i2c/hi556.c @@ -0,0 +1,1200 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation. + +#include <asm/unaligned.h> +#include <linux/acpi.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-device.h> +#include <media/v4l2-fwnode.h> + +#define HI556_REG_VALUE_08BIT 1 +#define HI556_REG_VALUE_16BIT 2 +#define HI556_REG_VALUE_24BIT 3 + +#define HI556_LINK_FREQ_437MHZ 437000000ULL +#define HI556_MCLK 19200000 +#define HI556_DATA_LANES 2 +#define HI556_RGB_DEPTH 10 + +#define HI556_REG_CHIP_ID 0x0f16 +#define HI556_CHIP_ID 0x0556 + +#define HI556_REG_MODE_SELECT 0x0a00 +#define HI556_MODE_STANDBY 0x0000 +#define HI556_MODE_STREAMING 0x0100 + +/* vertical-timings from sensor */ +#define HI556_REG_FLL 0x0006 +#define HI556_FLL_30FPS 0x0814 +#define HI556_FLL_30FPS_MIN 0x0814 +#define HI556_FLL_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define HI556_REG_LLP 0x0008 + +/* Exposure controls from sensor */ +#define HI556_REG_EXPOSURE 0x0074 +#define HI556_EXPOSURE_MIN 6 +#define HI556_EXPOSURE_MAX_MARGIN 2 +#define HI556_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define HI556_REG_ANALOG_GAIN 0x0077 +#define HI556_ANAL_GAIN_MIN 0 +#define HI556_ANAL_GAIN_MAX 240 +#define HI556_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define HI556_REG_MWB_GR_GAIN 0x0078 +#define HI556_REG_MWB_GB_GAIN 0x007a +#define HI556_REG_MWB_R_GAIN 0x007c +#define HI556_REG_MWB_B_GAIN 0x007e +#define HI556_DGTL_GAIN_MIN 0 +#define HI556_DGTL_GAIN_MAX 2048 +#define HI556_DGTL_GAIN_STEP 1 +#define HI556_DGTL_GAIN_DEFAULT 256 + +/* Test Pattern Control */ +#define HI556_REG_ISP 0X0a05 +#define HI556_REG_ISP_TPG_EN 0x01 +#define HI556_REG_TEST_PATTERN 0x0201 + +enum { + HI556_LINK_FREQ_437MHZ_INDEX, +}; + +struct hi556_reg { + u16 address; + u16 val; +}; + +struct hi556_reg_list { + u32 num_of_regs; + const struct hi556_reg *regs; +}; + +struct hi556_link_freq_config { + const struct hi556_reg_list reg_list; +}; + +struct hi556_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 llp; + + /* Default vertical timining size */ + u32 fll_def; + + /* Min vertical timining size */ + u32 fll_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct hi556_reg_list reg_list; +}; + +#define to_hi556(_sd) container_of(_sd, struct hi556, sd) + +//SENSOR_INITIALIZATION +static const struct hi556_reg mipi_data_rate_874mbps[] = { + {0x0e00, 0x0102}, + {0x0e02, 0x0102}, + {0x0e0c, 0x0100}, + {0x2000, 0x7400}, + {0x2002, 0x001c}, + {0x2004, 0x0242}, + {0x2006, 0x0942}, + {0x2008, 0x7007}, + {0x200a, 0x0fd9}, + {0x200c, 0x0259}, + {0x200e, 0x7008}, + {0x2010, 0x160e}, + {0x2012, 0x0047}, + {0x2014, 0x2118}, + {0x2016, 0x0041}, + {0x2018, 0x00d8}, + {0x201a, 0x0145}, + {0x201c, 0x0006}, + {0x201e, 0x0181}, + {0x2020, 0x13cc}, + {0x2022, 0x2057}, + {0x2024, 0x7001}, + {0x2026, 0x0fca}, + {0x2028, 0x00cb}, + {0x202a, 0x009f}, + {0x202c, 0x7002}, + {0x202e, 0x13cc}, + {0x2030, 0x019b}, + {0x2032, 0x014d}, + {0x2034, 0x2987}, + {0x2036, 0x2766}, + {0x2038, 0x0020}, + {0x203a, 0x2060}, + {0x203c, 0x0e5d}, + {0x203e, 0x181d}, + {0x2040, 0x2066}, + {0x2042, 0x20c4}, + {0x2044, 0x5000}, + {0x2046, 0x0005}, + {0x2048, 0x0000}, + {0x204a, 0x01db}, + {0x204c, 0x025a}, + {0x204e, 0x00c0}, + {0x2050, 0x0005}, + {0x2052, 0x0006}, + {0x2054, 0x0ad9}, + {0x2056, 0x0259}, + {0x2058, 0x0618}, + {0x205a, 0x0258}, + {0x205c, 0x2266}, + {0x205e, 0x20c8}, + {0x2060, 0x2060}, + {0x2062, 0x707b}, + {0x2064, 0x0fdd}, + {0x2066, 0x81b8}, + {0x2068, 0x5040}, + {0x206a, 0x0020}, + {0x206c, 0x5060}, + {0x206e, 0x3143}, + {0x2070, 0x5081}, + {0x2072, 0x025c}, + {0x2074, 0x7800}, + {0x2076, 0x7400}, + {0x2078, 0x001c}, + {0x207a, 0x0242}, + {0x207c, 0x0942}, + {0x207e, 0x0bd9}, + {0x2080, 0x0259}, + {0x2082, 0x7008}, + {0x2084, 0x160e}, + {0x2086, 0x0047}, + {0x2088, 0x2118}, + {0x208a, 0x0041}, + {0x208c, 0x00d8}, + {0x208e, 0x0145}, + {0x2090, 0x0006}, + {0x2092, 0x0181}, + {0x2094, 0x13cc}, + {0x2096, 0x2057}, + {0x2098, 0x7001}, + {0x209a, 0x0fca}, + {0x209c, 0x00cb}, + {0x209e, 0x009f}, + {0x20a0, 0x7002}, + {0x20a2, 0x13cc}, + {0x20a4, 0x019b}, + {0x20a6, 0x014d}, + {0x20a8, 0x2987}, + {0x20aa, 0x2766}, + {0x20ac, 0x0020}, + {0x20ae, 0x2060}, + {0x20b0, 0x0e5d}, + {0x20b2, 0x181d}, + {0x20b4, 0x2066}, + {0x20b6, 0x20c4}, + {0x20b8, 0x50a0}, + {0x20ba, 0x0005}, + {0x20bc, 0x0000}, + {0x20be, 0x01db}, + {0x20c0, 0x025a}, + {0x20c2, 0x00c0}, + {0x20c4, 0x0005}, + {0x20c6, 0x0006}, + {0x20c8, 0x0ad9}, + {0x20ca, 0x0259}, + {0x20cc, 0x0618}, + {0x20ce, 0x0258}, + {0x20d0, 0x2266}, + {0x20d2, 0x20c8}, + {0x20d4, 0x2060}, + {0x20d6, 0x707b}, + {0x20d8, 0x0fdd}, + {0x20da, 0x86b8}, + {0x20dc, 0x50e0}, + {0x20de, 0x0020}, + {0x20e0, 0x5100}, + {0x20e2, 0x3143}, + {0x20e4, 0x5121}, + {0x20e6, 0x7800}, + {0x20e8, 0x3140}, + {0x20ea, 0x01c4}, + {0x20ec, 0x01c1}, + {0x20ee, 0x01c0}, + {0x20f0, 0x01c4}, + {0x20f2, 0x2700}, + {0x20f4, 0x3d40}, + {0x20f6, 0x7800}, + {0x20f8, 0xffff}, + {0x27fe, 0xe000}, + {0x3000, 0x60f8}, + {0x3002, 0x187f}, + {0x3004, 0x7060}, + {0x3006, 0x0114}, + {0x3008, 0x60b0}, + {0x300a, 0x1473}, + {0x300c, 0x0013}, + {0x300e, 0x140f}, + {0x3010, 0x0040}, + {0x3012, 0x100f}, + {0x3014, 0x60f8}, + {0x3016, 0x187f}, + {0x3018, 0x7060}, + {0x301a, 0x0114}, + {0x301c, 0x60b0}, + {0x301e, 0x1473}, + {0x3020, 0x0013}, + {0x3022, 0x140f}, + {0x3024, 0x0040}, + {0x3026, 0x000f}, + + {0x0b00, 0x0000}, + {0x0b02, 0x0045}, + {0x0b04, 0xb405}, + {0x0b06, 0xc403}, + {0x0b08, 0x0081}, + {0x0b0a, 0x8252}, + {0x0b0c, 0xf814}, + {0x0b0e, 0xc618}, + {0x0b10, 0xa828}, + {0x0b12, 0x004c}, + {0x0b14, 0x4068}, + {0x0b16, 0x0000}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7067}, + {0x0954, 0x0009}, + {0x0956, 0x0000}, + {0x0958, 0xbb80}, + {0x095a, 0x5140}, + {0x0c00, 0x1110}, + {0x0c02, 0x0011}, + {0x0c04, 0x0000}, + {0x0c06, 0x0200}, + {0x0c10, 0x0040}, + {0x0c12, 0x0040}, + {0x0c14, 0x0040}, + {0x0c16, 0x0040}, + {0x0a10, 0x4000}, + {0x3068, 0xf800}, + {0x306a, 0xf876}, + {0x006c, 0x0000}, + {0x005e, 0x0200}, + {0x000e, 0x0100}, + {0x0e0a, 0x0001}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0022}, + {0x0008, 0x0b00}, + {0x005a, 0x0202}, + {0x0012, 0x000e}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0028}, + {0x002a, 0x002d}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x1111}, + {0x0030, 0x1111}, + {0x0032, 0x1111}, + {0x0006, 0x07bc}, + {0x0a22, 0x0000}, + {0x0a12, 0x0a20}, + {0x0a14, 0x0798}, + {0x003e, 0x0000}, + {0x0074, 0x080e}, + {0x0070, 0x0407}, + {0x0002, 0x0000}, + {0x0a02, 0x0100}, + {0x0a24, 0x0100}, + {0x0046, 0x0000}, + {0x0076, 0x0000}, + {0x0060, 0x0000}, + {0x0062, 0x0530}, + {0x0064, 0x0500}, + {0x0066, 0x0530}, + {0x0068, 0x0500}, + {0x0122, 0x0300}, + {0x015a, 0xff08}, + {0x0804, 0x0300}, + {0x0806, 0x0100}, + {0x005c, 0x0102}, + {0x0a1a, 0x0800}, +}; + +static const struct hi556_reg mode_2592x1944_regs[] = { + {0x0a00, 0x0000}, + {0x0b0a, 0x8252}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7067}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0022}, + {0x0008, 0x0b00}, + {0x005a, 0x0202}, + {0x0012, 0x000e}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0028}, + {0x002a, 0x002d}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x1111}, + {0x0030, 0x1111}, + {0x0032, 0x1111}, + {0x0006, 0x0814}, + {0x0a22, 0x0000}, + {0x0a12, 0x0a20}, + {0x0a14, 0x0798}, + {0x003e, 0x0000}, + {0x0074, 0x0812}, + {0x0070, 0x0409}, + {0x0804, 0x0300}, + {0x0806, 0x0100}, + {0x0a04, 0x014a}, + {0x090c, 0x0fdc}, + {0x090e, 0x002d}, + + {0x0902, 0x4319}, + {0x0914, 0xc10a}, + {0x0916, 0x071f}, + {0x0918, 0x0408}, + {0x091a, 0x0c0d}, + {0x091c, 0x0f09}, + {0x091e, 0x0a00}, + {0x0958, 0xbb80}, +}; + +static const struct hi556_reg mode_1296x972_regs[] = { + {0x0a00, 0x0000}, + {0x0b0a, 0x8259}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7167}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0122}, + {0x0008, 0x0b00}, + {0x005a, 0x0404}, + {0x0012, 0x000c}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0022}, + {0x002a, 0x002b}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x3311}, + {0x0030, 0x3311}, + {0x0032, 0x3311}, + {0x0006, 0x0814}, + {0x0a22, 0x0000}, + {0x0a12, 0x0510}, + {0x0a14, 0x03cc}, + {0x003e, 0x0000}, + {0x0074, 0x0812}, + {0x0070, 0x0409}, + {0x0804, 0x0308}, + {0x0806, 0x0100}, + {0x0a04, 0x016a}, + {0x090e, 0x0010}, + {0x090c, 0x09c0}, + + {0x0902, 0x4319}, + {0x0914, 0xc106}, + {0x0916, 0x040e}, + {0x0918, 0x0304}, + {0x091a, 0x0708}, + {0x091c, 0x0e06}, + {0x091e, 0x0300}, + {0x0958, 0xbb80}, +}; + +static const char * const hi556_test_pattern_menu[] = { + "Disabled", + "Solid Colour", + "100% Colour Bars", + "Fade To Grey Colour Bars", + "PN9", + "Gradient Horizontal", + "Gradient Vertical", + "Check Board", + "Slant Pattern", +}; + +static const s64 link_freq_menu_items[] = { + HI556_LINK_FREQ_437MHZ, +}; + +static const struct hi556_link_freq_config link_freq_configs[] = { + [HI556_LINK_FREQ_437MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_874mbps), + .regs = mipi_data_rate_874mbps, + } + } +}; + +static const struct hi556_mode supported_modes[] = { + { + .width = 2592, + .height = 1944, + .fll_def = HI556_FLL_30FPS, + .fll_min = HI556_FLL_30FPS_MIN, + .llp = 0x0b00, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, + }, + .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, + }, + { + .width = 1296, + .height = 972, + .fll_def = HI556_FLL_30FPS, + .fll_min = HI556_FLL_30FPS_MIN, + .llp = 0x0b00, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x972_regs), + .regs = mode_1296x972_regs, + }, + .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, + } +}; + +struct hi556 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct hi556_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * HI556_DATA_LANES; + + do_div(pixel_rate, HI556_RGB_DEPTH); + + return pixel_rate; +} + +static int hi556_read_reg(struct hi556 *hi556, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + 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; +} + +static int hi556_write_reg(struct hi556 *hi556, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + u8 buf[6]; + + if (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; +} + +static int hi556_write_reg_list(struct hi556 *hi556, + const struct hi556_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = hi556_write_reg(hi556, r_list->regs[i].address, + HI556_REG_VALUE_16BIT, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int hi556_update_digital_gain(struct hi556 *hi556, u32 d_gain) +{ + int ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_GR_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_GB_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_R_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return hi556_write_reg(hi556, HI556_REG_MWB_B_GAIN, + HI556_REG_VALUE_16BIT, d_gain); +} + +static int hi556_test_pattern(struct hi556 *hi556, u32 pattern) +{ + int ret; + u32 val; + + if (pattern) { + ret = hi556_read_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, + val | HI556_REG_ISP_TPG_EN); + if (ret) + return ret; + } + + return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN, + HI556_REG_VALUE_08BIT, pattern); +} + +static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hi556 *hi556 = container_of(ctrl->handler, + struct hi556, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = hi556->cur_mode->height + ctrl->val - + HI556_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(hi556->exposure, + hi556->exposure->minimum, + exposure_max, hi556->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = hi556_write_reg(hi556, HI556_REG_ANALOG_GAIN, + HI556_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = hi556_update_digital_gain(hi556, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = hi556_write_reg(hi556, HI556_REG_EXPOSURE, + HI556_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = hi556_write_reg(hi556, HI556_REG_FLL, + HI556_REG_VALUE_16BIT, + hi556->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = hi556_test_pattern(hi556, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops hi556_ctrl_ops = { + .s_ctrl = hi556_set_ctrl, +}; + +static int hi556_init_controls(struct hi556 *hi556) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &hi556->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &hi556->mutex; + hi556->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (hi556->link_freq) + hi556->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hi556->pixel_rate = v4l2_ctrl_new_std + (ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX), + 1, + to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX)); + hi556->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_VBLANK, + hi556->cur_mode->fll_min - + hi556->cur_mode->height, + HI556_FLL_MAX - + hi556->cur_mode->height, 1, + hi556->cur_mode->fll_def - + hi556->cur_mode->height); + + h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; + + hi556->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (hi556->hblank) + hi556->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + HI556_ANAL_GAIN_MIN, HI556_ANAL_GAIN_MAX, + HI556_ANAL_GAIN_STEP, HI556_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + HI556_DGTL_GAIN_MIN, HI556_DGTL_GAIN_MAX, + HI556_DGTL_GAIN_STEP, HI556_DGTL_GAIN_DEFAULT); + exposure_max = hi556->cur_mode->fll_def - HI556_EXPOSURE_MAX_MARGIN; + hi556->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_EXPOSURE, + HI556_EXPOSURE_MIN, exposure_max, + HI556_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(hi556_test_pattern_menu) - 1, + 0, 0, hi556_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + hi556->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void hi556_assign_pad_format(const struct hi556_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int hi556_start_streaming(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + const struct hi556_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = hi556->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = hi556_write_reg_list(hi556, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &hi556->cur_mode->reg_list; + ret = hi556_write_reg_list(hi556, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(hi556->sd.ctrl_handler); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MODE_SELECT, + HI556_REG_VALUE_16BIT, HI556_MODE_STREAMING); + + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void hi556_stop_streaming(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + + if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, + HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int hi556_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct hi556 *hi556 = to_hi556(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (hi556->streaming == enable) + return 0; + + mutex_lock(&hi556->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&hi556->mutex); + return ret; + } + + ret = hi556_start_streaming(hi556); + if (ret) { + enable = 0; + hi556_stop_streaming(hi556); + pm_runtime_put(&client->dev); + } + } else { + hi556_stop_streaming(hi556); + pm_runtime_put(&client->dev); + } + + hi556->streaming = enable; + mutex_unlock(&hi556->mutex); + + return ret; +} + +static int __maybe_unused hi556_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + if (hi556->streaming) + hi556_stop_streaming(hi556); + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int __maybe_unused hi556_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + int ret; + + mutex_lock(&hi556->mutex); + if (hi556->streaming) { + ret = hi556_start_streaming(hi556); + if (ret) + goto error; + } + + mutex_unlock(&hi556->mutex); + + return 0; + +error: + hi556_stop_streaming(hi556); + hi556->streaming = 0; + mutex_unlock(&hi556->mutex); + return ret; +} + +static int hi556_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct hi556 *hi556 = to_hi556(sd); + const struct hi556_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&hi556->mutex); + hi556_assign_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + hi556->cur_mode = mode; + __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(hi556->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->fll_def - mode->height; + __v4l2_ctrl_modify_range(hi556->vblank, + mode->fll_min - mode->height, + HI556_FLL_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(hi556->vblank, vblank_def); + + h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; + + __v4l2_ctrl_modify_range(hi556->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int hi556_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, cfg, + fmt->pad); + else + hi556_assign_pad_format(hi556->cur_mode, &fmt->format); + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int hi556_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int hi556_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + hi556_assign_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&hi556->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops hi556_video_ops = { + .s_stream = hi556_set_stream, +}; + +static const struct v4l2_subdev_pad_ops hi556_pad_ops = { + .set_fmt = hi556_set_format, + .get_fmt = hi556_get_format, + .enum_mbus_code = hi556_enum_mbus_code, + .enum_frame_size = hi556_enum_frame_size, +}; + +static const struct v4l2_subdev_ops hi556_subdev_ops = { + .video = &hi556_video_ops, + .pad = &hi556_pad_ops, +}; + +static const struct media_entity_operations hi556_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops hi556_internal_ops = { + .open = hi556_open, +}; + +static int hi556_identify_module(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + int ret; + u32 val; + + ret = hi556_read_reg(hi556, HI556_REG_CHIP_ID, + HI556_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + if (val != HI556_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + HI556_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int hi556_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret = 0; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != HI556_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + 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 != 2) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int hi556_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&hi556->mutex); + + return 0; +} + +static int hi556_probe(struct i2c_client *client) +{ + struct hi556 *hi556; + int ret; + + ret = hi556_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); + if (!hi556) + return -ENOMEM; + + v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); + ret = hi556_identify_module(hi556); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&hi556->mutex); + hi556->cur_mode = &supported_modes[0]; + ret = hi556_init_controls(hi556); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + hi556->sd.internal_ops = &hi556_internal_ops; + hi556->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + hi556->sd.entity.ops = &hi556_subdev_entity_ops; + hi556->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + hi556->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&hi556->sd.entity, 1, &hi556->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&hi556->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&hi556->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); + mutex_destroy(&hi556->mutex); + + return ret; +} + +static const struct dev_pm_ops hi556_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hi556_suspend, hi556_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hi556_acpi_ids[] = { + {"INT3537"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, hi556_acpi_ids); +#endif + +static struct i2c_driver hi556_i2c_driver = { + .driver = { + .name = "hi556", + .pm = &hi556_pm_ops, + .acpi_match_table = ACPI_PTR(hi556_acpi_ids), + }, + .probe_new = hi556_probe, + .remove = hi556_remove, +}; + +module_i2c_driver(hi556_i2c_driver); + +MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_DESCRIPTION("Hynix HI556 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 159a3a604f0e..adcaaa8c86d1 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -47,6 +47,7 @@ struct imx214 { struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *link_freq; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *unit_size; struct regulator_bulk_data supplies[IMX214_NUM_SUPPLIES]; @@ -948,6 +949,10 @@ static int imx214_probe(struct i2c_client *client) static const s64 link_freq[] = { IMX214_DEFAULT_LINK_FREQ, }; + static const struct v4l2_area unit_size = { + .width = 1120, + .height = 1120, + }; int ret; ret = imx214_parse_fwnode(dev); @@ -1029,6 +1034,10 @@ static int imx214_probe(struct i2c_client *client) V4L2_CID_EXPOSURE, 0, 3184, 1, 0x0c70); + imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls, + NULL, + V4L2_CID_UNIT_CELL_SIZE, + v4l2_ctrl_ptr_create((void *)&unit_size)); ret = imx214->ctrls.error; if (ret) { dev_err(&client->dev, "%s control init failed (%d)\n", diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f3ff1af209f9..6011cec5e351 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1821,8 +1821,7 @@ static const struct i2c_device_id imx274_id[] = { }; MODULE_DEVICE_TABLE(i2c, imx274_id); -static int imx274_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int imx274_probe(struct i2c_client *client) { struct v4l2_subdev *sd; struct stimx274 *imx274; @@ -1984,7 +1983,7 @@ static struct i2c_driver imx274_i2c_driver = { .name = DRIVER_NAME, .of_match_table = imx274_of_id_table, }, - .probe = imx274_probe, + .probe_new = imx274_probe, .remove = imx274_remove, .id_table = imx274_id, }; diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c new file mode 100644 index 000000000000..f7678e5a5d87 --- /dev/null +++ b/drivers/media/i2c/imx290.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sony IMX290 CMOS Image Sensor Driver + * + * Copyright (C) 2019 FRAMOS GmbH. + * + * Copyright (C) 2019 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define IMX290_STANDBY 0x3000 +#define IMX290_REGHOLD 0x3001 +#define IMX290_XMSTA 0x3002 +#define IMX290_GAIN 0x3014 + +#define IMX290_DEFAULT_LINK_FREQ 445500000 + +static const char * const imx290_supply_name[] = { + "vdda", + "vddd", + "vdddo", +}; + +#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name) + +struct imx290_regval { + u16 reg; + u8 val; +}; + +struct imx290_mode { + u32 width; + u32 height; + u32 pixel_rate; + u32 link_freq_index; + + const struct imx290_regval *data; + u32 data_size; +}; + +struct imx290 { + struct device *dev; + struct clk *xclk; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct v4l2_fwnode_endpoint ep; + struct media_pad pad; + struct v4l2_mbus_framefmt current_format; + const struct imx290_mode *current_mode; + + struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES]; + struct gpio_desc *rst_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + + struct mutex lock; +}; + +struct imx290_pixfmt { + u32 code; +}; + +static const struct imx290_pixfmt imx290_formats[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10 }, +}; + +static const struct regmap_config imx290_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct imx290_regval imx290_global_init_settings[] = { + { 0x3007, 0x00 }, + { 0x3009, 0x00 }, + { 0x3018, 0x65 }, + { 0x3019, 0x04 }, + { 0x301a, 0x00 }, + { 0x3443, 0x03 }, + { 0x3444, 0x20 }, + { 0x3445, 0x25 }, + { 0x3407, 0x03 }, + { 0x303a, 0x0c }, + { 0x3040, 0x00 }, + { 0x3041, 0x00 }, + { 0x303c, 0x00 }, + { 0x303d, 0x00 }, + { 0x3042, 0x9c }, + { 0x3043, 0x07 }, + { 0x303e, 0x49 }, + { 0x303f, 0x04 }, + { 0x304b, 0x0a }, + { 0x300f, 0x00 }, + { 0x3010, 0x21 }, + { 0x3012, 0x64 }, + { 0x3016, 0x09 }, + { 0x3070, 0x02 }, + { 0x3071, 0x11 }, + { 0x309b, 0x10 }, + { 0x309c, 0x22 }, + { 0x30a2, 0x02 }, + { 0x30a6, 0x20 }, + { 0x30a8, 0x20 }, + { 0x30aa, 0x20 }, + { 0x30ac, 0x20 }, + { 0x30b0, 0x43 }, + { 0x3119, 0x9e }, + { 0x311c, 0x1e }, + { 0x311e, 0x08 }, + { 0x3128, 0x05 }, + { 0x313d, 0x83 }, + { 0x3150, 0x03 }, + { 0x317e, 0x00 }, + { 0x32b8, 0x50 }, + { 0x32b9, 0x10 }, + { 0x32ba, 0x00 }, + { 0x32bb, 0x04 }, + { 0x32c8, 0x50 }, + { 0x32c9, 0x10 }, + { 0x32ca, 0x00 }, + { 0x32cb, 0x04 }, + { 0x332c, 0xd3 }, + { 0x332d, 0x10 }, + { 0x332e, 0x0d }, + { 0x3358, 0x06 }, + { 0x3359, 0xe1 }, + { 0x335a, 0x11 }, + { 0x3360, 0x1e }, + { 0x3361, 0x61 }, + { 0x3362, 0x10 }, + { 0x33b0, 0x50 }, + { 0x33b2, 0x1a }, + { 0x33b3, 0x04 }, +}; + +static const struct imx290_regval imx290_1080p_settings[] = { + /* mode settings */ + { 0x3007, 0x00 }, + { 0x303a, 0x0c }, + { 0x3414, 0x0a }, + { 0x3472, 0x80 }, + { 0x3473, 0x07 }, + { 0x3418, 0x38 }, + { 0x3419, 0x04 }, + { 0x3012, 0x64 }, + { 0x3013, 0x00 }, + { 0x305c, 0x18 }, + { 0x305d, 0x03 }, + { 0x305e, 0x20 }, + { 0x305f, 0x01 }, + { 0x315e, 0x1a }, + { 0x3164, 0x1a }, + { 0x3480, 0x49 }, + /* data rate settings */ + { 0x3009, 0x01 }, + { 0x3405, 0x10 }, + { 0x3446, 0x57 }, + { 0x3447, 0x00 }, + { 0x3448, 0x37 }, + { 0x3449, 0x00 }, + { 0x344a, 0x1f }, + { 0x344b, 0x00 }, + { 0x344c, 0x1f }, + { 0x344d, 0x00 }, + { 0x344e, 0x1f }, + { 0x344f, 0x00 }, + { 0x3450, 0x77 }, + { 0x3451, 0x00 }, + { 0x3452, 0x1f }, + { 0x3453, 0x00 }, + { 0x3454, 0x17 }, + { 0x3455, 0x00 }, + { 0x301c, 0x98 }, + { 0x301d, 0x08 }, +}; + +static const struct imx290_regval imx290_720p_settings[] = { + /* mode settings */ + { 0x3007, 0x10 }, + { 0x303a, 0x06 }, + { 0x3414, 0x04 }, + { 0x3472, 0x00 }, + { 0x3473, 0x05 }, + { 0x3418, 0xd0 }, + { 0x3419, 0x02 }, + { 0x3012, 0x64 }, + { 0x3013, 0x00 }, + { 0x305c, 0x20 }, + { 0x305d, 0x00 }, + { 0x305e, 0x20 }, + { 0x305f, 0x01 }, + { 0x315e, 0x1a }, + { 0x3164, 0x1a }, + { 0x3480, 0x49 }, + /* data rate settings */ + { 0x3009, 0x01 }, + { 0x3405, 0x10 }, + { 0x3446, 0x4f }, + { 0x3447, 0x00 }, + { 0x3448, 0x2f }, + { 0x3449, 0x00 }, + { 0x344a, 0x17 }, + { 0x344b, 0x00 }, + { 0x344c, 0x17 }, + { 0x344d, 0x00 }, + { 0x344e, 0x17 }, + { 0x344f, 0x00 }, + { 0x3450, 0x57 }, + { 0x3451, 0x00 }, + { 0x3452, 0x17 }, + { 0x3453, 0x00 }, + { 0x3454, 0x17 }, + { 0x3455, 0x00 }, + { 0x301c, 0xe4 }, + { 0x301d, 0x0c }, +}; + +static const struct imx290_regval imx290_10bit_settings[] = { + { 0x3005, 0x00}, + { 0x3046, 0x00}, + { 0x3129, 0x1d}, + { 0x317c, 0x12}, + { 0x31ec, 0x37}, + { 0x3441, 0x0a}, + { 0x3442, 0x0a}, + { 0x300a, 0x3c}, + { 0x300b, 0x00}, +}; + +/* supported link frequencies */ +static const s64 imx290_link_freq[] = { + IMX290_DEFAULT_LINK_FREQ, +}; + +/* Mode configs */ +static const struct imx290_mode imx290_modes[] = { + { + .width = 1920, + .height = 1080, + .data = imx290_1080p_settings, + .data_size = ARRAY_SIZE(imx290_1080p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, + { + .width = 1280, + .height = 720, + .data = imx290_720p_settings, + .data_size = ARRAY_SIZE(imx290_720p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, +}; + +static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx290, sd); +} + +static inline int imx290_read_reg(struct imx290 *imx290, u16 addr, u8 *value) +{ + unsigned int regval; + int ret; + + ret = regmap_read(imx290->regmap, addr, ®val); + if (ret) { + dev_err(imx290->dev, "I2C read failed for addr: %x\n", addr); + return ret; + } + + *value = regval & 0xff; + + return 0; +} + +static int imx290_write_reg(struct imx290 *imx290, u16 addr, u8 value) +{ + int ret; + + ret = regmap_write(imx290->regmap, addr, value); + if (ret) { + dev_err(imx290->dev, "I2C write failed for addr: %x\n", addr); + return ret; + } + + return ret; +} + +static int imx290_set_register_array(struct imx290 *imx290, + const struct imx290_regval *settings, + unsigned int num_settings) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_settings; ++i, ++settings) { + ret = imx290_write_reg(imx290, settings->reg, settings->val); + if (ret < 0) + return ret; + + /* Settle time is 10ms for all registers */ + msleep(10); + } + + return 0; +} + +static int imx290_write_buffered_reg(struct imx290 *imx290, u16 address_low, + u8 nr_regs, u32 value) +{ + unsigned int i; + int ret; + + ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x01); + if (ret) { + dev_err(imx290->dev, "Error setting hold register\n"); + return ret; + } + + for (i = 0; i < nr_regs; i++) { + ret = imx290_write_reg(imx290, address_low + i, + (u8)(value >> (i * 8))); + if (ret) { + dev_err(imx290->dev, "Error writing buffered registers\n"); + return ret; + } + } + + ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x00); + if (ret) { + dev_err(imx290->dev, "Error setting hold register\n"); + return ret; + } + + return ret; +} + +static int imx290_set_gain(struct imx290 *imx290, u32 value) +{ + int ret; + + ret = imx290_write_buffered_reg(imx290, IMX290_GAIN, 1, value); + if (ret) + dev_err(imx290->dev, "Unable to write gain\n"); + + return ret; +} + +/* Stop streaming */ +static int imx290_stop_streaming(struct imx290 *imx290) +{ + int ret; + + ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x01); + if (ret < 0) + return ret; + + msleep(30); + + return imx290_write_reg(imx290, IMX290_XMSTA, 0x01); +} + +static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx290 *imx290 = container_of(ctrl->handler, + struct imx290, ctrls); + int ret = 0; + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(imx290->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ret = imx290_set_gain(imx290, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(imx290->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx290_ctrl_ops = { + .s_ctrl = imx290_set_ctrl, +}; + +static int imx290_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(imx290_formats)) + return -EINVAL; + + code->code = imx290_formats[code->index].code; + + return 0; +} + +static int imx290_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx290 *imx290 = to_imx290(sd); + struct v4l2_mbus_framefmt *framefmt; + + mutex_lock(&imx290->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + framefmt = v4l2_subdev_get_try_format(&imx290->sd, cfg, + fmt->pad); + else + framefmt = &imx290->current_format; + + fmt->format = *framefmt; + + mutex_unlock(&imx290->lock); + + return 0; +} + +static int imx290_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx290 *imx290 = to_imx290(sd); + const struct imx290_mode *mode; + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&imx290->lock); + + mode = v4l2_find_nearest_size(imx290_modes, + ARRAY_SIZE(imx290_modes), + width, height, + fmt->format.width, fmt->format.height); + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + + for (i = 0; i < ARRAY_SIZE(imx290_formats); i++) + if (imx290_formats[i].code == fmt->format.code) + break; + + if (i >= ARRAY_SIZE(imx290_formats)) + i = 0; + + fmt->format.code = imx290_formats[i].code; + fmt->format.field = V4L2_FIELD_NONE; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + } else { + format = &imx290->current_format; + __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); + + imx290->current_mode = mode; + } + + *format = fmt->format; + + mutex_unlock(&imx290->lock); + + return 0; +} + +static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = 1920; + fmt.format.height = 1080; + + imx290_set_fmt(subdev, cfg, &fmt); + + return 0; +} + +static int imx290_write_current_format(struct imx290 *imx290, + struct v4l2_mbus_framefmt *format) +{ + int ret; + + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + ret = imx290_set_register_array(imx290, imx290_10bit_settings, + ARRAY_SIZE( + imx290_10bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; + default: + dev_err(imx290->dev, "Unknown pixel format\n"); + return -EINVAL; + } + + return 0; +} + +/* Start streaming */ +static int imx290_start_streaming(struct imx290 *imx290) +{ + int ret; + + /* Set init register settings */ + ret = imx290_set_register_array(imx290, imx290_global_init_settings, + ARRAY_SIZE( + imx290_global_init_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set init registers\n"); + return ret; + } + + /* Set current frame format */ + ret = imx290_write_current_format(imx290, &imx290->current_format); + if (ret < 0) { + dev_err(imx290->dev, "Could not set frame format\n"); + return ret; + } + + /* Apply default values of current mode */ + ret = imx290_set_register_array(imx290, imx290->current_mode->data, + imx290->current_mode->data_size); + if (ret < 0) { + dev_err(imx290->dev, "Could not set current mode\n"); + return ret; + } + + /* Apply customized values from user */ + ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); + if (ret) { + dev_err(imx290->dev, "Could not sync v4l2 controls\n"); + return ret; + } + + ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x00); + if (ret < 0) + return ret; + + msleep(30); + + /* Start streaming */ + return imx290_write_reg(imx290, IMX290_XMSTA, 0x00); +} + +static int imx290_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx290 *imx290 = to_imx290(sd); + int ret = 0; + + if (enable) { + ret = pm_runtime_get_sync(imx290->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx290->dev); + goto unlock_and_return; + } + + ret = imx290_start_streaming(imx290); + if (ret) { + dev_err(imx290->dev, "Start stream failed\n"); + pm_runtime_put(imx290->dev); + goto unlock_and_return; + } + } else { + imx290_stop_streaming(imx290); + pm_runtime_put(imx290->dev); + } + +unlock_and_return: + + return ret; +} + +static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) +{ + unsigned int i; + + for (i = 0; i < IMX290_NUM_SUPPLIES; i++) + imx290->supplies[i].supply = imx290_supply_name[i]; + + return devm_regulator_bulk_get(dev, IMX290_NUM_SUPPLIES, + imx290->supplies); +} + +static int imx290_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + int ret; + + ret = clk_prepare_enable(imx290->xclk); + if (ret) { + dev_err(imx290->dev, "Failed to enable clock\n"); + return ret; + } + + ret = regulator_bulk_enable(IMX290_NUM_SUPPLIES, imx290->supplies); + if (ret) { + dev_err(imx290->dev, "Failed to enable regulators\n"); + clk_disable_unprepare(imx290->xclk); + return ret; + } + + usleep_range(1, 2); + gpiod_set_value_cansleep(imx290->rst_gpio, 1); + usleep_range(30000, 31000); + + return 0; +} + +static int imx290_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + + clk_disable_unprepare(imx290->xclk); + gpiod_set_value_cansleep(imx290->rst_gpio, 0); + regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); + + return 0; +} + +static const struct dev_pm_ops imx290_pm_ops = { + SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) +}; + +static const struct v4l2_subdev_video_ops imx290_video_ops = { + .s_stream = imx290_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx290_pad_ops = { + .init_cfg = imx290_entity_init_cfg, + .enum_mbus_code = imx290_enum_mbus_code, + .get_fmt = imx290_get_fmt, + .set_fmt = imx290_set_fmt, +}; + +static const struct v4l2_subdev_ops imx290_subdev_ops = { + .video = &imx290_video_ops, + .pad = &imx290_pad_ops, +}; + +static const struct media_entity_operations imx290_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int imx290_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct imx290 *imx290; + u32 xclk_freq; + int ret; + + imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); + if (!imx290) + return -ENOMEM; + + imx290->dev = dev; + imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); + if (IS_ERR(imx290->regmap)) { + dev_err(dev, "Unable to initialize I2C\n"); + return -ENODEV; + } + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "Parsing endpoint node failed\n"); + goto free_err; + } + + if (!imx290->ep.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto free_err; + } + + if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { + dev_err(dev, "Unsupported link frequency\n"); + ret = -EINVAL; + goto free_err; + } + + /* Only CSI2 is supported for now */ + if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(dev, "Unsupported bus type, should be CSI2\n"); + ret = -EINVAL; + goto free_err; + } + + /* Set default mode to max resolution */ + imx290->current_mode = &imx290_modes[0]; + + /* get system clock (xclk) */ + imx290->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(imx290->xclk)) { + dev_err(dev, "Could not get xclk"); + ret = PTR_ERR(imx290->xclk); + goto free_err; + } + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &xclk_freq); + if (ret) { + dev_err(dev, "Could not get xclk frequency\n"); + goto free_err; + } + + /* external clock must be 37.125 MHz */ + if (xclk_freq != 37125000) { + dev_err(dev, "External clock frequency %u is not supported\n", + xclk_freq); + ret = -EINVAL; + goto free_err; + } + + ret = clk_set_rate(imx290->xclk, xclk_freq); + if (ret) { + dev_err(dev, "Could not set xclk frequency\n"); + goto free_err; + } + + ret = imx290_get_regulators(dev, imx290); + if (ret < 0) { + dev_err(dev, "Cannot get regulators\n"); + goto free_err; + } + + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(imx290->rst_gpio)) { + dev_err(dev, "Cannot get reset gpio\n"); + ret = PTR_ERR(imx290->rst_gpio); + goto free_err; + } + + mutex_init(&imx290->lock); + + v4l2_ctrl_handler_init(&imx290->ctrls, 3); + + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_GAIN, 0, 72, 1, 0); + imx290->link_freq = + v4l2_ctrl_new_int_menu(&imx290->ctrls, + &imx290_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx290_link_freq) - 1, + 0, imx290_link_freq); + if (imx290->link_freq) + imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, + imx290_modes[0].pixel_rate); + + imx290->sd.ctrl_handler = &imx290->ctrls; + + if (imx290->ctrls.error) { + dev_err(dev, "Control initialization error %d\n", + imx290->ctrls.error); + ret = imx290->ctrls.error; + goto free_ctrl; + } + + v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); + imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx290->sd.dev = &client->dev; + imx290->sd.entity.ops = &imx290_subdev_entity_ops; + imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + imx290->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad); + if (ret < 0) { + dev_err(dev, "Could not register media entity\n"); + goto free_ctrl; + } + + ret = v4l2_async_register_subdev(&imx290->sd); + if (ret < 0) { + dev_err(dev, "Could not register v4l2 device\n"); + goto free_entity; + } + + /* Power on the device to match runtime PM state below */ + ret = imx290_power_on(dev); + if (ret < 0) { + dev_err(dev, "Could not power on the device\n"); + goto free_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + v4l2_fwnode_endpoint_free(&imx290->ep); + + return 0; + +free_entity: + media_entity_cleanup(&imx290->sd.entity); +free_ctrl: + v4l2_ctrl_handler_free(&imx290->ctrls); + mutex_destroy(&imx290->lock); +free_err: + v4l2_fwnode_endpoint_free(&imx290->ep); + + return ret; +} + +static int imx290_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + mutex_destroy(&imx290->lock); + + pm_runtime_disable(imx290->dev); + if (!pm_runtime_status_suspended(imx290->dev)) + imx290_power_off(imx290->dev); + pm_runtime_set_suspended(imx290->dev); + + return 0; +} + +static const struct of_device_id imx290_of_match[] = { + { .compatible = "sony,imx290" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx290_of_match); + +static struct i2c_driver imx290_i2c_driver = { + .probe_new = imx290_probe, + .remove = imx290_remove, + .driver = { + .name = "imx290", + .pm = &imx290_pm_ops, + .of_match_table = of_match_ptr(imx290_of_match), + }, +}; + +module_i2c_driver(imx290_i2c_driver); + +MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver"); +MODULE_AUTHOR("FRAMOS GmbH"); +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 876d7587a1da..e8119ad0bc71 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -885,9 +885,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) INIT_DELAYED_WORK(&ir->work, ir_work); if (probe_tx) { - ir->tx_c = i2c_new_dummy(client->adapter, 0x70); - if (!ir->tx_c) { + ir->tx_c = i2c_new_dummy_device(client->adapter, 0x70); + if (IS_ERR(ir->tx_c)) { dev_err(&client->dev, "failed to setup tx i2c address"); + err = PTR_ERR(ir->tx_c); + goto err_out_free; } else if (!zilog_init(ir)) { ir->carrier = 38000; ir->duty_cycle = 40; @@ -904,7 +906,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; err_out_free: - if (ir->tx_c) + if (!IS_ERR(ir->tx_c)) i2c_unregister_device(ir->tx_c); /* Only frees rc if it were allocated internally */ @@ -916,16 +918,12 @@ static int ir_remove(struct i2c_client *client) { struct IR_i2c *ir = i2c_get_clientdata(client); - /* kill outstanding polls */ cancel_delayed_work_sync(&ir->work); - if (ir->tx_c) - i2c_unregister_device(ir->tx_c); + i2c_unregister_device(ir->tx_c); - /* unregister device */ rc_unregister_device(ir->rc); - /* free memory */ return 0; } diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index d8a8853f9a2b..c76ccf67a909 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -134,7 +134,7 @@ static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl) { struct lm3646_flash *flash = to_lm3646_flash(ctrl); unsigned int reg_val; - int rval = -EINVAL; + int rval; switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 7b226fadcdb8..506a30e69ced 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -591,8 +591,8 @@ static int max2175_set_lo_freq(struct max2175 *ctx, u32 lo_freq) lo_freq *= lo_mult; int_desired = lo_freq / ctx->xtal_freq; - frac_desired = div_u64((u64)(lo_freq % ctx->xtal_freq) << 20, - ctx->xtal_freq); + frac_desired = div64_ul((u64)(lo_freq % ctx->xtal_freq) << 20, + ctx->xtal_freq); /* Check CSM is not busy */ ret = max2175_poll_csm_ready(ctx); @@ -1271,8 +1271,7 @@ static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load, return 0; } -static int max2175_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max2175_probe(struct i2c_client *client) { bool master = true, am_hiz = false; u32 refout_load, refout_bits = 0; /* REFOUT disabled */ @@ -1433,7 +1432,7 @@ static struct i2c_driver max2175_driver = { .name = DRIVER_NAME, .of_match_table = max2175_of_ids, }, - .probe = max2175_probe, + .probe_new = max2175_probe, .remove = max2175_remove, .id_table = max2175_id, }; diff --git a/drivers/media/i2c/max2175.h b/drivers/media/i2c/max2175.h index 1ece587c153d..4c722ea3e5f1 100644 --- a/drivers/media/i2c/max2175.h +++ b/drivers/media/i2c/max2175.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +/* SPDX-License-Identifier: GPL-2.0 */ +/* * Maxim Integrated MAX2175 RF to Bits tuner driver * * This driver & most of the hard coded values are based on the reference diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 2df743cbe09d..210ea76adb53 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -167,7 +167,7 @@ static int multi_reg_write(struct i2c_client *client, static int mt9m001_init(struct i2c_client *client) { - const struct mt9m001_reg init_regs[] = { + static const struct mt9m001_reg init_regs[] = { /* * Issue a soft reset. This returns all registers to their * default values. @@ -726,8 +726,7 @@ static const struct v4l2_subdev_ops mt9m001_subdev_ops = { .pad = &mt9m001_subdev_pad_ops, }; -static int mt9m001_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int mt9m001_probe(struct i2c_client *client) { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = client->adapter; @@ -872,7 +871,7 @@ static struct i2c_driver mt9m001_i2c_driver = { .pm = &mt9m001_pm_ops, .of_match_table = mt9m001_of_match, }, - .probe = mt9m001_probe, + .probe_new = mt9m001_probe, .remove = mt9m001_remove, .id_table = mt9m001_id, }; diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 12cb012d91f7..17e8253f5748 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -533,7 +533,7 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, format->format = *mf; return 0; #else - return -ENOTTY; + return -EINVAL; #endif } @@ -1243,8 +1243,7 @@ out_put_fw: return ret; } -static int mt9m111_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int mt9m111_probe(struct i2c_client *client) { struct mt9m111 *mt9m111; struct i2c_adapter *adapter = client->adapter; @@ -1388,7 +1387,7 @@ static struct i2c_driver mt9m111_i2c_driver = { .name = "mt9m111", .of_match_table = of_match_ptr(mt9m111_of_match), }, - .probe = mt9m111_probe, + .probe_new = mt9m111_probe, .remove = mt9m111_remove, .id_table = mt9m111_id, }; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 4b9b98cf6674..5bd3ae82992f 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -428,10 +428,12 @@ static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + code->code = mt9v032->format.code; return 0; } @@ -439,7 +441,11 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + if (fse->index >= 3) + return -EINVAL; + if (mt9v032->format.code != fse->code) return -EINVAL; fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index bb41bea950ac..61ae6a0d5679 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -103,7 +103,7 @@ #define MT9V111_MAX_CLKIN 27000000 /* The default sensor configuration at startup time. */ -static struct v4l2_mbus_framefmt mt9v111_def_fmt = { +static const struct v4l2_mbus_framefmt mt9v111_def_fmt = { .width = 640, .height = 480, .code = MEDIA_BUS_FMT_UYVY8_2X8, diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index ecd167d7c4d2..4a4bd5b665a1 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -929,7 +929,7 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, format->format = *mf; return 0; #else - return -ENOTTY; + return -EINVAL; #endif } @@ -1190,8 +1190,7 @@ static int ov2640_probe_dt(struct i2c_client *client, /* * i2c_driver functions */ -static int ov2640_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ov2640_probe(struct i2c_client *client) { struct ov2640_priv *priv; struct i2c_adapter *adapter = client->adapter; @@ -1302,7 +1301,7 @@ static struct i2c_driver ov2640_i2c_driver = { .name = "ov2640", .of_match_table = of_match_ptr(ov2640_of_match), }, - .probe = ov2640_probe, + .probe_new = ov2640_probe, .remove = ov2640_remove, .id_table = ov2640_id, }; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 5ed2413eac8a..42f64175a6df 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Omnivision OV2659 CMOS Image Sensor driver * @@ -5,46 +6,21 @@ * * Benoit Parrot <bparrot@ti.com> * Lad, Prabhakar <prabhakar.csengg@gmail.com> - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include <linux/clk.h> #include <linux/delay.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/kernel.h> -#include <linux/media.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/of_graph.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/videodev2.h> +#include <linux/pm_runtime.h> -#include <media/media-entity.h> #include <media/i2c/ov2659.h> -#include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> #include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-image-sizes.h> -#include <media/v4l2-mediabus.h> #include <media/v4l2-subdev.h> #define DRIVER_NAME "ov2659" @@ -232,6 +208,10 @@ struct ov2659 { struct sensor_register *format_ctrl_regs; struct ov2659_pll_ctrl pll; int streaming; + /* used to control the sensor PWDN pin */ + struct gpio_desc *pwdn_gpio; + /* used to control the sensor RESETB pin */ + struct gpio_desc *resetb_gpio; }; static const struct sensor_register ov2659_init_regs[] = { @@ -419,10 +399,14 @@ static struct sensor_register ov2659_720p[] = { { REG_TIMING_YINC, 0x11 }, { REG_TIMING_VERT_FORMAT, 0x80 }, { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x370a, 0x12 }, { 0x3a03, 0xe8 }, { 0x3a09, 0x6f }, { 0x3a0b, 0x5d }, { 0x3a15, 0x9a }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, { REG_NULL, 0x00 }, }; @@ -661,7 +645,7 @@ static struct sensor_register ov2659_vga[] = { { REG_TIMING_HORIZ_FORMAT, 0x01 }, { 0x370a, 0x52 }, { REG_VFIFO_READ_START_H, 0x00 }, - { REG_VFIFO_READ_START_L, 0x80 }, + { REG_VFIFO_READ_START_L, 0xa0 }, { REG_ISP_CTRL02, 0x10 }, { REG_NULL, 0x00 }, }; @@ -709,7 +693,7 @@ static struct sensor_register ov2659_qvga[] = { { REG_TIMING_HORIZ_FORMAT, 0x01 }, { 0x370a, 0x52 }, { REG_VFIFO_READ_START_H, 0x00 }, - { REG_VFIFO_READ_START_L, 0x80 }, + { REG_VFIFO_READ_START_L, 0xa0 }, { REG_ISP_CTRL02, 0x10 }, { REG_NULL, 0x00 }, }; @@ -1055,7 +1039,7 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, mutex_unlock(&ov2659->lock); return 0; #else - return -ENOTTY; + return -EINVAL; #endif } @@ -1131,8 +1115,6 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; -#else - ret = -ENOTTY; #endif } else { s64 val; @@ -1200,14 +1182,27 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on) /* Stop Streaming Sequence */ ov2659_set_streaming(ov2659, 0); ov2659->streaming = on; + pm_runtime_put(&client->dev); + goto unlock; + } + + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); goto unlock; } - ov2659_set_pixel_clock(ov2659); - ov2659_set_frame_size(ov2659); - ov2659_set_format(ov2659); - ov2659_set_streaming(ov2659, 1); - ov2659->streaming = on; + ret = ov2659_init(sd, 0); + if (!ret) + ret = ov2659_set_pixel_clock(ov2659); + if (!ret) + ret = ov2659_set_frame_size(ov2659); + if (!ret) + ret = ov2659_set_format(ov2659); + if (!ret) { + ov2659_set_streaming(ov2659, 1); + ov2659->streaming = on; + } unlock: mutex_unlock(&ov2659->lock); @@ -1241,12 +1236,18 @@ static int ov2659_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov2659 *ov2659 = container_of(ctrl->handler, struct ov2659, ctrls); + struct i2c_client *client = ov2659->client; + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; switch (ctrl->id) { case V4L2_CID_TEST_PATTERN: return ov2659_set_test_pattern(ov2659, ctrl->val); } + pm_runtime_put(&client->dev); return 0; } @@ -1259,6 +1260,39 @@ static const char * const ov2659_test_pattern_menu[] = { "Vertical Color Bars", }; +static int ov2659_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2659 *ov2659 = to_ov2659(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + gpiod_set_value(ov2659->pwdn_gpio, 1); + + return 0; +} + +static int ov2659_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2659 *ov2659 = to_ov2659(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + gpiod_set_value(ov2659->pwdn_gpio, 0); + + if (ov2659->resetb_gpio) { + gpiod_set_value(ov2659->resetb_gpio, 1); + usleep_range(500, 1000); + gpiod_set_value(ov2659->resetb_gpio, 0); + usleep_range(3000, 5000); + } + + return 0; +} + /* ----------------------------------------------------------------------------- * V4L2 subdev internal operations */ @@ -1332,13 +1366,13 @@ static int ov2659_detect(struct v4l2_subdev *sd) unsigned short id; id = OV265X_ID(pid, ver); - if (id != OV2659_ID) + if (id != OV2659_ID) { dev_err(&client->dev, "Sensor detection failed (%04X, %d)\n", id, ret); - else { + ret = -ENODEV; + } else { dev_info(&client->dev, "Found OV%04X sensor\n", id); - ret = ov2659_init(sd, 0); } } @@ -1386,8 +1420,7 @@ done: return pdata; } -static int ov2659_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov2659_probe(struct i2c_client *client) { const struct ov2659_platform_data *pdata = ov2659_get_pdata(client); struct v4l2_subdev *sd; @@ -1416,6 +1449,18 @@ static int ov2659_probe(struct i2c_client *client, ov2659->xvclk_frequency > 27000000) return -EINVAL; + /* Optional gpio don't fail if not present */ + ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(ov2659->pwdn_gpio)) + return PTR_ERR(ov2659->pwdn_gpio); + + /* Optional gpio don't fail if not present */ + ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov2659->resetb_gpio)) + return PTR_ERR(ov2659->resetb_gpio); + v4l2_ctrl_handler_init(&ov2659->ctrls, 2); ov2659->link_frequency = v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops, @@ -1461,6 +1506,8 @@ static int ov2659_probe(struct i2c_client *client, ov2659->frame_size = &ov2659_framesizes[2]; ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs; + ov2659_power_on(&client->dev); + ret = ov2659_detect(sd); if (ret < 0) goto error; @@ -1474,10 +1521,15 @@ static int ov2659_probe(struct i2c_client *client, dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + return 0; error: v4l2_ctrl_handler_free(&ov2659->ctrls); + ov2659_power_off(&client->dev); media_entity_cleanup(&sd->entity); mutex_destroy(&ov2659->lock); return ret; @@ -1493,9 +1545,18 @@ static int ov2659_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); mutex_destroy(&ov2659->lock); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov2659_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + return 0; } +static const struct dev_pm_ops ov2659_pm_ops = { + SET_RUNTIME_PM_OPS(ov2659_power_off, ov2659_power_on, NULL) +}; + static const struct i2c_device_id ov2659_id[] = { { "ov2659", 0 }, { /* sentinel */ }, @@ -1513,9 +1574,10 @@ MODULE_DEVICE_TABLE(of, ov2659_of_match); static struct i2c_driver ov2659_i2c_driver = { .driver = { .name = DRIVER_NAME, + .pm = &ov2659_pm_ops, .of_match_table = of_match_ptr(ov2659_of_match), }, - .probe = ov2659_probe, + .probe_new = ov2659_probe, .remove = ov2659_remove, .id_table = ov2659_id, }; diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index b10bcfabaeeb..59cdbc33658c 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -675,7 +675,7 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); #else - ret = -ENOTTY; + ret = -EINVAL; #endif } else { fmt = &sensor->fmt; @@ -723,10 +723,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); format->format = *try_fmt; -#else - ret = -ENOTTY; #endif - goto unlock; } @@ -1023,7 +1020,7 @@ static int ov2680_check_id(struct ov2680_dev *sensor) return 0; } -static int ov2860_parse_dt(struct ov2680_dev *sensor) +static int ov2680_parse_dt(struct ov2680_dev *sensor) { struct device *dev = ov2680_to_dev(sensor); int ret; @@ -1064,7 +1061,7 @@ static int ov2680_probe(struct i2c_client *client) sensor->i2c_client = client; - ret = ov2860_parse_dt(sensor); + ret = ov2680_parse_dt(sensor); if (ret < 0) return -EINVAL; diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 759d60c6d630..854031f0b64a 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -158,8 +158,8 @@ static const int ov5640_framerates[] = { /* regulator supplies */ static const char * const ov5640_supply_name[] = { "DOVDD", /* Digital I/O (1.8V) supply */ - "DVDD", /* Digital Core (1.5V) supply */ "AVDD", /* Analog (2.8V) supply */ + "DVDD", /* Digital Core (1.5V) supply */ }; #define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name) @@ -189,10 +189,12 @@ struct ov5640_mode_info { u32 vtot; const struct reg_value *reg_data; u32 reg_data_size; + u32 max_fps; }; struct ov5640_ctrls { struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; struct { struct v4l2_ctrl *auto_exp; struct v4l2_ctrl *exposure; @@ -489,7 +491,6 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = { }; static const struct reg_value ov5640_setting_1080P_1920_1080[] = { - {0x3008, 0x42, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, @@ -517,7 +518,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, - {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, + {0x4005, 0x1a, 0, 0}, }; static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { @@ -544,6 +545,7 @@ static const struct ov5640_mode_info ov5640_mode_init_data = { 0, SUBSAMPLING, 640, 1896, 480, 984, ov5640_init_setting_30fps_VGA, ARRAY_SIZE(ov5640_init_setting_30fps_VGA), + OV5640_30_FPS, }; static const struct ov5640_mode_info @@ -551,39 +553,48 @@ ov5640_mode_data[OV5640_NUM_MODES] = { {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 1896, 144, 984, ov5640_setting_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_QCIF_176_144)}, + ARRAY_SIZE(ov5640_setting_QCIF_176_144), + OV5640_30_FPS}, {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 1896, 240, 984, ov5640_setting_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_QVGA_320_240)}, + ARRAY_SIZE(ov5640_setting_QVGA_320_240), + OV5640_30_FPS}, {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 1896, 480, 1080, ov5640_setting_VGA_640_480, - ARRAY_SIZE(ov5640_setting_VGA_640_480)}, + ARRAY_SIZE(ov5640_setting_VGA_640_480), + OV5640_60_FPS}, {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 1896, 480, 984, ov5640_setting_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_NTSC_720_480)}, + ARRAY_SIZE(ov5640_setting_NTSC_720_480), + OV5640_30_FPS}, {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 1896, 576, 984, ov5640_setting_PAL_720_576, - ARRAY_SIZE(ov5640_setting_PAL_720_576)}, + ARRAY_SIZE(ov5640_setting_PAL_720_576), + OV5640_30_FPS}, {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 1896, 768, 1080, ov5640_setting_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_XGA_1024_768)}, + ARRAY_SIZE(ov5640_setting_XGA_1024_768), + OV5640_30_FPS}, {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 1892, 720, 740, ov5640_setting_720P_1280_720, - ARRAY_SIZE(ov5640_setting_720P_1280_720)}, + ARRAY_SIZE(ov5640_setting_720P_1280_720), + OV5640_30_FPS}, {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 2500, 1080, 1120, ov5640_setting_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_1080P_1920_1080)}, + ARRAY_SIZE(ov5640_setting_1080P_1920_1080), + OV5640_30_FPS}, {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 2844, 1944, 1968, ov5640_setting_QSXGA_2592_1944, - ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)}, + ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944), + OV5640_15_FPS}, }; static int ov5640_init_slave_id(struct ov5640_dev *sensor) @@ -874,7 +885,7 @@ static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor, * We have reached the maximum allowed PLL1 output, * increase sysdiv. */ - if (!rate) + if (!_rate) break; /* @@ -1606,14 +1617,23 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, (!nearest && (mode->hact != width || mode->vact != height))) return NULL; - /* Only 640x480 can operate at 60fps (for now) */ - if (fr == OV5640_60_FPS && - !(mode->hact == 640 && mode->vact == 480)) + /* Check to see if the current mode exceeds the max frame rate */ + if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps]) return NULL; return mode; } +static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) +{ + u64 rate; + + rate = sensor->current_mode->vtot * sensor->current_mode->htot; + rate *= ov5640_framerates[sensor->current_fr]; + + return rate; +} + /* * sensor changes between scaling and subsampling, go through * exposure calculation @@ -1818,8 +1838,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) * All the formats we support have 16 bits per pixel, seems to require * the same rate than YUV, so we can just use 16 bpp all the time. */ - rate = mode->vtot * mode->htot * 16; - rate *= ov5640_framerates[sensor->current_fr]; + rate = ov5640_calc_pixel_rate(sensor) * 16; if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; ret = ov5640_set_mipi_pclk(sensor, rate); @@ -2233,6 +2252,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (mbus_fmt->code != sensor->fmt.code) sensor->pending_fmt_change = true; + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); out: mutex_unlock(&sensor->lock); return ret; @@ -2657,6 +2678,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) /* we can use our own mutex for the ctrl lock */ hdl->lock = &sensor->lock; + /* Clock related controls */ + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + 0, INT_MAX, 1, + ov5640_calc_pixel_rate(sensor)); + /* Auto/manual white balance */ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, @@ -2704,6 +2730,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) goto free_ctrls; } + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; @@ -2816,6 +2843,9 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->frame_interval = fi->interval; sensor->current_mode = mode; sensor->pending_mode_change = true; + + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); } out: mutex_unlock(&sensor->lock); @@ -2936,8 +2966,7 @@ power_off: return ret; } -static int ov5640_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov5640_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; @@ -3022,9 +3051,14 @@ static int ov5640_probe(struct i2c_client *client, /* request optional power down pin */ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->pwdn_gpio)) + return PTR_ERR(sensor->pwdn_gpio); + /* request optional reset pin */ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return PTR_ERR(sensor->reset_gpio); v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); @@ -3050,7 +3084,7 @@ static int ov5640_probe(struct i2c_client *client, if (ret) goto entity_cleanup; - ret = v4l2_async_register_subdev(&sensor->sd); + ret = v4l2_async_register_subdev_sensor_common(&sensor->sd); if (ret) goto free_ctrls; @@ -3095,7 +3129,7 @@ static struct i2c_driver ov5640_i2c_driver = { .of_match_table = ov5640_dt_ids, }, .id_table = ov5640_id, - .probe = ov5640_probe, + .probe_new = ov5640_probe, .remove = ov5640_remove, }; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 124c8df04633..a6c17d15d754 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -34,10 +34,6 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define OV5645_VOLTAGE_ANALOG 2800000 -#define OV5645_VOLTAGE_DIGITAL_CORE 1500000 -#define OV5645_VOLTAGE_DIGITAL_IO 1800000 - #define OV5645_SYSTEM_CTRL0 0x3008 #define OV5645_SYSTEM_CTRL0_START 0x02 #define OV5645_SYSTEM_CTRL0_STOP 0x42 @@ -45,6 +41,8 @@ #define OV5645_CHIP_ID_HIGH_BYTE 0x56 #define OV5645_CHIP_ID_LOW 0x300b #define OV5645_CHIP_ID_LOW_BYTE 0x45 +#define OV5645_IO_MIPI_CTRL00 0x300e +#define OV5645_PAD_OUTPUT00 0x3019 #define OV5645_AWB_MANUAL_CONTROL 0x3406 #define OV5645_AWB_MANUAL_ENABLE BIT(0) #define OV5645_AEC_PK_MANUAL 0x3503 @@ -55,6 +53,7 @@ #define OV5645_ISP_VFLIP BIT(2) #define OV5645_TIMING_TC_REG21 0x3821 #define OV5645_SENSOR_MIRROR BIT(1) +#define OV5645_MIPI_CTRL00 0x4800 #define OV5645_PRE_ISP_TEST_SETTING_1 0x503d #define OV5645_TEST_PATTERN_MASK 0x3 #define OV5645_SET_TEST_PATTERN(x) ((x) & OV5645_TEST_PATTERN_MASK) @@ -62,6 +61,15 @@ #define OV5645_SDE_SAT_U 0x5583 #define OV5645_SDE_SAT_V 0x5584 +/* regulator supplies */ +static const char * const ov5645_supply_name[] = { + "vdddo", /* Digital I/O (1.8V) supply */ + "vdda", /* Analog (2.8V) supply */ + "vddd", /* Digital Core (1.5V) supply */ +}; + +#define OV5645_NUM_SUPPLIES ARRAY_SIZE(ov5645_supply_name) + struct reg_value { u16 reg; u8 val; @@ -86,9 +94,7 @@ struct ov5645 { struct v4l2_rect crop; struct clk *xclk; - struct regulator *io_regulator; - struct regulator *core_regulator; - struct regulator *analog_regulator; + struct regulator_bulk_data supplies[OV5645_NUM_SUPPLIES]; const struct ov5645_mode_info *current_mode; @@ -121,7 +127,6 @@ static const struct reg_value ov5645_global_init_setting[] = { { 0x3503, 0x07 }, { 0x3002, 0x1c }, { 0x3006, 0xc3 }, - { 0x300e, 0x45 }, { 0x3017, 0x00 }, { 0x3018, 0x00 }, { 0x302e, 0x0b }, @@ -350,7 +355,10 @@ static const struct reg_value ov5645_global_init_setting[] = { { 0x3a1f, 0x14 }, { 0x0601, 0x02 }, { 0x3008, 0x42 }, - { 0x3008, 0x02 } + { 0x3008, 0x02 }, + { OV5645_IO_MIPI_CTRL00, 0x40 }, + { OV5645_MIPI_CTRL00, 0x24 }, + { OV5645_PAD_OUTPUT00, 0x70 } }; static const struct reg_value ov5645_setting_sxga[] = { @@ -533,55 +541,6 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { }, }; -static int ov5645_regulators_enable(struct ov5645 *ov5645) -{ - int ret; - - ret = regulator_enable(ov5645->io_regulator); - if (ret < 0) { - dev_err(ov5645->dev, "set io voltage failed\n"); - return ret; - } - - ret = regulator_enable(ov5645->analog_regulator); - if (ret) { - dev_err(ov5645->dev, "set analog voltage failed\n"); - goto err_disable_io; - } - - ret = regulator_enable(ov5645->core_regulator); - if (ret) { - dev_err(ov5645->dev, "set core voltage failed\n"); - goto err_disable_analog; - } - - return 0; - -err_disable_analog: - regulator_disable(ov5645->analog_regulator); -err_disable_io: - regulator_disable(ov5645->io_regulator); - - return ret; -} - -static void ov5645_regulators_disable(struct ov5645 *ov5645) -{ - int ret; - - ret = regulator_disable(ov5645->core_regulator); - if (ret < 0) - dev_err(ov5645->dev, "core regulator disable failed\n"); - - ret = regulator_disable(ov5645->analog_regulator); - if (ret < 0) - dev_err(ov5645->dev, "analog regulator disable failed\n"); - - ret = regulator_disable(ov5645->io_regulator); - if (ret < 0) - dev_err(ov5645->dev, "io regulator disable failed\n"); -} - static int ov5645_write_reg(struct ov5645 *ov5645, u16 reg, u8 val) { u8 regbuf[3]; @@ -680,15 +639,14 @@ static int ov5645_set_power_on(struct ov5645 *ov5645) { int ret; - ret = ov5645_regulators_enable(ov5645); - if (ret < 0) { + ret = regulator_bulk_enable(OV5645_NUM_SUPPLIES, ov5645->supplies); + if (ret < 0) return ret; - } ret = clk_prepare_enable(ov5645->xclk); if (ret < 0) { dev_err(ov5645->dev, "clk prepare enable failed\n"); - ov5645_regulators_disable(ov5645); + regulator_bulk_disable(OV5645_NUM_SUPPLIES, ov5645->supplies); return ret; } @@ -708,7 +666,7 @@ static void ov5645_set_power_off(struct ov5645 *ov5645) gpiod_set_value_cansleep(ov5645->rst_gpio, 1); gpiod_set_value_cansleep(ov5645->enable_gpio, 0); clk_disable_unprepare(ov5645->xclk); - ov5645_regulators_disable(ov5645); + regulator_bulk_disable(OV5645_NUM_SUPPLIES, ov5645->supplies); } static int ov5645_s_power(struct v4l2_subdev *sd, int on) @@ -737,13 +695,9 @@ static int ov5645_s_power(struct v4l2_subdev *sd, int on) goto exit; } - ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, - OV5645_SYSTEM_CTRL0_STOP); - if (ret < 0) { - ov5645_set_power_off(ov5645); - goto exit; - } + usleep_range(500, 1000); } else { + ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x58); ov5645_set_power_off(ov5645); } } @@ -1049,11 +1003,20 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) dev_err(ov5645->dev, "could not sync v4l2 controls\n"); return ret; } + + ret = ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x45); + if (ret < 0) + return ret; + ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, OV5645_SYSTEM_CTRL0_START); if (ret < 0) return ret; } else { + ret = ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x40); + if (ret < 0) + return ret; + ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, OV5645_SYSTEM_CTRL0_STOP); if (ret < 0) @@ -1086,13 +1049,13 @@ static const struct v4l2_subdev_ops ov5645_subdev_ops = { .pad = &ov5645_subdev_pad_ops, }; -static int ov5645_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov5645_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *endpoint; struct ov5645 *ov5645; u8 chip_id_high, chip_id_low; + unsigned int i; u32 xclk_freq; int ret; @@ -1150,47 +1113,13 @@ static int ov5645_probe(struct i2c_client *client, return ret; } - ov5645->io_regulator = devm_regulator_get(dev, "vdddo"); - if (IS_ERR(ov5645->io_regulator)) { - dev_err(dev, "cannot get io regulator\n"); - return PTR_ERR(ov5645->io_regulator); - } - - ret = regulator_set_voltage(ov5645->io_regulator, - OV5645_VOLTAGE_DIGITAL_IO, - OV5645_VOLTAGE_DIGITAL_IO); - if (ret < 0) { - dev_err(dev, "cannot set io voltage\n"); - return ret; - } - - ov5645->core_regulator = devm_regulator_get(dev, "vddd"); - if (IS_ERR(ov5645->core_regulator)) { - dev_err(dev, "cannot get core regulator\n"); - return PTR_ERR(ov5645->core_regulator); - } - - ret = regulator_set_voltage(ov5645->core_regulator, - OV5645_VOLTAGE_DIGITAL_CORE, - OV5645_VOLTAGE_DIGITAL_CORE); - if (ret < 0) { - dev_err(dev, "cannot set core voltage\n"); - return ret; - } - - ov5645->analog_regulator = devm_regulator_get(dev, "vdda"); - if (IS_ERR(ov5645->analog_regulator)) { - dev_err(dev, "cannot get analog regulator\n"); - return PTR_ERR(ov5645->analog_regulator); - } + for (i = 0; i < OV5645_NUM_SUPPLIES; i++) + ov5645->supplies[i].supply = ov5645_supply_name[i]; - ret = regulator_set_voltage(ov5645->analog_regulator, - OV5645_VOLTAGE_ANALOG, - OV5645_VOLTAGE_ANALOG); - if (ret < 0) { - dev_err(dev, "cannot set analog voltage\n"); + ret = devm_regulator_bulk_get(dev, OV5645_NUM_SUPPLIES, + ov5645->supplies); + if (ret < 0) return ret; - } ov5645->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(ov5645->enable_gpio)) { @@ -1355,7 +1284,7 @@ static struct i2c_driver ov5645_i2c_driver = { .of_match_table = of_match_ptr(ov5645_of_match), .name = "ov5645", }, - .probe = ov5645_probe, + .probe_new = ov5645_probe, .remove = ov5645_remove, .id_table = ov5645_id, }; diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 4589631798c9..e7d2e5b4ad4b 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -547,8 +547,7 @@ static int ov5647_parse_dt(struct device_node *np) return ret; } -static int ov5647_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov5647_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ov5647 *sensor; @@ -644,7 +643,7 @@ static struct i2c_driver ov5647_driver = { .of_match_table = of_match_ptr(ov5647_of_match), .name = SENSOR_NAME, }, - .probe = ov5647_probe, + .probe_new = ov5647_probe, .remove = ov5647_remove, .id_table = ov5647_id, }; diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c new file mode 100644 index 000000000000..1ae252378799 --- /dev/null +++ b/drivers/media/i2c/ov5675.c @@ -0,0 +1,1183 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation. + +#include <asm/unaligned.h> +#include <linux/acpi.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-device.h> +#include <media/v4l2-fwnode.h> + +#define OV5675_REG_VALUE_08BIT 1 +#define OV5675_REG_VALUE_16BIT 2 +#define OV5675_REG_VALUE_24BIT 3 + +#define OV5675_LINK_FREQ_450MHZ 450000000ULL +#define OV5675_SCLK 90000000LL +#define OV5675_MCLK 19200000 +#define OV5675_DATA_LANES 2 +#define OV5675_RGB_DEPTH 10 + +#define OV5675_REG_CHIP_ID 0x300a +#define OV5675_CHIP_ID 0x5675 + +#define OV5675_REG_MODE_SELECT 0x0100 +#define OV5675_MODE_STANDBY 0x00 +#define OV5675_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV5675_REG_VTS 0x380e +#define OV5675_VTS_30FPS 0x07e4 +#define OV5675_VTS_30FPS_MIN 0x07e4 +#define OV5675_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV5675_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV5675_REG_EXPOSURE 0x3500 +#define OV5675_EXPOSURE_MIN 4 +#define OV5675_EXPOSURE_MAX_MARGIN 4 +#define OV5675_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV5675_REG_ANALOG_GAIN 0x3508 +#define OV5675_ANAL_GAIN_MIN 128 +#define OV5675_ANAL_GAIN_MAX 2047 +#define OV5675_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV5675_REG_MWB_R_GAIN 0x5019 +#define OV5675_REG_MWB_G_GAIN 0x501b +#define OV5675_REG_MWB_B_GAIN 0x501d +#define OV5675_DGTL_GAIN_MIN 0 +#define OV5675_DGTL_GAIN_MAX 4095 +#define OV5675_DGTL_GAIN_STEP 1 +#define OV5675_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV5675_REG_TEST_PATTERN 0x4503 +#define OV5675_TEST_PATTERN_ENABLE BIT(7) +#define OV5675_TEST_PATTERN_BAR_SHIFT 2 + +#define to_ov5675(_sd) container_of(_sd, struct ov5675, sd) + +enum { + OV5675_LINK_FREQ_900MBPS, +}; + +struct ov5675_reg { + u16 address; + u8 val; +}; + +struct ov5675_reg_list { + u32 num_of_regs; + const struct ov5675_reg *regs; +}; + +struct ov5675_link_freq_config { + const struct ov5675_reg_list reg_list; +}; + +struct ov5675_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov5675_reg_list reg_list; +}; + +static const struct ov5675_reg mipi_data_rate_900mbps[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0300, 0x04}, + {0x0302, 0x8d}, + {0x0303, 0x00}, + {0x030d, 0x26}, +}; + +static const struct ov5675_reg mode_2592x1944_regs[] = { + {0x3002, 0x21}, + {0x3107, 0x23}, + {0x3501, 0x20}, + {0x3503, 0x0c}, + {0x3508, 0x03}, + {0x3509, 0x00}, + {0x3600, 0x66}, + {0x3602, 0x30}, + {0x3610, 0xa5}, + {0x3612, 0x93}, + {0x3620, 0x80}, + {0x3642, 0x0e}, + {0x3661, 0x00}, + {0x3662, 0x10}, + {0x3664, 0xf3}, + {0x3665, 0x9e}, + {0x3667, 0xa5}, + {0x366e, 0x55}, + {0x366f, 0x55}, + {0x3670, 0x11}, + {0x3671, 0x11}, + {0x3672, 0x11}, + {0x3673, 0x11}, + {0x3714, 0x24}, + {0x371a, 0x3e}, + {0x3733, 0x10}, + {0x3734, 0x00}, + {0x373d, 0x24}, + {0x3764, 0x20}, + {0x3765, 0x20}, + {0x3766, 0x12}, + {0x37a1, 0x14}, + {0x37a8, 0x1c}, + {0x37ab, 0x0f}, + {0x37c2, 0x04}, + {0x37cb, 0x00}, + {0x37cc, 0x00}, + {0x37cd, 0x00}, + {0x37ce, 0x00}, + {0x37d8, 0x02}, + {0x37d9, 0x08}, + {0x37dc, 0x04}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xb3}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x380c, 0x02}, + {0x380d, 0xee}, + {0x380e, 0x07}, + {0x380f, 0xe4}, + {0x3811, 0x10}, + {0x3813, 0x0d}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x381e, 0x02}, + {0x3820, 0x88}, + {0x3821, 0x01}, + {0x3832, 0x04}, + {0x3c80, 0x01}, + {0x3c82, 0x00}, + {0x3c83, 0xc8}, + {0x3c8c, 0x0f}, + {0x3c8d, 0xa0}, + {0x3c90, 0x07}, + {0x3c91, 0x00}, + {0x3c92, 0x00}, + {0x3c93, 0x00}, + {0x3c94, 0xd0}, + {0x3c95, 0x50}, + {0x3c96, 0x35}, + {0x3c97, 0x00}, + {0x4001, 0xe0}, + {0x4008, 0x02}, + {0x4009, 0x0d}, + {0x400f, 0x80}, + {0x4013, 0x02}, + {0x4040, 0x00}, + {0x4041, 0x07}, + {0x404c, 0x50}, + {0x404e, 0x20}, + {0x4500, 0x06}, + {0x4503, 0x00}, + {0x450a, 0x04}, + {0x4809, 0x04}, + {0x480c, 0x12}, + {0x4819, 0x70}, + {0x4825, 0x32}, + {0x4826, 0x32}, + {0x482a, 0x06}, + {0x4833, 0x08}, + {0x4837, 0x0d}, + {0x5000, 0x77}, + {0x5b00, 0x01}, + {0x5b01, 0x10}, + {0x5b02, 0x01}, + {0x5b03, 0xdb}, + {0x5b05, 0x6c}, + {0x5e10, 0xfc}, + {0x3500, 0x00}, + {0x3501, 0x3E}, + {0x3502, 0x60}, + {0x3503, 0x08}, + {0x3508, 0x04}, + {0x3509, 0x00}, + {0x3832, 0x48}, + {0x5780, 0x3e}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x06}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x4003, 0x40}, + {0x3107, 0x01}, + {0x3c80, 0x08}, + {0x3c83, 0xb1}, + {0x3c8c, 0x10}, + {0x3c8d, 0x00}, + {0x3c90, 0x00}, + {0x3c94, 0x00}, + {0x3c95, 0x00}, + {0x3c96, 0x00}, + {0x37cb, 0x09}, + {0x37cc, 0x15}, + {0x37cd, 0x1f}, + {0x37ce, 0x1f}, +}; + +static const struct ov5675_reg mode_1296x972_regs[] = { + {0x3002, 0x21}, + {0x3107, 0x23}, + {0x3501, 0x20}, + {0x3503, 0x0c}, + {0x3508, 0x03}, + {0x3509, 0x00}, + {0x3600, 0x66}, + {0x3602, 0x30}, + {0x3610, 0xa5}, + {0x3612, 0x93}, + {0x3620, 0x80}, + {0x3642, 0x0e}, + {0x3661, 0x00}, + {0x3662, 0x08}, + {0x3664, 0xf3}, + {0x3665, 0x9e}, + {0x3667, 0xa5}, + {0x366e, 0x55}, + {0x366f, 0x55}, + {0x3670, 0x11}, + {0x3671, 0x11}, + {0x3672, 0x11}, + {0x3673, 0x11}, + {0x3714, 0x28}, + {0x371a, 0x3e}, + {0x3733, 0x10}, + {0x3734, 0x00}, + {0x373d, 0x24}, + {0x3764, 0x20}, + {0x3765, 0x20}, + {0x3766, 0x12}, + {0x37a1, 0x14}, + {0x37a8, 0x1c}, + {0x37ab, 0x0f}, + {0x37c2, 0x14}, + {0x37cb, 0x00}, + {0x37cc, 0x00}, + {0x37cd, 0x00}, + {0x37ce, 0x00}, + {0x37d8, 0x02}, + {0x37d9, 0x04}, + {0x37dc, 0x04}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0xf4}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x06}, + {0x3807, 0xb3}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x02}, + {0x380b, 0xd0}, + {0x380c, 0x02}, + {0x380d, 0xee}, + {0x380e, 0x07}, + {0x380f, 0xe4}, + {0x3811, 0x10}, + {0x3813, 0x09}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x03}, + {0x3817, 0x01}, + {0x381e, 0x02}, + {0x3820, 0x8b}, + {0x3821, 0x01}, + {0x3832, 0x04}, + {0x3c80, 0x01}, + {0x3c82, 0x00}, + {0x3c83, 0xc8}, + {0x3c8c, 0x0f}, + {0x3c8d, 0xa0}, + {0x3c90, 0x07}, + {0x3c91, 0x00}, + {0x3c92, 0x00}, + {0x3c93, 0x00}, + {0x3c94, 0xd0}, + {0x3c95, 0x50}, + {0x3c96, 0x35}, + {0x3c97, 0x00}, + {0x4001, 0xe0}, + {0x4008, 0x00}, + {0x4009, 0x07}, + {0x400f, 0x80}, + {0x4013, 0x02}, + {0x4040, 0x00}, + {0x4041, 0x03}, + {0x404c, 0x50}, + {0x404e, 0x20}, + {0x4500, 0x06}, + {0x4503, 0x00}, + {0x450a, 0x04}, + {0x4809, 0x04}, + {0x480c, 0x12}, + {0x4819, 0x70}, + {0x4825, 0x32}, + {0x4826, 0x32}, + {0x482a, 0x06}, + {0x4833, 0x08}, + {0x4837, 0x0d}, + {0x5000, 0x77}, + {0x5b00, 0x01}, + {0x5b01, 0x10}, + {0x5b02, 0x01}, + {0x5b03, 0xdb}, + {0x5b05, 0x6c}, + {0x5e10, 0xfc}, + {0x3500, 0x00}, + {0x3501, 0x1F}, + {0x3502, 0x20}, + {0x3503, 0x08}, + {0x3508, 0x04}, + {0x3509, 0x00}, + {0x3832, 0x48}, + {0x5780, 0x3e}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x06}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x4003, 0x40}, + {0x3107, 0x01}, + {0x3c80, 0x08}, + {0x3c83, 0xb1}, + {0x3c8c, 0x10}, + {0x3c8d, 0x00}, + {0x3c90, 0x00}, + {0x3c94, 0x00}, + {0x3c95, 0x00}, + {0x3c96, 0x00}, + {0x37cb, 0x09}, + {0x37cc, 0x15}, + {0x37cd, 0x1f}, + {0x37ce, 0x1f}, +}; + +static const char * const ov5675_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar" +}; + +static const s64 link_freq_menu_items[] = { + OV5675_LINK_FREQ_450MHZ, +}; + +static const struct ov5675_link_freq_config link_freq_configs[] = { + [OV5675_LINK_FREQ_900MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_900mbps), + .regs = mipi_data_rate_900mbps, + } + } +}; + +static const struct ov5675_mode supported_modes[] = { + { + .width = 2592, + .height = 1944, + .hts = 1500, + .vts_def = OV5675_VTS_30FPS, + .vts_min = OV5675_VTS_30FPS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, + }, + .link_freq_index = OV5675_LINK_FREQ_900MBPS, + }, + { + .width = 1296, + .height = 972, + .hts = 1500, + .vts_def = OV5675_VTS_30FPS, + .vts_min = OV5675_VTS_30FPS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x972_regs), + .regs = mode_1296x972_regs, + }, + .link_freq_index = OV5675_LINK_FREQ_900MBPS, + } +}; + +struct ov5675 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov5675_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV5675_DATA_LANES; + + do_div(pixel_rate, OV5675_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV5675_SCLK); + + return ppl; +} + +static int ov5675_read_reg(struct ov5675 *ov5675, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + 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; +} + +static int ov5675_write_reg(struct ov5675 *ov5675, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + u8 buf[6]; + + if (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; +} + +static int ov5675_write_reg_list(struct ov5675 *ov5675, + const struct ov5675_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov5675_write_reg(ov5675, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov5675_update_digital_gain(struct ov5675 *ov5675, u32 d_gain) +{ + int ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_MWB_R_GAIN, + OV5675_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_MWB_G_GAIN, + OV5675_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return ov5675_write_reg(ov5675, OV5675_REG_MWB_B_GAIN, + OV5675_REG_VALUE_16BIT, d_gain); +} + +static int ov5675_test_pattern(struct ov5675 *ov5675, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV5675_TEST_PATTERN_BAR_SHIFT | + OV5675_TEST_PATTERN_ENABLE; + + return ov5675_write_reg(ov5675, OV5675_REG_TEST_PATTERN, + OV5675_REG_VALUE_08BIT, pattern); +} + +static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5675 *ov5675 = container_of(ctrl->handler, + struct ov5675, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = (ov5675->cur_mode->height + ctrl->val - + OV5675_EXPOSURE_MAX_MARGIN) / 2; + __v4l2_ctrl_modify_range(ov5675->exposure, + ov5675->exposure->minimum, + exposure_max, ov5675->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov5675_write_reg(ov5675, OV5675_REG_ANALOG_GAIN, + OV5675_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov5675_update_digital_gain(ov5675, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + /* 3 least significant bits of expsoure are fractional part */ + ret = ov5675_write_reg(ov5675, OV5675_REG_EXPOSURE, + OV5675_REG_VALUE_24BIT, ctrl->val << 3); + break; + + case V4L2_CID_VBLANK: + ret = ov5675_write_reg(ov5675, OV5675_REG_VTS, + OV5675_REG_VALUE_16BIT, + ov5675->cur_mode->height + ctrl->val + + 10); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov5675_test_pattern(ov5675, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov5675_ctrl_ops = { + .s_ctrl = ov5675_set_ctrl, +}; + +static int ov5675_init_controls(struct ov5675 *ov5675) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &ov5675->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov5675->mutex; + ov5675->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (ov5675->link_freq) + ov5675->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov5675->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(OV5675_LINK_FREQ_900MBPS), + 1, + to_pixel_rate(OV5675_LINK_FREQ_900MBPS)); + ov5675->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_VBLANK, + ov5675->cur_mode->vts_min - ov5675->cur_mode->height, + OV5675_VTS_MAX - ov5675->cur_mode->height, 1, + ov5675->cur_mode->vts_def - ov5675->cur_mode->height); + h_blank = to_pixels_per_line(ov5675->cur_mode->hts, + ov5675->cur_mode->link_freq_index) - ov5675->cur_mode->width; + ov5675->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (ov5675->hblank) + ov5675->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV5675_ANAL_GAIN_MIN, OV5675_ANAL_GAIN_MAX, + OV5675_ANAL_GAIN_STEP, OV5675_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV5675_DGTL_GAIN_MIN, OV5675_DGTL_GAIN_MAX, + OV5675_DGTL_GAIN_STEP, OV5675_DGTL_GAIN_DEFAULT); + exposure_max = (ov5675->cur_mode->vts_def - + OV5675_EXPOSURE_MAX_MARGIN) / 2; + ov5675->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_EXPOSURE, + OV5675_EXPOSURE_MIN, exposure_max, + OV5675_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5675_test_pattern_menu) - 1, + 0, 0, ov5675_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov5675->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov5675_update_pad_format(const struct ov5675_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov5675_start_streaming(struct ov5675 *ov5675) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + const struct ov5675_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov5675->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = ov5675_write_reg_list(ov5675, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov5675->cur_mode->reg_list; + ret = ov5675_write_reg_list(ov5675, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov5675->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_MODE_SELECT, + OV5675_REG_VALUE_08BIT, OV5675_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void ov5675_stop_streaming(struct ov5675 *ov5675) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + + if (ov5675_write_reg(ov5675, OV5675_REG_MODE_SELECT, + OV5675_REG_VALUE_08BIT, OV5675_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov5675 *ov5675 = to_ov5675(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov5675->streaming == enable) + return 0; + + mutex_lock(&ov5675->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov5675->mutex); + return ret; + } + + ret = ov5675_start_streaming(ov5675); + if (ret) { + enable = 0; + ov5675_stop_streaming(ov5675); + pm_runtime_put(&client->dev); + } + } else { + ov5675_stop_streaming(ov5675); + pm_runtime_put(&client->dev); + } + + ov5675->streaming = enable; + mutex_unlock(&ov5675->mutex); + + return ret; +} + +static int __maybe_unused ov5675_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5675 *ov5675 = to_ov5675(sd); + + mutex_lock(&ov5675->mutex); + if (ov5675->streaming) + ov5675_stop_streaming(ov5675); + + mutex_unlock(&ov5675->mutex); + + return 0; +} + +static int __maybe_unused ov5675_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5675 *ov5675 = to_ov5675(sd); + int ret; + + mutex_lock(&ov5675->mutex); + if (ov5675->streaming) { + ret = ov5675_start_streaming(ov5675); + if (ret) { + ov5675->streaming = false; + ov5675_stop_streaming(ov5675); + mutex_unlock(&ov5675->mutex); + return ret; + } + } + + mutex_unlock(&ov5675->mutex); + + return 0; +} + +static int ov5675_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5675 *ov5675 = to_ov5675(sd); + const struct ov5675_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov5675->mutex); + ov5675_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov5675->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov5675->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov5675->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov5675->vblank, + mode->vts_min - mode->height, + OV5675_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov5675->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov5675->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov5675->mutex); + + return 0; +} + +static int ov5675_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5675 *ov5675 = to_ov5675(sd); + + mutex_lock(&ov5675->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov5675->sd, cfg, + fmt->pad); + else + ov5675_update_pad_format(ov5675->cur_mode, &fmt->format); + + mutex_unlock(&ov5675->mutex); + + return 0; +} + +static int ov5675_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov5675_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov5675_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov5675 *ov5675 = to_ov5675(sd); + + mutex_lock(&ov5675->mutex); + ov5675_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&ov5675->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov5675_video_ops = { + .s_stream = ov5675_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov5675_pad_ops = { + .set_fmt = ov5675_set_format, + .get_fmt = ov5675_get_format, + .enum_mbus_code = ov5675_enum_mbus_code, + .enum_frame_size = ov5675_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov5675_subdev_ops = { + .video = &ov5675_video_ops, + .pad = &ov5675_pad_ops, +}; + +static const struct media_entity_operations ov5675_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov5675_internal_ops = { + .open = ov5675_open, +}; + +static int ov5675_identify_module(struct ov5675 *ov5675) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov5675->sd); + int ret; + u32 val; + + ret = ov5675_read_reg(ov5675, OV5675_REG_CHIP_ID, + OV5675_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV5675_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV5675_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int ov5675_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != OV5675_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + 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 != OV5675_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov5675_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5675 *ov5675 = to_ov5675(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov5675->mutex); + + return 0; +} + +static int ov5675_probe(struct i2c_client *client) +{ + struct ov5675 *ov5675; + int ret; + + ret = ov5675_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + ov5675 = devm_kzalloc(&client->dev, sizeof(*ov5675), GFP_KERNEL); + if (!ov5675) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops); + ret = ov5675_identify_module(ov5675); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&ov5675->mutex); + ov5675->cur_mode = &supported_modes[0]; + ret = ov5675_init_controls(ov5675); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov5675->sd.internal_ops = &ov5675_internal_ops; + ov5675->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov5675->sd.entity.ops = &ov5675_subdev_entity_ops; + ov5675->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov5675->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov5675->sd.entity, 1, &ov5675->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&ov5675->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov5675->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov5675->sd.ctrl_handler); + mutex_destroy(&ov5675->mutex); + + return ret; +} + +static const struct dev_pm_ops ov5675_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov5675_suspend, ov5675_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov5675_acpi_ids[] = { + {"OVTI5675"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov5675_acpi_ids); +#endif + +static struct i2c_driver ov5675_i2c_driver = { + .driver = { + .name = "ov5675", + .pm = &ov5675_pm_ops, + .acpi_match_table = ACPI_PTR(ov5675_acpi_ids), + }, + .probe_new = ov5675_probe, + .remove = ov5675_remove, +}; + +module_i2c_driver(ov5675_i2c_driver); + +MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_DESCRIPTION("OmniVision OV5675 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index e65a94353175..d6cd15bb699a 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -823,9 +823,6 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; -#else - mutex_unlock(&ov5695->mutex); - return -ENOTTY; #endif } else { ov5695->cur_mode = mode; @@ -856,7 +853,7 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); #else mutex_unlock(&ov5695->mutex); - return -ENOTTY; + return -EINVAL; #endif } else { fmt->format.width = mode->width; @@ -1328,7 +1325,7 @@ static int ov5695_probe(struct i2c_client *client, goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 5b9af5e5b7f1..91906b94f978 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -124,12 +124,13 @@ #define DEF_AECH 0x4D -#define CLKRC_6MHz 0x00 +#define CLKRC_8MHz 0x00 #define CLKRC_12MHz 0x40 #define CLKRC_16MHz 0x80 #define CLKRC_24MHz 0xc0 #define CLKRC_DIV_MASK 0x3f #define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1) +#define DEF_CLKRC 0x00 #define COMA_RESET BIT(7) #define COMA_QCIF BIT(5) @@ -196,13 +197,33 @@ struct ov6650 { struct v4l2_clk *clk; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ - unsigned long pclk_limit; /* from host */ - unsigned long pclk_max; /* from resolution and format */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; - enum v4l2_colorspace colorspace; }; +struct ov6650_xclk { + unsigned long rate; + u8 clkrc; +}; + +static const struct ov6650_xclk ov6650_xclk[] = { +{ + .rate = 8000000, + .clkrc = CLKRC_8MHz, +}, +{ + .rate = 12000000, + .clkrc = CLKRC_12MHz, +}, +{ + .rate = 16000000, + .clkrc = CLKRC_16MHz, +}, +{ + .rate = 24000000, + .clkrc = CLKRC_24MHz, +}, +}; static u32 ov6650_codes[] = { MEDIA_BUS_FMT_YUYV8_2X8, @@ -213,6 +234,17 @@ static u32 ov6650_codes[] = { MEDIA_BUS_FMT_Y8_1X8, }; +static const struct v4l2_mbus_framefmt ov6650_def_fmt = { + .width = W_CIF, + .height = H_CIF, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + /* read a register */ static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) { @@ -465,38 +497,39 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect rect = sel->r; int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - v4l_bound_align_image(&rect.width, 2, W_CIF, 1, - &rect.height, 2, H_CIF, 1, 0); - v4l_bound_align_image(&rect.left, DEF_HSTRT << 1, - (DEF_HSTRT << 1) + W_CIF - (__s32)rect.width, 1, - &rect.top, DEF_VSTRT << 1, - (DEF_VSTRT << 1) + H_CIF - (__s32)rect.height, 1, - 0); + v4l_bound_align_image(&sel->r.width, 2, W_CIF, 1, + &sel->r.height, 2, H_CIF, 1, 0); + v4l_bound_align_image(&sel->r.left, DEF_HSTRT << 1, + (DEF_HSTRT << 1) + W_CIF - (__s32)sel->r.width, 1, + &sel->r.top, DEF_VSTRT << 1, + (DEF_VSTRT << 1) + H_CIF - (__s32)sel->r.height, + 1, 0); - ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1); + ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1); if (!ret) { - priv->rect.left = rect.left; + priv->rect.width += priv->rect.left - sel->r.left; + priv->rect.left = sel->r.left; ret = ov6650_reg_write(client, REG_HSTOP, - (rect.left + rect.width) >> 1); + (sel->r.left + sel->r.width) >> 1); } if (!ret) { - priv->rect.width = rect.width; - ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1); + priv->rect.width = sel->r.width; + ret = ov6650_reg_write(client, REG_VSTRT, sel->r.top >> 1); } if (!ret) { - priv->rect.top = rect.top; + priv->rect.height += priv->rect.top - sel->r.top; + priv->rect.top = sel->r.top; ret = ov6650_reg_write(client, REG_VSTOP, - (rect.top + rect.height) >> 1); + (sel->r.top + sel->r.height) >> 1); } if (!ret) - priv->rect.height = rect.height; + priv->rect.height = sel->r.height; return ret; } @@ -512,12 +545,20 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; - mf->width = priv->rect.width >> priv->half_scale; - mf->height = priv->rect.height >> priv->half_scale; - mf->code = priv->code; - mf->colorspace = priv->colorspace; - mf->field = V4L2_FIELD_NONE; + /* initialize response with default media bus frame format */ + *mf = ov6650_def_fmt; + + /* update media bus format code and frame size */ + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + } else { + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } @@ -526,22 +567,7 @@ static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) return width > rect->width >> 1 || height > rect->height >> 1; } -static u8 to_clkrc(struct v4l2_fract *timeperframe, - unsigned long pclk_limit, unsigned long pclk_max) -{ - unsigned long pclk; - - if (timeperframe->numerator && timeperframe->denominator) - pclk = pclk_max * timeperframe->denominator / - (FRAME_RATE_MAX * timeperframe->numerator); - else - pclk = pclk_max; - - if (pclk_limit && pclk_limit < pclk) - pclk = pclk_limit; - - return (pclk_max - 1) / pclk; -} +#define to_clkrc(div) ((div) - 1) /* set the format we will capture in */ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) @@ -560,8 +586,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) .r.height = mf->height << half_scale, }; u32 code = mf->code; - unsigned long mclk, pclk; - u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc; + u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask; int ret; /* select color matrix configuration for given color encoding */ @@ -610,58 +635,35 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code); return -EINVAL; } - priv->code = code; if (code == MEDIA_BUS_FMT_Y8_1X8 || code == MEDIA_BUS_FMT_SBGGR8_1X8) { coml_mask = COML_ONE_CHANNEL; coml_set = 0; - priv->pclk_max = 4000000; } else { coml_mask = 0; coml_set = COML_ONE_CHANNEL; - priv->pclk_max = 8000000; } - if (code == MEDIA_BUS_FMT_SBGGR8_1X8) - priv->colorspace = V4L2_COLORSPACE_SRGB; - else if (code != 0) - priv->colorspace = V4L2_COLORSPACE_JPEG; - if (half_scale) { dev_dbg(&client->dev, "max resolution: QCIF\n"); coma_set |= COMA_QCIF; - priv->pclk_max /= 2; } else { dev_dbg(&client->dev, "max resolution: CIF\n"); coma_mask |= COMA_QCIF; } - priv->half_scale = half_scale; - - clkrc = CLKRC_12MHz; - mclk = 12000000; - priv->pclk_limit = 1334000; - dev_dbg(&client->dev, "using 12MHz input clock\n"); - - clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); - - pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); - dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", - mclk / pclk, 10 * mclk % pclk / pclk); ret = ov6650_set_selection(sd, NULL, &sel); if (!ret) ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); - if (!ret) - ret = ov6650_reg_write(client, REG_CLKRC, clkrc); - if (!ret) - ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); - if (!ret) { - mf->colorspace = priv->colorspace; - mf->width = priv->rect.width >> half_scale; - mf->height = priv->rect.height >> half_scale; + priv->half_scale = half_scale; + + ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); } + if (!ret) + priv->code = code; + return ret; } @@ -680,8 +682,6 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 2, W_CIF, 1, &mf->height, 2, H_CIF, 1, 0); - mf->field = V4L2_FIELD_NONE; - switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; @@ -691,20 +691,39 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; break; default: mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; /* fall through */ case MEDIA_BUS_FMT_SBGGR8_1X8: - mf->colorspace = V4L2_COLORSPACE_SRGB; break; } - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov6650_s_fmt(sd, mf); - cfg->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + /* store media bus format code and frame size in pad config */ + cfg->try_fmt.width = mf->width; + cfg->try_fmt.height = mf->height; + cfg->try_fmt.code = mf->code; + /* return default mbus frame format updated with pad config */ + *mf = ov6650_def_fmt; + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + + } else { + /* apply new media bus format code and frame size */ + int ret = ov6650_s_fmt(sd, mf); + + if (ret) + return ret; + + /* return default format updated with active size and code */ + *mf = ov6650_def_fmt; + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } @@ -725,9 +744,7 @@ static int ov6650_g_frame_interval(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, - priv->pclk_limit, priv->pclk_max)); - ival->interval.denominator = FRAME_RATE_MAX; + ival->interval = priv->tpf; dev_dbg(&client->dev, "Frame interval: %u/%u s\n", ival->interval.numerator, ival->interval.denominator); @@ -742,7 +759,6 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, struct ov6650 *priv = to_ov6650(client); struct v4l2_fract *tpf = &ival->interval; int div, ret; - u8 clkrc; if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ @@ -754,19 +770,12 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) div = GET_CLKRC_DIV(CLKRC_DIV_MASK); - /* - * Keep result to be used as tpf limit - * for subsequent clock divider calculations - */ - priv->tpf.numerator = div; - priv->tpf.denominator = FRAME_RATE_MAX; - - clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); - - ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); + ret = ov6650_reg_rmw(client, REG_CLKRC, to_clkrc(div), CLKRC_DIV_MASK); if (!ret) { - tpf->numerator = GET_CLKRC_DIV(clkrc); - tpf->denominator = FRAME_RATE_MAX; + priv->tpf.numerator = div; + priv->tpf.denominator = FRAME_RATE_MAX; + + *tpf = priv->tpf; } return ret; @@ -788,7 +797,7 @@ static int ov6650_reset(struct i2c_client *client) } /* program default register values */ -static int ov6650_prog_dflt(struct i2c_client *client) +static int ov6650_prog_dflt(struct i2c_client *client, u8 clkrc) { int ret; @@ -796,6 +805,8 @@ static int ov6650_prog_dflt(struct i2c_client *client) ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ if (!ret) + ret = ov6650_reg_write(client, REG_CLKRC, clkrc); + if (!ret) ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); return ret; @@ -805,8 +816,10 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - u8 pidh, pidl, midh, midl; - int ret; + const struct ov6650_xclk *xclk = NULL; + unsigned long rate; + u8 pidh, pidl, midh, midl; + int i, ret = 0; priv->clk = v4l2_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { @@ -815,6 +828,33 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) return ret; } + rate = v4l2_clk_get_rate(priv->clk); + for (i = 0; rate && i < ARRAY_SIZE(ov6650_xclk); i++) { + if (rate != ov6650_xclk[i].rate) + continue; + + xclk = &ov6650_xclk[i]; + dev_info(&client->dev, "using host default clock rate %lukHz\n", + rate / 1000); + break; + } + for (i = 0; !xclk && i < ARRAY_SIZE(ov6650_xclk); i++) { + ret = v4l2_clk_set_rate(priv->clk, ov6650_xclk[i].rate); + if (ret || v4l2_clk_get_rate(priv->clk) != ov6650_xclk[i].rate) + continue; + + xclk = &ov6650_xclk[i]; + dev_info(&client->dev, "using negotiated clock rate %lukHz\n", + xclk->rate / 1000); + break; + } + if (!xclk) { + dev_err(&client->dev, "unable to get supported clock rate\n"); + if (!ret) + ret = -EINVAL; + goto eclkput; + } + ret = ov6650_s_power(sd, 1); if (ret < 0) goto eclkput; @@ -848,7 +888,12 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) ret = ov6650_reset(client); if (!ret) - ret = ov6650_prog_dflt(client); + ret = ov6650_prog_dflt(client, xclk->clkrc); + if (!ret) { + struct v4l2_mbus_framefmt mf = ov6650_def_fmt; + + ret = ov6650_s_fmt(sd, &mf); + } if (!ret) ret = v4l2_ctrl_handler_setup(&priv->hdl); @@ -989,8 +1034,10 @@ static int ov6650_probe(struct i2c_client *client, V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; + if (priv->hdl.error) { + ret = priv->hdl.error; + goto ectlhdlfree; + } v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); @@ -1001,15 +1048,18 @@ static int ov6650_probe(struct i2c_client *client, priv->rect.top = DEF_VSTRT << 1; priv->rect.width = W_CIF; priv->rect.height = H_CIF; - priv->half_scale = false; - priv->code = MEDIA_BUS_FMT_YUYV8_2X8; - priv->colorspace = V4L2_COLORSPACE_JPEG; + + /* Hardware default frame interval */ + priv->tpf.numerator = GET_CLKRC_DIV(DEF_CLKRC); + priv->tpf.denominator = FRAME_RATE_MAX; priv->subdev.internal_ops = &ov6650_internal_ops; ret = v4l2_async_register_subdev(&priv->subdev); - if (ret) - v4l2_ctrl_handler_free(&priv->hdl); + if (!ret) + return 0; +ectlhdlfree: + v4l2_ctrl_handler_free(&priv->hdl); return ret; } @@ -1041,6 +1091,6 @@ static struct i2c_driver ov6650_i2c_driver = { module_i2c_driver(ov6650_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); -MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); +MODULE_DESCRIPTION("V4L2 subdevice driver for OmniVision OV6650 camera sensor"); +MODULE_AUTHOR("Janusz Krzysztofik <jmkrzyszt@gmail.com"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 53385c277792..b42b289faaef 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1110,10 +1110,8 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); *mbus_fmt = format->format; - return 0; -#else - return -ENOTTY; #endif + return 0; } ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize); @@ -1146,7 +1144,7 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd, format->format = *mbus_fmt; return 0; #else - return -ENOTTY; + return -EINVAL; #endif } else { format->format = info->format; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 2e9a758736a1..2cc6a678069a 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1352,8 +1352,7 @@ static const struct v4l2_subdev_ops ov772x_subdev_ops = { * i2c_driver function */ -static int ov772x_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ov772x_probe(struct i2c_client *client) { struct ov772x_priv *priv; int ret; @@ -1486,7 +1485,7 @@ static struct i2c_driver ov772x_i2c_driver = { .name = "ov772x", .of_match_table = ov772x_of_match, }, - .probe = ov772x_probe, + .probe_new = ov772x_probe, .remove = ov772x_remove, .id_table = ov772x_id, }; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 70bb870b1d08..732655fe4ba3 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -827,13 +827,9 @@ static int ov7740_set_fmt(struct v4l2_subdev *sd, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); *mbus_fmt = format->format; - +#endif mutex_unlock(&ov7740->mutex); return 0; -#else - ret = -ENOTTY; - goto error; -#endif } ret = ov7740_try_fmt_internal(sd, &format->format, &ovfmt, &fsize); @@ -868,7 +864,7 @@ static int ov7740_get_fmt(struct v4l2_subdev *sd, format->format = *mbus_fmt; ret = 0; #else - ret = -ENOTTY; + ret = -EINVAL; #endif } else { format->format = ov7740->format; @@ -1066,8 +1062,7 @@ static const struct regmap_config ov7740_regmap_config = { .max_register = OV7740_MAX_REGISTER, }; -static int ov7740_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov7740_probe(struct i2c_client *client) { struct ov7740 *ov7740; struct v4l2_subdev *sd; @@ -1229,7 +1224,7 @@ static struct i2c_driver ov7740_i2c_driver = { .pm = &ov7740_pm_ops, .of_match_table = of_match_ptr(ov7740_of_match), }, - .probe = ov7740_probe, + .probe_new = ov7740_probe, .remove = ov7740_remove, .id_table = ov7740_id, }; diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index cd347d6b7b9d..8655842af275 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1106,7 +1106,10 @@ static int ov8856_check_hwcfg(struct device *dev) if (!fwnode) return -ENXIO; - fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) + return ret; + if (mclk != OV8856_MCLK) { dev_err(dev, "external clock %d is not supported", mclk); return -EINVAL; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 30ab2225fbd0..4fe68aa55789 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -703,6 +703,11 @@ static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) for (m = 6; m >= 0; m--) if (gain >= (1 << m) * 16) break; + + /* Sanity check: don't adjust the gain with a negative value */ + if (m < 0) + return -EINVAL; + rgain = (gain - ((1 << m) * 16)) / (1 << m); rgain |= (((1 << m) - 1) << 4); @@ -1485,8 +1490,7 @@ out: return ret; } -static int ov965x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov965x_probe(struct i2c_client *client) { const struct ov9650_platform_data *pdata = client->dev.platform_data; struct v4l2_subdev *sd; @@ -1613,7 +1617,7 @@ static struct i2c_driver ov965x_i2c_driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ov965x_of_match), }, - .probe = ov965x_probe, + .probe_new = ov965x_probe, .remove = ov965x_remove, .id_table = ov965x_id, }; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 7633aebd8c06..5b4c4a3547c9 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1650,8 +1650,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) return 0; } -static int s5c73m3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5c73m3_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct v4l2_subdev *sd; @@ -1806,7 +1805,7 @@ static struct i2c_driver s5c73m3_i2c_driver = { .of_match_table = of_match_ptr(s5c73m3_of_match), .name = DRIVER_NAME, }, - .probe = s5c73m3_probe, + .probe_new = s5c73m3_probe, .remove = s5c73m3_remove, .id_table = s5c73m3_id, }; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 8e6de06b3e72..cdfe008ba39f 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1946,8 +1946,7 @@ static int s5k5baf_configure_regulators(struct s5k5baf *state) return ret; } -static int s5k5baf_probe(struct i2c_client *c, - const struct i2c_device_id *id) +static int s5k5baf_probe(struct i2c_client *c) { struct s5k5baf *state; int ret; @@ -2046,7 +2045,7 @@ static struct i2c_driver s5k5baf_i2c_driver = { .of_match_table = s5k5baf_of_match, .name = S5K5BAF_DRIVER_NAME }, - .probe = s5k5baf_probe, + .probe_new = s5k5baf_probe, .remove = s5k5baf_remove, .id_table = s5k5baf_id, }; diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index 3b7721f81be2..bc6cc5a558db 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -275,8 +275,7 @@ static const struct v4l2_subdev_ops s5k6a3_subdev_ops = { .pad = &s5k6a3_pad_ops, }; -static int s5k6a3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5k6a3_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct s5k6a3 *sensor; @@ -378,7 +377,7 @@ static struct i2c_driver s5k6a3_driver = { .of_match_table = of_match_ptr(s5k6a3_of_match), .name = S5K6A3_DRV_NAME, }, - .probe = s5k6a3_probe, + .probe_new = s5k6a3_probe, .remove = s5k6a3_remove, .id_table = s5k6a3_ids, }; diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h index 44fabe08234d..4b5f6985710b 100644 --- a/drivers/media/i2c/saa711x_regs.h +++ b/drivers/media/i2c/saa711x_regs.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* - * SPDX-License-Identifier: GPL-2.0+ * saa711x - Philips SAA711x video decoder register specifications * * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@kernel.org> diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 2d78e846d822..a80d7701b519 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -413,21 +413,14 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) struct smiapp_sensor *sensor = container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) ->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int pm_status; u32 orient = 0; + unsigned int i; int exposure; int rval; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - return smiapp_write( - sensor, - SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); - - case V4L2_CID_EXPOSURE: - return smiapp_write( - sensor, - SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); - case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: if (sensor->streaming) @@ -440,15 +433,10 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; orient ^= sensor->hvflip_inv_mask; - rval = smiapp_write(sensor, SMIAPP_REG_U8_IMAGE_ORIENTATION, - orient); - if (rval < 0) - return rval; smiapp_update_mbus_formats(sensor); - return 0; - + break; case V4L2_CID_VBLANK: exposure = sensor->exposure->val; @@ -461,59 +449,105 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) return rval; } - return smiapp_write( - sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + ctrl->val); - - case V4L2_CID_HBLANK: - return smiapp_write( - sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + ctrl->val); - + break; case V4L2_CID_LINK_FREQ: if (sensor->streaming) return -EBUSY; - return smiapp_pll_update(sensor); - - case V4L2_CID_TEST_PATTERN: { - unsigned int i; + rval = smiapp_pll_update(sensor); + if (rval) + return rval; + return 0; + case V4L2_CID_TEST_PATTERN: for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) v4l2_ctrl_activate( sensor->test_data[i], ctrl->val == V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR); - return smiapp_write( - sensor, SMIAPP_REG_U16_TEST_PATTERN_MODE, ctrl->val); + break; } + pm_runtime_get_noresume(&client->dev); + pm_status = pm_runtime_get_if_in_use(&client->dev); + pm_runtime_put_noidle(&client->dev); + if (!pm_status) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + rval = smiapp_write( + sensor, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + break; + case V4L2_CID_EXPOSURE: + rval = smiapp_write( + sensor, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + rval = smiapp_write(sensor, SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + + break; + case V4L2_CID_VBLANK: + rval = smiapp_write( + sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + break; + case V4L2_CID_HBLANK: + rval = smiapp_write( + sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN: + rval = smiapp_write( + sensor, SMIAPP_REG_U16_TEST_PATTERN_MODE, ctrl->val); + + break; case V4L2_CID_TEST_PATTERN_RED: - return smiapp_write( + rval = smiapp_write( sensor, SMIAPP_REG_U16_TEST_DATA_RED, ctrl->val); + break; case V4L2_CID_TEST_PATTERN_GREENR: - return smiapp_write( + rval = smiapp_write( sensor, SMIAPP_REG_U16_TEST_DATA_GREENR, ctrl->val); + break; case V4L2_CID_TEST_PATTERN_BLUE: - return smiapp_write( + rval = smiapp_write( sensor, SMIAPP_REG_U16_TEST_DATA_BLUE, ctrl->val); + break; case V4L2_CID_TEST_PATTERN_GREENB: - return smiapp_write( + rval = smiapp_write( sensor, SMIAPP_REG_U16_TEST_DATA_GREENB, ctrl->val); + break; case V4L2_CID_PIXEL_RATE: /* For v4l2_ctrl_s_ctrl_int64() used internally. */ - return 0; + rval = 0; + break; default: - return -EINVAL; + rval = -EINVAL; } + + if (pm_status > 0) { + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + return rval; } static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { @@ -682,66 +716,6 @@ static int smiapp_get_all_limits(struct smiapp_sensor *sensor) return 0; } -static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - static u32 const limits[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, - }; - static u32 const limits_replace[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, - }; - unsigned int i; - int rval; - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == - SMIAPP_BINNING_CAPABILITY_NO) { - for (i = 0; i < ARRAY_SIZE(limits); i++) - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - - return 0; - } - - rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); - if (rval < 0) - return rval; - - /* - * Sanity check whether the binning limits are valid. If not, - * use the non-binning ones. - */ - if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) - return 0; - - for (i = 0; i < ARRAY_SIZE(limits); i++) { - dev_dbg(&client->dev, - "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limits[i]].addr, - smiapp_reg_limits[limits[i]].what, - sensor->limits[limits_replace[i]], - sensor->limits[limits_replace[i]]); - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - } - - return 0; -} - static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -891,60 +865,47 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) { struct v4l2_ctrl *vblank = sensor->vblank; struct v4l2_ctrl *hblank = sensor->hblank; + uint16_t min_fll, max_fll, min_llp, max_llp, min_lbp; int min, max; + if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { + min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]; + max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN]; + min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]; + max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN]; + min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]; + } else { + min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES]; + max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES]; + min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK]; + max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK]; + min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK]; + } + min = max_t(int, sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + min_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - max = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + max = max_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); min = max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + min_llp - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); - max = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + min_lbp); + max = max_llp - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); __smiapp_update_exposure_limits(sensor); } -static int smiapp_update_mode(struct smiapp_sensor *sensor) +static int smiapp_pll_blanking_update(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; int rval; - /* Binning has to be set up here; it affects limits */ - if (sensor->binning_horizontal == 1 && - sensor->binning_vertical == 1) { - binning_mode = 0; - } else { - u8 binning_type = - (sensor->binning_horizontal << 4) - | sensor->binning_vertical; - - rval = smiapp_write( - sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); - if (rval < 0) - return rval; - - binning_mode = 1; - } - rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); - if (rval < 0) - return rval; - - /* Get updated limits due to binning */ - rval = smiapp_get_limits_binning(sensor); - if (rval < 0) - return rval; - rval = smiapp_pll_update(sensor); if (rval < 0) return rval; @@ -970,62 +931,91 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) * SMIA++ NVM handling * */ -static int smiapp_read_nvm(struct smiapp_sensor *sensor, - unsigned char *nvm) + +static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm, + u8 *status) { - u32 i, s, p, np, v; - int rval = 0, rval2; + unsigned int i; + int rval; + u32 s; - np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; - for (p = 0; p < np; p++) { - rval = smiapp_write( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); - if (rval) - goto out; + *status = 0; - rval = smiapp_write(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | - SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); - if (rval) - goto out; + rval = smiapp_write(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + return rval; - for (i = 1000; i > 0; i--) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN); + if (rval) + return rval; - if (rval) - goto out; + rval = smiapp_read(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, + &s); + if (rval) + return rval; + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) { + *status = s; + return -ENODATA; + } + + if (sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL) { + for (i = 1000; i > 0; i--) { if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) break; - } - if (!i) { - rval = -ETIMEDOUT; - goto out; - } - - for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { rval = smiapp_read( sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, - &v); - if (rval) - goto out; + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, + &s); - *nvm++ = v; + if (rval) + return rval; } + + if (!i) + return -ETIMEDOUT; } -out: + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + u32 v; + + rval = smiapp_read(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + return rval; + + *nvm++ = v; + } + + return 0; +} + +static int smiapp_read_nvm(struct smiapp_sensor *sensor, unsigned char *nvm, + size_t nvm_size) +{ + u8 status = 0; + u32 p; + int rval = 0, rval2; + + for (p = 0; p < nvm_size / SMIAPP_NVM_PAGE_SIZE && !rval; p++) { + rval = smiapp_read_nvm_page(sensor, p, nvm, &status); + nvm += SMIAPP_NVM_PAGE_SIZE; + } + + if (rval == -ENODATA && + status & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) + rval = 0; + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); if (rval < 0) return rval; else - return rval2; + return rval2 ?: p * SMIAPP_NVM_PAGE_SIZE; } /* @@ -1228,10 +1218,6 @@ static int smiapp_power_on(struct device *dev) sleep = SMIAPP_RESET_DELAY(sensor->hwcfg->ext_clk); usleep_range(sleep, sleep); - mutex_lock(&sensor->mutex); - - sensor->active = true; - /* * Failures to respond to the address change command have been noticed. * Those failures seem to be caused by the sensor requiring a longer @@ -1314,28 +1300,9 @@ static int smiapp_power_on(struct device *dev) goto out_cci_addr_fail; } - /* Are we still initialising...? If not, proceed with control setup. */ - if (sensor->pixel_array) { - rval = __v4l2_ctrl_handler_setup( - &sensor->pixel_array->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - rval = __v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); - if (rval) - goto out_cci_addr_fail; - - rval = smiapp_update_mode(sensor); - if (rval < 0) - goto out_cci_addr_fail; - } - - mutex_unlock(&sensor->mutex); - return 0; out_cci_addr_fail: - mutex_unlock(&sensor->mutex); gpiod_set_value(sensor->xshutdown, 0); clk_disable_unprepare(sensor->ext_clk); @@ -1353,8 +1320,6 @@ static int smiapp_power_off(struct device *dev) struct smiapp_sensor *sensor = container_of(ssd, struct smiapp_sensor, ssds[0]); - mutex_lock(&sensor->mutex); - /* * Currently power/clock to lens are enable/disabled separately * but they are essentially the same signals. So if the sensor is @@ -1367,10 +1332,6 @@ static int smiapp_power_off(struct device *dev) SMIAPP_REG_U8_SOFTWARE_RESET, SMIAPP_SOFTWARE_RESET); - sensor->active = false; - - mutex_unlock(&sensor->mutex); - gpiod_set_value(sensor->xshutdown, 0); clk_disable_unprepare(sensor->ext_clk); usleep_range(5000, 5000); @@ -1387,6 +1348,7 @@ static int smiapp_power_off(struct device *dev) static int smiapp_start_streaming(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; int rval; mutex_lock(&sensor->mutex); @@ -1397,6 +1359,27 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (rval) goto out; + /* Binning configuration */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + goto out; + + binning_mode = 1; + } + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + goto out; + + /* Set up PLL */ rval = smiapp_pll_configure(sensor); if (rval) goto out; @@ -1533,6 +1516,30 @@ out: * V4L2 subdev video operations */ +static int smiapp_pm_get_init(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + rval = pm_runtime_get_sync(&client->dev); + if (rval < 0) { + if (rval != -EBUSY && rval != -EAGAIN) + pm_runtime_set_active(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return rval; + } else if (!rval) { + rval = v4l2_ctrl_handler_setup(&sensor->pixel_array-> + ctrl_handler); + if (rval) + return rval; + + return v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + } + + return 0; +} + static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -1542,22 +1549,23 @@ static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) if (sensor->streaming == enable) return 0; - if (enable) { - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) { - if (rval != -EBUSY && rval != -EAGAIN) - pm_runtime_set_active(&client->dev); - pm_runtime_put(&client->dev); - return rval; - } + if (!enable) { + smiapp_stop_streaming(sensor); + sensor->streaming = false; + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); - sensor->streaming = true; + return 0; + } - rval = smiapp_start_streaming(sensor); - if (rval < 0) - sensor->streaming = false; - } else { - rval = smiapp_stop_streaming(sensor); + rval = smiapp_pm_get_init(sensor); + if (rval) + return rval; + + sensor->streaming = true; + + rval = smiapp_start_streaming(sensor); + if (rval < 0) { sensor->streaming = false; pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_autosuspend(&client->dev); @@ -2073,7 +2081,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return smiapp_update_mode(sensor); + return smiapp_pll_blanking_update(sensor); return 0; } @@ -2312,41 +2320,30 @@ smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); struct i2c_client *client = v4l2_get_subdevdata(subdev); struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int nbytes; + int rval; if (!sensor->dev_init_done) return -EBUSY; - if (!sensor->nvm_size) { - int rval; - - /* NVM not read yet - read it now */ - sensor->nvm_size = sensor->hwcfg->nvm_size; + rval = smiapp_pm_get_init(sensor); + if (rval < 0) + return -ENODEV; - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) { - if (rval != -EBUSY && rval != -EAGAIN) - pm_runtime_set_active(&client->dev); - pm_runtime_put(&client->dev); - return -ENODEV; - } + rval = smiapp_read_nvm(sensor, buf, PAGE_SIZE); + if (rval < 0) { + pm_runtime_put(&client->dev); + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } - if (smiapp_read_nvm(sensor, sensor->nvm)) { - dev_err(&client->dev, "nvm read failed\n"); - return -ENODEV; - } + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - } /* * NVM is still way below a PAGE_SIZE, so we can safely * assume this for now. */ - nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); - memcpy(buf, sensor->nvm, nbytes); - - return nbytes; + return rval; } static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); @@ -2810,16 +2807,13 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) } } - /* NVM size is not mandatory */ - fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size); - rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &hwcfg->ext_clk); if (rval) dev_info(dev, "can't get clock-frequency\n"); - dev_dbg(dev, "nvm %d, clk %d, mode %d\n", - hwcfg->nvm_size, hwcfg->ext_clk, hwcfg->csi_signalling_mode); + dev_dbg(dev, "clk %d, mode %d\n", hwcfg->ext_clk, + hwcfg->csi_signalling_mode); if (!bus_cfg.nr_of_link_frequencies) { dev_warn(dev, "no link frequencies defined\n"); @@ -2847,8 +2841,7 @@ out_err: return NULL; } -static int smiapp_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int smiapp_probe(struct i2c_client *client) { struct smiapp_sensor *sensor; struct smiapp_hwconfig *hwcfg = smiapp_get_hwconfig(&client->dev); @@ -2863,7 +2856,6 @@ static int smiapp_probe(struct i2c_client *client, return -ENOMEM; sensor->hwcfg = hwcfg; - mutex_init(&sensor->mutex); sensor->src = &sensor->ssds[sensor->ssds_used]; v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); @@ -2927,6 +2919,8 @@ static int smiapp_probe(struct i2c_client *client, if (rval < 0) return rval; + mutex_init(&sensor->mutex); + rval = smiapp_identify_module(sensor); if (rval) { rval = -ENODEV; @@ -3004,17 +2998,10 @@ static int smiapp_probe(struct i2c_client *client, rval = -ENOENT; goto out_power_off; } - /* SMIA++ NVM initialization - it will be read from the sensor - * when it is first requested by userspace. - */ - if (sensor->minfo.smiapp_version && sensor->hwcfg->nvm_size) { - sensor->nvm = devm_kzalloc(&client->dev, - sensor->hwcfg->nvm_size, GFP_KERNEL); - if (sensor->nvm == NULL) { - rval = -ENOMEM; - goto out_cleanup; - } + if (sensor->minfo.smiapp_version && + sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) { if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { dev_err(&client->dev, "sysfs nvm entry failed\n"); rval = -EBUSY; @@ -3087,7 +3074,7 @@ static int smiapp_probe(struct i2c_client *client, } mutex_lock(&sensor->mutex); - rval = smiapp_update_mode(sensor); + rval = smiapp_pll_blanking_update(sensor); mutex_unlock(&sensor->mutex); if (rval) { dev_err(&client->dev, "update mode failed\n"); @@ -3102,19 +3089,23 @@ static int smiapp_probe(struct i2c_client *client, if (rval < 0) goto out_media_entity_cleanup; - rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); - if (rval < 0) - goto out_media_entity_cleanup; - pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); pm_runtime_enable(&client->dev); + + rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); + if (rval < 0) + goto out_disable_runtime_pm; + pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); pm_runtime_put_autosuspend(&client->dev); return 0; +out_disable_runtime_pm: + pm_runtime_disable(&client->dev); + out_media_entity_cleanup: media_entity_cleanup(&sensor->src->sd.entity); @@ -3123,6 +3114,7 @@ out_cleanup: out_power_off: smiapp_power_off(&client->dev); + mutex_destroy(&sensor->mutex); return rval; } @@ -3145,6 +3137,7 @@ static int smiapp_remove(struct i2c_client *client) media_entity_cleanup(&sensor->ssds[i].sd.entity); } smiapp_cleanup(sensor); + mutex_destroy(&sensor->mutex); return 0; } @@ -3172,7 +3165,7 @@ static struct i2c_driver smiapp_i2c_driver = { .name = SMIAPP_NAME, .pm = &smiapp_pm_ops, }, - .probe = smiapp_probe, + .probe_new = smiapp_probe, .remove = smiapp_remove, .id_table = smiapp_id_table, }; diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index 2804a4d9a4e1..43505cd0616e 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -11,25 +11,29 @@ #ifndef __SMIAPP_REG_H_ #define __SMIAPP_REG_H_ +#include <linux/bits.h> + #include "smiapp-reg-defs.h" /* Bits for above register */ -#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) -#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) - -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) - -#define SMIAPP_SOFTWARE_RESET (1 << 0) - -#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) -#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) +#define SMIAPP_IMAGE_ORIENTATION_HFLIP BIT(0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP BIT(1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE BIT(3) + +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL BIT(2) + +#define SMIAPP_SOFTWARE_RESET BIT(0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) #define SMIAPP_DPHY_CTRL_AUTOMATIC 0 /* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index 0470e47c2f7a..ce8c1d47fbf0 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -223,9 +223,6 @@ int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) len != SMIAPP_REG_32BIT) || flags) return -EINVAL; - if (!sensor->active) - return 0; - msg.addr = client->addr; msg.flags = 0; /* Write */ msg.len = 2 + len; diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index ecf8a17dbe37..4837d80dc453 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -198,7 +198,6 @@ struct smiapp_sensor { u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ u8 frame_skip; - bool active; /* is the sensor powered on? */ u16 embedded_start; /* embedded data start line */ u16 embedded_end; u16 image_start; /* image data start line */ @@ -208,9 +207,6 @@ struct smiapp_sensor { bool dev_init_done; u8 compressed_min_bpp; - u8 *nvm; /* nvm memory buffer */ - unsigned int nvm_size; /* bytes */ - struct smiapp_module_info minfo; struct smiapp_pll pll; diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 81285b8d5cfb..003ba22334cd 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -971,6 +971,11 @@ static int mipid02_probe(struct i2c_client *client) bridge->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(bridge->reset_gpio)) { + dev_err(dev, "failed to get reset GPIO\n"); + return PTR_ERR(bridge->reset_gpio); + } + ret = mipid02_get_regulators(bridge); if (ret) { dev_err(dev, "failed to get regulators %d", ret); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index bc2e35e5ce61..dbbab75f135e 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2026,8 +2026,7 @@ static inline int tc358743_probe_of(struct tc358743_state *state) } #endif -static int tc358743_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tc358743_probe(struct i2c_client *client) { static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_640X480P59_94; @@ -2222,7 +2221,7 @@ static struct i2c_driver tc358743_driver = { .name = "tc358743", .of_match_table = of_match_ptr(tc358743_of_match), }, - .probe = tc358743_probe, + .probe_new = tc358743_probe, .remove = tc358743_remove, .id_table = tc358743_id, }; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index a62ede096636..5e68182001ec 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2691,7 +2691,13 @@ static int tda1997x_probe(struct i2c_client *client, } ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03); - state->client_cec = i2c_new_dummy(client->adapter, ret); + state->client_cec = devm_i2c_new_dummy_device(&client->dev, + client->adapter, ret); + if (IS_ERR(state->client_cec)) { + ret = PTR_ERR(state->client_cec); + goto err_free_mutex; + } + v4l_info(client, "CEC slave address 0x%02x\n", ret); ret = tda1997x_core_init(sd); @@ -2798,7 +2804,6 @@ static int tda1997x_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&state->hdl); regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies); - i2c_unregister_device(state->client_cec); cancel_delayed_work(&state->delayed_work_enable_hpd); mutex_destroy(&state->page_lock); mutex_destroy(&state->lock); diff --git a/drivers/media/i2c/tda1997x_regs.h b/drivers/media/i2c/tda1997x_regs.h index ecf87534613b..d9b3daada07d 100644 --- a/drivers/media/i2c/tda1997x_regs.h +++ b/drivers/media/i2c/tda1997x_regs.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2018 Gateworks Corporation */ diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index f5ee28058ea2..c52fe84cba1b 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -436,8 +436,7 @@ static const struct v4l2_subdev_ops ths8200_ops = { .pad = &ths8200_pad_ops, }; -static int ths8200_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ths8200_probe(struct i2c_client *client) { struct ths8200_state *state; struct v4l2_subdev *sd; @@ -502,7 +501,7 @@ static struct i2c_driver ths8200_driver = { .name = "ths8200", .of_match_table = of_match_ptr(ths8200_of_match), }, - .probe = ths8200_probe, + .probe_new = ths8200_probe, .remove = ths8200_remove, .id_table = ths8200_id, }; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index eaddd977ba40..edad49cebcdf 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1636,11 +1636,13 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) dev_err(decoder->sd.dev, "missing type property in node %pOFn\n", child); + of_node_put(child); goto err_connector; } if (input_type >= TVP5150_INPUT_NUM) { ret = -EINVAL; + of_node_put(child); goto err_connector; } @@ -1651,6 +1653,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) dev_err(decoder->sd.dev, "input %s with same type already exists\n", input->name); + of_node_put(child); ret = -EINVAL; goto err_connector; } @@ -1672,6 +1675,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) dev_err(decoder->sd.dev, "missing label property in node %pOFn\n", child); + of_node_put(child); goto err_connector; } @@ -1691,8 +1695,7 @@ static const char * const tvp5150_test_patterns[2] = { "Black screen" }; -static int tvp5150_probe(struct i2c_client *c, - const struct i2c_device_id *id) +static int tvp5150_probe(struct i2c_client *c) { struct tvp5150 *core; struct v4l2_subdev *sd; @@ -1841,7 +1844,7 @@ static struct i2c_driver tvp5150_driver = { .of_match_table = of_match_ptr(tvp5150_of_match), .name = "tvp5150", }, - .probe = tvp5150_probe, + .probe_new = tvp5150_probe, .remove = tvp5150_remove, .id_table = tvp5150_id, }; diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h index 9088186c24d1..f716129adf09 100644 --- a/drivers/media/i2c/tvp5150_reg.h +++ b/drivers/media/i2c/tvp5150_reg.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers * diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 1b8175cab017..de313b1306da 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -930,7 +930,7 @@ done: * Returns zero when successful, -EINVAL if register read fails or * -EIO if i2c access is not available. */ -static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) +static int tvp7002_probe(struct i2c_client *c) { struct tvp7002_config *pdata = tvp7002_get_pdata(c); struct v4l2_subdev *sd; @@ -1075,7 +1075,7 @@ static struct i2c_driver tvp7002_driver = { .of_match_table = of_match_ptr(tvp7002_of_match), .name = TVP7002_MODULE_NAME, }, - .probe = tvp7002_probe, + .probe_new = tvp7002_probe, .remove = tvp7002_remove, .id_table = tvp7002_id, }; diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 39f66e7a0e42..8be03fe5928c 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -375,7 +375,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, input = 1: COMPOSITE input input = 2: SVHS input */ - const int input_vals[3][2] = { + static const int input_vals[3][2] = { {0x0c, 0}, {0x0d, 0}, {0x0e, 1} |