Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASoC: intel: sof_sdw: Add CS42L43 CODEC support #4546

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sound/soc/intel/boards/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,10 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_RT1318_SDW
select SND_SOC_RT5682_SDW
select SND_SOC_CS42L42_SDW
select SND_SOC_CS42L43
select SND_SOC_CS42L43_SDW
select MFD_CS42L43
select MFD_CS42L43_SDW
select SND_SOC_CS35L56_SDW
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON
Expand Down
3 changes: 2 additions & 1 deletion sound/soc/intel/boards/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ snd-soc-sof-sdw-objs += sof_sdw.o \
sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
sof_sdw_rt712_sdca.o sof_sdw_rt715.o \
sof_sdw_rt715_sdca.o sof_sdw_dmic.o \
sof_sdw_cs42l42.o sof_sdw_cs_amp.o \
sof_sdw_cs42l42.o sof_sdw_cs42l43.o \
sof_sdw_cs_amp.o \
sof_sdw_hdmi.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
Expand Down
27 changes: 27 additions & 0 deletions sound/soc/intel/boards/sof_sdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,33 @@ static struct sof_sdw_codec_info codec_info_list[] = {
},
.dai_num = 1,
},
{
.part_id = 0x4243,
.codec_name = "cs42l43-codec",
.dais = {
{
.direction = {true, false},
.dai_name = "cs42l43-dp5",
.dai_type = SOF_SDW_DAI_TYPE_JACK,
.dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
.init = sof_sdw_cs42l43_hs_init,
},
{
.direction = {false, true},
.dai_name = "cs42l43-dp1",
.dai_type = SOF_SDW_DAI_TYPE_JACK,
.dailink = {SDW_UNUSED_DAI_ID, SDW_JACK_IN_DAI_ID},
},
{
.direction = {false, true},
.dai_name = "cs42l43-dp2",
.dai_type = SOF_SDW_DAI_TYPE_MIC,
.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
.init = sof_sdw_cs42l43_dmic_init,
}
},
.dai_num = 3,
},
{
.part_id = 0xaaaa, /* generic codec mockup */
.version_id = 0,
Expand Down
13 changes: 13 additions & 0 deletions sound/soc/intel/boards/sof_sdw_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ int sof_sdw_cs42l42_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback);

/* CS42L43 support */
int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback);

int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback);

/* CS AMP support */
int sof_sdw_cs_amp_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
Expand Down
145 changes: 145 additions & 0 deletions sound/soc/intel/boards/sof_sdw_cs42l43.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-only
// Based on sof_sdw_rt5682.c
// Copyright (c) 2023 Intel Corporation

/*
* sof_sdw_cs42l43 - Helpers to handle CS42L43 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <sound/jack.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/cs42l43.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include "sof_sdw_common.h"

static const struct snd_soc_dapm_widget cs42l43_hs_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};

static const struct snd_soc_dapm_route cs42l43_hs_map[] = {
{ "Headphone", NULL, "cs42l43 AMP3_OUT" },
{ "Headphone", NULL, "cs42l43 AMP4_OUT" },
{ "cs42l43 ADC1_IN1_P", NULL, "Headset Mic" },
{ "cs42l43 ADC1_IN1_N", NULL, "Headset Mic" },
};

static const struct snd_soc_dapm_widget cs42l43_dmic_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};

static const struct snd_soc_dapm_route cs42l43_dmic_map[] = {
{ "cs42l43 PDM1_DIN", NULL, "DMIC" },
{ "cs42l43 PDM2_DIN", NULL, "DMIC" },
};

static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_jack *jack = &ctx->sdw_headset;
struct snd_soc_card *card = rtd->card;
int ret;

card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l43", card->components);
plbossart marked this conversation as resolved.
Show resolved Hide resolved
if (!card->components)
return -ENOMEM;

ret = snd_soc_dapm_new_controls(&card->dapm, cs42l43_hs_widgets,
ARRAY_SIZE(cs42l43_hs_widgets));
if (ret) {
dev_err(card->dev, "cs42l43 hs widgets addition failed: %d\n", ret);
return ret;
}

ret = snd_soc_dapm_add_routes(&card->dapm, cs42l43_hs_map,
ARRAY_SIZE(cs42l43_hs_map));

if (ret) {
dev_err(card->dev, "cs42l43 hs map addition failed: %d\n", ret);
return ret;
}

ret = snd_soc_card_jack_new(card, "Headphone Jack",
SND_JACK_MECHANICAL | SND_JACK_AVOUT |
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
jack);
if (ret) {
dev_err(card->dev, "Failed to create jack: %d\n", ret);
return ret;
}

snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);

ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(card->dev, "Failed to register jack: %d\n", ret);
return ret;
}

ret = snd_soc_component_set_sysclk(component, CS42L43_SYSCLK, CS42L43_SYSCLK_SDW,
0, SND_SOC_CLOCK_IN);
if (ret)
dev_err(card->dev, "Failed to set sysclk: %d\n", ret);

return ret;
}

int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info,
bool playback)
{
/*
* No need to test if (!playback) like other codecs as cs42l43 uses separated dai for
* playback and capture, and sof_sdw_cs42l43_init is only linked to the playback dai.
*/

dai_links->init = cs42l43_hs_rtd_init;

return 0;
}

