ASoC: intel: sof_sdw: Add support for cs42l43-cs35l56 sidecar amps

The cs42l43 has both a SPI master and an I2S interface, these can
be used to populate 2 cs35l56 amplifiers as sidecar devices along
side the cs42l43. Giving a system that looks like:

  +-----+           +---------+ <- SPI -> +---------+
  | CPU | <- SDW -> | CS42L43 |           | CS35L56 |
  +-----+           +---------+ <- I2S -> +---------+

Add a quirk to specify this feature is present and use it to add
codec to codec DAI link to connect the amplifiers into the sound
card, add appropriate widgets, and setup clocking on the
amplifiers.

Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20240426152123.36284-13-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Maciej Strozek 2024-04-26 10:21:23 -05:00 committed by Mark Brown
parent da52441802
commit b831b4dca4
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
6 changed files with 173 additions and 6 deletions

View File

@ -690,6 +690,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_CS42L43_SDW
select MFD_CS42L43
select MFD_CS42L43_SDW
select SND_SOC_CS35L56_SPI
select SND_SOC_CS35L56_SDW
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON

View File

@ -37,6 +37,7 @@ snd-soc-ehl-rt5660-objs := ehl_rt5660.o
snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o
snd-soc-sof-sdw-objs += sof_sdw.o \
sof_sdw_maxim.o sof_sdw_rt_amp.o \
bridge_cs35l56.o \
sof_sdw_rt5682.o sof_sdw_rt700.o \
sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
sof_sdw_rt712_sdca.o sof_sdw_rt722_sdca.o \

View File

@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Intel SOF Machine Driver with Cirrus Logic CS35L56 Smart Amp
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "sof_sdw_common.h"
static const struct snd_soc_dapm_widget bridge_widgets[] = {
SND_SOC_DAPM_SPK("Bridge Speaker", NULL),
};
static const struct snd_soc_dapm_route bridge_map[] = {
{"Bridge Speaker", NULL, "AMPL SPK"},
{"Bridge Speaker", NULL, "AMPR SPK"},
};
static const char * const bridge_cs35l56_name_prefixes[] = {
"AMPL",
"AMPR",
};
static int bridge_cs35l56_asp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
int i, ret;
unsigned int rx_mask = 3; // ASP RX1, RX2
unsigned int tx_mask = 3; // ASP TX1, TX2
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s spk:cs35l56-bridge",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_new_controls(&card->dapm, bridge_widgets,
ARRAY_SIZE(bridge_widgets));
if (ret) {
dev_err(card->dev, "widgets addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_dapm_add_routes(&card->dapm, bridge_map, ARRAY_SIZE(bridge_map));
if (ret) {
dev_err(card->dev, "map addition failed: %d\n", ret);
return ret;
}
/* 4 x 16-bit sample slots and FSYNC=48000, BCLK=3.072 MHz */
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask, rx_mask, 4, 16);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 3072000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
}
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask, rx_mask, 4, 16);
if (ret < 0)
return ret;
}
return 0;
}
static const struct snd_soc_pcm_stream bridge_params = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
};
SND_SOC_DAILINK_DEFS(bridge_dai,
DAILINK_COMP_ARRAY(COMP_CODEC("cs42l43-codec", "cs42l43-asp")),
DAILINK_COMP_ARRAY(COMP_CODEC("spi-cs35l56-left", "cs35l56-asp1"),
COMP_CODEC("spi-cs35l56-right", "cs35l56-asp1")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("cs42l43-codec")));
static const struct snd_soc_dai_link bridge_dai_template = {
.name = "cs42l43-cs35l56",
.init = bridge_cs35l56_asp_init,
.c2c_params = &bridge_params,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(bridge_dai),
};
int bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
int *num_dais, int *num_devs)
{
if (sof_sdw_quirk & SOF_SIDECAR_AMPS) {
(*num_dais)++;
(*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
}
return 0;
}
int bridge_cs35l56_add_sidecar(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links,
struct snd_soc_codec_conf **codec_conf)
{
if (sof_sdw_quirk & SOF_SIDECAR_AMPS) {
**dai_links = bridge_dai_template;
for (int i = 0; i < ARRAY_SIZE(bridge_cs35l56_name_prefixes); i++) {
(*codec_conf)->dlc.name = (*dai_links)->codecs[i].name;
(*codec_conf)->name_prefix = bridge_cs35l56_name_prefixes[i];
(*codec_conf)++;
}
(*dai_links)++;
}
return 0;
}
int bridge_cs35l56_spk_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback)
{
if (sof_sdw_quirk & SOF_SIDECAR_AMPS)
info->amp_num += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
return 0;
}

