// SPDX-License-Identifier: GPL-2.0-or-later /* * v4l2-i2c - I2C helpers for Video4Linux2 */ #include #include #include #include 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_probed_device(adapter, info, probe_addrs, NULL); else client = i2c_new_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 (!client || !client->dev.driver) 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 = 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 (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);