| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * v4l2-i2c - I2C helpers for Video4Linux2 |
| */ |
| |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <media/v4l2-common.h> |
| #include <media/v4l2-device.h> |
| |
| void v4l2_i2c_subdev_unregister(struct v4l2_subdev *sd) |
| { |
| struct i2c_client *client = v4l2_get_subdevdata(sd); |
| |
| /* |
| * We need to unregister the i2c client |
| * explicitly. We cannot rely on |
| * i2c_del_adapter to always unregister |
| * clients for us, since if the i2c bus is a |
| * platform bus, then it is never deleted. |
| * |
| * Device tree or ACPI based devices must not |
| * be unregistered as they have not been |
| * registered by us, and would not be |
| * re-created by just probing the V4L2 driver. |
| */ |
| if (client && !client->dev.of_node && !client->dev.fwnode) |
| i2c_unregister_device(client); |
| } |
| |
| void v4l2_i2c_subdev_set_name(struct v4l2_subdev *sd, |
| struct i2c_client *client, |
| const char *devname, const char *postfix) |
| { |
| if (!devname) |
| devname = client->dev.driver->name; |
| if (!postfix) |
| postfix = ""; |
| |
| snprintf(sd->name, sizeof(sd->name), "%s%s %d-%04x", devname, postfix, |
| i2c_adapter_id(client->adapter), client->addr); |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_set_name); |
| |
| void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, |
| const struct v4l2_subdev_ops *ops) |
| { |
| v4l2_subdev_init(sd, ops); |
| sd->flags |= V4L2_SUBDEV_FL_IS_I2C; |
| /* the owner is the same as the i2c_client's driver owner */ |
| sd->owner = client->dev.driver->owner; |
| sd->dev = &client->dev; |
| /* i2c_client and v4l2_subdev point to one another */ |
| v4l2_set_subdevdata(sd, client); |
| i2c_set_clientdata(client, sd); |
| v4l2_i2c_subdev_set_name(sd, client, NULL, NULL); |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); |
| |
| /* Load an i2c sub-device. */ |
| struct v4l2_subdev |
| *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, |
| struct i2c_adapter *adapter, |
| struct i2c_board_info *info, |
| const unsigned short *probe_addrs) |
| { |
| struct v4l2_subdev *sd = NULL; |
| struct i2c_client *client; |
| |
| if (!v4l2_dev) |
| return NULL; |
| |
| request_module(I2C_MODULE_PREFIX "%s", info->type); |
| |
| /* Create the i2c client */ |
| if (info->addr == 0 && probe_addrs) |
| client = i2c_new_scanned_device(adapter, info, probe_addrs, |
| NULL); |
| else |
| client = i2c_new_client_device(adapter, info); |
| |
| /* |
| * Note: by loading the module first we are certain that c->driver |
| * will be set if the driver was found. If the module was not loaded |
| * first, then the i2c core tries to delay-load the module for us, |
| * and then c->driver is still NULL until the module is finally |
| * loaded. This delay-load mechanism doesn't work if other drivers |
| * want to use the i2c device, so explicitly loading the module |
| * is the best alternative. |
| */ |
| if (!i2c_client_has_driver(client)) |
| goto error; |
| |
| /* Lock the module so we can safely get the v4l2_subdev pointer */ |
| if (!try_module_get(client->dev.driver->owner)) |
| goto error; |
| sd = i2c_get_clientdata(client); |
| |
| /* |
| * Register with the v4l2_device which increases the module's |
| * use count as well. |
| */ |
| if (__v4l2_device_register_subdev(v4l2_dev, sd, sd->owner)) |
| sd = NULL; |
| /* Decrease the module use count to match the first try_module_get. */ |
| module_put(client->dev.driver->owner); |
| |
| error: |
| /* |
| * If we have a client but no subdev, then something went wrong and |
| * we must unregister the client. |
| */ |
| if (!IS_ERR(client) && !sd) |
| i2c_unregister_device(client); |
| return sd; |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); |
| |
| struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, |
| struct i2c_adapter *adapter, |
| const char *client_type, |
| u8 addr, |
| const unsigned short *probe_addrs) |
| { |
| struct i2c_board_info info; |
| |
| /* |
| * Setup the i2c board info with the device type and |
| * the device address. |
| */ |
| memset(&info, 0, sizeof(info)); |
| strscpy(info.type, client_type, sizeof(info.type)); |
| info.addr = addr; |
| |
| return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, |
| probe_addrs); |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); |
| |
| /* Return i2c client address of v4l2_subdev. */ |
| unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) |
| { |
| struct i2c_client *client = v4l2_get_subdevdata(sd); |
| |
| return client ? client->addr : I2C_CLIENT_END; |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); |
| |
| /* |
| * Return a list of I2C tuner addresses to probe. Use only if the tuner |
| * addresses are unknown. |
| */ |
| const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) |
| { |
| static const unsigned short radio_addrs[] = { |
| #if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) |
| 0x10, |
| #endif |
| 0x60, |
| I2C_CLIENT_END |
| }; |
| static const unsigned short demod_addrs[] = { |
| 0x42, 0x43, 0x4a, 0x4b, |
| I2C_CLIENT_END |
| }; |
| static const unsigned short tv_addrs[] = { |
| 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ |
| 0x60, 0x61, 0x62, 0x63, 0x64, |
| I2C_CLIENT_END |
| }; |
| |
| switch (type) { |
| case ADDRS_RADIO: |
| return radio_addrs; |
| case ADDRS_DEMOD: |
| return demod_addrs; |
| case ADDRS_TV: |
| return tv_addrs; |
| case ADDRS_TV_WITH_DEMOD: |
| return tv_addrs + 4; |
| } |
| return NULL; |
| } |
| EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); |