From 72826381215e2f9d2bd2f32f63f76a80942b7fdf Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 6 Feb 2025 16:52:22 +0530 Subject: ASoC: dt-bindings: wcd937x-sdw: Add static channel mapping support Add static channel mapping between master and slave rx/tx ports for Qualcomm wcd937x soundwire codec. Currently, the channel map index value for each soundwire port is hardcoded in the wcd937x-sdw driver, and the same channel map index value is configured in the soundwire master. The Qualcomm board like the QCM6490-IDP require static channel map settings for the soundwire master and slave ports. If another boards which are using enable wcd937x, the channel mapping index values between master and slave may be different depending on the board hw design and requirements. If the above properties are not used in a SoC specific device tree, the channel mapping index values are set to default. With the introduction of the following channel mapping properties, it is now possible to configure the master channel mapping directly from the device tree. The qcom,tx-channel-mapping property specifies the static channel mapping between the slave and master tx ports in the order of slave port channels which is adc1, adc2, adc3, adc4, dmic0, dmic1, mbhc, dmic2, dmic3, dmci4, dmic5, dmic6, dmic7. The qcom,rx-channel-mapping property specifies the static channel mapping between the slave and master rx ports in the order of slave port channels which is hph_l, hph_r, clsh, comp_l, comp_r, lo, dsd_r, dsd_l. Signed-off-by: Mohammad Rafi Shaik Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250206112225.3270400-2-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,wcd937x-sdw.yaml | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml index d3cf8f59cb23..c8543f969ebb 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml @@ -58,6 +58,40 @@ properties: items: enum: [1, 2, 3, 4, 5] + qcom,tx-channel-mapping: + description: | + Specifies static channel mapping between slave and master tx port + channels. + In the order of slave port channels which is adc1, adc2, adc3, + dmic0, dmic1, mbhc, dmic2, dmic3, dmci4, dmic5, dmic6, dmic7. + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 12 + maxItems: 12 + additionalItems: false + items: + enum: + - 1 # WCD9370_SWRM_CH1 + - 2 # WCD9370_SWRM_CH2 + - 3 # WCD9370_SWRM_CH3 + - 4 # WCD9370_SWRM_CH4 + + qcom,rx-channel-mapping: + description: | + Specifies static channels mapping between slave and master rx port + channels. + In the order of slave port channels, which is + hph_l, hph_r, clsh, comp_l, comp_r, lo, dsd_r, dsd_l. + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 8 + maxItems: 8 + additionalItems: false + items: + enum: + - 1 # WCD9370_SWRM_CH1 + - 2 # WCD9370_SWRM_CH2 + - 3 # WCD9370_SWRM_CH3 + - 4 # WCD9370_SWRM_CH4 + required: - compatible - reg @@ -74,6 +108,7 @@ examples: compatible = "sdw20217010a00"; reg = <0 4>; qcom,rx-port-mapping = <1 2 3 4 5>; + qcom,rx-channel-mapping = /bits/ 8 <1 2 1 1 2 1 1 2>; }; }; @@ -85,6 +120,7 @@ examples: compatible = "sdw20217010a00"; reg = <0 3>; qcom,tx-port-mapping = <2 2 3 4>; + qcom,tx-channel-mapping = /bits/ 8 <1 2 1 1 2 3 3 4 1 2 3 4>; }; }; -- cgit v1.2.3 From c06c4f7cbea1d8dc71485bfddef2849a1b721e67 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 6 Feb 2025 16:52:23 +0530 Subject: ASoC: codecs: wcd937x: Add static channel mapping support in wcd937x-sdw Add static channel mapping between master and slave ports in wcd937x-sdw driver. Currently, the channel mask for each soundwire port is hardcoded in the wcd937x-sdw driver, and the same channel mask value is configured in the soundwire master. The Qualcomm boards like the QCM6490-IDP require different channel mask settings for the soundwire master and slave ports. Implemented logic to read TX/RX channel mappings from device tree properties (qcom,tx-channel-mapping and qcom,rx-channel-mapping). Modified the wcd937x_connect_port to handle master channel masks during port enable/disable operations. Added wcd937x_get_channel_map api to retrieve the current master channel map for TX and RX paths. Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20250206112225.3270400-3-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x-sdw.c | 39 +++++++++++++++++++++++++++---- sound/soc/codecs/wcd937x.c | 53 ++++++++++++++++++++++++++++++++++++++++-- sound/soc/codecs/wcd937x.h | 7 +++++- 3 files changed, 92 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index 0c33f7f3dc25..1fbff313b965 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -19,7 +19,7 @@ #include #include "wcd937x.h" -static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { +static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), @@ -30,7 +30,7 @@ static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), }; -static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { +static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), @@ -1019,7 +1019,9 @@ static int wcd9370_probe(struct sdw_slave *pdev, { struct device *dev = &pdev->dev; struct wcd937x_sdw_priv *wcd; - int ret; + u8 master_ch_mask[WCD937X_MAX_SWR_CH_IDS]; + int master_ch_mask_size = 0; + int ret, i; wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); if (!wcd) @@ -1048,10 +1050,36 @@ static int wcd9370_probe(struct sdw_slave *pdev, SDW_SCP_INT1_PARITY; pdev->prop.lane_control_support = true; pdev->prop.simple_clk_stop_capable = true; + + memset(master_ch_mask, 0, WCD937X_MAX_SWR_CH_IDS); + if (wcd->is_tx) { - pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS - 1, 0); + master_ch_mask_size = of_property_count_u8_elems(dev->of_node, + "qcom,tx-channel-mapping"); + + if (master_ch_mask_size) + ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping", + master_ch_mask, master_ch_mask_size); + } else { + master_ch_mask_size = of_property_count_u8_elems(dev->of_node, + "qcom,rx-channel-mapping"); + + if (master_ch_mask_size) + ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping", + master_ch_mask, master_ch_mask_size); + } + + if (ret < 0) + dev_info(dev, "Static channel mapping not specified using device channel maps\n"); + + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS, 0); pdev->prop.src_dpn_prop = wcd937x_dpn_prop; wcd->ch_info = &wcd937x_sdw_tx_ch_info[0]; + + for (i = 0; i < master_ch_mask_size; i++) + wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]); + pdev->prop.wake_capable = true; wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_regmap_config); @@ -1065,6 +1093,9 @@ static int wcd9370_probe(struct sdw_slave *pdev, pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS - 1, 0); pdev->prop.sink_dpn_prop = wcd937x_dpn_prop; wcd->ch_info = &wcd937x_sdw_rx_ch_info[0]; + + for (i = 0; i < master_ch_mask_size; i++) + wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]); } diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index c9d5e67bf66e..e8d3fddbc7b1 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -1197,13 +1197,21 @@ static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; u8 port_num = ch_info->port_num; u8 ch_mask = ch_info->ch_mask; + u8 mstr_port_num, mstr_ch_mask; + struct sdw_slave *sdev = wcd->sdev; port_config->num = port_num; - if (enable) + mstr_port_num = sdev->m_port_map[port_num]; + mstr_ch_mask = ch_info->master_ch_mask; + + if (enable) { port_config->ch_mask |= ch_mask; - else + wcd->master_channel_map[mstr_port_num] |= mstr_ch_mask; + } else { port_config->ch_mask &= ~ch_mask; + wcd->master_channel_map[mstr_port_num] &= ~mstr_ch_mask; + } return 0; } @@ -2689,10 +2697,51 @@ static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai, return 0; } +static int wcd937x_get_channel_map(const struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + int i; + + switch (dai->id) { + case AIF1_PB: + if (!rx_slot || !rx_num) { + dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + for (i = 0; i < SDW_MAX_PORTS; i++) + rx_slot[i] = wcd->master_channel_map[i]; + + *rx_num = i; + break; + case AIF1_CAP: + if (!tx_slot || !tx_num) { + dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + + for (i = 0; i < SDW_MAX_PORTS; i++) + tx_slot[i] = wcd->master_channel_map[i]; + + *tx_num = i; + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = { .hw_params = wcd937x_codec_hw_params, .hw_free = wcd937x_codec_free, .set_stream = wcd937x_codec_set_sdw_stream, + .get_channel_map = wcd937x_get_channel_map, }; static struct snd_soc_dai_driver wcd937x_dais[] = { diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 4afa48dcaf74..4ef57c496c37 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -489,6 +489,7 @@ #define WCD937X_MAX_MICBIAS 3 #define WCD937X_MAX_BULK_SUPPLY 4 #define WCD937X_MAX_SWR_CH_IDS 15 +#define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1) enum wcd937x_tx_sdw_ports { WCD937X_ADC_1_PORT = 1, @@ -510,12 +511,14 @@ enum wcd937x_rx_sdw_ports { struct wcd937x_sdw_ch_info { int port_num; unsigned int ch_mask; + unsigned int master_ch_mask; }; #define WCD_SDW_CH(id, pn, cmask) \ [id] = { \ .port_num = pn, \ .ch_mask = cmask, \ + .master_ch_mask = cmask, \ } struct wcd937x_priv; @@ -524,9 +527,11 @@ struct wcd937x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; - const struct wcd937x_sdw_ch_info *ch_info; + struct wcd937x_sdw_ch_info *ch_info; bool port_enable[WCD937X_MAX_SWR_CH_IDS]; + unsigned int master_channel_map[SDW_MAX_PORTS]; int active_ports; + int num_ports; bool is_tx; struct wcd937x_priv *wcd937x; struct irq_domain *slave_irq; -- cgit v1.2.3 From 7796c97df6b1b2206681a07f3c80f6023a6593d5 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 6 Feb 2025 16:52:24 +0530 Subject: soundwire: qcom: Add set_channel_map api support Added qcom_swrm_set_channel_map api to set the master channel mask for TX and RX paths based on the provided slots. Added a new field ch_mask to the qcom_swrm_port_config structure. This field is used to store the master channel mask, which allows more flexible to configure channel mask in runtime for specific active soundwire ports. Modified the qcom_swrm_port_enable function to configure master channel mask. If the ch_mask is set to SWR_INVALID_PARAM or is zero, the function will use the default channel mask. Signed-off-by: Mohammad Rafi Shaik Acked-by: Vinod Koul Link: https://patch.msgid.link/20250206112225.3270400-4-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- drivers/soundwire/qcom.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 0f45e3404756..295a46dc2be7 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -156,6 +156,7 @@ struct qcom_swrm_port_config { u8 word_length; u8 blk_group_count; u8 lane_control; + u8 ch_mask; }; /* @@ -1048,9 +1049,13 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, { u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank); struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); + struct qcom_swrm_port_config *pcfg; u32 val; + pcfg = &ctrl->pconfig[enable_ch->port_num]; ctrl->reg_read(ctrl, reg, &val); + if (pcfg->ch_mask != SWR_INVALID_PARAM && pcfg->ch_mask != 0) + enable_ch->ch_mask = pcfg->ch_mask; if (enable_ch->enable) val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); @@ -1270,6 +1275,26 @@ static void *qcom_swrm_get_sdw_stream(struct snd_soc_dai *dai, int direction) return ctrl->sruntime[dai->id]; } +static int qcom_swrm_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + int i; + + if (tx_slot) { + for (i = 0; i < tx_num; i++) + ctrl->pconfig[i].ch_mask = tx_slot[i]; + } + + if (rx_slot) { + for (i = 0; i < rx_num; i++) + ctrl->pconfig[i].ch_mask = rx_slot[i]; + } + + return 0; +} + static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1306,6 +1331,7 @@ static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { .shutdown = qcom_swrm_shutdown, .set_stream = qcom_swrm_set_sdw_stream, .get_stream = qcom_swrm_get_sdw_stream, + .set_channel_map = qcom_swrm_set_channel_map, }; static const struct snd_soc_component_driver qcom_swrm_dai_component = { -- cgit v1.2.3 From 0e9a970d7b2cb98d741bc0e32ad8c8f30c009c63 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 6 Feb 2025 16:52:25 +0530 Subject: ASoC: qcom: sdw: Add get and set channel maps support from codec to cpu dais Add get and set channel maps support from codec to cpu dais. Implemented logic to get the channel map in case of only sdw stream and set channel map only for specific cpu dais. Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20250206112225.3270400-5-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/sdw.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c index f2eda2ff46c0..1d01b9329e08 100644 --- a/sound/soc/qcom/sdw.c +++ b/sound/soc/qcom/sdw.c @@ -23,9 +23,11 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS]; struct sdw_stream_runtime *sruntime; struct snd_soc_dai *codec_dai; - int ret, i; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret, i, j; sruntime = sdw_alloc_stream(cpu_dai->name); if (!sruntime) @@ -35,9 +37,35 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream) ret = snd_soc_dai_set_stream(codec_dai, sruntime, substream->stream); if (ret < 0 && ret != -ENOTSUPP) { - dev_err(rtd->dev, "Failed to set sdw stream on %s\n", - codec_dai->name); + dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name); goto err_set_stream; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + + ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch, + &rx_ch_cnt, rx_ch); + if (ret != 0 && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name); + goto err_set_stream; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + } + + switch (cpu_dai->id) { + case RX_CODEC_DMA_RX_0: + case TX_CODEC_DMA_TX_3: + if (tx_ch_cnt || rx_ch_cnt) { + for_each_rtd_codec_dais(rtd, j, codec_dai) { + ret = snd_soc_dai_set_channel_map(codec_dai, + tx_ch_cnt, tx_ch, + rx_ch_cnt, rx_ch); + if (ret != 0 && ret != -ENOTSUPP) + goto err_set_stream; + } } } -- cgit v1.2.3