|
5 | 5 | // Copyright (C) 2023 Cirrus Logic, Inc. and
|
6 | 6 | // Cirrus Logic International Semiconductor Ltd.
|
7 | 7 |
|
| 8 | +#include <linux/acpi.h> |
8 | 9 | #include <linux/completion.h>
|
9 | 10 | #include <linux/debugfs.h>
|
10 | 11 | #include <linux/delay.h>
|
|
15 | 16 | #include <linux/module.h>
|
16 | 17 | #include <linux/pm.h>
|
17 | 18 | #include <linux/pm_runtime.h>
|
| 19 | +#include <linux/property.h> |
18 | 20 | #include <linux/regmap.h>
|
19 | 21 | #include <linux/regulator/consumer.h>
|
20 | 22 | #include <linux/slab.h>
|
@@ -1260,6 +1262,94 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
|
1260 | 1262 | return 0;
|
1261 | 1263 | }
|
1262 | 1264 |
|
| 1265 | +/* |
| 1266 | + * Some SoundWire laptops have a spk-id-gpios property but it points to |
| 1267 | + * the wrong ACPI Device node so can't be used to get the GPIO. Try to |
| 1268 | + * find the SDCA node containing the GpioIo resource and add a GPIO |
| 1269 | + * mapping to it. |
| 1270 | + */ |
| 1271 | +static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false }; |
| 1272 | +static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = { |
| 1273 | + { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 }, |
| 1274 | + { } |
| 1275 | +}; |
| 1276 | + |
| 1277 | +static void cs35l56_acpi_dev_release_driver_gpios(void *adev) |
| 1278 | +{ |
| 1279 | + acpi_dev_remove_driver_gpios(adev); |
| 1280 | +} |
| 1281 | + |
| 1282 | +static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56) |
| 1283 | +{ |
| 1284 | + struct fwnode_handle *af01_fwnode; |
| 1285 | + const union acpi_object *obj; |
| 1286 | + struct gpio_desc *desc; |
| 1287 | + int ret; |
| 1288 | + |
| 1289 | + /* Find the SDCA node containing the GpioIo */ |
| 1290 | + af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01"); |
| 1291 | + if (!af01_fwnode) { |
| 1292 | + dev_dbg(cs35l56->base.dev, "No AF01 node\n"); |
| 1293 | + return -ENOENT; |
| 1294 | + } |
| 1295 | + |
| 1296 | + ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev), |
| 1297 | + "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj); |
| 1298 | + if (ret) { |
| 1299 | + dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret); |
| 1300 | + return -ENOENT; |
| 1301 | + } |
| 1302 | + |
| 1303 | + /* The broken properties we can handle are a 4-element package (one GPIO) */ |
| 1304 | + if (obj->package.count != 4) { |
| 1305 | + dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n", |
| 1306 | + obj->package.count); |
| 1307 | + return -ENOENT; |
| 1308 | + } |
| 1309 | + |
| 1310 | + /* Add a GPIO mapping if it doesn't already have one */ |
| 1311 | + if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) { |
| 1312 | + struct acpi_device *adev = to_acpi_device_node(af01_fwnode); |
| 1313 | + |
| 1314 | + /* |
| 1315 | + * Can't use devm_acpi_dev_add_driver_gpios() because the |
| 1316 | + * mapping isn't being added to the node pointed to by |
| 1317 | + * ACPI_COMPANION(). |
| 1318 | + */ |
| 1319 | + ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping); |
| 1320 | + if (ret) { |
| 1321 | + return dev_err_probe(cs35l56->base.dev, ret, |
| 1322 | + "Failed to add gpio mapping to AF01\n"); |
| 1323 | + } |
| 1324 | + |
| 1325 | + ret = devm_add_action_or_reset(cs35l56->base.dev, |
| 1326 | + cs35l56_acpi_dev_release_driver_gpios, |
| 1327 | + adev); |
| 1328 | + if (ret) |
| 1329 | + return ret; |
| 1330 | + |
| 1331 | + dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n"); |
| 1332 | + } |
| 1333 | + |
| 1334 | + desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL); |
| 1335 | + if (IS_ERR(desc)) { |
| 1336 | + ret = PTR_ERR(desc); |
| 1337 | + return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n"); |
| 1338 | + } |
| 1339 | + |
| 1340 | + ret = gpiod_get_value_cansleep(desc); |
| 1341 | + gpiod_put(desc); |
| 1342 | + |
| 1343 | + if (ret < 0) { |
| 1344 | + dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n"); |
| 1345 | + return ret; |
| 1346 | + } |
| 1347 | + |
| 1348 | + dev_info(cs35l56->base.dev, "Got spk-id from AF01\n"); |
| 1349 | + |
| 1350 | + return ret; |
| 1351 | +} |
| 1352 | + |
1263 | 1353 | int cs35l56_common_probe(struct cs35l56_private *cs35l56)
|
1264 | 1354 | {
|
1265 | 1355 | int ret;
|
@@ -1304,6 +1394,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
|
1304 | 1394 | }
|
1305 | 1395 |
|
1306 | 1396 | ret = cs35l56_get_speaker_id(&cs35l56->base);
|
| 1397 | + if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT)) |
| 1398 | + ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56); |
| 1399 | + |
1307 | 1400 | if ((ret < 0) && (ret != -ENOENT))
|
1308 | 1401 | goto err;
|
1309 | 1402 |
|
|
0 commit comments