.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * drivers/extcon/extcon.c - External Connector (extcon) framework. |
---|
3 | 4 | * |
---|
.. | .. |
---|
11 | 12 | * based on android/drivers/switch/switch_class.c |
---|
12 | 13 | * Copyright (C) 2008 Google, Inc. |
---|
13 | 14 | * Author: Mike Lockwood <lockwood@android.com> |
---|
14 | | - * |
---|
15 | | - * This software is licensed under the terms of the GNU General Public |
---|
16 | | - * License version 2, as published by the Free Software Foundation, and |
---|
17 | | - * may be copied, distributed, and modified under those terms. |
---|
18 | | - * |
---|
19 | | - * This program is distributed in the hope that it will be useful, |
---|
20 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
22 | | - * GNU General Public License for more details. |
---|
23 | 15 | */ |
---|
24 | 16 | |
---|
25 | 17 | #include <linux/module.h> |
---|
.. | .. |
---|
33 | 25 | #include <linux/sysfs.h> |
---|
34 | 26 | |
---|
35 | 27 | #include "extcon.h" |
---|
36 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
---|
37 | | -#include "../base/base.h" |
---|
38 | | -#endif |
---|
39 | 28 | |
---|
40 | 29 | #define SUPPORTED_CABLE_MAX 32 |
---|
41 | 30 | |
---|
.. | .. |
---|
158 | 147 | .id = EXTCON_DISP_HDMI, |
---|
159 | 148 | .name = "HDMI", |
---|
160 | 149 | }, |
---|
161 | | - [EXTCON_DISP_HDMI_AUDIO] = { |
---|
162 | | - .type = EXTCON_TYPE_DISP, |
---|
163 | | - .id = EXTCON_DISP_HDMI_AUDIO, |
---|
164 | | - .name = "HDMI-AUDIO", |
---|
165 | | - }, |
---|
166 | 150 | [EXTCON_DISP_MHL] = { |
---|
167 | 151 | .type = EXTCON_TYPE_DISP, |
---|
168 | 152 | .id = EXTCON_DISP_MHL, |
---|
.. | .. |
---|
187 | 171 | .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, |
---|
188 | 172 | .id = EXTCON_DISP_HMD, |
---|
189 | 173 | .name = "HMD", |
---|
| 174 | + }, |
---|
| 175 | + [EXTCON_DISP_CVBS] = { |
---|
| 176 | + .type = EXTCON_TYPE_DISP, |
---|
| 177 | + .id = EXTCON_DISP_CVBS, |
---|
| 178 | + .name = "CVBS", |
---|
| 179 | + }, |
---|
| 180 | + [EXTCON_DISP_EDP] = { |
---|
| 181 | + .type = EXTCON_TYPE_DISP, |
---|
| 182 | + .id = EXTCON_DISP_EDP, |
---|
| 183 | + .name = "EDP", |
---|
190 | 184 | }, |
---|
191 | 185 | |
---|
192 | 186 | /* Miscellaneous external connector */ |
---|
.. | .. |
---|
217 | 211 | * @attr_name: "name" sysfs entry |
---|
218 | 212 | * @attr_state: "state" sysfs entry |
---|
219 | 213 | * @attrs: the array pointing to attr_name and attr_state for attr_g |
---|
| 214 | + * @usb_propval: the array of USB connector properties |
---|
| 215 | + * @chg_propval: the array of charger connector properties |
---|
| 216 | + * @jack_propval: the array of jack connector properties |
---|
| 217 | + * @disp_propval: the array of display connector properties |
---|
| 218 | + * @usb_bits: the bit array of the USB connector property capabilities |
---|
| 219 | + * @chg_bits: the bit array of the charger connector property capabilities |
---|
| 220 | + * @jack_bits: the bit array of the jack connector property capabilities |
---|
| 221 | + * @disp_bits: the bit array of the display connector property capabilities |
---|
220 | 222 | */ |
---|
221 | 223 | struct extcon_cable { |
---|
222 | 224 | struct extcon_dev *edev; |
---|
.. | .. |
---|
500 | 502 | } |
---|
501 | 503 | EXPORT_SYMBOL_GPL(extcon_sync); |
---|
502 | 504 | |
---|
503 | | -int extcon_blocking_sync(struct extcon_dev *edev, unsigned int id, bool val) |
---|
504 | | -{ |
---|
505 | | - int index; |
---|
506 | | - |
---|
507 | | - if (!edev) |
---|
508 | | - return -EINVAL; |
---|
509 | | - |
---|
510 | | - index = find_cable_index_by_id(edev, id); |
---|
511 | | - if (index < 0) |
---|
512 | | - return index; |
---|
513 | | - |
---|
514 | | - return blocking_notifier_call_chain(&edev->bnh[index], val, edev); |
---|
515 | | -} |
---|
516 | | -EXPORT_SYMBOL(extcon_blocking_sync); |
---|
517 | | - |
---|
518 | 505 | /** |
---|
519 | 506 | * extcon_get_state() - Get the state of an external connector. |
---|
520 | 507 | * @edev: the extcon device |
---|
.. | .. |
---|
656 | 643 | unsigned long flags; |
---|
657 | 644 | int index, ret = 0; |
---|
658 | 645 | |
---|
659 | | - *prop_val = (union extcon_property_value)(0); |
---|
| 646 | + *prop_val = (union extcon_property_value){0}; |
---|
660 | 647 | |
---|
661 | 648 | if (!edev) |
---|
662 | 649 | return -EINVAL; |
---|
.. | .. |
---|
936 | 923 | struct notifier_block *nb) |
---|
937 | 924 | { |
---|
938 | 925 | unsigned long flags; |
---|
939 | | - int ret, idx = -EINVAL; |
---|
| 926 | + int ret, idx; |
---|
940 | 927 | |
---|
941 | 928 | if (!edev || !nb) |
---|
942 | 929 | return -EINVAL; |
---|
.. | .. |
---|
952 | 939 | return ret; |
---|
953 | 940 | } |
---|
954 | 941 | EXPORT_SYMBOL_GPL(extcon_register_notifier); |
---|
955 | | - |
---|
956 | | -int extcon_register_blocking_notifier(struct extcon_dev *edev, unsigned int id, |
---|
957 | | - struct notifier_block *nb) |
---|
958 | | -{ |
---|
959 | | - int idx = -EINVAL; |
---|
960 | | - |
---|
961 | | - if (!edev || !nb) |
---|
962 | | - return -EINVAL; |
---|
963 | | - |
---|
964 | | - idx = find_cable_index_by_id(edev, id); |
---|
965 | | - if (idx < 0) |
---|
966 | | - return idx; |
---|
967 | | - |
---|
968 | | - return blocking_notifier_chain_register(&edev->bnh[idx], nb); |
---|
969 | | -} |
---|
970 | | -EXPORT_SYMBOL(extcon_register_blocking_notifier); |
---|
971 | | - |
---|
972 | | -int extcon_unregister_blocking_notifier(struct extcon_dev *edev, |
---|
973 | | - unsigned int id, struct notifier_block *nb) |
---|
974 | | -{ |
---|
975 | | - int idx; |
---|
976 | | - |
---|
977 | | - if (!edev || !nb) |
---|
978 | | - return -EINVAL; |
---|
979 | | - |
---|
980 | | - idx = find_cable_index_by_id(edev, id); |
---|
981 | | - if (idx < 0) |
---|
982 | | - return idx; |
---|
983 | | - |
---|
984 | | - return blocking_notifier_chain_unregister(&edev->bnh[idx], nb); |
---|
985 | | -} |
---|
986 | | -EXPORT_SYMBOL(extcon_unregister_blocking_notifier); |
---|
987 | 942 | |
---|
988 | 943 | /** |
---|
989 | 944 | * extcon_unregister_notifier() - Unregister a notifier block from the extcon. |
---|
.. | .. |
---|
1133 | 1088 | } |
---|
1134 | 1089 | EXPORT_SYMBOL_GPL(extcon_dev_free); |
---|
1135 | 1090 | |
---|
1136 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
---|
1137 | | -static const char *extcon_get_link_name(struct extcon_dev *edev) |
---|
1138 | | -{ |
---|
1139 | | - const char *dot = strchr(edev->name, '.'); |
---|
1140 | | - const char *name = dot + 1; |
---|
1141 | | - |
---|
1142 | | - if (!dot || !name || !(*name)) |
---|
1143 | | - name = edev->name; |
---|
1144 | | - |
---|
1145 | | - return name; |
---|
1146 | | -} |
---|
1147 | | -#endif |
---|
1148 | | - |
---|
1149 | 1091 | /** |
---|
1150 | 1092 | * extcon_dev_register() - Register an new extcon device |
---|
1151 | 1093 | * @edev: the extcon device to be registered |
---|
.. | .. |
---|
1196 | 1138 | (unsigned long)atomic_inc_return(&edev_no)); |
---|
1197 | 1139 | |
---|
1198 | 1140 | if (edev->max_supported) { |
---|
1199 | | - char buf[10]; |
---|
1200 | 1141 | char *str; |
---|
1201 | 1142 | struct extcon_cable *cable; |
---|
1202 | 1143 | |
---|
.. | .. |
---|
1210 | 1151 | for (index = 0; index < edev->max_supported; index++) { |
---|
1211 | 1152 | cable = &edev->cables[index]; |
---|
1212 | 1153 | |
---|
1213 | | - snprintf(buf, 10, "cable.%d", index); |
---|
1214 | | - str = kzalloc(strlen(buf) + 1, |
---|
1215 | | - GFP_KERNEL); |
---|
| 1154 | + str = kasprintf(GFP_KERNEL, "cable.%d", index); |
---|
1216 | 1155 | if (!str) { |
---|
1217 | 1156 | for (index--; index >= 0; index--) { |
---|
1218 | 1157 | cable = &edev->cables[index]; |
---|
.. | .. |
---|
1222 | 1161 | |
---|
1223 | 1162 | goto err_alloc_cables; |
---|
1224 | 1163 | } |
---|
1225 | | - strcpy(str, buf); |
---|
1226 | 1164 | |
---|
1227 | 1165 | cable->edev = edev; |
---|
1228 | 1166 | cable->cable_index = index; |
---|
.. | .. |
---|
1245 | 1183 | } |
---|
1246 | 1184 | |
---|
1247 | 1185 | if (edev->max_supported && edev->mutually_exclusive) { |
---|
1248 | | - char buf[80]; |
---|
1249 | 1186 | char *name; |
---|
1250 | 1187 | |
---|
1251 | 1188 | /* Count the size of mutually_exclusive array */ |
---|
.. | .. |
---|
1270 | 1207 | } |
---|
1271 | 1208 | |
---|
1272 | 1209 | for (index = 0; edev->mutually_exclusive[index]; index++) { |
---|
1273 | | - sprintf(buf, "0x%x", edev->mutually_exclusive[index]); |
---|
1274 | | - name = kzalloc(strlen(buf) + 1, |
---|
1275 | | - GFP_KERNEL); |
---|
| 1210 | + name = kasprintf(GFP_KERNEL, "0x%x", |
---|
| 1211 | + edev->mutually_exclusive[index]); |
---|
1276 | 1212 | if (!name) { |
---|
1277 | 1213 | for (index--; index >= 0; index--) { |
---|
1278 | 1214 | kfree(edev->d_attrs_muex[index].attr. |
---|
.. | .. |
---|
1283 | 1219 | ret = -ENOMEM; |
---|
1284 | 1220 | goto err_muex; |
---|
1285 | 1221 | } |
---|
1286 | | - strcpy(name, buf); |
---|
1287 | 1222 | sysfs_attr_init(&edev->d_attrs_muex[index].attr); |
---|
1288 | 1223 | edev->d_attrs_muex[index].attr.name = name; |
---|
1289 | 1224 | edev->d_attrs_muex[index].attr.mode = 0000; |
---|
.. | .. |
---|
1318 | 1253 | edev->dev.type = &edev->extcon_dev_type; |
---|
1319 | 1254 | } |
---|
1320 | 1255 | |
---|
1321 | | - ret = device_register(&edev->dev); |
---|
1322 | | - if (ret) { |
---|
1323 | | - put_device(&edev->dev); |
---|
1324 | | - goto err_dev; |
---|
1325 | | - } |
---|
1326 | | - |
---|
1327 | 1256 | spin_lock_init(&edev->lock); |
---|
1328 | | - edev->nh = devm_kcalloc(&edev->dev, edev->max_supported, |
---|
1329 | | - sizeof(*edev->nh), GFP_KERNEL); |
---|
1330 | | - if (!edev->nh) { |
---|
1331 | | - ret = -ENOMEM; |
---|
1332 | | - device_unregister(&edev->dev); |
---|
1333 | | - goto err_dev; |
---|
1334 | | - } |
---|
1335 | | - |
---|
1336 | | - edev->bnh = devm_kzalloc(&edev->dev, |
---|
1337 | | - sizeof(*edev->bnh) * edev->max_supported, GFP_KERNEL); |
---|
1338 | | - if (!edev->bnh) { |
---|
1339 | | - ret = -ENOMEM; |
---|
1340 | | - goto err_dev; |
---|
| 1257 | + if (edev->max_supported) { |
---|
| 1258 | + edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh), |
---|
| 1259 | + GFP_KERNEL); |
---|
| 1260 | + if (!edev->nh) { |
---|
| 1261 | + ret = -ENOMEM; |
---|
| 1262 | + goto err_alloc_nh; |
---|
| 1263 | + } |
---|
1341 | 1264 | } |
---|
1342 | 1265 | |
---|
1343 | 1266 | for (index = 0; index < edev->max_supported; index++) |
---|
.. | .. |
---|
1348 | 1271 | dev_set_drvdata(&edev->dev, edev); |
---|
1349 | 1272 | edev->state = 0; |
---|
1350 | 1273 | |
---|
| 1274 | + ret = device_register(&edev->dev); |
---|
| 1275 | + if (ret) { |
---|
| 1276 | + put_device(&edev->dev); |
---|
| 1277 | + goto err_dev; |
---|
| 1278 | + } |
---|
| 1279 | + |
---|
1351 | 1280 | mutex_lock(&extcon_dev_list_lock); |
---|
1352 | 1281 | list_add(&edev->entry, &extcon_dev_list); |
---|
1353 | 1282 | mutex_unlock(&extcon_dev_list_lock); |
---|
1354 | 1283 | |
---|
1355 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
---|
1356 | | - { |
---|
1357 | | - const char *name = extcon_get_link_name(edev); |
---|
1358 | | - |
---|
1359 | | - ret = sysfs_create_link_nowarn(&edev->dev.class->p->subsys.kobj, |
---|
1360 | | - &edev->dev.kobj, name); |
---|
1361 | | - if (ret) |
---|
1362 | | - dev_err(&edev->dev, |
---|
1363 | | - "failed to create extcon %s link\n", name); |
---|
1364 | | - } |
---|
1365 | | -#endif |
---|
1366 | | - |
---|
1367 | 1284 | return 0; |
---|
1368 | 1285 | |
---|
1369 | 1286 | err_dev: |
---|
| 1287 | + if (edev->max_supported) |
---|
| 1288 | + kfree(edev->nh); |
---|
| 1289 | +err_alloc_nh: |
---|
1370 | 1290 | if (edev->max_supported) |
---|
1371 | 1291 | kfree(edev->extcon_dev_type.groups); |
---|
1372 | 1292 | err_alloc_groups: |
---|
.. | .. |
---|
1411 | 1331 | return; |
---|
1412 | 1332 | } |
---|
1413 | 1333 | |
---|
1414 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
---|
1415 | | - sysfs_delete_link(&edev->dev.class->p->subsys.kobj, |
---|
1416 | | - &edev->dev.kobj, extcon_get_link_name(edev)); |
---|
1417 | | -#endif |
---|
1418 | | - |
---|
1419 | 1334 | device_unregister(&edev->dev); |
---|
1420 | 1335 | |
---|
1421 | 1336 | if (edev->mutually_exclusive && edev->max_supported) { |
---|
.. | .. |
---|
1432 | 1347 | if (edev->max_supported) { |
---|
1433 | 1348 | kfree(edev->extcon_dev_type.groups); |
---|
1434 | 1349 | kfree(edev->cables); |
---|
| 1350 | + kfree(edev->nh); |
---|
1435 | 1351 | } |
---|
1436 | 1352 | |
---|
1437 | 1353 | put_device(&edev->dev); |
---|