static int cs42l43_dmic_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 mic:cs42l43-dmic",
card->components);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit weird, it will unconditionally tell userspace/UCM about the presence of dmics attached to the Cirrus Logic codec.

What happens if the dmics are attached to the PCH, as done on Chromebooks? Is this not a supported configuration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit weird, it will unconditionally tell userspace/UCM about the presence of dmics attached to the Cirrus Logic codec.

What happens if the dmics are attached to the PCH, as done on Chromebooks? Is this not a supported configuration?

@plbossart I think the question is how do we know what functions are used in a multi-function codec? Maybe use a mask, but where can we get the mask? Using a dmi quirk? That's something we need to think about. My idea is that we can create dai links based on what functions are used. Then, the card->components will be added only when the function is used.
Btw, I think it will be better if a codec can use different SDW ADR to tell what functions are supported, and customers select the codec that fits their requirement. Like, rt712 supports jack, dmic, and speaker, and rt713 supports jack and dmic. So that we can assume that the speaker will be used when a device uses rt712 instead of rt713.
Maybe using different unique id is a way to tell what functions are used?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would involve OTPing the parts differently would it not? I suspect the hardware folks are very unlikely to go for that plan. The part should have all the disco stuff in ACPI I suspect the best plan might be to parse that to see which bits are being used. I will have a bit of a look and see where I get to on that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have limited hopes that _DSD properties would help, most BIOS vendors don't get this right usually. I think all the DisCo plans are vaporware for now, see how well it went for the version 1.0, the only thing we use is the _ADR and even that is wrong on all Intel development devices and some HP/NUC commercial devices derived from the Intel reference.

And anyways the PCH-attached DMIC is not really covered in such properties, it's part of the other Intel NHLT stuff.

When we have a discrete microphone codec present, it's relatively easy to make a decision, but if the Cirrus codec may or may not have microphones attached then it's more difficult, isn't it?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Windows is making more of a move to use the disco stuff now so that should to some extent help it along, but if we dont want to trust it for now, I think that mostly leaves us with quirks as an option. I guess another option might be to hook up all the interfaces regardless of if they are attached to something although I suppose that doesnt give user-space any info about what it can and cant do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC we have a restriction in the machine driver, I think we are assuming that either the intel PCH DMIC is used or the SoundWire MIC is used, not both.

Maybe best to check with @bardliao

BTW This isn't a random academic problem I am asking. The RT711 is capable of handling headset and local mics, but we've always used the PCH-DMIC.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC we have a restriction in the machine driver, I think we are assuming that either the intel PCH DMIC is used or the SoundWire MIC is used, not both.

Maybe best to check with @bardliao

I think the machine driver can have both SoundWire MIC and PCH DMIC. But, we never have such configuration.

BTW This isn't a random academic problem I am asking. The RT711 is capable of handling headset and local mics, but we've always used the PCH-DMIC.

Right, but I feel that we will face the issue soon when people want to use Cohen's speaker.

if (!card->components)
return -ENOMEM;

ret = snd_soc_dapm_new_controls(&card->dapm, cs42l43_dmic_widgets,
ARRAY_SIZE(cs42l43_dmic_widgets));
if (ret) {
dev_err(card->dev, "cs42l43 dmic widgets addition failed: %d\n", ret);
return ret;
}

ret = snd_soc_dapm_add_routes(&card->dapm, cs42l43_dmic_map,
ARRAY_SIZE(cs42l43_dmic_map));

if (ret)
dev_err(card->dev, "cs42l43 dmic map addition failed: %d\n", ret);

return ret;
}

int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info,
bool playback)
{
dai_links->init = cs42l43_dmic_rtd_init;
return 0;
}
78 changes: 78 additions & 0 deletions sound/soc/intel/common/soc-acpi-intel-mtl-match.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
},
};

static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 2,
.group_id = 1,
};

static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 3,
.group_id = 1,
};

static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
{
.adr = 0x000030025D071101ull,
Expand Down Expand Up @@ -236,6 +250,45 @@ static const struct snd_soc_acpi_link_adr mtl_712_only[] = {
{}
};

static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
{
.adr = 0x00003001FA424301ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "cs42l43"
}
};

static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = {
{
.adr = 0x00013701FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "cs35l56-8"
},
{
.adr = 0x00013601FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "cs35l56-7"
}
};

static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = {
{
.adr = 0x00023301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "cs35l56-1"
},
{
.adr = 0x00023201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "cs35l56-2"
}
};

static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = {
/* Expected order: jack -> amp */
{
Expand Down Expand Up @@ -342,6 +395,25 @@ static const struct snd_soc_acpi_link_adr cs42l42_link0_max98363_link2[] = {
{}
};

static const struct snd_soc_acpi_link_adr mtl_cs42l43_cs35l56[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_0_adr),
.adr_d = cs42l43_0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56_1_adr),
.adr_d = cs35l56_1_adr,
},
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(cs35l56_2_adr),
.adr_d = cs35l56_2_adr,
},
{}
};

/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
/* mockup tests need to be first */
Expand Down Expand Up @@ -375,6 +447,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg"
},
{
.link_mask = GENMASK(2, 0),
.links = mtl_cs42l43_cs35l56,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg",
},
{
.link_mask = GENMASK(3, 0),
.links = mtl_3_in_1_sdca,
Expand Down