mirror of
https://github.com/torvalds/linux
synced 2024-10-06 19:34:19 +00:00
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:
parent
da52441802
commit
b831b4dca4
|
@ -690,6 +690,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
|
||||||
select SND_SOC_CS42L43_SDW
|
select SND_SOC_CS42L43_SDW
|
||||||
select MFD_CS42L43
|
select MFD_CS42L43
|
||||||
select MFD_CS42L43_SDW
|
select MFD_CS42L43_SDW
|
||||||
|
select SND_SOC_CS35L56_SPI
|
||||||
select SND_SOC_CS35L56_SDW
|
select SND_SOC_CS35L56_SDW
|
||||||
select SND_SOC_DMIC
|
select SND_SOC_DMIC
|
||||||
select SND_SOC_INTEL_HDA_DSP_COMMON
|
select SND_SOC_INTEL_HDA_DSP_COMMON
|
||||||
|
|
|
@ -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-ssp-amp-objs := sof_ssp_amp.o
|
||||||
snd-soc-sof-sdw-objs += sof_sdw.o \
|
snd-soc-sof-sdw-objs += sof_sdw.o \
|
||||||
sof_sdw_maxim.o sof_sdw_rt_amp.o \
|
sof_sdw_maxim.o sof_sdw_rt_amp.o \
|
||||||
|
bridge_cs35l56.o \
|
||||||
sof_sdw_rt5682.o sof_sdw_rt700.o \
|
sof_sdw_rt5682.o sof_sdw_rt700.o \
|
||||||
sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
|
sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
|
||||||
sof_sdw_rt712_sdca.o sof_sdw_rt722_sdca.o \
|
sof_sdw_rt712_sdca.o sof_sdw_rt722_sdca.o \
|
||||||
|
|
137
sound/soc/intel/boards/bridge_cs35l56.c
Normal file
137
sound/soc/intel/boards/bridge_cs35l56.c
Normal 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;
|
||||||
|
}
|
|
@ -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");
|
dev_err(dev, "quirk SOF_SDW_NO_AGGREGATION enabled but no longer supported\n");
|
||||||
if (sof_sdw_quirk & SOF_CODEC_SPKR)
|
if (sof_sdw_quirk & SOF_CODEC_SPKR)
|
||||||
dev_dbg(dev, "quirk SOF_CODEC_SPKR enabled\n");
|
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)
|
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,
|
.part_id = 0x4243,
|
||||||
.codec_name = "cs42l43-codec",
|
.codec_name = "cs42l43-codec",
|
||||||
|
.count_sidecar = bridge_cs35l56_count_sidecar,
|
||||||
|
.add_sidecar = bridge_cs35l56_add_sidecar,
|
||||||
.dais = {
|
.dais = {
|
||||||
{
|
{
|
||||||
.direction = {true, false},
|
.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},
|
.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
|
||||||
.init = sof_sdw_cs42l43_spk_init,
|
.init = sof_sdw_cs42l43_spk_init,
|
||||||
.rtd_init = cs42l43_spk_rtd_init,
|
.rtd_init = cs42l43_spk_rtd_init,
|
||||||
.quirk = SOF_CODEC_SPKR,
|
.quirk = SOF_CODEC_SPKR | SOF_SIDECAR_AMPS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.dai_num = 4,
|
.dai_num = 4,
|
||||||
|
|
|
@ -55,6 +55,16 @@ enum {
|
||||||
#define SOF_SDW_NO_AGGREGATION BIT(14)
|
#define SOF_SDW_NO_AGGREGATION BIT(14)
|
||||||
/* If a CODEC has an optional speaker output, this quirk will enable it */
|
/* If a CODEC has an optional speaker output, this quirk will enable it */
|
||||||
#define SOF_CODEC_SPKR BIT(15)
|
#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 */
|
/* BT audio offload: reserve 3 bits for future */
|
||||||
#define SOF_BT_OFFLOAD_SSP_SHIFT 15
|
#define SOF_BT_OFFLOAD_SSP_SHIFT 15
|
||||||
|
@ -177,6 +187,16 @@ int sof_sdw_cs42l43_spk_init(struct snd_soc_card *card,
|
||||||
bool playback);
|
bool playback);
|
||||||
|
|
||||||
/* CS AMP support */
|
/* 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,
|
int sof_sdw_cs_amp_init(struct snd_soc_card *card,
|
||||||
struct snd_soc_dai_link *dai_links,
|
struct snd_soc_dai_link *dai_links,
|
||||||
struct sof_sdw_codec_info *info,
|
struct sof_sdw_codec_info *info,
|
||||||
|
|
|
@ -124,10 +124,14 @@ int cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
struct snd_soc_card *card = rtd->card;
|
struct snd_soc_card *card = rtd->card;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:cs42l43-spk",
|
if (!(sof_sdw_quirk & SOF_SIDECAR_AMPS)) {
|
||||||
card->components);
|
/* Will be set by the bridge code in this case */
|
||||||
if (!card->components)
|
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
|
||||||
return -ENOMEM;
|
"%s spk:cs42l43-spk",
|
||||||
|
card->components);
|
||||||
|
if (!card->components)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
ret = snd_soc_dapm_new_controls(&card->dapm, cs42l43_spk_widgets,
|
ret = snd_soc_dapm_new_controls(&card->dapm, cs42l43_spk_widgets,
|
||||||
ARRAY_SIZE(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++;
|
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)
|
int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
|
|
Loading…
Reference in a new issue