View File

@ -39,6 +39,8 @@ static void log_quirks(struct device *dev)
dev_err(dev, "quirk SOF_SDW_NO_AGGREGATION enabled but no longer supported\n");
if (sof_sdw_quirk & SOF_CODEC_SPKR)
dev_dbg(dev, "quirk SOF_CODEC_SPKR enabled\n");
if (sof_sdw_quirk & SOF_SIDECAR_AMPS)
dev_dbg(dev, "quirk SOF_SIDECAR_AMPS enabled\n");
}
static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
@ -995,6 +997,8 @@ static struct sof_sdw_codec_info codec_info_list[] = {
{
.part_id = 0x4243,
.codec_name = "cs42l43-codec",
.count_sidecar = bridge_cs35l56_count_sidecar,
.add_sidecar = bridge_cs35l56_add_sidecar,
.dais = {
{
.direction = {true, false},
@ -1023,7 +1027,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
.init = sof_sdw_cs42l43_spk_init,
.rtd_init = cs42l43_spk_rtd_init,
.quirk = SOF_CODEC_SPKR,
.quirk = SOF_CODEC_SPKR | SOF_SIDECAR_AMPS,
},
},
.dai_num = 4,

View File

@ -55,6 +55,16 @@ enum {
#define SOF_SDW_NO_AGGREGATION BIT(14)
/* If a CODEC has an optional speaker output, this quirk will enable it */
#define SOF_CODEC_SPKR BIT(15)
/*
* If the CODEC has additional devices attached directly to it.
*
* For the cs42l43:
* - 0 - No speaker output
* - SOF_CODEC_SPKR - CODEC internal speaker
* - SOF_SIDECAR_AMPS - 2x Sidecar amplifiers + CODEC internal speaker
* - SOF_CODEC_SPKR | SOF_SIDECAR_AMPS - Not currently supported
*/
#define SOF_SIDECAR_AMPS BIT(16)
/* BT audio offload: reserve 3 bits for future */
#define SOF_BT_OFFLOAD_SSP_SHIFT 15
@ -177,6 +187,16 @@ int sof_sdw_cs42l43_spk_init(struct snd_soc_card *card,
bool playback);
/* CS AMP support */
int bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
int *num_dais, int *num_devs);
int bridge_cs35l56_add_sidecar(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links,
struct snd_soc_codec_conf **codec_conf);
int bridge_cs35l56_spk_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback);
int sof_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,

View File

@ -124,10 +124,14 @@ int cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = rtd->card;
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:cs42l43-spk",
card->components);
if (!card->components)
return -ENOMEM;
if (!(sof_sdw_quirk & SOF_SIDECAR_AMPS)) {
/* Will be set by the bridge code in this case */
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s spk:cs42l43-spk",
card->components);
if (!card->components)
return -ENOMEM;
}
ret = snd_soc_dapm_new_controls(&card->dapm, cs42l43_spk_widgets,
ARRAY_SIZE(cs42l43_spk_widgets));
@ -155,7 +159,7 @@ int sof_sdw_cs42l43_spk_init(struct snd_soc_card *card,
info->amp_num++;
return 0;
return bridge_cs35l56_spk_init(card, dai_links, info, playback);
}
int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)