|
19 | 19 | #include <linux/regmap.h>
|
20 | 20 | #include <linux/soundwire/sdw_registers.h>
|
21 | 21 | #include <linux/string_helpers.h>
|
| 22 | +#include <linux/types.h> |
22 | 23 | #include <sound/control.h>
|
23 | 24 | #include <sound/pcm.h>
|
24 | 25 | #include <sound/sdca.h>
|
@@ -1368,3 +1369,77 @@ void sdca_asoc_free_constraints(struct snd_pcm_substream *substream,
|
1368 | 1369 | kfree(constraint);
|
1369 | 1370 | }
|
1370 | 1371 | EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA");
|
| 1372 | + |
| 1373 | +/** |
| 1374 | + * sdca_asoc_get_port - return SoundWire port for a DAI |
| 1375 | + * @dev: Pointer to the device, used for error messages. |
| 1376 | + * @regmap: Pointer to the Function register map. |
| 1377 | + * @function: Pointer to the Function information. |
| 1378 | + * @dai: Pointer to the ASoC DAI. |
| 1379 | + * |
| 1380 | + * Typically called from hw_params(). |
| 1381 | + * |
| 1382 | + * Return: Returns a positive port number on success, and a negative error |
| 1383 | + * code on failure. |
| 1384 | + */ |
| 1385 | +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, |
| 1386 | + struct sdca_function_data *function, |
| 1387 | + struct snd_soc_dai *dai) |
| 1388 | +{ |
| 1389 | + struct sdca_entity *entity = &function->entities[dai->id]; |
| 1390 | + struct sdca_control_range *range; |
| 1391 | + unsigned int reg, val; |
| 1392 | + int sel = -EINVAL; |
| 1393 | + int i, ret; |
| 1394 | + |
| 1395 | + switch (entity->type) { |
| 1396 | + case SDCA_ENTITY_TYPE_IT: |
| 1397 | + sel = SDCA_CTL_IT_DATAPORT_SELECTOR; |
| 1398 | + break; |
| 1399 | + case SDCA_ENTITY_TYPE_OT: |
| 1400 | + sel = SDCA_CTL_OT_DATAPORT_SELECTOR; |
| 1401 | + break; |
| 1402 | + default: |
| 1403 | + break; |
| 1404 | + } |
| 1405 | + |
| 1406 | + if (sel < 0 || !entity->iot.is_dataport) { |
| 1407 | + dev_err(dev, "%s: port number only available for dataports\n", |
| 1408 | + entity->label); |
| 1409 | + return -EINVAL; |
| 1410 | + } |
| 1411 | + |
| 1412 | + range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS, |
| 1413 | + SDCA_DATAPORT_SELECTOR_NROWS); |
| 1414 | + if (!range) |
| 1415 | + return -EINVAL; |
| 1416 | + |
| 1417 | + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0); |
| 1418 | + |
| 1419 | + ret = regmap_read(regmap, reg, &val); |
| 1420 | + if (ret) { |
| 1421 | + dev_err(dev, "%s: failed to read dataport selector: %d\n", |
| 1422 | + entity->label, ret); |
| 1423 | + return ret; |
| 1424 | + } |
| 1425 | + |
| 1426 | + for (i = 0; i < range->rows; i++) { |
| 1427 | + static const u8 port_mask = 0xF; |
| 1428 | + |
| 1429 | + sel = sdca_range(range, val & port_mask, i); |
| 1430 | + |
| 1431 | + /* |
| 1432 | + * FIXME: Currently only a single dataport is supported, so |
| 1433 | + * return the first one found, technically up to 4 dataports |
| 1434 | + * could be linked, but this is not yet supported. |
| 1435 | + */ |
| 1436 | + if (sel != 0xFF) |
| 1437 | + return sel; |
| 1438 | + |
| 1439 | + val >>= hweight8(port_mask); |
| 1440 | + } |
| 1441 | + |
| 1442 | + dev_err(dev, "%s: no dataport found\n", entity->label); |
| 1443 | + return -ENODEV; |
| 1444 | +} |
| 1445 | +EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA"); |
0 commit comments