Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config SND_SOC_SDW_UTILS
tristate
help
This option enables to use SoundWire common helper functions and
SoundWire codec helper functions in machine driver.
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-sdw-utils-y := soc_sdw_utils.o soc_sdw_dmic.o soc_sdw_rt_dmic.o \
soc_sdw_rt700.o soc_sdw_rt711.o \
soc_sdw_rt5682.o soc_sdw_rt_sdca_jack_common.o \
soc_sdw_rt_amp.o soc_sdw_rt_mf_sdca.o \
soc_sdw_bridge_cs35l56.o \
soc_sdw_cs42l42.o soc_sdw_cs42l43.o \
soc_sdw_cs42l45.o \
soc_sdw_cs47l47.o \
soc_sdw_cs_amp.o \
soc_sdw_es9356.o \
soc_sdw_maxim.o \
soc_sdw_ti_amp.o
obj-$(CONFIG_SND_SOC_SDW_UTILS) += snd-soc-sdw-utils.o
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2024 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_bridge_cs35l56 - codec helper functions for handling 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 <sound/soc_sdw_utils.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 asoc_sdw_bridge_cs35l56_asp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(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;
ret = snd_soc_dapm_new_controls(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(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 = asoc_sdw_cs35l56_volume_limit(card, codec_dai->component->name_prefix);
if (ret)
return ret;
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 asoc_sdw_bridge_params = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
};
SND_SOC_DAILINK_DEFS(asoc_sdw_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 = asoc_sdw_bridge_cs35l56_asp_init,
.c2c_params = &asoc_sdw_bridge_params,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai),
};
int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
int *num_dais, int *num_devs)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) {
(*num_dais)++;
(*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_count_sidecar, "SND_SOC_SDW_UTILS");
int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card,
struct snd_soc_dai_link **dai_links,
struct snd_soc_codec_conf **codec_conf)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (ctx->mc_quirk & SOC_SDW_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;
}
EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_add_sidecar, "SND_SOC_SDW_UTILS");
int asoc_sdw_bridge_cs35l56_spk_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)
info->amp_num += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_bridge_cs35l56_spk_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_cs42l42 - Helpers to handle CS42L42 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
static const struct snd_soc_dapm_route cs42l42_map[] = {
/* HP jack connectors - unknown if we have jack detection */
{"Headphone", NULL, "cs42l42 HP"},
/* other jacks */
{"cs42l42 HS", NULL, "Headset Mic"},
};
static struct snd_soc_jack_pin cs42l42_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:cs42l42",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, cs42l42_map,
ARRAY_SIZE(cs42l42_map));
if (ret) {
dev_err(card->dev, "cs42l42 map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
cs42l42_jack_pins,
ARRAY_SIZE(cs42l42_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret)
dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l42_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0-only
// Based on sof_sdw_rt5682.c
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_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 <sound/soc_sdw_utils.h>
#define CS42L43_SPK_VOLUME_0DB 128 /* 0dB Max */
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_route cs42l43_spk_map[] = {
{ "Speaker", NULL, "cs42l43 AMP1_OUT_P", },
{ "Speaker", NULL, "cs42l43 AMP1_OUT_N", },
{ "Speaker", NULL, "cs42l43 AMP2_OUT_P", },
{ "Speaker", NULL, "cs42l43 AMP2_OUT_N", },
};
static const struct snd_soc_dapm_route cs42l43_dmic_map[] = {
{ "cs42l43 PDM1_DIN", NULL, "DMIC" },
{ "cs42l43 PDM2_DIN", NULL, "DMIC" },
};
static struct snd_soc_jack_pin soc_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct asoc_sdw_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;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l43",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(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_pins(card, "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, soc_jack_pins,
ARRAY_SIZE(soc_jack_pins));
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;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
ret = snd_soc_limit_volume(card, "cs42l43 Speaker Digital Volume",
CS42L43_SPK_VOLUME_0DB);
if (ret)
dev_err(card->dev, "cs42l43 speaker volume limit failed: %d\n", ret);
else
dev_info(card->dev, "Setting CS42L43 Speaker volume limit to %d\n",
CS42L43_SPK_VOLUME_0DB);
ret = snd_soc_dapm_add_routes(dapm, cs42l43_spk_map,
ARRAY_SIZE(cs42l43_spk_map));
if (ret) {
dev_err(card->dev, "cs42l43 speaker map addition failed: %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;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs42l43_spk_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
/* Do init on playback link only. */
if (!playback)
return 0;
info->amp_num++;
return asoc_sdw_bridge_cs35l56_spk_init(card, dai_links, info, playback);
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_spk_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs42l43-dmic",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(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;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_dmic_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
// Based on sof_sdw_rt5682.c
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_cs42l45 - Helpers to handle CS42L45 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-card.h>
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc_sdw_utils.h>
static struct snd_soc_jack_pin soc_jack_pins[] = {
{
.pin = "cs42l45 OT 43 Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "cs42l45 OT 45 Headset",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "cs42l45 IT 31 Microphone",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "cs42l45 IT 33 Headset",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_cs42l45_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_jack *jack = &ctx->sdw_headset;
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l45",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Jack", SND_JACK_MECHANICAL |
SND_JACK_HEADSET | SND_JACK_LINEOUT, jack,
soc_jack_pins, ARRAY_SIZE(soc_jack_pins));
if (ret) {
dev_err(card->dev, "Failed to create jack: %d\n", ret);
return ret;
}
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(card->dev, "Failed to register jack: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l45_hs_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs42l45_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs42l45-dmic",
card->components);
if (!card->components)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs42l45_dmic_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
// Based on sof_sdw_cs42l45.c
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_cs47l47 - Helpers to handle CS47L47 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-card.h>
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc_sdw_utils.h>
static struct snd_soc_jack_pin soc_jack_pins[] = {
{
.pin = "cs47l47 OT 43 Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "cs47l47 OT 45 Headset",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "cs47l47 IT 31 Microphone",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "cs47l47 IT 33 Headset",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_cs47l47_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_jack *jack = &ctx->sdw_headset;
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs47l47",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Jack", SND_JACK_MECHANICAL |
SND_JACK_HEADSET | SND_JACK_LINEOUT, jack,
soc_jack_pins, ARRAY_SIZE(soc_jack_pins));
if (ret) {
dev_err(card->dev, "Failed to create jack: %d\n", ret);
return ret;
}
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(card->dev, "Failed to register jack: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_hs_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs47l47_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:cs47l47-dmic",
card->components);
if (!card->components)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs47l47_dmic_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_cs_amp - Helpers to handle CS35L56 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dai.h>
#include <sound/soc_sdw_utils.h>
#define CS_AMP_CHANNELS_PER_AMP 4
#define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */
int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix)
{
char *volume_ctl_name;
int ret;
volume_ctl_name = kasprintf(GFP_KERNEL, "%s Speaker Volume", name_prefix);
if (!volume_ctl_name)
return -ENOMEM;
ret = snd_soc_limit_volume(card, volume_ctl_name, CS35L56_SPK_VOLUME_0DB);
if (ret)
dev_err(card->dev, "%s limit set failed: %d\n", volume_ctl_name, ret);
kfree(volume_ctl_name);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs35l56_volume_limit, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
char widget_name[16];
struct snd_soc_dapm_route route = { "Speaker", NULL, widget_name };
struct snd_soc_dai *codec_dai;
int i, ret;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!strstr(codec_dai->name, "cs35l56"))
continue;
snprintf(widget_name, sizeof(widget_name), "%s SPK",
codec_dai->component->name_prefix);
ret = asoc_sdw_cs35l56_volume_limit(card, codec_dai->component->name_prefix);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, &route, 1);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
const struct snd_soc_dai_link *dai_link = rtd->dai_link;
const struct snd_soc_dai_link_ch_map *ch_map;
const struct snd_soc_dai_link_component *codec_dlc;
struct snd_soc_dai *codec_dai;
u8 ch_slot[8] = {};
unsigned int amps_per_bus, ch_per_amp, mask;
int i, ret;
WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot));
/*
* CS35L56 has 4 TX channels. When the capture is aggregated the
* same bus slots will be allocated to all the amps on a bus. Only
* one amp on that bus can be transmitting in each slot so divide
* the available 4 slots between all the amps on a bus.
*/
amps_per_bus = dai_link->num_codecs / dai_link->num_cpus;
if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) {
dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n",
dai_link->num_codecs, dai_link->num_cpus);
return -EINVAL;
}
ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus;
for_each_rtd_ch_maps(rtd, i, ch_map) {
codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i);
codec_dai = snd_soc_find_dai(codec_dlc);
mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu];
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32);
if (ret < 0) {
dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret);
return ret;
}
ch_slot[ch_map->cpu] += ch_per_amp;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
/* Do init on playback link only. */
if (!playback)
return 0;
info->amp_num++;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_amp_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_dmic - Helpers to handle dmic from generic machine driver
*/
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/soc_sdw_utils.h>
static const struct snd_soc_dapm_widget dmic_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
};
static const struct snd_soc_dapm_route dmic_map[] = {
/* digital mics */
{"DMic", NULL, "SoC DMIC"},
};
int asoc_sdw_dmic_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
ret = snd_soc_dapm_new_controls(dapm, dmic_widgets,
ARRAY_SIZE(dmic_widgets));
if (ret) {
dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
/* Don't need to add routes if widget addition failed */
return ret;
}
ret = snd_soc_dapm_add_routes(dapm, dmic_map,
ARRAY_SIZE(dmic_map));
if (ret)
dev_err(card->dev, "DMic map addition failed: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_dmic_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: GPL-2.0-only
// Based on sof_sdw_rt5682.c
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2023 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
// Copyright (c) 2025 Everest Semiconductor Co., Ltd
/*
* soc_sdw_es9356 - Helpers to handle ES9356 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
/*
* Note this MUST be called before snd_soc_register_card(), so that the props
* are in place before the codec component driver's probe function parses them.
*/
static int es9356_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
{
struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
struct fwnode_handle *fwnode;
int ret;
if (!SOC_SDW_JACK_JDSRC(quirk))
return 0;
props[0] = PROPERTY_ENTRY_U32("everest,jd-src", SOC_SDW_JACK_JDSRC(quirk));
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
fwnode_handle_put(fwnode);
return ret;
}
static const struct snd_soc_dapm_route es9356_map[] = {
/* Headphones */
{ "Headphone", NULL, "es9356 HP" },
{ "es9356 MIC1", NULL, "Headset Mic" },
};
static const struct snd_soc_dapm_route es9356_spk_map[] = {
/* Speaker */
{ "Speaker", NULL, "es9356 SPK" },
};
static const struct snd_soc_dapm_route es9356_dmic_map[] = {
/* DMIC */
{ "es9356 PDM_DIN", NULL, "DMIC" },
};
static struct snd_soc_jack_pin es9356_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_es9356_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s spk:es9356-spk",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, es9356_spk_map,
ARRAY_SIZE(es9356_spk_map));
if (ret)
dev_err(card->dev, "es9356 map addition failed: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_es9356_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:es9356-dmic",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, es9356_dmic_map,
ARRAY_SIZE(es9356_dmic_map));
if (ret)
dev_err(card->dev, "es9356 map addition failed: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_dmic_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_es9356_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:es9356",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, es9356_map,
ARRAY_SIZE(es9356_map));
if (ret) {
dev_err(card->dev, "es9356 map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_BTN_4,
&ctx->sdw_headset,
es9356_jack_pins,
ARRAY_SIZE(es9356_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_NEXTSONG);
snd_jack_set_key(jack->jack, SND_JACK_BTN_4, KEY_PREVIOUSSONG);
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret)
dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (!ctx->headset_codec_dev)
return 0;
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_exit, "SND_SOC_SDW_UTILS");
int asoc_sdw_es9356_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct device *sdw_dev;
int ret;
/*
* headset should be initialized once.
* Do it with dai link for playback.
*/
if (!playback)
return 0;
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
if (!sdw_dev)
return -EPROBE_DEFER;
ret = es9356_add_codec_device_props(sdw_dev, ctx->mc_quirk);
if (ret < 0) {
put_device(sdw_dev);
return ret;
}
ctx->headset_codec_dev = sdw_dev;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_es9356_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
if (!playback)
return 0;
info->amp_num++;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_es9356_amp_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
//
// soc_sdw_maxim - Helpers to handle maxim codecs
// codec devices from generic machine driver
#include <linux/device.h>
#include <linux/errno.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/soc_sdw_utils.h>
static int maxim_part_id;
#define SOC_SDW_PART_ID_MAX98363 0x8363
#define SOC_SDW_PART_ID_MAX98373 0x8373
static const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
{ "Left Spk", NULL, "Left BE_OUT" },
{ "Right Spk", NULL, "Right BE_OUT" },
};
int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
ret = snd_soc_dapm_add_routes(dapm, max_98373_dapm_routes, 2);
if (ret)
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_maxim_spk_rtd_init, "SND_SOC_SDW_UTILS");
static int asoc_sdw_mx8373_enable_spk_pin(struct snd_pcm_substream *substream, bool enable)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
int ret;
int j;
/* set spk pin by playback only */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return 0;
cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
for_each_rtd_codec_dais(rtd, j, codec_dai) {
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cpu_dai->component);
char pin_name[16];
snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",
codec_dai->component->name_prefix);
if (enable)
ret = snd_soc_dapm_enable_pin(dapm, pin_name);
else
ret = snd_soc_dapm_disable_pin(dapm, pin_name);
if (!ret)
snd_soc_dapm_sync(dapm);
}
return 0;
}
static int asoc_sdw_mx8373_prepare(struct snd_pcm_substream *substream)
{
int ret;
/* according to soc_pcm_prepare dai link prepare is called first */
ret = asoc_sdw_prepare(substream);
if (ret < 0)
return ret;
return asoc_sdw_mx8373_enable_spk_pin(substream, true);
}
static int asoc_sdw_mx8373_hw_free(struct snd_pcm_substream *substream)
{
int ret;
/* according to soc_pcm_hw_free dai link free is called first */
ret = asoc_sdw_hw_free(substream);
if (ret < 0)
return ret;
return asoc_sdw_mx8373_enable_spk_pin(substream, false);
}
static const struct snd_soc_ops max_98373_sdw_ops = {
.startup = asoc_sdw_startup,
.prepare = asoc_sdw_mx8373_prepare,
.trigger = asoc_sdw_trigger,
.hw_params = asoc_sdw_hw_params,
.hw_free = asoc_sdw_mx8373_hw_free,
.shutdown = asoc_sdw_shutdown,
};
static int asoc_sdw_mx8373_sdw_late_probe(struct snd_soc_card *card)
{
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
/* Disable Left and Right Spk pin after boot */
snd_soc_dapm_disable_pin(dapm, "Left Spk");
snd_soc_dapm_disable_pin(dapm, "Right Spk");
return snd_soc_dapm_sync(dapm);
}
int asoc_sdw_maxim_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
info->amp_num++;
maxim_part_id = info->part_id;
switch (maxim_part_id) {
case SOC_SDW_PART_ID_MAX98363:
/* Default ops are set in function init_dai_link.
* called as part of function create_sdw_dailink
*/
break;
case SOC_SDW_PART_ID_MAX98373:
info->codec_card_late_probe = asoc_sdw_mx8373_sdw_late_probe;
dai_links->ops = &max_98373_sdw_ops;
break;
default:
dev_err(card->dev, "Invalid maxim_part_id %#x\n", maxim_part_id);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_maxim_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt5682 - Helpers to handle RT5682 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
static const struct snd_soc_dapm_route rt5682_map[] = {
/*Headphones*/
{ "Headphone", NULL, "rt5682 HPOL" },
{ "Headphone", NULL, "rt5682 HPOR" },
{ "rt5682 IN1P", NULL, "Headset Mic" },
};
static struct snd_soc_jack_pin rt5682_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:rt5682",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, rt5682_map,
ARRAY_SIZE(rt5682_map));
if (ret) {
dev_err(card->dev, "rt5682 map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
rt5682_jack_pins,
ARRAY_SIZE(rt5682_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
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(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt5682_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt700 - Helpers to handle RT700 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
static const struct snd_soc_dapm_route rt700_map[] = {
/* Headphones */
{ "Headphones", NULL, "rt700 HP" },
{ "Speaker", NULL, "rt700 SPK" },
{ "rt700 MIC2", NULL, "AMIC" },
};
static struct snd_soc_jack_pin rt700_jack_pins[] = {
{
.pin = "Headphones",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "AMIC",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_rt700_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:rt700",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, rt700_map,
ARRAY_SIZE(rt700_map));
if (ret) {
dev_err(card->dev, "rt700 map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
rt700_jack_pins,
ARRAY_SIZE(rt700_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
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(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt700_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt711 - Helpers to handle RT711 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
/*
* Note this MUST be called before snd_soc_register_card(), so that the props
* are in place before the codec component driver's probe function parses them.
*/
static int rt711_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
{
struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
struct fwnode_handle *fwnode;
int ret;
if (!SOC_SDW_JACK_JDSRC(quirk))
return 0;
props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(quirk));
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
fwnode_handle_put(fwnode);
return ret;
}
static const struct snd_soc_dapm_route rt711_map[] = {
/* Headphones */
{ "Headphone", NULL, "rt711 HP" },
{ "rt711 MIC2", NULL, "Headset Mic" },
};
static struct snd_soc_jack_pin rt711_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:rt711",
card->components);
if (!card->components)
return -ENOMEM;
ret = snd_soc_dapm_add_routes(dapm, rt711_map,
ARRAY_SIZE(rt711_map));
if (ret) {
dev_err(card->dev, "rt711 map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
rt711_jack_pins,
ARRAY_SIZE(rt711_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
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(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt711_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (!ctx->headset_codec_dev)
return 0;
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt711_exit, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt711_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct device *sdw_dev;
int ret;
/*
* headset should be initialized once.
* Do it with dai link for playback.
*/
if (!playback)
return 0;
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
if (!sdw_dev)
return -EPROBE_DEFER;
ret = rt711_add_codec_device_props(sdw_dev, ctx->mc_quirk);
if (ret < 0) {
put_device(sdw_dev);
return ret;
}
ctx->headset_codec_dev = sdw_dev;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt711_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2022 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/dmi.h>
#include <sound/soc_sdw_utils.h>
#include "soc_sdw_rt_amp_coeff_tables.h"
#include "../codecs/rt1308.h"
#define CODEC_NAME_SIZE 7
/* choose a larger value to resolve compatibility issues */
#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
struct rt_amp_platform_data {
const unsigned char *bq_params;
const unsigned int bq_params_cnt;
};
static const struct rt_amp_platform_data dell_0a5d_platform_data = {
.bq_params = dell_0a5d_bq_params,
.bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
};
static const struct rt_amp_platform_data dell_0b00_platform_data = {
.bq_params = dell_0b00_bq_params,
.bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
};
static const struct dmi_system_id dmi_platform_data[] = {
/* CometLake devices */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990")
},
.driver_data = (void *)&dell_0a5d_platform_data,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F")
},
.driver_data = (void *)&dell_0a5d_platform_data,
},
/* TigerLake devices */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
},
.driver_data = (void *)&dell_0a5d_platform_data,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
},
.driver_data = (void *)&dell_0a5d_platform_data,
},
/* AlderLake devices */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
},
.driver_data = (void *)&dell_0b00_platform_data,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
},
.driver_data = (void *)&dell_0b00_platform_data,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
},
.driver_data = (void *)&dell_0b00_platform_data,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
},
.driver_data = (void *)&dell_0b00_platform_data,
},
{},
};
static int rt_amp_add_device_props(struct device *sdw_dev)
{
struct property_entry props[3] = {};
struct fwnode_handle *fwnode;
const struct dmi_system_id *dmi_data;
const struct rt_amp_platform_data *pdata;
unsigned char params[RT_AMP_MAX_BQ_REG];
int ret;
dmi_data = dmi_first_match(dmi_platform_data);
if (!dmi_data)
return 0;
pdata = dmi_data->driver_data;
memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
fwnode_handle_put(fwnode);
return ret;
}
/*
* dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
* according to the number of rt1308/rt1316/rt1318 used. The first two
* entries will be registered for one codec case, and the last two entries
* are also registered if two 1308s/1316s/1318s are used.
*/
static const struct snd_soc_dapm_route rt1308_map[] = {
{ "Speaker", NULL, "rt1308-1 SPOL" },
{ "Speaker", NULL, "rt1308-1 SPOR" },
{ "Speaker", NULL, "rt1308-2 SPOL" },
{ "Speaker", NULL, "rt1308-2 SPOR" },
};
static const struct snd_soc_dapm_route rt1316_map[] = {
{ "Speaker", NULL, "rt1316-1 SPOL" },
{ "Speaker", NULL, "rt1316-1 SPOR" },
{ "Speaker", NULL, "rt1316-2 SPOL" },
{ "Speaker", NULL, "rt1316-2 SPOR" },
};
static const struct snd_soc_dapm_route rt1318_map[] = {
{ "Speaker", NULL, "rt1318-1 SPOL" },
{ "Speaker", NULL, "rt1318-1 SPOR" },
{ "Speaker", NULL, "rt1318-2 SPOL" },
{ "Speaker", NULL, "rt1318-2 SPOR" },
};
static const struct snd_soc_dapm_route rt1320_map[] = {
{ "Speaker", NULL, "rt1320-1 SPOL" },
{ "Speaker", NULL, "rt1320-1 SPOR" },
{ "Speaker", NULL, "rt1320-2 SPOL" },
{ "Speaker", NULL, "rt1320-2 SPOR" },
};
static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_dai *dai,
char *codec_name)
{
/* get the codec name */
snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
/* choose the right codec's map */
if (strcmp(codec_name, "rt1308") == 0)
return rt1308_map;
else if (strcmp(codec_name, "rt1316") == 0)
return rt1316_map;
else if (strcmp(codec_name, "rt1318") == 0)
return rt1318_map;
else
return rt1320_map;
}
int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
const struct snd_soc_dapm_route *rt_amp_map;
char codec_name[CODEC_NAME_SIZE];
struct snd_soc_dai *codec_dai;
int ret = -EINVAL;
int i;
rt_amp_map = get_codec_name_and_route(dai, codec_name);
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (strstr(codec_dai->component->name_prefix, "-1"))
ret = snd_soc_dapm_add_routes(dapm, rt_amp_map, 2);
else if (strstr(codec_dai->component->name_prefix, "-2"))
ret = snd_soc_dapm_add_routes(dapm, rt_amp_map + 2, 2);
}
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, "SND_SOC_SDW_UTILS");
static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int clk_id, clk_freq, pll_out;
int err;
clk_id = RT1308_PLL_S_MCLK;
clk_freq = 38400000;
pll_out = params_rate(params) * 512;
/* Set rt1308 pll */
err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
if (err < 0) {
dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);
return err;
}
/* Set rt1308 sysclk */
err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
SND_SOC_CLOCK_IN);
if (err < 0) {
dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);
return err;
}
return 0;
}
/* machine stream operations */
const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = {
.hw_params = rt1308_i2s_hw_params,
};
EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (ctx->amp_dev1) {
device_remove_software_node(ctx->amp_dev1);
put_device(ctx->amp_dev1);
}
if (ctx->amp_dev2) {
device_remove_software_node(ctx->amp_dev2);
put_device(ctx->amp_dev2);
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct device *sdw_dev1, *sdw_dev2;
int ret;
/* Count amp number and do init on playback link only. */
if (!playback)
return 0;
info->amp_num++;
if (info->amp_num == 2) {
sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
if (!sdw_dev1)
return -EPROBE_DEFER;
ret = rt_amp_add_device_props(sdw_dev1);
if (ret < 0) {
put_device(sdw_dev1);
return ret;
}
ctx->amp_dev1 = sdw_dev1;
sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
if (!sdw_dev2)
return -EPROBE_DEFER;
ret = rt_amp_add_device_props(sdw_dev2);
if (ret < 0) {
put_device(sdw_dev2);
return ret;
}
ctx->amp_dev2 = sdw_dev2;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,300 @@
/* SPDX-License-Identifier: GPL-2.0-only
*/
/*
* soc_sdw_rt_amp_coeff_tables.h - related coefficients for RTK amplifier parameters
*/
#ifndef SND_SOC_SDW_RT_SDW_AMP_COEFF_H
#define SND_SOC_SDW_RT_SDW_AMP_COEFF_H
#define RT1308_MAX_BQ_REG 480
#define RT1316_MAX_BQ_REG 580
static const u8 __maybe_unused dell_0a5d_bq_params[] = {
0xb0, 0xc5, 0x00, /* address: 0xc5b0; data: 0x00 */
0xb1, 0xc5, 0x32,
0xb2, 0xc5, 0x44,
0xb3, 0xc5, 0x19,
0xc0, 0xc5, 0x04,
0xc1, 0xc5, 0x00,
0xc2, 0xc5, 0x00,
0xc3, 0xc5, 0x00,
0xd0, 0xc5, 0x02,
0xd1, 0xc5, 0x00,
0xd2, 0xc5, 0x00,
0xd3, 0xc5, 0x00,
0xe0, 0xc5, 0x01,
0xe1, 0xc5, 0xe8,
0xe2, 0xc5, 0x5f,
0xe3, 0xc5, 0x8a,
0xf0, 0xc5, 0x1f,
0xf1, 0xc5, 0x4e,
0xf2, 0xc5, 0x90,
0xf3, 0xc5, 0x11,
0x50, 0xc6, 0x01,
0x51, 0xc6, 0xff,
0x52, 0xc6, 0x45,
0x53, 0xc6, 0x41,
0x60, 0xc6, 0x1c,
0x61, 0xc6, 0x00,
0x62, 0xc6, 0x00,
0x63, 0xc6, 0x00,
0x70, 0xc6, 0x02,
0x71, 0xc6, 0x00,
0x72, 0xc6, 0x00,
0x73, 0xc6, 0x00,
0x80, 0xc6, 0x03,
0x81, 0xc6, 0xfe,
0x82, 0xc6, 0x89,
0x83, 0xc6, 0xfa,
0x90, 0xc6, 0x1e,
0x91, 0xc6, 0x01,
0x92, 0xc6, 0x74,
0x93, 0xc6, 0xf6,
0x00, 0xc6, 0x01,
0x01, 0xc6, 0xd9,
0x02, 0xc6, 0xfb,
0x03, 0xc6, 0xc4,
0x10, 0xc6, 0x1c,
0x11, 0xc6, 0x00,
0x12, 0xc6, 0x00,
0x13, 0xc6, 0x00,
0x20, 0xc6, 0x02,
0x21, 0xc6, 0x00,
0x22, 0xc6, 0x00,
0x23, 0xc6, 0x00,
0x30, 0xc6, 0x03,
0x31, 0xc6, 0xaf,
0x32, 0xc6, 0x23,
0x33, 0xc6, 0xcb,
0x40, 0xc6, 0x1e,
0x41, 0xc6, 0x47,
0x42, 0xc6, 0x34,
0x43, 0xc6, 0xba,
0xa0, 0xc6, 0x01,
0xa1, 0xc6, 0xff,
0xa2, 0xc6, 0x45,
0xa3, 0xc6, 0x41,
0xb0, 0xc6, 0x1c,
0xb1, 0xc6, 0x00,
0xb2, 0xc6, 0x00,
0xb3, 0xc6, 0x00,
0xc0, 0xc6, 0x02,
0xc1, 0xc6, 0x00,
0xc2, 0xc6, 0x00,
0xc3, 0xc6, 0x00,
0xd0, 0xc6, 0x03,
0xd1, 0xc6, 0xfe,
0xd2, 0xc6, 0x89,
0xd3, 0xc6, 0xfa,
0xe0, 0xc6, 0x1e,
0xe1, 0xc6, 0x01,
0xe2, 0xc6, 0x74,
0xe3, 0xc6, 0xf6,
0x40, 0xc5, 0x0d,
0x30, 0xc7, 0x15,
0x31, 0xc7, 0x7c,
0x32, 0xc7, 0x0f,
0x33, 0xc7, 0xa0,
0x40, 0xc7, 0x00,
0x41, 0xc7, 0x00,
0x42, 0xc7, 0xf8,
0x43, 0xc7, 0xf8,
0x50, 0xc7, 0x00,
0x51, 0xc7, 0x00,
0x52, 0xc7, 0x00,
0x53, 0xc7, 0x01,
0x90, 0xc7, 0x00,
0x91, 0xc7, 0x14,
0x92, 0xc7, 0x00,
0x93, 0xc7, 0x14,
0xa0, 0xc7, 0x00,
0xa1, 0xc7, 0x00,
0xa2, 0xc7, 0xf8,
0xa3, 0xc7, 0xf8,
0xb0, 0xc7, 0x00,
0xb1, 0xc7, 0x00,
0xb2, 0xc7, 0x00,
0xb3, 0xc7, 0x00,
0x60, 0xc7, 0x03,
0x61, 0xc7, 0xe8,
0x62, 0xc7, 0x03,
0x63, 0xc7, 0xb6,
0x70, 0xc7, 0x00,
0x71, 0xc7, 0x00,
0x72, 0xc7, 0xf8,
0x73, 0xc7, 0xf8,
0x80, 0xc7, 0x00,
0x81, 0xc7, 0x00,
0x82, 0xc7, 0x00,
0x83, 0xc7, 0x00,
0xc0, 0xc7, 0x00,
0xc1, 0xc7, 0x14,
0xc2, 0xc7, 0x00,
0xc3, 0xc7, 0x14,
0xd0, 0xc7, 0x00,
0xd1, 0xc7, 0x00,
0xd2, 0xc7, 0xf8,
0xd3, 0xc7, 0xf8,
0xe0, 0xc7, 0x00,
0xe1, 0xc7, 0x00,
0xe2, 0xc7, 0x00,
0xe3, 0xc7, 0x00,
0x60, 0xc5, 0x02,
0x61, 0xc5, 0x00,
0x62, 0xc5, 0x00,
0x63, 0xc5, 0x00,
0x70, 0xc5, 0x02,
0x71, 0xc5, 0x00,
0x72, 0xc5, 0x00,
0x73, 0xc5, 0x00,
0x80, 0xc5, 0x02,
0x81, 0xc5, 0x00,
0x82, 0xc5, 0x00,
0x83, 0xc5, 0x00,
0x90, 0xc5, 0x02,
0x91, 0xc5, 0x00,
0x92, 0xc5, 0x00,
0x93, 0xc5, 0x00,
0x50, 0xc5, 0x01,
};
static const u8 __maybe_unused dell_0b00_bq_params[] = {
0x03, 0xc2, 0x00,
0x04, 0xc2, 0xb2,
0x05, 0xc2, 0xe0,
0x06, 0xc2, 0x3a,
0x07, 0xc2, 0x01,
0x08, 0xc2, 0x65,
0x09, 0xc2, 0xc0,
0x0a, 0xc2, 0x75,
0x0b, 0xc2, 0x00,
0x0c, 0xc2, 0xb2,
0x0d, 0xc2, 0xe0,
0x0e, 0xc2, 0x3a,
0x0f, 0xc2, 0xf7,
0x10, 0xc2, 0x4d,
0x11, 0xc2, 0x5b,
0x12, 0xc2, 0xe9,
0x13, 0xc2, 0x03,
0x14, 0xc2, 0x7e,
0x15, 0xc2, 0x25,
0x16, 0xc2, 0x01,
0x17, 0xc2, 0x07,
0x18, 0xc2, 0xfd,
0x19, 0xc2, 0x15,
0x1a, 0xc2, 0x04,
0x1b, 0xc2, 0xf0,
0x1c, 0xc2, 0x05,
0x1d, 0xc2, 0xd5,
0x1e, 0xc2, 0xf7,
0x1f, 0xc2, 0x07,
0x20, 0xc2, 0xfd,
0x21, 0xc2, 0x15,
0x22, 0xc2, 0x04,
0x23, 0xc2, 0xf0,
0x24, 0xc2, 0x05,
0x25, 0xc2, 0xd8,
0x26, 0xc2, 0x17,
0x27, 0xc2, 0x07,
0x28, 0xc2, 0xfa,
0x29, 0xc2, 0x2c,
0x2a, 0xc2, 0x29,
0x2b, 0xc2, 0x07,
0x2c, 0xc2, 0x74,
0x2d, 0xc2, 0xe0,
0x2e, 0xc2, 0x33,
0x2f, 0xc2, 0xf1,
0x30, 0xc2, 0x16,
0x31, 0xc2, 0x3f,
0x32, 0xc2, 0x9b,
0x33, 0xc2, 0x07,
0x34, 0xc2, 0x74,
0x35, 0xc2, 0xe0,
0x36, 0xc2, 0x33,
0x37, 0xc2, 0xf1,
0x38, 0xc2, 0x29,
0x39, 0xc2, 0xb0,
0x3a, 0xc2, 0x4d,
0x3b, 0xc2, 0x06,
0x3c, 0xc2, 0xfd,
0x3d, 0xc2, 0x31,
0x3e, 0xc2, 0x18,
0x3f, 0xc2, 0x07,
0x40, 0xc2, 0xfd,
0x41, 0xc2, 0x15,
0x42, 0xc2, 0x04,
0x43, 0xc2, 0xf0,
0x44, 0xc2, 0x05,
0x45, 0xc2, 0xd5,
0x46, 0xc2, 0xf7,
0x47, 0xc2, 0x07,
0x48, 0xc2, 0xfd,
0x49, 0xc2, 0x15,
0x4a, 0xc2, 0x04,
0x4b, 0xc2, 0xf0,
0x4c, 0xc2, 0x05,
0x4d, 0xc2, 0xd8,
0x4e, 0xc2, 0x17,
0x4f, 0xc2, 0x07,
0x50, 0xc2, 0xfa,
0x51, 0xc2, 0x2c,
0x52, 0xc2, 0x29,
0x0b, 0xc0, 0x30,
0x80, 0xc3, 0x13,
0x81, 0xc3, 0x88,
0x82, 0xc3, 0x17,
0x83, 0xc3, 0x70,
0x84, 0xc3, 0x00,
0x85, 0xc3, 0x00,
0x86, 0xc3, 0xff,
0x87, 0xc3, 0xee,
0x88, 0xc3, 0x02,
0x92, 0xc3, 0x00,
0x93, 0xc3, 0x14,
0x94, 0xc3, 0x00,
0x95, 0xc3, 0x14,
0x96, 0xc3, 0x00,
0x97, 0xc3, 0x00,
0x98, 0xc3, 0x00,
0x99, 0xc3, 0x00,
0x9a, 0xc3, 0x01,
0x89, 0xc3, 0x03,
0x8a, 0xc3, 0xe8,
0x8b, 0xc3, 0x03,
0x8c, 0xc3, 0xb6,
0x8d, 0xc3, 0x00,
0x8e, 0xc3, 0x00,
0x8f, 0xc3, 0xff,
0x90, 0xc3, 0xee,
0x91, 0xc3, 0x01,
0x9b, 0xc3, 0x00,
0x9c, 0xc3, 0x14,
0x9d, 0xc3, 0x00,
0x9e, 0xc3, 0x14,
0x9f, 0xc3, 0x00,
0xa0, 0xc3, 0x00,
0xa1, 0xc3, 0x00,
0xa2, 0xc3, 0x00,
0xa3, 0xc3, 0x01,
0x61, 0xc2, 0x08,
0x62, 0xc2, 0x00,
0x63, 0xc2, 0x00,
0x64, 0xc2, 0x00,
0x65, 0xc2, 0x08,
0x66, 0xc2, 0x00,
0x67, 0xc2, 0x00,
0x68, 0xc2, 0x00,
0x69, 0xc2, 0x08,
0x6a, 0xc2, 0x00,
0x6b, 0xc2, 0x00,
0x6c, 0xc2, 0x00,
0x6d, 0xc2, 0x08,
0x6e, 0xc2, 0x00,
0x6f, 0xc2, 0x00,
0x70, 0xc2, 0x00,
0x00, 0xc2, 0xc0,
};
#endif
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2024 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt_dmic - Helpers to handle Realtek SDW DMIC from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc_sdw_utils.h>
#include <sound/sdca_function.h>
int asoc_sdw_rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_component *component;
struct sdw_slave *sdw_peripheral = NULL;
char *mic_name;
int rt1320_dmic_num = 0, part_id, i;
component = dai->component;
/*
* rt715-sdca (aka rt714) is a special case that uses different name in card->components
* and component->name_prefix.
*/
if (!strcmp(component->name_prefix, "rt714"))
mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "rt715-sdca");
else
mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s", component->name_prefix);
if (!mic_name)
return -ENOMEM;
/*
* If there is any rt1320/rt1321 DMIC belonging to this card, try to count the `cfg-mics`
* to be used in card->components.
* Note: The rt1320 drivers register the peripheral dev to component->dev, so get the
* sdw_peripheral from component->dev.
*/
if (is_sdw_slave(component->dev))
sdw_peripheral = dev_to_sdw_dev(component->dev);
if (sdw_peripheral &&
(sdw_peripheral->id.part_id == 0x1320 || sdw_peripheral->id.part_id == 0x1321)) {
part_id = sdw_peripheral->id.part_id;
/*
* This rtd init callback is called once, so count the rt1320/rt1321 with SDCA
* function SmartMic type in this card.
*/
for_each_card_components(card, component) {
if (!is_sdw_slave(component->dev))
continue;
sdw_peripheral = dev_to_sdw_dev(component->dev);
if (sdw_peripheral->id.part_id != part_id)
continue;
for (i = 0; i < sdw_peripheral->sdca_data.num_functions; i++) {
if (sdw_peripheral->sdca_data.function[i].type ==
SDCA_FUNCTION_TYPE_SMART_MIC) {
rt1320_dmic_num++;
break;
}
}
}
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:%s cfg-mics:%d", card->components,
mic_name, rt1320_dmic_num);
} else {
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:%s", card->components,
mic_name);
}
if (!card->components)
return -ENOMEM;
dev_dbg(card->dev, "card->components: %s\n", card->components);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_dmic_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2024 Intel Corporation.
/*
* soc_sdw_rt_mf_sdca
* - Helpers to handle RT Multifunction Codec from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/soc_sdw_utils.h>
#define CODEC_NAME_SIZE 6
/* dapm routes for RT-SPK will be registered dynamically */
static const struct snd_soc_dapm_route rt712_spk_map[] = {
{ "Speaker", NULL, "rt712 SPOL" },
{ "Speaker", NULL, "rt712 SPOR" },
};
static const struct snd_soc_dapm_route rt721_spk_map[] = {
{ "Speaker", NULL, "rt721 SPK" },
};
static const struct snd_soc_dapm_route rt722_spk_map[] = {
{ "Speaker", NULL, "rt722 SPK" },
};
/* Structure to map codec names to respective route arrays and sizes */
struct codec_route_map {
const char *codec_name;
const struct snd_soc_dapm_route *route_map;
size_t route_size;
};
/* Codec route maps array */
static const struct codec_route_map codec_routes[] = {
{ "rt712", rt712_spk_map, ARRAY_SIZE(rt712_spk_map) },
{ "rt721", rt721_spk_map, ARRAY_SIZE(rt721_spk_map) },
{ "rt722", rt722_spk_map, ARRAY_SIZE(rt722_spk_map) },
};
static const struct codec_route_map *get_codec_route_map(const char *codec_name)
{
for (size_t i = 0; i < ARRAY_SIZE(codec_routes); i++) {
if (strcmp(codec_routes[i].codec_name, codec_name) == 0)
return &codec_routes[i];
}
return NULL;
}
int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
char codec_name[CODEC_NAME_SIZE];
int ret;
/* acquire codec name */
snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
/* acquire corresponding route map and size */
const struct codec_route_map *route_map = get_codec_route_map(codec_name);
if (!route_map) {
dev_err(rtd->dev, "failed to get codec name and route map\n");
return -EINVAL;
}
/* Add routes */
ret = snd_soc_dapm_add_routes(dapm, route_map->route_map, route_map->route_size);
if (ret)
dev_err(rtd->dev, "failed to add rt sdca spk map: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_mf_sdca_spk_rtd_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0-only
// This file incorporates work covered by the following copyright notice:
// Copyright (c) 2020 Intel Corporation
// Copyright (c) 2024 Advanced Micro Devices, Inc.
/*
* soc_sdw_rt711_sdca - Helpers to handle RT711-SDCA from generic machine driver
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc_sdw_utils.h>
/*
* Note this MUST be called before snd_soc_register_card(), so that the props
* are in place before the codec component driver's probe function parses them.
*/
static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
{
struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
struct fwnode_handle *fwnode;
int ret;
if (!SOC_SDW_JACK_JDSRC(quirk))
return 0;
props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(quirk));
fwnode = fwnode_create_software_node(props, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
fwnode_handle_put(fwnode);
return ret;
}
static const struct snd_soc_dapm_route rt711_sdca_map[] = {
{ "Headphone", NULL, "rt711 HP" },
{ "rt711 MIC2", NULL, "Headset Mic" },
};
static const struct snd_soc_dapm_route rt712_sdca_map[] = {
{ "Headphone", NULL, "rt712 HP" },
{ "rt712 MIC2", NULL, "Headset Mic" },
};
static const struct snd_soc_dapm_route rt713_sdca_map[] = {
{ "Headphone", NULL, "rt713 HP" },
{ "rt713 MIC2", NULL, "Headset Mic" },
};
static const struct snd_soc_dapm_route rt721_sdca_map[] = {
{ "Headphone", NULL, "rt721 HP" },
{ "rt721 MIC2", NULL, "Headset Mic" },
};
static const struct snd_soc_dapm_route rt722_sdca_map[] = {
{ "Headphone", NULL, "rt722 HP" },
{ "rt722 MIC2", NULL, "Headset Mic" },
};
static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
/*
* The sdca suffix is required for rt711 since there are two generations of the same chip.
* RT713 is an SDCA device but the sdca suffix is required for backwards-compatibility with
* previous UCM definitions.
*/
static const char * const need_sdca_suffix[] = {
"rt711", "rt713"
};
int asoc_sdw_rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
int i;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:%s",
card->components, component->name_prefix);
if (!card->components)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(need_sdca_suffix); i++) {
if (strstr(component->name_prefix, need_sdca_suffix[i])) {
/* Add -sdca suffix for existing UCMs */
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s-sdca", card->components);
if (!card->components)
return -ENOMEM;
break;
}
}
if (strstr(component->name_prefix, "rt711")) {
ret = snd_soc_dapm_add_routes(dapm, rt711_sdca_map,
ARRAY_SIZE(rt711_sdca_map));
} else if (strstr(component->name_prefix, "rt712")) {
ret = snd_soc_dapm_add_routes(dapm, rt712_sdca_map,
ARRAY_SIZE(rt712_sdca_map));
} else if (strstr(component->name_prefix, "rt713")) {
ret = snd_soc_dapm_add_routes(dapm, rt713_sdca_map,
ARRAY_SIZE(rt713_sdca_map));
} else if (strstr(component->name_prefix, "rt721")) {
ret = snd_soc_dapm_add_routes(dapm, rt721_sdca_map,
ARRAY_SIZE(rt721_sdca_map));
} else if (strstr(component->name_prefix, "rt722")) {
ret = snd_soc_dapm_add_routes(dapm, rt722_sdca_map,
ARRAY_SIZE(rt722_sdca_map));
} else {
dev_err(card->dev, "%s is not supported\n", component->name_prefix);
return -EINVAL;
}
if (ret) {
dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
&ctx->sdw_headset,
rt_sdca_jack_pins,
ARRAY_SIZE(rt_sdca_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
ret);
return ret;
}
jack = &ctx->sdw_headset;
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(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
if (!ctx->headset_codec_dev)
return 0;
if (!SOC_SDW_JACK_JDSRC(ctx->mc_quirk))
return 0;
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
ctx->headset_codec_dev = NULL;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_exit, "SND_SOC_SDW_UTILS");
int asoc_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct device *sdw_dev;
int ret;
/*
* Jack detection should be only initialized once for headsets since
* the playback/capture is sharing the same jack
*/
if (ctx->headset_codec_dev)
return 0;
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
if (!sdw_dev)
return -EPROBE_DEFER;
ret = rt_sdca_jack_add_codec_device_props(sdw_dev, ctx->mc_quirk);
if (ret < 0) {
put_device(sdw_dev);
return ret;
}
ctx->headset_codec_dev = sdw_dev;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_init, "SND_SOC_SDW_UTILS");
@@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2025 Texas Instruments Inc.
/*
* soc_sdw_ti_amp - Helpers to handle TI's soundwire based codecs
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <sound/jack.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dai.h>
#include <sound/soc.h>
#include <sound/soc_sdw_utils.h>
#define TIAMP_SPK_VOLUME_0DB 200
#define TAC5XX2_WIDGET_NAME_MAX 32
int asoc_sdw_ti_amp_initial_settings(struct snd_soc_card *card,
const char *name_prefix)
{
char *volume_ctl_name;
int ret;
volume_ctl_name = kasprintf(GFP_KERNEL, "%s Speaker Volume",
name_prefix);
if (!volume_ctl_name)
return -ENOMEM;
ret = snd_soc_limit_volume(card, volume_ctl_name,
TIAMP_SPK_VOLUME_0DB);
if (ret)
dev_err(card->dev,
"%s update failed %d\n",
volume_ctl_name, ret);
kfree(volume_ctl_name);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_amp_initial_settings, "SND_SOC_SDW_UTILS");
int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
char widget_name[16];
char speaker[16];
struct snd_soc_dapm_route route = {speaker, NULL, widget_name};
struct snd_soc_dai *codec_dai;
const char *prefix;
int i, ret = 0;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!strstr(codec_dai->name, "tas2783"))
continue;
prefix = codec_dai->component->name_prefix;
if (!strncmp(prefix, "tas2783-1", strlen("tas2783-1"))) {
strscpy(speaker, "Left Spk", sizeof(speaker));
} else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) {
strscpy(speaker, "Right Spk", sizeof(speaker));
} else if (!strncmp(prefix, "tas2783-3", strlen("tas2783-3"))) {
strscpy(speaker, "Left Spk2", sizeof(speaker));
} else if (!strncmp(prefix, "tas2783-4", strlen("tas2783-4"))) {
strscpy(speaker, "Right Spk2", sizeof(speaker));
} else {
ret = -EINVAL;
dev_err(card->dev, "unhandled prefix %s", prefix);
break;
}
snprintf(widget_name, sizeof(widget_name), "%s SPK", prefix);
ret = asoc_sdw_ti_amp_initial_settings(card, prefix);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, &route, 1);
if (ret)
return ret;
}
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_ti_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback)
{
if (!playback)
return 0;
info->amp_num++;
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_amp_init, "SND_SOC_SDW_UTILS");
static int asoc_sdw_ti_add_tac5xx2_routes(struct snd_soc_dapm_context *dapm,
const char *name_prefix)
{
struct snd_soc_dapm_route routes[2];
char left_widget[TAC5XX2_WIDGET_NAME_MAX];
char right_widget[TAC5XX2_WIDGET_NAME_MAX];
if (strlen(name_prefix) > (TAC5XX2_WIDGET_NAME_MAX - 7))
return -ENAMETOOLONG;
scnprintf(left_widget, sizeof(left_widget), "%s SPK_L", name_prefix);
scnprintf(right_widget, sizeof(right_widget), "%s SPK_R", name_prefix);
routes[0] = (struct snd_soc_dapm_route){"Left Spk", NULL, left_widget};
routes[1] = (struct snd_soc_dapm_route){"Right Spk", NULL, right_widget};
return snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
}
int asoc_sdw_ti_tac5xx2_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret, i;
struct snd_soc_dai *codec_dai;
const char *prefix;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!strstr(codec_dai->name, "tac5") &&
!strstr(codec_dai->name, "tas2883"))
continue;
prefix = codec_dai->component->name_prefix;
if (!prefix) {
dev_warn(card->dev,
"No name prefix found for codec DAI: %s\n",
codec_dai->name);
continue;
}
ret = asoc_sdw_ti_add_tac5xx2_routes(dapm, prefix);
if (ret) {
dev_err(card->dev, "Failed to add routes for %s: %d\n",
prefix, ret);
return ret;
}
}
dev_dbg(card->dev, "Added TAC5XX2 speaker routes\n");
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_tac5xx2_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_ti_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_component *component;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:%s", card->components,
component->name_prefix);
if (!card->components)
return -ENOMEM;
dev_dbg(card->dev, "card->components: %s\n", card->components);
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_dmic_rtd_init, "SND_SOC_SDW_UTILS");
static struct snd_soc_jack_pin ti_sdca_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
int asoc_sdw_ti_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct snd_soc_card *card = rtd->card;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component;
struct snd_soc_jack *jack;
int ret;
component = dai->component;
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s hs:%s", card->components,
component->name_prefix);
if (!card->components)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_BTN_4,
&ctx->sdw_headset,
ti_sdca_jack_pins,
ARRAY_SIZE(ti_sdca_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "Jack create failed%d\n", ret);
return ret;
}
jack = &ctx->sdw_headset;
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);
snd_jack_set_key(jack->jack, SND_JACK_BTN_4, KEY_NEXTSONG);
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret)
dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_ti_sdca_jack_rtd_init, "SND_SOC_SDW_UTILS");
File diff suppressed because it is too large Load Diff