1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * v4l2-i2c - I2C helpers for Video4Linux2 |
4 | */ |
5 | |
6 | #include <linux/i2c.h> |
7 | #include <linux/module.h> |
8 | #include <media/v4l2-common.h> |
9 | #include <media/v4l2-device.h> |
10 | |
11 | void v4l2_i2c_subdev_unregister(struct v4l2_subdev *sd) |
12 | { |
13 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
14 | |
15 | /* |
16 | * We need to unregister the i2c client |
17 | * explicitly. We cannot rely on |
18 | * i2c_del_adapter to always unregister |
19 | * clients for us, since if the i2c bus is a |
20 | * platform bus, then it is never deleted. |
21 | * |
22 | * Device tree or ACPI based devices must not |
23 | * be unregistered as they have not been |
24 | * registered by us, and would not be |
25 | * re-created by just probing the V4L2 driver. |
26 | */ |
27 | if (client && !client->dev.of_node && !client->dev.fwnode) |
28 | i2c_unregister_device(client); |
29 | } |
30 | |
31 | void v4l2_i2c_subdev_set_name(struct v4l2_subdev *sd, |
32 | struct i2c_client *client, |
33 | const char *devname, const char *postfix) |
34 | { |
35 | if (!devname) |
36 | devname = client->dev.driver->name; |
37 | if (!postfix) |
38 | postfix = "" ; |
39 | |
40 | snprintf(buf: sd->name, size: sizeof(sd->name), fmt: "%s%s %d-%04x" , devname, postfix, |
41 | i2c_adapter_id(adap: client->adapter), client->addr); |
42 | } |
43 | EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_set_name); |
44 | |
45 | void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, |
46 | const struct v4l2_subdev_ops *ops) |
47 | { |
48 | v4l2_subdev_init(sd, ops); |
49 | sd->flags |= V4L2_SUBDEV_FL_IS_I2C; |
50 | /* the owner is the same as the i2c_client's driver owner */ |
51 | sd->owner = client->dev.driver->owner; |
52 | sd->dev = &client->dev; |
53 | /* i2c_client and v4l2_subdev point to one another */ |
54 | v4l2_set_subdevdata(sd, p: client); |
55 | i2c_set_clientdata(client, data: sd); |
56 | v4l2_i2c_subdev_set_name(sd, client, NULL, NULL); |
57 | } |
58 | EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); |
59 | |
60 | /* Load an i2c sub-device. */ |
61 | struct v4l2_subdev |
62 | *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, |
63 | struct i2c_adapter *adapter, |
64 | struct i2c_board_info *info, |
65 | const unsigned short *probe_addrs) |
66 | { |
67 | struct v4l2_subdev *sd = NULL; |
68 | struct i2c_client *client; |
69 | |
70 | if (!v4l2_dev) |
71 | return NULL; |
72 | |
73 | request_module(I2C_MODULE_PREFIX "%s" , info->type); |
74 | |
75 | /* Create the i2c client */ |
76 | if (info->addr == 0 && probe_addrs) |
77 | client = i2c_new_scanned_device(adap: adapter, info, addr_list: probe_addrs, |
78 | NULL); |
79 | else |
80 | client = i2c_new_client_device(adap: adapter, info); |
81 | |
82 | /* |
83 | * Note: by loading the module first we are certain that c->driver |
84 | * will be set if the driver was found. If the module was not loaded |
85 | * first, then the i2c core tries to delay-load the module for us, |
86 | * and then c->driver is still NULL until the module is finally |
87 | * loaded. This delay-load mechanism doesn't work if other drivers |
88 | * want to use the i2c device, so explicitly loading the module |
89 | * is the best alternative. |
90 | */ |
91 | if (!i2c_client_has_driver(client)) |
92 | goto error; |
93 | |
94 | /* Lock the module so we can safely get the v4l2_subdev pointer */ |
95 | if (!try_module_get(module: client->dev.driver->owner)) |
96 | goto error; |
97 | sd = i2c_get_clientdata(client); |
98 | |
99 | /* |
100 | * Register with the v4l2_device which increases the module's |
101 | * use count as well. |
102 | */ |
103 | if (v4l2_device_register_subdev(v4l2_dev, sd)) |
104 | sd = NULL; |
105 | /* Decrease the module use count to match the first try_module_get. */ |
106 | module_put(module: client->dev.driver->owner); |
107 | |
108 | error: |
109 | /* |
110 | * If we have a client but no subdev, then something went wrong and |
111 | * we must unregister the client. |
112 | */ |
113 | if (!IS_ERR(ptr: client) && !sd) |
114 | i2c_unregister_device(client); |
115 | return sd; |
116 | } |
117 | EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); |
118 | |
119 | struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, |
120 | struct i2c_adapter *adapter, |
121 | const char *client_type, |
122 | u8 addr, |
123 | const unsigned short *probe_addrs) |
124 | { |
125 | struct i2c_board_info info; |
126 | |
127 | /* |
128 | * Setup the i2c board info with the device type and |
129 | * the device address. |
130 | */ |
131 | memset(&info, 0, sizeof(info)); |
132 | strscpy(info.type, client_type, sizeof(info.type)); |
133 | info.addr = addr; |
134 | |
135 | return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, |
136 | probe_addrs); |
137 | } |
138 | EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); |
139 | |
140 | /* Return i2c client address of v4l2_subdev. */ |
141 | unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) |
142 | { |
143 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
144 | |
145 | return client ? client->addr : I2C_CLIENT_END; |
146 | } |
147 | EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); |
148 | |
149 | /* |
150 | * Return a list of I2C tuner addresses to probe. Use only if the tuner |
151 | * addresses are unknown. |
152 | */ |
153 | const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) |
154 | { |
155 | static const unsigned short radio_addrs[] = { |
156 | #if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) |
157 | 0x10, |
158 | #endif |
159 | 0x60, |
160 | I2C_CLIENT_END |
161 | }; |
162 | static const unsigned short demod_addrs[] = { |
163 | 0x42, 0x43, 0x4a, 0x4b, |
164 | I2C_CLIENT_END |
165 | }; |
166 | static const unsigned short tv_addrs[] = { |
167 | 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ |
168 | 0x60, 0x61, 0x62, 0x63, 0x64, |
169 | I2C_CLIENT_END |
170 | }; |
171 | |
172 | switch (type) { |
173 | case ADDRS_RADIO: |
174 | return radio_addrs; |
175 | case ADDRS_DEMOD: |
176 | return demod_addrs; |
177 | case ADDRS_TV: |
178 | return tv_addrs; |
179 | case ADDRS_TV_WITH_DEMOD: |
180 | return tv_addrs + 4; |
181 | } |
182 | return NULL; |
183 | } |
184 | EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); |
185 | |