diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index a3316efdf8ac27..106a38f936a5ee 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -769,7 +769,7 @@ static int amd_sdw_hw_params(struct snd_pcm_substream *substream, { struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai); struct sdw_amd_dai_runtime *dai_runtime; - struct sdw_stream_config sconfig; + struct sdw_stream_config sconfig = {0}; int ch, dir; int ret; diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 29d9fcef8c709b..5c2d6d4a23d766 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -70,7 +70,11 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, port_bo += bps * ch; slave_total_ch += ch; + } + /* Make sure all Peripherals use the same bit slots */ + if (m_rt->stream->transport_type == SDW_TRANSPORT_PERIPHERAL_PERIPHERAL) + port_bo = t_data->block_offset; if (m_rt->direction == SDW_DATA_DIR_TX && m_rt->ch_count == slave_total_ch) { diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index af65214836b4d0..8d769e5864ed09 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -725,7 +725,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dai_runtime *dai_runtime; struct sdw_cdns_pdi *pdi; - struct sdw_stream_config sconfig; + struct sdw_stream_config sconfig = {0}; int ch, dir; int ret; diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 642b33ab552606..f7a256adc85331 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -53,7 +53,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; struct sdw_stream_runtime *stream; - struct sdw_stream_config sconfig; + struct sdw_stream_config sconfig = {0}; struct sdw_port_config *pconfig; unsigned int pdi0_buf_size_pre_frame; unsigned int pdi1_buf_size_pre_frame; @@ -724,7 +724,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dai_runtime *dai_runtime; struct sdw_cdns_pdi *pdi; - struct sdw_stream_config sconfig; + struct sdw_stream_config sconfig = {0}; int ch, dir; int ret; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 6e0c5fb10efad4..a3bffe8a2869c4 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1301,7 +1301,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, struct snd_pcm_hw_params *params, int direction) { - struct sdw_stream_config sconfig; + struct sdw_stream_config sconfig = {0}; struct sdw_master_runtime *m_rt; struct sdw_slave_runtime *s_rt; struct sdw_port_runtime *p_rt; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index c158bf91e020f9..9f6c06df9859a8 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1391,6 +1391,7 @@ static int sdw_config_stream(struct device *dev, } stream->type = stream_config->type; + stream->transport_type = stream_config->transport_type; stream->params.rate = stream_config->frame_rate; stream->params.bps = stream_config->bps; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 326db122bbe6ea..1dea2e4da7fdbd 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -760,6 +760,17 @@ struct sdw_port_params { unsigned int data_mode; }; +/** + * enum sdw_transport_type: Data Port transport type + * + * @SDW_TRANSPORT_MANAGER_PERIPHERAL: Data is exchanged between the Manager and a Peripheral + * @SDW_TRANSPORT_PERIPHERAL_PERIPHERAL: Data is exchanged between Peripherals + */ +enum sdw_transport_type { + SDW_TRANSPORT_MANAGER_PERIPHERAL = 0, + SDW_TRANSPORT_PERIPHERAL_PERIPHERAL = 1, +}; + /** * struct sdw_transport_params: Data Port Transport Parameters * @@ -924,6 +935,7 @@ struct sdw_port_config { * @bps: Number of bits per audio sample * @direction: Data direction * @type: Stream type PCM, PDM or BPT + * @transport_type: Manager-Peripheral (default) or Peripheral-Peripheral transport */ struct sdw_stream_config { unsigned int frame_rate; @@ -931,6 +943,7 @@ struct sdw_stream_config { unsigned int bps; enum sdw_data_direction direction; enum sdw_stream_type type; + enum sdw_transport_type transport_type; }; /** @@ -974,6 +987,7 @@ struct sdw_stream_params { * @params: Stream parameters * @state: Current state of the stream * @type: Stream type PCM, PDM or BPT + * @transport_type: Manager-Peripheral or Peripheral-Peripheral transport * @m_rt_count: Count of Master runtime(s) in this stream * @master_list: List of Master runtime(s) in this stream. * master_list can contain only one m_rt per Master instance @@ -984,6 +998,7 @@ struct sdw_stream_runtime { struct sdw_stream_params params; enum sdw_stream_state state; enum sdw_stream_type type; + enum sdw_transport_type transport_type; int m_rt_count; struct list_head master_list; }; diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 418466438ea1cb..aa2a9225492c93 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -30,6 +30,13 @@ */ #define SOC_SDW_SIDECAR_AMPS BIT(16) #define SOC_SDW_CODEC_MIC BIT(17) +/* + * Unlike the sidecar amps, which are not on the SoundWire bus the companion amps are SoundWire + * peripherals. They get their audio data from another SoundWire peripheral (the main amp) over + * the SoundWire bus instead of from the host, so a codec-to-codec dai link is created between + * the main amp and the companion amps. + */ +#define SOC_SDW_COMPANION_AMPS BIT(18) #define SOC_SDW_UNUSED_DAI_ID -1 #define SOC_SDW_JACK_OUT_DAI_ID 0 @@ -128,6 +135,11 @@ struct asoc_sdw_dailink { struct list_head endpoints; }; +struct asoc_sdw_companion_amp_endpoint { + struct asoc_sdw_endpoint *main_amp; + struct list_head comp_ends; +}; + extern struct asoc_sdw_codec_info codec_info_list[]; int asoc_sdw_get_codec_info_list_count(void); @@ -140,6 +152,17 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, int asoc_sdw_hw_free(struct snd_pcm_substream *substream); void asoc_sdw_shutdown(struct snd_pcm_substream *substream); +int asoc_sdw_c_amp_probe(struct snd_soc_dai *dai); +void *asoc_sdw_c_amp_get_sdw_stream(struct snd_soc_dai *dai, int direction); +int asoc_sdw_c_amp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); +int asoc_sdw_c_amp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); +int asoc_sdw_c_amp_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int asoc_sdw_c_amp_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); +void asoc_sdw_c_amp_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + const char *asoc_sdw_get_codec_name(struct device *dev, const struct asoc_sdw_dai_info *dai_info, const struct snd_soc_acpi_link_adr *adr_link, @@ -176,10 +199,13 @@ int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *d const struct snd_soc_ops *ops); int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, - int *num_devs, int *num_ends, int *num_aux); + int *num_devs, int *num_ends, int *num_aux, int *num_comp_amps); struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks, const struct snd_soc_acpi_endpoint *new); +int asoc_sdw_find_amp_endpoints(struct asoc_sdw_dailink *dailink, + struct asoc_sdw_endpoint **amp_ends, + int max_ends); int asoc_sdw_get_dai_type(u32 type); int asoc_sdw_parse_sdw_endpoints(struct device *dev, @@ -187,6 +213,7 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, + struct asoc_sdw_companion_amp_endpoint *comp_ends, int *num_devs); int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd); diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index a8b62817b2344e..0a6d1d91a5c4fa 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -415,9 +415,11 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) struct device *dev = card->dev; int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct asoc_sdw_companion_amp_endpoint *sof_comp_ends; struct snd_soc_aux_dev *soc_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; + int num_comp_amps = 0; int num_devs = 0; int num_ends = 0; int num_aux = 0; @@ -425,8 +427,9 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) int num_links; int be_id = 0; int ret; + int i; - ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux); + ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux, &num_comp_amps); if (ret < 0) { dev_err(dev, "failed to count devices/endpoints: %d\n", ret); return ret; @@ -442,7 +445,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) /* One per endpoint, ie. each DAI on each codec/amp */ struct asoc_sdw_endpoint *soc_ends __free(kfree) = - kzalloc_objs(*soc_ends, num_ends); + kzalloc_objs(*soc_ends, num_ends + num_comp_amps); if (!soc_ends) return -ENOMEM; @@ -450,7 +453,15 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); + sof_comp_ends = devm_kcalloc(dev, num_comp_amps, sizeof(*sof_comp_ends), GFP_KERNEL); + if (!sof_comp_ends) + return -ENOMEM; + + for (i = 0; i < num_comp_amps; i++) + INIT_LIST_HEAD(&sof_comp_ends[i].comp_ends); + + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, sof_comp_ends, + &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index ec3e1f5f1052c5..73a6b71ac95397 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -280,9 +280,11 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; + struct asoc_sdw_companion_amp_endpoint *sof_comp_ends; struct snd_soc_aux_dev *sof_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; + int num_comp_amps = 0; int num_devs = 0; int num_ends = 0; int num_aux = 0; @@ -290,8 +292,9 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int num_links; int be_id = 0; int ret; + int i; - ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux); + ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux, &num_comp_amps); if (ret < 0) { dev_err(dev, "failed to count devices/endpoints: %d\n", ret); return ret; @@ -306,7 +309,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) /* One per endpoint, ie. each DAI on each codec/amp */ struct asoc_sdw_endpoint *sof_ends __free(kfree) = - kzalloc_objs(*sof_ends, num_ends); + kzalloc_objs(*sof_ends, num_ends + num_comp_amps); if (!sof_ends) return -ENOMEM; @@ -314,7 +317,15 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); + sof_comp_ends = devm_kcalloc(dev, num_comp_amps, sizeof(*sof_comp_ends), GFP_KERNEL); + if (!sof_comp_ends) + return -ENOMEM; + + for (i = 0; i < num_comp_amps; i++) + INIT_LIST_HEAD(&sof_comp_ends[i].comp_ends); + + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, sof_comp_ends, + &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index fa89db09665149..e286e0f94b9022 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -251,6 +252,7 @@ static const struct reg_sequence rt1320_vc_blind_write[] = { { 0xc06b, 0x0a }, { 0xd172, 0x2a }, { 0xc5d6, 0x01 }, + { 0xc5d0, 0x8f }, // DP6 Data Select: SRCIN OUT // for companion output setting { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, }; @@ -1090,7 +1092,7 @@ static int rt1320_read_prop(struct sdw_slave *slave) prop->lane_control_support = true; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = BIT(4) | BIT(8) | BIT(10); + prop->source_ports = BIT(4) | BIT(6) | BIT(8) | BIT(10); prop->sink_ports = BIT(1); nval = hweight32(prop->source_ports); @@ -3183,6 +3185,9 @@ static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = { /* Audio Interface */ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Comp-Out", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP6RX", "DP6 Comp-Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP8-10TX", "DP8-10 Capture", 0, SND_SOC_NOPM, 0, 0), /* Digital Interface */ @@ -3221,6 +3226,9 @@ static const struct snd_soc_dapm_route rt1320_dapm_routes[] = { { "AEC Data", NULL, "AEC Gen" }, { "DP4TX", NULL, "AEC Data" }, + { "DP6TX", NULL, "DP1RX" }, + { "DP6RX", NULL, "DP1RX" }, + {"DP8-10TX", NULL, "FU"}, {"FU", NULL, "PDE 11"}, {"FU", NULL, "FU 113"}, @@ -3232,6 +3240,8 @@ static const struct snd_soc_dapm_route rt1320_dapm_routes[] = { static int rt1320_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) { + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s direction %d", __func__, direction); snd_soc_dai_dma_data_set(dai, direction, sdw_stream); return 0; } @@ -3239,6 +3249,9 @@ static int rt1320_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, static void rt1320_sdw_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + snd_soc_dai_set_dma_data(dai, substream, NULL); } @@ -3251,21 +3264,34 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; struct sdw_port_config dmic_port_config[2]; + struct sdw_port_config companion_config = { + .num = 6, + .ch_mask = BIT(0) | BIT(1), + }; struct sdw_stream_runtime *sdw_stream; int retval, num_channels; unsigned int sampling_rate; + dev_err(dai->dev, "bard: %s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + dev_dbg(dai->dev, "%s %s", __func__, dai->name); sdw_stream = snd_soc_dai_get_dma_data(dai, substream); if (!sdw_stream) return -EINVAL; + printk("%s, sdw_stream 0x%p\n", __func__, sdw_stream); + if (!rt1320->sdw_slave) return -EINVAL; /* SoundWire specific configuration */ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + dev_dbg(dai->dev, "%s stream %d", __func__, substream->stream); + + if (dai->id == RT1320_AIF3) + goto _add_slave_; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (dai->id == RT1320_AIF1) @@ -3295,6 +3321,7 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } +_add_slave_: if (dai->id == RT1320_AIF1) retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, &port_config, 1, sdw_stream); @@ -3312,6 +3339,21 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); return -EINVAL; } + } else if (dai->id == RT1320_AIF3) { + retval = asoc_sdw_c_amp_hw_params(substream, params, dai); + if (retval) { + dev_err(dai->dev, "%s: master companion stream configuration failed, ret=%d\n", + __func__, retval); + return retval; + } + stream_config.type = SDW_STREAM_COMPANION; + retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, + &companion_config, 1, sdw_stream); + if (retval) { + sdw_stream_remove_master(rt1320->sdw_slave->bus, sdw_stream); + dev_err(dai->dev, "%s: Unable to add slave, ret=%d\n", __func__, retval); + return retval; + } } else return -EINVAL; if (retval) { @@ -3469,6 +3511,17 @@ static const struct snd_soc_dai_ops rt1320_aif_dai_ops = { .shutdown = rt1320_sdw_shutdown, }; +static const struct snd_soc_dai_ops rt1320_comp_dai_asoc_ops = { + .probe = asoc_sdw_c_amp_probe, + .startup = asoc_sdw_c_amp_startup, + .prepare = asoc_sdw_c_amp_prepare, + .hw_params = rt1320_sdw_hw_params, + .hw_free = asoc_sdw_c_amp_hw_free, + .set_stream = rt1320_set_sdw_stream, + .shutdown = asoc_sdw_c_amp_shutdown, + .get_stream = asoc_sdw_c_amp_get_sdw_stream, +}; + #define RT1320_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) #define RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ @@ -3507,6 +3560,28 @@ static struct snd_soc_dai_driver rt1320_sdw_dai[] = { }, .ops = &rt1320_aif_dai_ops, }, + /* Companion stream output */ + { + .name = "rt1320-aif3", + .id = RT1320_AIF3, + .playback = { + .stream_name = "DP6 Comp-Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .capture = { + .stream_name = "DP6 Comp-Out", + .channels_min = 2, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .ops = &rt1320_comp_dai_asoc_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + }, }; static int rt1320_parse_dp(struct rt1320_sdw_priv *rt1320, struct device *dev) @@ -3694,3 +3769,4 @@ module_sdw_driver(rt1320_sdw_driver); MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW"); MODULE_AUTHOR("Shuming Fan "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDW_UTILS"); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index df9ccdf92d361c..9af4670f2fbb01 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -87,6 +87,7 @@ enum { RT1320_AIF1, RT1320_AIF2, + RT1320_AIF3, }; /* diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 20961e5760c652..f47e6eaba89eca 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -1183,6 +1183,7 @@ static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = { static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX-Comp", "DP3 Comp-In", 0, SND_SOC_NOPM, 0, 0), /* Digital Interface */ SND_SOC_DAPM_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1200,6 +1201,7 @@ static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = { { "FU06", NULL, "DP3RX" }, + { "FU06", NULL, "DP3RX-Comp" }, { "FU06", NULL, "PDE 23" }, { "OT23 L", "Switch", "FU06" }, { "OT23 R", "Switch", "FU06" }, @@ -1657,9 +1659,25 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, struct sdw_stream_runtime *sdw_stream; int retval, port; unsigned int sampling_rate; + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai; dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); - sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + if (dai->id == RT712_AIF4) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + dev_dbg(dai->dev, "%s cpu_dai %s id %d", __func__, cpu_dai->name, cpu_dai->id); + + sdw_stream = snd_soc_dai_get_stream(cpu_dai, 1); + if (IS_ERR(sdw_stream)) { + dev_err(rtd->dev, "%s no stream found for DAI %s id %d\n", + __func__, cpu_dai->name, cpu_dai->id); + return PTR_ERR(sdw_stream); + } + } else { + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + } if (!sdw_stream) return -EINVAL; @@ -1675,7 +1693,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (dai->id == RT712_AIF1) port = 1; - else if (dai->id == RT712_AIF2) + else if (dai->id == RT712_AIF2 || dai->id == RT712_AIF4) port = 3; else return -EINVAL; @@ -1690,6 +1708,9 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (dai->id == RT712_AIF4) + stream_config.type = SDW_STREAM_COMPANION; port_config.num = port; retval = sdw_stream_add_slave(rt712->slave, &stream_config, @@ -1736,6 +1757,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, sampling_rate); break; case RT712_AIF2: + case RT712_AIF4: regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); @@ -1814,6 +1836,18 @@ static struct snd_soc_dai_driver rt712_sdca_dai[] = { .formats = RT712_FORMATS, }, .ops = &rt712_sdca_ops, + }, + { + .name = "rt712-sdca-aif4", + .id = RT712_AIF4, + .playback = { + .stream_name = "DP3 Comp-In", + .channels_min = 2, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, } }; diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index 7e6ee21c8a172d..0ea6568c6edb66 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -249,6 +249,7 @@ enum { RT712_AIF1, RT712_AIF2, RT712_AIF3, + RT712_AIF4, }; enum rt712_sdca_jd_src { diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b263f554dcc9a9..d9c2ec23fac1e8 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1225,6 +1225,99 @@ static int create_bt_dailinks(struct snd_soc_card *card, return 0; } +static const struct snd_soc_pcm_stream asoc_sdw_bridge_params = { + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static const struct snd_soc_dai_link bridge_dai_template = { + .name = "SDW Companion Amp", + .stream_name = "Companion Amp", + .c2c_params = &asoc_sdw_bridge_params, + .num_c2c_params = 1, +}; + +static int create_sdw_comp_dailinks(struct snd_soc_card *card, + struct snd_soc_dai_link **dai_links, int *be_id, + struct snd_soc_codec_conf **codec_conf, + struct asoc_sdw_companion_amp_endpoint *comp_ends, + int comp_ends_num) +{ + struct device *dev = card->dev; + struct asoc_sdw_endpoint *end; + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link_component *codecs; + struct snd_soc_dai_link_component *cpus; + char *name; + int num_codecs; + int i; + + for (i = 0; i < comp_ends_num; i++) { + if (!comp_ends[i].main_amp || list_empty(&comp_ends[i].comp_ends)) { + dev_err(dev, "companion amp group %d: missing main amp or companion amps\n", i); + return -EINVAL; + } + + num_codecs = 0; + list_for_each_entry(end, &comp_ends[i].comp_ends, list) + num_codecs++; + + cpus = devm_kzalloc(dev, sizeof(*cpus), GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL); + if (!codecs) + return -ENOMEM; + + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!platform) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "SDW Companion Amp %d", i); + if (!name) + return -ENOMEM; + + cpus[0].name = comp_ends[i].main_amp->codec_name; + cpus[0].dai_name = comp_ends[i].main_amp->dai_info->dai_name; + platform->name = cpus[0].name; + + (*codec_conf)->dlc.name = comp_ends[i].main_amp->codec_name; + (*codec_conf)->name_prefix = comp_ends[i].main_amp->name_prefix; + (*codec_conf)++; + + num_codecs = 0; + list_for_each_entry(end, &comp_ends[i].comp_ends, list) { + codecs[num_codecs].name = end->codec_name; + codecs[num_codecs].dai_name = end->dai_info->dai_name; + num_codecs++; + + (*codec_conf)->dlc.name = end->codec_name; + (*codec_conf)->name_prefix = end->name_prefix; + (*codec_conf)++; + } + + **dai_links = bridge_dai_template; + (*dai_links)->name = name; + (*dai_links)->stream_name = name; + (*dai_links)->cpus = cpus; + (*dai_links)->num_cpus = 1; + (*dai_links)->codecs = codecs; + (*dai_links)->num_codecs = num_codecs; + (*dai_links)->platforms = platform; + (*dai_links)->num_platforms = 1; + (*dai_links)++; + } + + dev_dbg(dev, "Added SDW Companion AMP Codec to Codec link\n"); + + return 0; +} + static int create_echoref_dailink(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id) { @@ -1261,11 +1354,13 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private; struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; + struct asoc_sdw_companion_amp_endpoint *sof_comp_ends; struct snd_soc_codec_conf *codec_conf; struct asoc_sdw_codec_info *ssp_info; struct asoc_sdw_endpoint *sof_ends; struct asoc_sdw_dailink *sof_dais; struct snd_soc_aux_dev *sof_aux; + int num_comp_amps = 0; int num_devs = 0; int num_ends = 0; int num_aux = 0; @@ -1276,8 +1371,9 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int hdmi_num; unsigned long ssp_mask; int ret; + int i; - ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux); + ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux, &num_comp_amps); if (ret < 0) { dev_err(dev, "failed to count devices/endpoints: %d\n", ret); return ret; @@ -1307,12 +1403,31 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); + sof_comp_ends = devm_kcalloc(dev, num_comp_amps, sizeof(*sof_comp_ends), GFP_KERNEL); + if (!sof_comp_ends) { + ret = -ENOMEM; + goto err_dai; + } + + for (i = 0; i < num_comp_amps; i++) + INIT_LIST_HEAD(&sof_comp_ends[i].comp_ends); + + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, sof_comp_ends, + &num_confs); if (ret < 0) goto err_end; sdw_be_num = ret; + /* Companion amps are not in num_ends but still need a name_prefix codec_conf */ + for (i = 0; i < num_comp_amps; i++) { + struct asoc_sdw_endpoint *comp_end; + + if (sof_comp_ends[i].main_amp) + num_confs++; + list_for_each_entry(comp_end, &sof_comp_ends[i].comp_ends, list) + num_confs++; + } /* * on generic tgl platform, I2S or sdw mode is supported * based on board rework. A ACPI device is registered in @@ -1366,8 +1481,8 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) * This should be the last DAI link and it is expected both for monolithic * and functional SOF topologies to support echo reference. */ - num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1; - dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); + num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + num_comp_amps + 1; + dai_links = devm_kcalloc(dev, num_links + 1, sizeof(*dai_links), GFP_KERNEL); if (!dai_links) { ret = -ENOMEM; goto err_end; @@ -1415,6 +1530,17 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_end; } + /* + * Companion amps are SoundWire peripherals. They get their audio data from another + * SoundWire peripheral (the main amp) over the SoundWire bus instead of from the host, + * so a codec-to-codec dai link is created between the main amp and the companion amps. + */ + if (num_comp_amps) { + ret = create_sdw_comp_dailinks(card, &dai_links, &be_id, &codec_conf, + sof_comp_ends, num_comp_amps); + if (ret) + goto err_end; + } /* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */ ret = create_echoref_dailink(card, &dai_links, &be_id); if (ret) { diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 7a19de75f32b53..dbe23ea040760c 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -370,8 +372,16 @@ struct asoc_sdw_codec_info codec_info_list[] = { .quirk = SOC_SDW_CODEC_MIC, .quirk_exclude = true, }, + /* Companion amp */ + { + .direction = {true, true}, + .dai_name = "rt712-sdca-aif4", + .dai_type = SOC_SDW_DAI_TYPE_COMP_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .quirk = SOC_SDW_COMPANION_AMPS, + }, }, - .dai_num = 3, + .dai_num = 4, }, { .vendor_id = 0x025d, @@ -535,8 +545,16 @@ struct asoc_sdw_codec_info codec_info_list[] = { .widgets = generic_dmic_widgets, .num_widgets = ARRAY_SIZE(generic_dmic_widgets), }, + /* Companion amp */ + { + .direction = {true, true}, + .dai_name = "rt1320-aif3", + .dai_type = SOC_SDW_DAI_TYPE_COMP_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .quirk = SOC_SDW_COMPANION_AMPS, + }, }, - .dai_num = 2, + .dai_num = 3, }, { .vendor_id = 0x025d, @@ -2015,6 +2033,202 @@ void asoc_sdw_shutdown(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS"); +int asoc_sdw_c_amp_probe(struct snd_soc_dai *dai) +{ + void *data = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + + snd_soc_dai_set_drvdata(dai, data); + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_probe, "SND_SOC_SDW_UTILS"); + +void *asoc_sdw_c_amp_get_sdw_stream(struct snd_soc_dai *dai, int direction) +{ + void *data = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s %s id %d direction %d", __func__, dai->name, dai->id, direction); + + if (data && dai->stream[direction].dma_data) + return dai->stream[direction].dma_data; + else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_get_sdw_stream, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_c_amp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct sdw_stream_runtime *sdw_stream = NULL; + int ret; + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + //dump_stack(); + ret = sdw_startup_stream(substream); + if (ret) { + dev_err(dai->dev, "%s startup failed: %d\n", __func__, ret); + return ret; + } + + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); + if (IS_ERR(sdw_stream)) { + dev_err(dai->dev, "no stream found for DAI %s\n", dai->name); + return PTR_ERR(sdw_stream); + } + + sdw_stream->type = SDW_STREAM_COMPANION; + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_startup, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_c_amp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dai->dev); + struct sdw_stream_runtime *sdw_stream = NULL; + struct sdw_stream_config sconfig; + struct sdw_port_config pconfig; + int retval; + + dev_err(dai->dev, "bard: %s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!sdw_stream) + return -EINVAL; + printk("%s, sdw_stream 0x%p\n", __func__, sdw_stream); + + snd_sdw_params_to_config(substream, params, &sconfig, &pconfig); + + //sconfig.direction = SDW_DATA_DIR_TX; // capture + //sconfig.direction = SDW_DATA_DIR_RX; // playback + //sconfig.ch_count = 2; + //sconfig.frame_rate = params_rate(params); + //sconfig.bps = snd_pcm_format_width(params_format(params)); + //pconfig.ch_mask = BIT(0) | BIT(1); + sconfig.type = sdw_stream->type; + pconfig.num = 0; // no meaning for companion stream + + retval = sdw_stream_add_master(slave->bus, &sconfig, &pconfig, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "%s: Unable to add master, ret=%d\n", __func__, retval); + return retval; + } + printk("%s, master sdw_stream 0x%p, type %d\n", __func__, sdw_stream, sdw_stream->type); + + snd_soc_dai_set_stream(dai, sdw_stream, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_stream(dai, sdw_stream, SNDRV_PCM_STREAM_CAPTURE); + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_hw_params, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_c_amp_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + //struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sdw_stream_runtime *sdw_stream; + //struct snd_soc_dai *dai; + int ret; + + // whether SNDRV_PCM_STREAM_PLAYBACK/SNDRV_PCM_STREAM_CAPTURE direction is used + // all can enables the companion stream + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + //dump_stack(); + + /* Find stream from first CPU DAI */ + //dai = snd_soc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); + if (IS_ERR(sdw_stream)) { + dev_err(dai->dev, "%s no stream found for DAI %s id %d\n", __func__, dai->name, dai->id); + return PTR_ERR(sdw_stream); + } + + ret = sdw_prepare_stream(sdw_stream); + if (ret) { + dev_err(dai->dev, "%s prepare failed: %d\n", __func__, ret); + return ret; + } + + ret = sdw_enable_stream(sdw_stream); + if (ret) + dev_err(dai->dev, "%s trigger failed: %d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_prepare, "SND_SOC_SDW_UTILS"); + +int asoc_sdw_c_amp_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + //struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sdw_slave *slave = dev_to_sdw_dev(dai->dev); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + int ret; + //struct sdw_stream_runtime *sdw_stream; + //struct snd_soc_dai *dai; + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + /* Find stream from first CPU DAI */ + //dai = snd_soc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); + if (IS_ERR(sdw_stream)) { + dev_err(dai->dev, "%s no stream found for DAI %s\n", __func__, dai->name); + return PTR_ERR(sdw_stream); + } + + ret = sdw_disable_stream(sdw_stream); + if (ret) { + dev_err(dai->dev, "%s trigger (disable stream) failed: %d\n", __func__, ret); + return ret; + } + + ret = sdw_deprepare_stream(sdw_stream); + if (ret) { + dev_err(dai->dev, "%s deprepare failed: %d\n", __func__, ret); + return ret; + } + + ret = sdw_stream_remove_slave(slave, sdw_stream); + if (ret < 0) { + dev_err(dai->dev, "%s: remove slave failed: %d\n", __func__, ret); + return ret; + } + + ret = sdw_stream_remove_master(slave->bus, sdw_stream); + if (ret < 0) { + dev_err(dai->dev, "remove master from stream %s failed: %d\n", + sdw_stream->name, ret); + return ret; + } + + printk("%s, id %d done\n", __func__, dai->id); + + return ret; +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_hw_free, "SND_SOC_SDW_UTILS"); + +void asoc_sdw_c_amp_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); + dev_dbg(dai->dev, "%s substream stream %d", __func__, substream->stream); + + snd_soc_dai_stream_dma_data_set_playback(dai, NULL); + snd_soc_dai_stream_dma_data_set_capture(dai, NULL); +} +EXPORT_SYMBOL_NS(asoc_sdw_c_amp_shutdown, "SND_SOC_SDW_UTILS"); + static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, unsigned int sdw_version, unsigned int mfg_id, @@ -2269,12 +2483,14 @@ static int is_sdca_aux_dev_present(struct device *dev, } int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, - int *num_devs, int *num_ends, int *num_aux) + int *num_devs, int *num_ends, int *num_aux, int *num_comp_amps) { struct device *dev = card->dev; + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; + u32 companion_amp_mask = 0; int i, j, ret; for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { @@ -2284,12 +2500,30 @@ int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; struct asoc_sdw_codec_info *codec_info; - *num_ends += adr_dev->num_endpoints; - codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); if (!codec_info) return -EINVAL; + for (j = 0; j < adr_dev->num_endpoints; j++) { + /* Skip companion amps */ + if (adr_dev->endpoints[j].num >= codec_info->dai_num) + continue; + if (codec_info->dais[adr_dev->endpoints[j].num].quirk & SOC_SDW_COMPANION_AMPS) { + if (adr_dev->endpoints[j].group_id >= + BITS_PER_TYPE(companion_amp_mask)) { + dev_err(dev, "invalid companion group_id %d\n", + adr_dev->endpoints[j].group_id); + return -EINVAL; + } + /* The companion amp is only present when the quirk is set */ + if (!(ctx->mc_quirk & SOC_SDW_COMPANION_AMPS)) + continue; + companion_amp_mask |= BIT(adr_dev->endpoints[j].group_id); + continue; + } + (*num_ends)++; + } + for (j = 0; j < codec_info->aux_num; j++) { ret = is_sdca_aux_dev_present(dev, codec_info->auxs[j].codec_name, adr_link, i); @@ -2301,6 +2535,7 @@ int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, } } + *num_comp_amps = hweight32(companion_amp_mask); dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends); return 0; @@ -2412,16 +2647,41 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, + struct asoc_sdw_companion_amp_endpoint *comp_ends, int *num_devs) { struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; struct asoc_sdw_endpoint *soc_end = soc_ends; + u32 companion_amp_mask = 0; int num_dais = 0; int i, j; int ret; + /* Get companion_amp_mask to calculate the companion amp group array index */ + for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { + for (i = 0; i < adr_link->num_adr; i++) { + const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; + struct asoc_sdw_codec_info *codec_info; + + codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); + if (!codec_info) + return -EINVAL; + + for (j = 0; j < adr_dev->num_endpoints; j++) { + if (adr_dev->endpoints[j].num >= codec_info->dai_num) + continue; + if (!(codec_info->dais[adr_dev->endpoints[j].num].quirk & + SOC_SDW_COMPANION_AMPS)) + continue; + if (!(ctx->mc_quirk & SOC_SDW_COMPANION_AMPS)) + continue; + companion_amp_mask |= BIT(adr_dev->endpoints[j].group_id); + } + } + } + for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { int num_link_dailinks = 0; @@ -2492,9 +2752,53 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, int stream; adr_end = &adr_dev->endpoints[j]; + if (adr_end->num >= codec_info->dai_num) { + dev_err(dev, + "%d is too many endpoints for codec: 0x%x\n", + adr_end->num, codec_info->part_id); + return -EINVAL; + } dai_info = &codec_info->dais[adr_end->num]; - soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); + if (dai_info->quirk & SOC_SDW_COMPANION_AMPS) { + struct asoc_sdw_endpoint *comp_end; + int comp_index; + + if (!(ctx->mc_quirk & SOC_SDW_COMPANION_AMPS)) + continue; + + comp_index = hweight32(companion_amp_mask & + (BIT(adr_end->group_id) - 1)); + + codec_name = asoc_sdw_get_codec_name(dev, dai_info, + adr_link, i); + if (IS_ERR(codec_name)) + return PTR_ERR(codec_name); + if (!codec_name) + return -ENOMEM; + + comp_end = devm_kcalloc(dev, 1, sizeof(*comp_end), + GFP_KERNEL); + if (!comp_end) + return -ENOMEM; + comp_end->name_prefix = adr_dev->name_prefix; + + comp_end->link_mask = adr_link->mask; + comp_end->codec_name = codec_name; + comp_end->codec_info = codec_info; + comp_end->dai_info = dai_info; + + /* group_position = 0 is the main amp */ + if (!adr_end->group_position) + comp_ends[comp_index].main_amp = comp_end; + else + list_add_tail(&comp_end->list, + &comp_ends[comp_index].comp_ends); + + continue; + } + + soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); /* * quirk should have higher priority than the sdca properties * in the BIOS. We can't always check the DAI quirk because we @@ -2538,13 +2842,6 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, adr_end->aggregated ? "group" : "solo", adr_end->group_id); - if (adr_end->num >= codec_info->dai_num) { - dev_err(dev, - "%d is too many endpoints for codec: 0x%x\n", - adr_end->num, codec_info->part_id); - return -EINVAL; - } - for_each_pcm_streams(stream) { if (dai_info->direction[stream] && dai_info->dailink[stream] < 0) { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e1815bfe358b5d..409e54edcb5196 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1209,8 +1209,13 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, return NULL; } for (j = 0; j < codec_info_list[i].dai_num; j++) { - /* Check if the endpoint is present by the SDCA DisCo table */ - if (!is_endpoint_present(sdw_device, codec_info_list[i].dais[j].dai_type)) + /* + * Check if the endpoint is present by the SDCA DisCo table. Also skip the + * companion amp endpoints. The companion amp endpoints should be added in + * the mach table if it is present. + */ + if (!is_endpoint_present(sdw_device, codec_info_list[i].dais[j].dai_type) || + (codec_info_list[i].dais[j].dai_type == SOC_SDW_DAI_TYPE_COMP_AMP)) continue; endpoints[ep_index].num = j;