|
22 | 22 | #include <linux/types.h>
|
23 | 23 | #include <sound/control.h>
|
24 | 24 | #include <sound/pcm.h>
|
| 25 | +#include <sound/pcm_params.h> |
25 | 26 | #include <sound/sdca.h>
|
26 | 27 | #include <sound/sdca_asoc.h>
|
27 | 28 | #include <sound/sdca_function.h>
|
@@ -1443,3 +1444,182 @@ int sdca_asoc_get_port(struct device *dev, struct regmap *regmap,
|
1443 | 1444 | return -ENODEV;
|
1444 | 1445 | }
|
1445 | 1446 | EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA");
|
| 1447 | + |
| 1448 | +static int set_cluster(struct device *dev, struct regmap *regmap, |
| 1449 | + struct sdca_function_data *function, |
| 1450 | + struct sdca_entity *entity, unsigned int channels) |
| 1451 | +{ |
| 1452 | + int sel = SDCA_CTL_IT_CLUSTERINDEX; |
| 1453 | + struct sdca_control_range *range; |
| 1454 | + int i, ret; |
| 1455 | + |
| 1456 | + range = sdca_selector_find_range(dev, entity, sel, SDCA_CLUSTER_NCOLS, 0); |
| 1457 | + if (!range) |
| 1458 | + return -EINVAL; |
| 1459 | + |
| 1460 | + for (i = 0; i < range->rows; i++) { |
| 1461 | + int cluster_id = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); |
| 1462 | + struct sdca_cluster *cluster; |
| 1463 | + |
| 1464 | + cluster = sdca_id_find_cluster(dev, function, cluster_id); |
| 1465 | + if (!cluster) |
| 1466 | + return -ENODEV; |
| 1467 | + |
| 1468 | + if (cluster->num_channels == channels) { |
| 1469 | + int index = sdca_range(range, SDCA_CLUSTER_BYTEINDEX, i); |
| 1470 | + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, |
| 1471 | + entity->id, sel, 0); |
| 1472 | + |
| 1473 | + ret = regmap_update_bits(regmap, reg, 0xFF, index); |
| 1474 | + if (ret) { |
| 1475 | + dev_err(dev, "%s: failed to write cluster index: %d\n", |
| 1476 | + entity->label, ret); |
| 1477 | + return ret; |
| 1478 | + } |
| 1479 | + |
| 1480 | + dev_dbg(dev, "%s: set cluster to %d (%d channels)\n", |
| 1481 | + entity->label, index, channels); |
| 1482 | + |
| 1483 | + return 0; |
| 1484 | + } |
| 1485 | + } |
| 1486 | + |
| 1487 | + dev_err(dev, "%s: no cluster for %d channels\n", entity->label, channels); |
| 1488 | + return -EINVAL; |
| 1489 | +} |
| 1490 | + |
| 1491 | +static int set_clock(struct device *dev, struct regmap *regmap, |
| 1492 | + struct sdca_function_data *function, |
| 1493 | + struct sdca_entity *entity, int target_rate) |
| 1494 | +{ |
| 1495 | + int sel = SDCA_CTL_CS_SAMPLERATEINDEX; |
| 1496 | + struct sdca_control_range *range; |
| 1497 | + int i, ret; |
| 1498 | + |
| 1499 | + range = sdca_selector_find_range(dev, entity, sel, SDCA_SAMPLERATEINDEX_NCOLS, 0); |
| 1500 | + if (!range) |
| 1501 | + return -EINVAL; |
| 1502 | + |
| 1503 | + for (i = 0; i < range->rows; i++) { |
| 1504 | + unsigned int rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i); |
| 1505 | + |
| 1506 | + if (rate == target_rate) { |
| 1507 | + unsigned int index = sdca_range(range, |
| 1508 | + SDCA_SAMPLERATEINDEX_INDEX, |
| 1509 | + i); |
| 1510 | + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, |
| 1511 | + entity->id, sel, 0); |
| 1512 | + |
| 1513 | + ret = regmap_update_bits(regmap, reg, 0xFF, index); |
| 1514 | + if (ret) { |
| 1515 | + dev_err(dev, "%s: failed to write clock rate: %d\n", |
| 1516 | + entity->label, ret); |
| 1517 | + return ret; |
| 1518 | + } |
| 1519 | + |
| 1520 | + dev_dbg(dev, "%s: set clock rate to %d (%dHz)\n", |
| 1521 | + entity->label, index, rate); |
| 1522 | + |
| 1523 | + return 0; |
| 1524 | + } |
| 1525 | + } |
| 1526 | + |
| 1527 | + dev_err(dev, "%s: no clock rate for %dHz\n", entity->label, target_rate); |
| 1528 | + return -EINVAL; |
| 1529 | +} |
| 1530 | + |
| 1531 | +static int set_usage(struct device *dev, struct regmap *regmap, |
| 1532 | + struct sdca_function_data *function, |
| 1533 | + struct sdca_entity *entity, int sel, |
| 1534 | + int target_rate, int target_width) |
| 1535 | +{ |
| 1536 | + struct sdca_control_range *range; |
| 1537 | + int i, ret; |
| 1538 | + |
| 1539 | + range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); |
| 1540 | + if (!range) |
| 1541 | + return -EINVAL; |
| 1542 | + |
| 1543 | + for (i = 0; i < range->rows; i++) { |
| 1544 | + unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i); |
| 1545 | + unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i); |
| 1546 | + |
| 1547 | + if ((!rate || rate == target_rate) && width == target_width) { |
| 1548 | + unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i); |
| 1549 | + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, |
| 1550 | + entity->id, sel, 0); |
| 1551 | + |
| 1552 | + ret = regmap_update_bits(regmap, reg, 0xFF, usage); |
| 1553 | + if (ret) { |
| 1554 | + dev_err(dev, "%s: failed to write usage: %d\n", |
| 1555 | + entity->label, ret); |
| 1556 | + return ret; |
| 1557 | + } |
| 1558 | + |
| 1559 | + dev_dbg(dev, "%s: set usage to %#x (%dHz, %d bits)\n", |
| 1560 | + entity->label, usage, target_rate, target_width); |
| 1561 | + |
| 1562 | + return 0; |
| 1563 | + } |
| 1564 | + } |
| 1565 | + |
| 1566 | + dev_err(dev, "%s: no usage for %dHz, %dbits\n", |
| 1567 | + entity->label, target_rate, target_width); |
| 1568 | + return -EINVAL; |
| 1569 | +} |
| 1570 | + |
| 1571 | +/** |
| 1572 | + * sdca_asoc_hw_params - set SDCA channels, sample rate and bit depth |
| 1573 | + * @dev: Pointer to the device, used for error messages. |
| 1574 | + * @regmap: Pointer to the Function register map. |
| 1575 | + * @function: Pointer to the Function information. |
| 1576 | + * @substream: Pointer to the PCM substream. |
| 1577 | + * @params: Pointer to the hardware parameters. |
| 1578 | + * @dai: Pointer to the ASoC DAI. |
| 1579 | + * |
| 1580 | + * Typically called from hw_params(). |
| 1581 | + * |
| 1582 | + * Return: Returns zero on success, and a negative error code on failure. |
| 1583 | + */ |
| 1584 | +int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, |
| 1585 | + struct sdca_function_data *function, |
| 1586 | + struct snd_pcm_substream *substream, |
| 1587 | + struct snd_pcm_hw_params *params, |
| 1588 | + struct snd_soc_dai *dai) |
| 1589 | +{ |
| 1590 | + struct sdca_entity *entity = &function->entities[dai->id]; |
| 1591 | + int channels = params_channels(params); |
| 1592 | + int width = params_width(params); |
| 1593 | + int rate = params_rate(params); |
| 1594 | + int usage_sel; |
| 1595 | + int ret; |
| 1596 | + |
| 1597 | + switch (entity->type) { |
| 1598 | + case SDCA_ENTITY_TYPE_IT: |
| 1599 | + ret = set_cluster(dev, regmap, function, entity, channels); |
| 1600 | + if (ret) |
| 1601 | + return ret; |
| 1602 | + |
| 1603 | + usage_sel = SDCA_CTL_IT_USAGE; |
| 1604 | + break; |
| 1605 | + case SDCA_ENTITY_TYPE_OT: |
| 1606 | + usage_sel = SDCA_CTL_OT_USAGE; |
| 1607 | + break; |
| 1608 | + default: |
| 1609 | + dev_err(dev, "%s: hw_params on non-terminal entity\n", entity->label); |
| 1610 | + return -EINVAL; |
| 1611 | + } |
| 1612 | + |
| 1613 | + if (entity->iot.clock) { |
| 1614 | + ret = set_clock(dev, regmap, function, entity->iot.clock, rate); |
| 1615 | + if (ret) |
| 1616 | + return ret; |
| 1617 | + } |
| 1618 | + |
| 1619 | + ret = set_usage(dev, regmap, function, entity, usage_sel, rate, width); |
| 1620 | + if (ret) |
| 1621 | + return ret; |
| 1622 | + |
| 1623 | + return 0; |
| 1624 | +} |
| 1625 | +EXPORT_SYMBOL_NS(sdca_asoc_hw_params, "SND_SOC_SDCA"); |
0 commit comments