| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * drivers/media/i2c/ad5820.c |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 11 | 12 | * Sakari Ailus <sakari.ailus@iki.fi> |
|---|
| 12 | 13 | * |
|---|
| 13 | 14 | * Based on af_d88.c by Texas Instruments. |
|---|
| 14 | | - * |
|---|
| 15 | | - * This program is free software; you can redistribute it and/or |
|---|
| 16 | | - * modify it under the terms of the GNU General Public License |
|---|
| 17 | | - * version 2 as published by the Free Software Foundation. |
|---|
| 18 | | - * |
|---|
| 19 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 20 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 22 | | - * General Public License for more details. |
|---|
| 23 | 15 | */ |
|---|
| 24 | 16 | |
|---|
| 25 | 17 | #include <linux/errno.h> |
|---|
| .. | .. |
|---|
| 27 | 19 | #include <linux/kernel.h> |
|---|
| 28 | 20 | #include <linux/module.h> |
|---|
| 29 | 21 | #include <linux/regulator/consumer.h> |
|---|
| 22 | +#include <linux/gpio/consumer.h> |
|---|
| 30 | 23 | |
|---|
| 31 | 24 | #include <media/v4l2-ctrls.h> |
|---|
| 32 | 25 | #include <media/v4l2-device.h> |
|---|
| 33 | 26 | #include <media/v4l2-subdev.h> |
|---|
| 34 | | - |
|---|
| 35 | | -#define AD5820_NAME "ad5820" |
|---|
| 36 | 27 | |
|---|
| 37 | 28 | /* Register definitions */ |
|---|
| 38 | 29 | #define AD5820_POWER_DOWN (1 << 15) |
|---|
| .. | .. |
|---|
| 54 | 45 | u32 focus_absolute; |
|---|
| 55 | 46 | u32 focus_ramp_time; |
|---|
| 56 | 47 | u32 focus_ramp_mode; |
|---|
| 48 | + |
|---|
| 49 | + struct gpio_desc *enable_gpio; |
|---|
| 57 | 50 | |
|---|
| 58 | 51 | struct mutex power_lock; |
|---|
| 59 | 52 | int power_count; |
|---|
| .. | .. |
|---|
| 122 | 115 | ret = ad5820_update_hw(coil); |
|---|
| 123 | 116 | } |
|---|
| 124 | 117 | |
|---|
| 118 | + gpiod_set_value_cansleep(coil->enable_gpio, 0); |
|---|
| 119 | + |
|---|
| 125 | 120 | ret2 = regulator_disable(coil->vana); |
|---|
| 126 | 121 | if (ret) |
|---|
| 127 | 122 | return ret; |
|---|
| .. | .. |
|---|
| 136 | 131 | if (ret < 0) |
|---|
| 137 | 132 | return ret; |
|---|
| 138 | 133 | |
|---|
| 134 | + gpiod_set_value_cansleep(coil->enable_gpio, 1); |
|---|
| 135 | + |
|---|
| 139 | 136 | if (restore) { |
|---|
| 140 | 137 | /* Restore the hardware settings. */ |
|---|
| 141 | 138 | coil->standby = false; |
|---|
| .. | .. |
|---|
| 146 | 143 | return 0; |
|---|
| 147 | 144 | |
|---|
| 148 | 145 | fail: |
|---|
| 146 | + gpiod_set_value_cansleep(coil->enable_gpio, 0); |
|---|
| 149 | 147 | coil->standby = true; |
|---|
| 150 | 148 | regulator_disable(coil->vana); |
|---|
| 151 | 149 | |
|---|
| .. | .. |
|---|
| 312 | 310 | return ret; |
|---|
| 313 | 311 | } |
|---|
| 314 | 312 | |
|---|
| 313 | + coil->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", |
|---|
| 314 | + GPIOD_OUT_LOW); |
|---|
| 315 | + if (IS_ERR(coil->enable_gpio)) { |
|---|
| 316 | + ret = PTR_ERR(coil->enable_gpio); |
|---|
| 317 | + if (ret != -EPROBE_DEFER) |
|---|
| 318 | + dev_err(&client->dev, "could not get enable gpio\n"); |
|---|
| 319 | + return ret; |
|---|
| 320 | + } |
|---|
| 321 | + |
|---|
| 315 | 322 | mutex_init(&coil->power_lock); |
|---|
| 316 | 323 | |
|---|
| 317 | 324 | v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); |
|---|
| 318 | 325 | coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
|---|
| 319 | 326 | coil->subdev.internal_ops = &ad5820_internal_ops; |
|---|
| 320 | | - strcpy(coil->subdev.name, "ad5820 focus"); |
|---|
| 327 | + coil->subdev.entity.function = MEDIA_ENT_F_LENS; |
|---|
| 328 | + strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name)); |
|---|
| 321 | 329 | |
|---|
| 322 | 330 | ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); |
|---|
| 323 | 331 | if (ret < 0) |
|---|
| 324 | | - goto cleanup2; |
|---|
| 332 | + goto clean_mutex; |
|---|
| 325 | 333 | |
|---|
| 326 | 334 | ret = v4l2_async_register_subdev(&coil->subdev); |
|---|
| 327 | 335 | if (ret < 0) |
|---|
| 328 | | - goto cleanup; |
|---|
| 336 | + goto clean_entity; |
|---|
| 329 | 337 | |
|---|
| 330 | 338 | return ret; |
|---|
| 331 | 339 | |
|---|
| 332 | | -cleanup2: |
|---|
| 333 | | - mutex_destroy(&coil->power_lock); |
|---|
| 334 | | -cleanup: |
|---|
| 340 | +clean_entity: |
|---|
| 335 | 341 | media_entity_cleanup(&coil->subdev.entity); |
|---|
| 342 | +clean_mutex: |
|---|
| 343 | + mutex_destroy(&coil->power_lock); |
|---|
| 336 | 344 | return ret; |
|---|
| 337 | 345 | } |
|---|
| 338 | 346 | |
|---|
| .. | .. |
|---|
| 349 | 357 | } |
|---|
| 350 | 358 | |
|---|
| 351 | 359 | static const struct i2c_device_id ad5820_id_table[] = { |
|---|
| 352 | | - { AD5820_NAME, 0 }, |
|---|
| 360 | + { "ad5820", 0 }, |
|---|
| 361 | + { "ad5821", 0 }, |
|---|
| 353 | 362 | { } |
|---|
| 354 | 363 | }; |
|---|
| 355 | 364 | MODULE_DEVICE_TABLE(i2c, ad5820_id_table); |
|---|
| 365 | + |
|---|
| 366 | +static const struct of_device_id ad5820_of_table[] = { |
|---|
| 367 | + { .compatible = "adi,ad5820" }, |
|---|
| 368 | + { .compatible = "adi,ad5821" }, |
|---|
| 369 | + { } |
|---|
| 370 | +}; |
|---|
| 371 | +MODULE_DEVICE_TABLE(of, ad5820_of_table); |
|---|
| 356 | 372 | |
|---|
| 357 | 373 | static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); |
|---|
| 358 | 374 | |
|---|
| 359 | 375 | static struct i2c_driver ad5820_i2c_driver = { |
|---|
| 360 | 376 | .driver = { |
|---|
| 361 | | - .name = AD5820_NAME, |
|---|
| 377 | + .name = "ad5820", |
|---|
| 362 | 378 | .pm = &ad5820_pm, |
|---|
| 379 | + .of_match_table = ad5820_of_table, |
|---|
| 363 | 380 | }, |
|---|
| 364 | 381 | .probe = ad5820_probe, |
|---|
| 365 | 382 | .remove = ad5820_remove, |
|---|