Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SDCA: Add hw_params() helper function

Add a helper function that can be called from hw_params() in the DAI ops
to configure the SDCA Cluster, Clock and Usage controls. These setup the
channels, sample rate, and bit depths that will be used by the Terminal.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://patch.msgid.link/20250707124155.2596744-8-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Charles Keepax and committed by
Mark Brown
4ed357f7 264d3d77

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