| .. | .. |
|---|
| 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 | |
|---|
| .. | .. |
|---|
| 182 | 171 | .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, |
|---|
| 183 | 172 | .id = EXTCON_DISP_HMD, |
|---|
| 184 | 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", |
|---|
| 185 | 184 | }, |
|---|
| 186 | 185 | |
|---|
| 187 | 186 | /* Miscellaneous external connector */ |
|---|
| .. | .. |
|---|
| 495 | 494 | } |
|---|
| 496 | 495 | EXPORT_SYMBOL_GPL(extcon_sync); |
|---|
| 497 | 496 | |
|---|
| 498 | | -int extcon_blocking_sync(struct extcon_dev *edev, unsigned int id, bool val) |
|---|
| 499 | | -{ |
|---|
| 500 | | - int index; |
|---|
| 501 | | - |
|---|
| 502 | | - if (!edev) |
|---|
| 503 | | - return -EINVAL; |
|---|
| 504 | | - |
|---|
| 505 | | - index = find_cable_index_by_id(edev, id); |
|---|
| 506 | | - if (index < 0) |
|---|
| 507 | | - return index; |
|---|
| 508 | | - |
|---|
| 509 | | - return blocking_notifier_call_chain(&edev->bnh[index], val, edev); |
|---|
| 510 | | -} |
|---|
| 511 | | -EXPORT_SYMBOL(extcon_blocking_sync); |
|---|
| 512 | | - |
|---|
| 513 | 497 | /** |
|---|
| 514 | 498 | * extcon_get_state() - Get the state of an external connector. |
|---|
| 515 | 499 | * @edev: the extcon device |
|---|
| .. | .. |
|---|
| 651 | 635 | unsigned long flags; |
|---|
| 652 | 636 | int index, ret = 0; |
|---|
| 653 | 637 | |
|---|
| 654 | | - *prop_val = (union extcon_property_value)(0); |
|---|
| 638 | + *prop_val = (union extcon_property_value){0}; |
|---|
| 655 | 639 | |
|---|
| 656 | 640 | if (!edev) |
|---|
| 657 | 641 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 931 | 915 | struct notifier_block *nb) |
|---|
| 932 | 916 | { |
|---|
| 933 | 917 | unsigned long flags; |
|---|
| 934 | | - int ret, idx = -EINVAL; |
|---|
| 918 | + int ret, idx; |
|---|
| 935 | 919 | |
|---|
| 936 | 920 | if (!edev || !nb) |
|---|
| 937 | 921 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 947 | 931 | return ret; |
|---|
| 948 | 932 | } |
|---|
| 949 | 933 | EXPORT_SYMBOL_GPL(extcon_register_notifier); |
|---|
| 950 | | - |
|---|
| 951 | | -int extcon_register_blocking_notifier(struct extcon_dev *edev, unsigned int id, |
|---|
| 952 | | - struct notifier_block *nb) |
|---|
| 953 | | -{ |
|---|
| 954 | | - int idx = -EINVAL; |
|---|
| 955 | | - |
|---|
| 956 | | - if (!edev || !nb) |
|---|
| 957 | | - return -EINVAL; |
|---|
| 958 | | - |
|---|
| 959 | | - idx = find_cable_index_by_id(edev, id); |
|---|
| 960 | | - if (idx < 0) |
|---|
| 961 | | - return idx; |
|---|
| 962 | | - |
|---|
| 963 | | - return blocking_notifier_chain_register(&edev->bnh[idx], nb); |
|---|
| 964 | | -} |
|---|
| 965 | | -EXPORT_SYMBOL(extcon_register_blocking_notifier); |
|---|
| 966 | | - |
|---|
| 967 | | -int extcon_unregister_blocking_notifier(struct extcon_dev *edev, |
|---|
| 968 | | - unsigned int id, struct notifier_block *nb) |
|---|
| 969 | | -{ |
|---|
| 970 | | - int idx; |
|---|
| 971 | | - |
|---|
| 972 | | - if (!edev || !nb) |
|---|
| 973 | | - return -EINVAL; |
|---|
| 974 | | - |
|---|
| 975 | | - idx = find_cable_index_by_id(edev, id); |
|---|
| 976 | | - if (idx < 0) |
|---|
| 977 | | - return idx; |
|---|
| 978 | | - |
|---|
| 979 | | - return blocking_notifier_chain_unregister(&edev->bnh[idx], nb); |
|---|
| 980 | | -} |
|---|
| 981 | | -EXPORT_SYMBOL(extcon_unregister_blocking_notifier); |
|---|
| 982 | 934 | |
|---|
| 983 | 935 | /** |
|---|
| 984 | 936 | * extcon_unregister_notifier() - Unregister a notifier block from the extcon. |
|---|
| .. | .. |
|---|
| 1128 | 1080 | } |
|---|
| 1129 | 1081 | EXPORT_SYMBOL_GPL(extcon_dev_free); |
|---|
| 1130 | 1082 | |
|---|
| 1131 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
|---|
| 1132 | | -static const char *extcon_get_link_name(struct extcon_dev *edev) |
|---|
| 1133 | | -{ |
|---|
| 1134 | | - const char *dot = strchr(edev->name, '.'); |
|---|
| 1135 | | - const char *name = dot + 1; |
|---|
| 1136 | | - |
|---|
| 1137 | | - if (!dot || !name || !(*name)) |
|---|
| 1138 | | - name = edev->name; |
|---|
| 1139 | | - |
|---|
| 1140 | | - return name; |
|---|
| 1141 | | -} |
|---|
| 1142 | | -#endif |
|---|
| 1143 | | - |
|---|
| 1144 | 1083 | /** |
|---|
| 1145 | 1084 | * extcon_dev_register() - Register an new extcon device |
|---|
| 1146 | 1085 | * @edev: the extcon device to be registered |
|---|
| .. | .. |
|---|
| 1191 | 1130 | (unsigned long)atomic_inc_return(&edev_no)); |
|---|
| 1192 | 1131 | |
|---|
| 1193 | 1132 | if (edev->max_supported) { |
|---|
| 1194 | | - char buf[10]; |
|---|
| 1195 | 1133 | char *str; |
|---|
| 1196 | 1134 | struct extcon_cable *cable; |
|---|
| 1197 | 1135 | |
|---|
| .. | .. |
|---|
| 1205 | 1143 | for (index = 0; index < edev->max_supported; index++) { |
|---|
| 1206 | 1144 | cable = &edev->cables[index]; |
|---|
| 1207 | 1145 | |
|---|
| 1208 | | - snprintf(buf, 10, "cable.%d", index); |
|---|
| 1209 | | - str = kzalloc(strlen(buf) + 1, |
|---|
| 1210 | | - GFP_KERNEL); |
|---|
| 1146 | + str = kasprintf(GFP_KERNEL, "cable.%d", index); |
|---|
| 1211 | 1147 | if (!str) { |
|---|
| 1212 | 1148 | for (index--; index >= 0; index--) { |
|---|
| 1213 | 1149 | cable = &edev->cables[index]; |
|---|
| .. | .. |
|---|
| 1217 | 1153 | |
|---|
| 1218 | 1154 | goto err_alloc_cables; |
|---|
| 1219 | 1155 | } |
|---|
| 1220 | | - strcpy(str, buf); |
|---|
| 1221 | 1156 | |
|---|
| 1222 | 1157 | cable->edev = edev; |
|---|
| 1223 | 1158 | cable->cable_index = index; |
|---|
| .. | .. |
|---|
| 1240 | 1175 | } |
|---|
| 1241 | 1176 | |
|---|
| 1242 | 1177 | if (edev->max_supported && edev->mutually_exclusive) { |
|---|
| 1243 | | - char buf[80]; |
|---|
| 1244 | 1178 | char *name; |
|---|
| 1245 | 1179 | |
|---|
| 1246 | 1180 | /* Count the size of mutually_exclusive array */ |
|---|
| .. | .. |
|---|
| 1265 | 1199 | } |
|---|
| 1266 | 1200 | |
|---|
| 1267 | 1201 | for (index = 0; edev->mutually_exclusive[index]; index++) { |
|---|
| 1268 | | - sprintf(buf, "0x%x", edev->mutually_exclusive[index]); |
|---|
| 1269 | | - name = kzalloc(strlen(buf) + 1, |
|---|
| 1270 | | - GFP_KERNEL); |
|---|
| 1202 | + name = kasprintf(GFP_KERNEL, "0x%x", |
|---|
| 1203 | + edev->mutually_exclusive[index]); |
|---|
| 1271 | 1204 | if (!name) { |
|---|
| 1272 | 1205 | for (index--; index >= 0; index--) { |
|---|
| 1273 | 1206 | kfree(edev->d_attrs_muex[index].attr. |
|---|
| .. | .. |
|---|
| 1278 | 1211 | ret = -ENOMEM; |
|---|
| 1279 | 1212 | goto err_muex; |
|---|
| 1280 | 1213 | } |
|---|
| 1281 | | - strcpy(name, buf); |
|---|
| 1282 | 1214 | sysfs_attr_init(&edev->d_attrs_muex[index].attr); |
|---|
| 1283 | 1215 | edev->d_attrs_muex[index].attr.name = name; |
|---|
| 1284 | 1216 | edev->d_attrs_muex[index].attr.mode = 0000; |
|---|
| .. | .. |
|---|
| 1313 | 1245 | edev->dev.type = &edev->extcon_dev_type; |
|---|
| 1314 | 1246 | } |
|---|
| 1315 | 1247 | |
|---|
| 1316 | | - ret = device_register(&edev->dev); |
|---|
| 1317 | | - if (ret) { |
|---|
| 1318 | | - put_device(&edev->dev); |
|---|
| 1319 | | - goto err_dev; |
|---|
| 1320 | | - } |
|---|
| 1321 | | - |
|---|
| 1322 | 1248 | spin_lock_init(&edev->lock); |
|---|
| 1323 | | - edev->nh = devm_kcalloc(&edev->dev, edev->max_supported, |
|---|
| 1324 | | - sizeof(*edev->nh), GFP_KERNEL); |
|---|
| 1325 | | - if (!edev->nh) { |
|---|
| 1326 | | - ret = -ENOMEM; |
|---|
| 1327 | | - device_unregister(&edev->dev); |
|---|
| 1328 | | - goto err_dev; |
|---|
| 1329 | | - } |
|---|
| 1330 | | - |
|---|
| 1331 | | - edev->bnh = devm_kzalloc(&edev->dev, |
|---|
| 1332 | | - sizeof(*edev->bnh) * edev->max_supported, GFP_KERNEL); |
|---|
| 1333 | | - if (!edev->bnh) { |
|---|
| 1334 | | - ret = -ENOMEM; |
|---|
| 1335 | | - goto err_dev; |
|---|
| 1249 | + if (edev->max_supported) { |
|---|
| 1250 | + edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh), |
|---|
| 1251 | + GFP_KERNEL); |
|---|
| 1252 | + if (!edev->nh) { |
|---|
| 1253 | + ret = -ENOMEM; |
|---|
| 1254 | + goto err_alloc_nh; |
|---|
| 1255 | + } |
|---|
| 1336 | 1256 | } |
|---|
| 1337 | 1257 | |
|---|
| 1338 | 1258 | for (index = 0; index < edev->max_supported; index++) |
|---|
| .. | .. |
|---|
| 1343 | 1263 | dev_set_drvdata(&edev->dev, edev); |
|---|
| 1344 | 1264 | edev->state = 0; |
|---|
| 1345 | 1265 | |
|---|
| 1266 | + ret = device_register(&edev->dev); |
|---|
| 1267 | + if (ret) { |
|---|
| 1268 | + put_device(&edev->dev); |
|---|
| 1269 | + goto err_dev; |
|---|
| 1270 | + } |
|---|
| 1271 | + |
|---|
| 1346 | 1272 | mutex_lock(&extcon_dev_list_lock); |
|---|
| 1347 | 1273 | list_add(&edev->entry, &extcon_dev_list); |
|---|
| 1348 | 1274 | mutex_unlock(&extcon_dev_list_lock); |
|---|
| 1349 | 1275 | |
|---|
| 1350 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
|---|
| 1351 | | - { |
|---|
| 1352 | | - const char *name = extcon_get_link_name(edev); |
|---|
| 1353 | | - |
|---|
| 1354 | | - ret = sysfs_create_link_nowarn(&edev->dev.class->p->subsys.kobj, |
|---|
| 1355 | | - &edev->dev.kobj, name); |
|---|
| 1356 | | - if (ret) |
|---|
| 1357 | | - dev_err(&edev->dev, |
|---|
| 1358 | | - "failed to create extcon %s link\n", name); |
|---|
| 1359 | | - } |
|---|
| 1360 | | -#endif |
|---|
| 1361 | | - |
|---|
| 1362 | 1276 | return 0; |
|---|
| 1363 | 1277 | |
|---|
| 1364 | 1278 | err_dev: |
|---|
| 1279 | + if (edev->max_supported) |
|---|
| 1280 | + kfree(edev->nh); |
|---|
| 1281 | +err_alloc_nh: |
|---|
| 1365 | 1282 | if (edev->max_supported) |
|---|
| 1366 | 1283 | kfree(edev->extcon_dev_type.groups); |
|---|
| 1367 | 1284 | err_alloc_groups: |
|---|
| .. | .. |
|---|
| 1406 | 1323 | return; |
|---|
| 1407 | 1324 | } |
|---|
| 1408 | 1325 | |
|---|
| 1409 | | -#ifdef CONFIG_ARCH_ROCKCHIP |
|---|
| 1410 | | - sysfs_delete_link(&edev->dev.class->p->subsys.kobj, |
|---|
| 1411 | | - &edev->dev.kobj, extcon_get_link_name(edev)); |
|---|
| 1412 | | -#endif |
|---|
| 1413 | | - |
|---|
| 1414 | 1326 | device_unregister(&edev->dev); |
|---|
| 1415 | 1327 | |
|---|
| 1416 | 1328 | if (edev->mutually_exclusive && edev->max_supported) { |
|---|
| .. | .. |
|---|
| 1427 | 1339 | if (edev->max_supported) { |
|---|
| 1428 | 1340 | kfree(edev->extcon_dev_type.groups); |
|---|
| 1429 | 1341 | kfree(edev->cables); |
|---|
| 1342 | + kfree(edev->nh); |
|---|
| 1430 | 1343 | } |
|---|
| 1431 | 1344 | |
|---|
| 1432 | 1345 | put_device(&edev->dev); |
|---|