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
@@ -0,0 +1,199 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Intel AVS Machine drivers"
depends on SND_SOC_INTEL_AVS
comment "Available DSP configurations"
config SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE
bool "Use obsolete card names"
default n
help
Use obsolete names for some of avs cards. This option should be
used if your system depends on old card names, for example having
not up to date UCM files.
config SND_SOC_INTEL_AVS_MACH_DA7219
tristate "da7219 I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_DA7219
help
This adds support for AVS with DA7219 I2S codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_DMIC
tristate "DMIC generic board"
select SND_SOC_DMIC
help
This adds support for AVS with Digital Mic array configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_ES8336
tristate "es8336 I2S board"
depends on X86 && I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_ES8316
help
This adds support for AVS with ES8336 I2S codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_HDAUDIO
tristate "HD-Audio generic board"
select SND_SOC_HDA
help
This adds support for AVS with HDAudio codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_I2S_TEST
tristate "I2S test board"
help
This adds support for I2S test-board which can be used to verify
transfer over I2S interface with SSP loopback scenarios.
config SND_SOC_INTEL_AVS_MACH_MAX98927
tristate "max98927 I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_MAX98927
help
This adds support for AVS with MAX98927 I2S codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_MAX98357A
tristate "max98357A I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_MAX98357A
help
This adds support for AVS with MAX98357A I2S codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_MAX98373
tristate "max98373 I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_MAX98373
help
This adds support for AVS with MAX98373 I2S codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_NAU8825
tristate "nau8825 I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_NAU8825
help
This adds support for ASoC machine driver with NAU8825 I2S audio codec.
It is meant to be used with AVS driver.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_PCM3168A
tristate "pcm3168a I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_PCM3168A_I2C
help
This adds support for AVS with PCM3168A I2C codec configuration.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_PROBE
tristate "Probing (data) board"
depends on DEBUG_FS
select SND_HWDEP
help
This adds support for data probing board which can be used to
gather data from runtime stream over compress operations.
config SND_SOC_INTEL_AVS_MACH_RT274
tristate "rt274 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT274
help
This adds support for ASoC machine driver with RT274 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_RT286
tristate "rt286 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT286
help
This adds support for ASoC machine driver with RT286 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_RT298
tristate "rt298 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT298
help
This adds support for ASoC machine driver with RT298 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_RT5514
tristate "rt5514 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT5514
help
This adds support for ASoC machine driver with RT5514 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_RT5640
tristate "rt5640 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT5640
help
This adds support for ASoC machine board connecting AVS with RT5640,
components representing Intel AudioDSP and Realtek 5640 codec respectively.
The codec chip is present on I2C bus and the streaming occurs over I2S
interface.
Say Y or m if you have such a device.
config SND_SOC_INTEL_AVS_MACH_RT5663
tristate "rt5663 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT5663
help
This adds support for ASoC machine driver with RT5663 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_RT5682
tristate "rt5682 in I2S mode"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_RT5682_I2C
help
This adds support for ASoC machine driver with RT5682 I2S audio codec.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
config SND_SOC_INTEL_AVS_MACH_SSM4567
tristate "ssm4567 I2S board"
depends on I2C
depends on MFD_INTEL_LPSS || COMPILE_TEST
select SND_SOC_SSM4567
help
This adds support for ASoC machine driver with SSM4567 I2S audio codec.
It is meant to be used with AVS driver.
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
endmenu
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-avs-da7219-y := da7219.o
snd-soc-avs-dmic-y := dmic.o
snd-soc-avs-es8336-y := es8336.o
snd-soc-avs-hdaudio-y := hdaudio.o
snd-soc-avs-i2s-test-y := i2s_test.o
snd-soc-avs-max98927-y := max98927.o
snd-soc-avs-max98357a-y := max98357a.o
snd-soc-avs-max98373-y := max98373.o
snd-soc-avs-nau8825-y := nau8825.o
snd-soc-avs-pcm3168a-y := pcm3168a.o
snd-soc-avs-probe-y := probe.o
snd-soc-avs-rt274-y := rt274.o
snd-soc-avs-rt286-y := rt286.o
snd-soc-avs-rt298-y := rt298.o
snd-soc-avs-rt5514-y := rt5514.o
snd-soc-avs-rt5640-y := rt5640.o
snd-soc-avs-rt5663-y := rt5663.o
snd-soc-avs-rt5682-y := rt5682.o
snd-soc-avs-ssm4567-y := ssm4567.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_ES8336) += snd-soc-avs-es8336.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO) += snd-soc-avs-hdaudio.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST) += snd-soc-avs-i2s-test.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o
@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/module.h>
#include <linux/platform_data/x86/soc.h>
#include <linux/platform_device.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include <uapi/linux/input-event-codes.h>
#include "../../../codecs/da7219.h"
#include "../utils.h"
#define DA7219_DAI_NAME "da7219-hifi"
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Line Out"),
};
static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret = 0;
codec_dai = snd_soc_card_get_codec_dai(card, DA7219_DAI_NAME);
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found. Unable to set/unset codec pll\n");
return -EIO;
}
if (SND_SOC_DAPM_EVENT_OFF(event)) {
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
if (ret)
dev_err(card->dev, "failed to stop PLL: %d\n", ret);
} else if (SND_SOC_DAPM_EVENT_ON(event)) {
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM,
0, DA7219_PLL_FREQ_OUT_98304);
if (ret)
dev_err(card->dev, "failed to start PLL: %d\n", ret);
}
return ret;
}
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
/* HP jack connectors - unknown if we have jack detection */
{"Headphone Jack", NULL, "HPL"},
{"Headphone Jack", NULL, "HPR"},
{"MIC", NULL, "Headset Mic"},
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
{ "Line Out", NULL, "Platform Clock" },
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Line Out",
.mask = SND_JACK_LINEOUT,
},
};
static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins;
int clk_freq;
int ret;
jack = snd_soc_card_get_drvdata(card);
if (soc_intel_is_apl())
clk_freq = 19200000;
else /* kbl */
clk_freq = 24576000;
ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, clk_freq, SND_SOC_CLOCK_IN);
if (ret) {
dev_err(card->dev, "can't set codec sysclk configuration\n");
return ret;
}
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
/*
* Headset buttons map to the google Reference headset.
* These can be configured by userspace.
*/
ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_LINEOUT,
jack, pins, num_pins);
if (ret) {
dev_err(card->dev, "Headset Jack creation failed: %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_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);
return snd_soc_component_set_jack(component, jack, NULL);
}
static void avs_da7219_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-DLGS7219:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, DA7219_DAI_NAME);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->be_hw_params_fixup = avs_da7219_be_fixup;
dl->init = avs_da7219_codec_init;
dl->exit = avs_da7219_codec_exit;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_da7219_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_da7219";
} else {
card->driver_name = "avs_da7219";
card->long_name = card->name = "AVS I2S DA7219";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_da7219_driver_ids[] = {
{
.name = "avs_da7219",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids);
static struct platform_driver avs_da7219_driver = {
.probe = avs_da7219_probe,
.driver = {
.name = "avs_da7219",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_da7219_driver_ids,
};
module_platform_driver(avs_da7219_driver);
MODULE_DESCRIPTION("Intel da7219 machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/device.h>
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../utils.h"
SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin")));
SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin")));
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
};
static const struct snd_soc_dapm_route card_routes[] = {
{"DMic", NULL, "SoC DMIC"},
};
static int avs_create_dai_links(struct device *dev, const char *codec_name,
struct snd_soc_dai_link **links, int *num_links)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
const int num_dl = 2;
dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->codecs)
return -ENOMEM;
dl->codecs->name = devm_kstrdup(dev, codec_name, GFP_KERNEL);
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "dmic-hifi");
if (!dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl[0].num_cpus = 1;
dl[0].num_codecs = 1;
dl[0].platforms = platform;
dl[0].num_platforms = 1;
dl[0].nonatomic = 1;
dl[0].no_pcm = 1;
dl[0].capture_only = 1;
memcpy(&dl[1], &dl[0], sizeof(*dl));
dl[0].name = "DMIC";
dl[0].cpus = dmic_pin;
dl[0].id = 0;
dl[1].name = "DMIC WoV";
dl[1].cpus = dmic_wov_pin;
dl[1].id = 1;
dl[1].ignore_suspend = 1;
*links = dl;
*num_links = num_dl;
return 0;
}
static int avs_dmic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
int ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
ret = avs_create_dai_links(dev, pdata->codec_name, &card->dai_link, &card->num_links);
if (ret)
return ret;
if (pdata->obsolete_card_names) {
card->name = "avs_dmic";
} else {
card->driver_name = "avs_dmic";
card->long_name = card->name = "AVS DMIC";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_dmic_driver_ids[] = {
{
.name = "avs_dmic",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids);
static struct platform_driver avs_dmic_driver = {
.probe = avs_dmic_probe,
.driver = {
.name = "avs_dmic",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_dmic_driver_ids,
};
module_platform_driver(avs_dmic_driver);
MODULE_DESCRIPTION("Intel DMIC machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,331 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2023 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/processor.h>
#include <linux/slab.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <asm/cpu_device_id.h>
#include "../utils.h"
#define ES8336_CODEC_DAI "ES8316 HiFi"
struct avs_card_drvdata {
struct snd_soc_jack jack;
struct gpio_desc *gpiod;
};
static const struct acpi_gpio_params enable_gpio = { 0, 0, true };
static const struct acpi_gpio_mapping speaker_gpios[] = {
{ "speaker-enable-gpios", &enable_gpio, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
{ }
};
static int avs_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct avs_card_drvdata *data;
bool speaker_en;
data = snd_soc_card_get_drvdata(card);
/* As enable_gpio has active_low=true, logic is inverted. */
speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
gpiod_set_value_cansleep(data->gpiod, speaker_en);
return 0;
}
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Internal Mic", NULL),
SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
avs_es8336_speaker_power_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
};
static const struct snd_soc_dapm_route card_routes[] = {
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
/*
* There is no separate speaker output instead the speakers are muxed to
* the HP outputs. The mux is controlled by the "Speaker Power" widget.
*/
{"Speaker", NULL, "HPOL"},
{"Speaker", NULL, "HPOR"},
{"Speaker", NULL, "Speaker Power"},
/* Mic route map */
{"MIC1", NULL, "Internal Mic"},
{"MIC2", NULL, "Headset Mic"},
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Speaker"),
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Internal Mic"),
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_card *card = runtime->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct snd_soc_jack_pin *pins;
struct avs_card_drvdata *data;
struct gpio_desc *gpiod;
int num_pins, ret;
data = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
&data->jack, pins, num_pins);
if (ret)
return ret;
ret = devm_acpi_dev_add_driver_gpios(codec_dai->dev, speaker_gpios);
if (ret)
dev_warn(codec_dai->dev, "Unable to add GPIO mapping table\n");
gpiod = gpiod_get_optional(codec_dai->dev, "speaker-enable", GPIOD_OUT_LOW);
if (IS_ERR(gpiod))
return dev_err_probe(codec_dai->dev, PTR_ERR(gpiod), "Get gpiod failed: %ld\n",
PTR_ERR(gpiod));
data->gpiod = gpiod;
snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_soc_component_set_jack(component, &data->jack, NULL);
snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime)
{
struct avs_card_drvdata *data = snd_soc_card_get_drvdata(runtime->card);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
gpiod_put(data->gpiod);
}
static int avs_es8336_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int clk_freq;
int ret;
switch (boot_cpu_data.x86_vfm) {
case INTEL_KABYLAKE_L:
case INTEL_KABYLAKE:
clk_freq = 24000000;
break;
default:
clk_freq = 19200000;
break;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 1, clk_freq, SND_SOC_CLOCK_OUT);
if (ret < 0)
dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_es8336_ops = {
.hw_params = avs_es8336_hw_params,
};
static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSPN to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_3LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-ESSX8336:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, ES8336_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_es8336_codec_init;
dl->exit = avs_es8336_codec_exit;
dl->be_hw_params_fixup = avs_es8336_be_fixup;
dl->ops = &avs_es8336_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
struct avs_card_drvdata *data = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, &data->jack, NULL);
}
static int avs_es8336_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct avs_card_drvdata *data;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!data || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_es8336";
} else {
card->driver_name = "avs_es8336";
card->long_name = card->name = "AVS I2S ES8336";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, data);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_es8336_driver_ids[] = {
{
.name = "avs_es8336",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids);
static struct platform_driver avs_es8336_driver = {
.probe = avs_es8336_probe,
.driver = {
.name = "avs_es8336",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_es8336_driver_ids,
};
module_platform_driver(avs_es8336_driver);
MODULE_DESCRIPTION("Intel es8336 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/hda_codec.h>
#include <sound/hda_i915.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/hda.h"
#include "../utils.h"
static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
struct snd_soc_dai_link **links)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
struct hda_pcm *pcm;
const char *cname = dev_name(&codec->core.dev);
int i;
dl = devm_kcalloc(dev, pcm_count, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
platform->name = dev_name(dev);
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
dl[i].name = devm_kasprintf(dev, GFP_KERNEL, "%s link%d", cname, i);
if (!dl[i].name)
return -ENOMEM;
dl[i].id = i;
dl[i].nonatomic = 1;
dl[i].no_pcm = 1;
dl[i].platforms = platform;
dl[i].num_platforms = 1;
dl[i].ignore_pmdown_time = 1;
dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
if (!dl[i].codecs || !dl[i].cpus)
return -ENOMEM;
dl[i].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d", cname, i);
if (!dl[i].cpus->dai_name)
return -ENOMEM;
dl[i].codecs->name = devm_kstrdup_const(dev, cname, GFP_KERNEL);
if (!dl[i].codecs->name)
return -ENOMEM;
dl[i].codecs->dai_name = pcm->name;
dl[i].num_codecs = 1;
dl[i].num_cpus = 1;
}
*links = dl;
return 0;
}
/* Should be aligned with SectionPCM's name from topology */
#define FEDAI_NAME_PREFIX "HDMI"
static struct snd_pcm *
avs_card_hdmi_pcm_at(struct snd_soc_card *card, int hdmi_idx)
{
struct snd_soc_pcm_runtime *rtd;
int dir = SNDRV_PCM_STREAM_PLAYBACK;
for_each_card_rtds(card, rtd) {
struct snd_pcm *spcm;
int ret, n;
spcm = rtd->pcm ? rtd->pcm->streams[dir].pcm : NULL;
if (!spcm || !strstr(spcm->id, FEDAI_NAME_PREFIX))
continue;
ret = sscanf(spcm->id, FEDAI_NAME_PREFIX "%d", &n);
if (ret != 1)
continue;
if (n == hdmi_idx)
return rtd->pcm;
}
return NULL;
}
static int avs_card_late_probe(struct snd_soc_card *card)
{
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
struct avs_mach_pdata *pdata = mach->pdata;
struct hda_codec *codec = pdata->codec;
struct hda_pcm *hpcm;
/* Topology pcm indexing is 1-based */
int i = 1;
list_for_each_entry(hpcm, &codec->pcm_list_head, list) {
struct snd_pcm *spcm;
spcm = avs_card_hdmi_pcm_at(card, i);
if (spcm) {
hpcm->pcm = spcm;
hpcm->device = spcm->device;
dev_info(card->dev, "%s: mapping HDMI converter %d to PCM %d (%p)\n",
__func__, i, hpcm->device, spcm);
} else {
hpcm->pcm = NULL;
hpcm->device = SNDRV_PCM_INVALID_DEVICE;
dev_warn(card->dev, "%s: no PCM in topology for HDMI converter %d\n",
__func__, i);
}
i++;
}
return hda_codec_probe_complete(codec);
}
static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
{
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_dai_link *links = NULL;
struct snd_soc_card *card = rtm->card;
struct hda_codec *codec;
struct hda_pcm *pcm;
int ret, pcm_count = 0;
mach = dev_get_platdata(card->dev);
pdata = mach->pdata;
codec = pdata->codec;
if (list_empty(&codec->pcm_list_head))
return -EINVAL;
list_for_each_entry(pcm, &codec->pcm_list_head, list)
pcm_count++;
ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
if (ret < 0) {
dev_err(card->dev, "create links failed: %d\n", ret);
return ret;
}
ret = snd_soc_add_pcm_runtimes(card, links, pcm_count);
if (ret < 0) {
dev_err(card->dev, "add links failed: %d\n", ret);
return ret;
}
return 0;
}
static const struct snd_soc_dai_link probing_link = {
.name = "probing-LINK",
.id = -1,
.nonatomic = 1,
.no_pcm = 1,
.cpus = &snd_soc_dummy_dlc,
.num_cpus = 1,
.init = avs_probing_link_init,
};
static int avs_hdaudio_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *binder;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
struct hda_codec *codec;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
codec = pdata->codec;
/* codec may be unloaded before card's probe() fires */
if (!device_is_registered(&codec->core.dev))
return -ENODEV;
binder = devm_kmemdup(dev, &probing_link, sizeof(probing_link), GFP_KERNEL);
if (!binder)
return -ENOMEM;
binder->platforms = devm_kzalloc(dev, sizeof(*binder->platforms), GFP_KERNEL);
binder->codecs = devm_kzalloc(dev, sizeof(*binder->codecs), GFP_KERNEL);
if (!binder->platforms || !binder->codecs)
return -ENOMEM;
binder->codecs->name = devm_kstrdup_const(dev, dev_name(&codec->core.dev), GFP_KERNEL);
if (!binder->codecs->name)
return -ENOMEM;
binder->platforms->name = dev_name(dev);
binder->num_platforms = 1;
binder->codecs->dai_name = "codec-probing-DAI";
binder->num_codecs = 1;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = devm_kasprintf(dev, GFP_KERNEL, "hdaudioB%dD%d", codec->bus->core.idx,
codec->core.addr);
if (!card->name)
return -ENOMEM;
} else {
card->driver_name = "avs_hdaudio";
if (hda_codec_is_display(codec))
card->long_name = card->name = "AVS HDMI";
else
card->long_name = card->name = "AVS HD-Audio";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = binder;
card->num_links = 1;
card->fully_routed = true;
if (hda_codec_is_display(codec))
card->late_probe = avs_card_late_probe;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_hdaudio_driver_ids[] = {
{
.name = "avs_hdaudio",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids);
static struct platform_driver avs_hdaudio_driver = {
.probe = avs_hdaudio_probe,
.driver = {
.name = "avs_hdaudio",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_hdaudio_driver_ids,
};
module_platform_driver(avs_hdaudio_driver)
MODULE_DESCRIPTION("Intel HD-Audio machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include "../utils.h"
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
if (!dl->name || !dl->cpus)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs = &snd_soc_dummy_dlc;
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_i2s_test_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
if (!avs_mach_singular_ssp(mach)) {
dev_err(dev, "Invalid SSP configuration\n");
return -EINVAL;
}
ssp_port = avs_mach_ssp_port(mach);
if (!avs_mach_singular_tdm(mach, ssp_port)) {
dev_err(dev, "Invalid TDM configuration\n");
return -EINVAL;
}
tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot));
} else {
card->driver_name = "avs_i2s_test";
card->long_name = card->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("AVS I2S TEST-", "",
ssp_port, tdm_slot));
}
if (!card->name)
return -ENOMEM;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d\n", ret);
return ret;
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_i2s_test_driver_ids[] = {
{
.name = "avs_i2s_test",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids);
static struct platform_driver avs_i2s_test_driver = {
.probe = avs_i2s_test_probe,
.driver = {
.name = "avs_i2s_test",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_i2s_test_driver_ids,
};
module_platform_driver(avs_i2s_test_driver);
MODULE_DESCRIPTION("Intel i2s test machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include "../utils.h"
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Spk"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_SPK("Spk", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{ "Spk", NULL, "Speaker" },
};
static int
avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 16 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "MX98357A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "HiFi");
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->be_hw_params_fixup = avs_max98357a_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->playback_only = 1;
*dai_link = dl;
return 0;
}
static int avs_max98357a_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_max98357a";
} else {
card->driver_name = "avs_max98357a";
card->long_name = card->name = "AVS I2S MAX98357A";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98357a_driver_ids[] = {
{
.name = "avs_max98357a",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids);
static struct platform_driver avs_max98357a_driver = {
.probe = avs_max98357a_probe,
.driver = {
.name = "avs_max98357a",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_max98357a_driver_ids,
};
module_platform_driver(avs_max98357a_driver)
MODULE_DESCRIPTION("Intel max98357a machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include "../utils.h"
#define MAX98373_DEV0_NAME "i2c-MX98373:00"
#define MAX98373_DEV1_NAME "i2c-MX98373:01"
#define MAX98373_CODEC_NAME "max98373-aif1"
static struct snd_soc_codec_conf card_codec_conf[] = {
{
.dlc = COMP_CODEC_CONF(MAX98373_DEV0_NAME),
.name_prefix = "Right",
},
{
.dlc = COMP_CODEC_CONF(MAX98373_DEV1_NAME),
.name_prefix = "Left",
},
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{ "Left Spk", NULL, "Left BE_OUT" },
{ "Right Spk", NULL, "Right BE_OUT" },
};
static int
avs_max98373_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 16 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static int avs_max98373_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
int ret, i;
for_each_rtd_codec_dais(runtime, i, codec_dai) {
if (!strcmp(codec_dai->component->name, MAX98373_DEV0_NAME)) {
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16);
if (ret < 0) {
dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret);
return ret;
}
}
if (!strcmp(codec_dai->component->name, MAX98373_DEV1_NAME)) {
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16);
if (ret < 0) {
dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret);
return ret;
}
}
}
return 0;
}
static const struct snd_soc_ops avs_max98373_ops = {
.hw_params = avs_max98373_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV0_NAME);
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_CODEC_NAME);
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_DEV1_NAME);
dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98373_CODEC_NAME);
if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->be_hw_params_fixup = avs_max98373_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->ignore_pmdown_time = 1;
dl->ops = &avs_max98373_ops;
*dai_link = dl;
return 0;
}
static int avs_max98373_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_max98373";
} else {
card->driver_name = "avs_max98373";
card->long_name = card->name = "AVS I2S MAX98373";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->codec_conf = card_codec_conf;
card->num_configs = ARRAY_SIZE(card_codec_conf);
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98373_driver_ids[] = {
{
.name = "avs_max98373",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids);
static struct platform_driver avs_max98373_driver = {
.probe = avs_max98373_probe,
.driver = {
.name = "avs_max98373",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_max98373_driver_ids,
};
module_platform_driver(avs_max98373_driver)
MODULE_DESCRIPTION("Intel max98373 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
#include "../utils.h"
#define MAX98927_DEV0_NAME "i2c-MX98927:00"
#define MAX98927_DEV1_NAME "i2c-MX98927:01"
#define MAX98927_CODEC_NAME "max98927-aif1"
static struct snd_soc_codec_conf card_codec_conf[] = {
{
.dlc = COMP_CODEC_CONF(MAX98927_DEV0_NAME),
.name_prefix = "Right",
},
{
.dlc = COMP_CODEC_CONF(MAX98927_DEV1_NAME),
.name_prefix = "Left",
},
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{ "Left Spk", NULL, "Left BE_OUT" },
{ "Right Spk", NULL, "Right BE_OUT" },
};
static int
avs_max98927_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 16 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static int avs_max98927_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
int ret = 0;
int i;
for_each_rtd_codec_dais(runtime, i, codec_dai) {
if (!strcmp(codec_dai->component->name, MAX98927_DEV0_NAME))
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16);
else if (!strcmp(codec_dai->component->name, MAX98927_DEV1_NAME))
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16);
if (ret < 0) {
dev_err(runtime->dev, "hw_params for %s failed: %d\n",
codec_dai->component->name, ret);
return ret;
}
}
return 0;
}
static const struct snd_soc_ops avs_max98927_ops = {
.hw_params = avs_max98927_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV0_NAME);
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME);
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV1_NAME);
dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME);
if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->be_hw_params_fixup = avs_max98927_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->ignore_pmdown_time = 1;
dl->ops = &avs_max98927_ops;
*dai_link = dl;
return 0;
}
static int avs_max98927_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_max98927";
} else {
card->driver_name = "avs_max98927";
card->long_name = card->name = "AVS I2S MAX98927";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->codec_conf = card_codec_conf;
card->num_configs = ARRAY_SIZE(card_codec_conf);
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98927_driver_ids[] = {
{
.name = "avs_max98927",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids);
static struct platform_driver avs_max98927_driver = {
.probe = avs_max98927_probe,
.driver = {
.name = "avs_max98927",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_max98927_driver_ids,
};
module_platform_driver(avs_max98927_driver)
MODULE_DESCRIPTION("Intel max98927 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/nau8825.h"
#include "../utils.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
static int
avs_nau8825_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
{
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret;
codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found\n");
return -EINVAL;
}
if (SND_SOC_DAPM_EVENT_ON(event))
ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_MCLK, 24000000,
SND_SOC_CLOCK_IN);
else
ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(card->dev, "Set sysclk failed: %d\n", ret);
return ret;
}
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, avs_nau8825_clock_control,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{ "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" },
{ "MIC", NULL, "Headset Mic" },
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
/*
* 4 buttons here map to the google Reference headset.
* The use of these buttons can be decided by the user space.
*/
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3,
jack, pins, num_pins);
if (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);
return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
avs_nau8825_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_nau8825_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
break;
}
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate, runtime->rate * 256);
if (ret < 0)
dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
break;
case SNDRV_PCM_TRIGGER_RESUME:
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate, runtime->rate * 256);
if (ret < 0)
dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
break;
}
return ret;
}
static const struct snd_soc_ops avs_nau8825_ops = {
.trigger = avs_nau8825_trigger,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10508825:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, SKL_NUVOTON_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_nau8825_codec_init;
dl->exit = avs_nau8825_codec_exit;
dl->be_hw_params_fixup = avs_nau8825_be_fixup;
dl->ops = &avs_nau8825_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
int stream = SNDRV_PCM_STREAM_PLAYBACK;
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found\n");
return -EINVAL;
}
if (snd_soc_dai_stream_active(codec_dai, stream) &&
snd_soc_dai_get_widget(codec_dai, stream)->active)
snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_nau8825_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_nau8825";
} else {
card->driver_name = "avs_nau8825";
card->long_name = card->name = "AVS I2S NAU8825";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_nau8825_driver_ids[] = {
{
.name = "avs_nau8825",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids);
static struct platform_driver avs_nau8825_driver = {
.probe = avs_nau8825_probe,
.driver = {
.name = "avs_nau8825",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_nau8825_driver_ids,
};
module_platform_driver(avs_nau8825_driver)
MODULE_DESCRIPTION("Intel nau8825 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2024-2025 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#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 "../utils.h"
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),
SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),
SND_SOC_DAPM_LINE("CPB Line Out", NULL),
SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),
SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),
SND_SOC_DAPM_LINE("CPB Line In", NULL),
};
static const struct snd_soc_dapm_route card_routes[] = {
{ "CPB Stereo HP 1", NULL, "AOUT1L" },
{ "CPB Stereo HP 1", NULL, "AOUT1R" },
{ "CPB Stereo HP 2", NULL, "AOUT2L" },
{ "CPB Stereo HP 2", NULL, "AOUT2R" },
{ "CPB Stereo HP 3", NULL, "AOUT3L" },
{ "CPB Stereo HP 3", NULL, "AOUT3R" },
{ "CPB Line Out", NULL, "AOUT4L" },
{ "CPB Line Out", NULL, "AOUT4R" },
{ "AIN1L", NULL, "CPB Stereo Mic 1" },
{ "AIN1R", NULL, "CPB Stereo Mic 1" },
{ "AIN2L", NULL, "CPB Stereo Mic 2" },
{ "AIN2R", NULL, "CPB Stereo Mic 2" },
{ "AIN3L", NULL, "CPB Line In" },
{ "AIN3R", NULL, "CPB Line In" },
};
static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime,
struct snd_pcm_hw_params *params)
{
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* Set SSP to 24 bit. */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
SND_SOC_DAILINK_DEF(pcm3168a_dac,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac")));
SND_SOC_DAILINK_DEF(pcm3168a_adc,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc")));
SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin")));
static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
const int num_dl = 2;
dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
platform->name = dev_name(dev);
dl[0].num_cpus = 1;
dl[0].num_codecs = 1;
dl[0].platforms = platform;
dl[0].num_platforms = 1;
dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup;
dl[0].nonatomic = 1;
dl[0].no_pcm = 1;
memcpy(&dl[1], &dl[0], sizeof(*dl));
dl[0].name = "SSP0-Codec-dac";
dl[0].cpus = cpu_ssp0;
dl[0].codecs = pcm3168a_dac;
dl[1].name = "SSP2-Codec-adc";
dl[1].cpus = cpu_ssp2;
dl[1].codecs = pcm3168a_adc;
*links = dl;
*num_links = num_dl;
return 0;
}
static int avs_pcm3168a_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
int ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
if (ret)
return ret;
if (pdata->obsolete_card_names) {
card->name = "avs_pcm3168a";
} else {
card->driver_name = "avs_pcm3168a";
card->long_name = card->name = "AVS I2S PCM3168A";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_pcm3168a_driver_ids[] = {
{
.name = "avs_pcm3168a",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids);
static struct platform_driver avs_pcm3168a_driver = {
.probe = avs_pcm3168a_probe,
.driver = {
.name = "avs_pcm3168a",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_pcm3168a_driver_ids,
};
module_platform_driver(avs_pcm3168a_driver);
MODULE_DESCRIPTION("Intel pcm3168a machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/device.h>
#include <linux/module.h>
#include <sound/soc.h>
static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
{
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
if (!dl)
return -ENOMEM;
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->platforms = devm_kzalloc(dev, sizeof(*dl->platforms), GFP_KERNEL);
if (!dl->cpus || !dl->platforms)
return -ENOMEM;
dl->name = "Compress Probe Capture";
dl->cpus->dai_name = "Probe Extraction CPU DAI";
dl->num_cpus = 1;
dl->codecs = &snd_soc_dummy_dlc;
dl->num_codecs = 1;
dl->platforms->name = dev_name(dev);
dl->num_platforms = 1;
dl->nonatomic = 1;
*links = dl;
*num_links = 1;
return 0;
}
static int avs_probe_mb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
int ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
if (ret)
return ret;
card->driver_name = "avs_probe_mb";
card->long_name = card->name = "AVS PROBE";
card->dev = dev;
card->owner = THIS_MODULE;
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_probe_mb_driver_ids[] = {
{
.name = "avs_probe_mb",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids);
static struct platform_driver avs_probe_mb_driver = {
.probe = avs_probe_mb_probe,
.driver = {
.name = "avs_probe_mb",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_probe_mb_driver_ids,
};
module_platform_driver(avs_probe_mb_driver);
MODULE_DESCRIPTION("Intel probe machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt274.h"
#include "../utils.h"
#define AVS_RT274_FREQ_OUT 24000000
#define AVS_RT274_BE_FIXUP_RATE 48000
#define RT274_CODEC_DAI "rt274-aif1"
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Mic Jack"),
};
static int
avs_rt274_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event)
{
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct snd_soc_dai *codec_dai;
int ret;
codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
if (!codec_dai)
return -EINVAL;
/* Codec needs clock for Jack detection and button press */
ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, AVS_RT274_FREQ_OUT,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret);
return ret;
}
if (SND_SOC_DAPM_EVENT_ON(event)) {
int ratio = 100;
snd_soc_dai_set_bclk_ratio(codec_dai, ratio);
ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK,
AVS_RT274_BE_FIXUP_RATE * ratio, AVS_RT274_FREQ_OUT);
if (ret) {
dev_err(codec_dai->dev, "failed to enable PLL2: %d\n", ret);
return ret;
}
}
return 0;
}
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, avs_rt274_clock_control,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{"Headphone Jack", NULL, "HPO Pin"},
{"MIC", NULL, "Mic Jack"},
{"Headphone Jack", NULL, "Platform Clock"},
{"MIC", NULL, "Platform Clock"},
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Mic Jack",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
struct snd_soc_card *card = runtime->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
num_pins);
if (ret)
return ret;
snd_soc_component_set_jack(component, jack, NULL);
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
if (ret < 0) {
dev_err(card->dev, "can't set codec pcm format %d\n", ret);
return ret;
}
snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
static void avs_rt274_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = AVS_RT274_BE_FIXUP_RATE;
channels->min = channels->max = 2;
/* set SSPN to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT34C2:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT274_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt274_codec_init;
dl->exit = avs_rt274_codec_exit;
dl->be_hw_params_fixup = avs_rt274_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt274_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt274";
} else {
card->driver_name = "avs_rt274";
card->long_name = card->name = "AVS I2S ALC274";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt274_driver_ids[] = {
{
.name = "avs_rt274",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids);
static struct platform_driver avs_rt274_driver = {
.probe = avs_rt274_probe,
.driver = {
.name = "avs_rt274",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt274_driver_ids,
};
module_platform_driver(avs_rt274_driver);
MODULE_DESCRIPTION("Intel rt274 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,250 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt286.h"
#include "../utils.h"
#define RT286_CODEC_DAI "rt286-aif1"
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Mic Jack"),
SOC_DAPM_PIN_SWITCH("Speaker"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
/* HP jack connectors - unknown if we have jack detect */
{"Headphone Jack", NULL, "HPO Pin"},
{"MIC1", NULL, "Mic Jack"},
{"Speaker", NULL, "SPOR"},
{"Speaker", NULL, "SPOL"},
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Mic Jack",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
jack, pins, num_pins);
if (ret)
return ret;
return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_rt286_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int
avs_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_rt286_ops = {
.hw_params = avs_rt286_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT286_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt286_codec_init;
dl->exit = avs_rt286_codec_exit;
dl->be_hw_params_fixup = avs_rt286_be_fixup;
dl->ops = &avs_rt286_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT286_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt286_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt286";
} else {
card->driver_name = "avs_rt286";
card->long_name = card->name = "AVS I2S ALC286";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt286_driver_ids[] = {
{
.name = "avs_rt286",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids);
static struct platform_driver avs_rt286_driver = {
.probe = avs_rt286_probe,
.driver = {
.name = "avs_rt286",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt286_driver_ids,
};
module_platform_driver(avs_rt286_driver);
MODULE_DESCRIPTION("Intel rt286 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,269 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/dmi.h>
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt298.h"
#include "../utils.h"
#define RT298_CODEC_DAI "rt298-aif1"
static const struct dmi_system_id kblr_dmi_table[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
},
},
{}
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Mic Jack"),
SOC_DAPM_PIN_SWITCH("Speaker"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
/* HP jack connectors - unknown if we have jack detect */
{"Headphone Jack", NULL, "HPO Pin"},
{"MIC1", NULL, "Mic Jack"},
{"Speaker", NULL, "SPOR"},
{"Speaker", NULL, "SPOL"},
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Mic Jack",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
jack, pins, num_pins);
if (ret)
return ret;
return snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
}
static void avs_rt298_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int
avs_rt298_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_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int clk_freq;
int ret;
if (dmi_first_match(kblr_dmi_table))
clk_freq = 24000000;
else
clk_freq = 19200000;
ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, clk_freq, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(rtd->dev, "Set codec sysclk failed: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_rt298_ops = {
.hw_params = avs_rt298_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343A:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT298_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
if (dmi_first_match(kblr_dmi_table))
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
else
dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt298_codec_init;
dl->exit = avs_rt298_codec_exit;
dl->be_hw_params_fixup = avs_rt298_be_fixup;
dl->ops = &avs_rt298_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT298_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt298_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt298";
} else {
card->driver_name = "avs_rt298";
card->long_name = card->name = "AVS I2S ALC298";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt298_driver_ids[] = {
{
.name = "avs_rt298",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids);
static struct platform_driver avs_rt298_driver = {
.probe = avs_rt298_probe,
.driver = {
.name = "avs_rt298",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt298_driver_ids,
};
module_platform_driver(avs_rt298_driver);
MODULE_DESCRIPTION("Intel rt298 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2023 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/clk.h>
#include <linux/input.h>
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt5514.h"
#include "../utils.h"
#define RT5514_CODEC_DAI "rt5514-aif1"
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
/* DMIC */
{ "DMIC1L", NULL, "DMIC" },
{ "DMIC1R", NULL, "DMIC" },
{ "DMIC2L", NULL, "DMIC" },
{ "DMIC2R", NULL, "DMIC" },
};
static int avs_rt5514_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(runtime->card);
int ret = snd_soc_dapm_ignore_suspend(dapm, "DMIC");
if (ret)
dev_err(runtime->dev, "DMIC - Ignore suspend failed = %d\n", ret);
return ret;
}
static int avs_rt5514_be_fixup(struct snd_soc_pcm_runtime *runtime,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
rate->min = rate->max = 48000;
channels->min = channels->max = 4;
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static int avs_rt5514_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_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16);
if (ret < 0) {
dev_err(rtd->dev, "set TDM slot err:%d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(rtd->dev, "set sysclk err: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_rt5514_ops = {
.hw_params = avs_rt5514_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5514:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5514_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt5514_codec_init;
dl->be_hw_params_fixup = avs_rt5514_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->capture_only = 1;
dl->ops = &avs_rt5514_ops;
*dai_link = dl;
return 0;
}
static int avs_rt5514_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt5514";
} else {
card->driver_name = "avs_rt5514";
card->long_name = card->name = "AVS I2S ALC5514";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5514_driver_ids[] = {
{
.name = "avs_rt5514",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids);
static struct platform_driver avs_rt5514_driver = {
.probe = avs_rt5514_probe,
.driver = {
.name = "avs_rt5514",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt5514_driver_ids,
};
module_platform_driver(avs_rt5514_driver);
MODULE_DESCRIPTION("Intel rt5514 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022-2025 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt5640.h"
#include "../utils.h"
#define AVS_RT5640_MCLK_HZ 19200000
#define RT5640_CODEC_DAI "rt5640-aif1"
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
static const struct snd_soc_dapm_route card_routes[] = {
{ "Headphone Jack", NULL, "HPOR" },
{ "Headphone Jack", NULL, "HPOL" },
{ "IN2P", NULL, "Mic Jack" },
{ "IN2P", NULL, "MICBIAS1" },
{ "Speaker", NULL, "SPOLP" },
{ "Speaker", NULL, "SPOLN" },
{ "Speaker", NULL, "SPORP" },
{ "Speaker", NULL, "SPORN" },
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Mic Jack",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
struct snd_soc_card *card = runtime->card;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
num_pins);
if (ret)
return ret;
snd_soc_component_set_jack(codec_dai->component, jack, NULL);
snd_soc_dapm_set_idle_bias(dapm, false);
return 0;
}
static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime,
struct snd_pcm_hw_params *params)
{
struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */
if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_rt5640_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int ret;
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ,
params_rate(params) * 512);
if (ret < 0) {
dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret);
return ret;
}
ret = rt5640_sel_asrc_clk_src(codec_dai->component,
RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER |
RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER |
RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER,
RT5640_CLK_SEL_ASRC);
if (ret)
dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_rt5640_ops = {
.hw_params = avs_rt5640_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_acpi_mach *mach,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
u32 uid = 0;
int ret;
if (mach->uid) {
ret = kstrtou32(mach->uid, 0, &uid);
if (ret)
return ret;
uid--; /* 0-based indexing. */
}
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid);
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt5640_codec_init;
dl->exit = avs_rt5640_codec_exit;
dl->be_hw_params_fixup = avs_rt5640_be_fixup;
dl->ops = &avs_rt5640_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt5640_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (mach->uid) {
card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid);
if (!card->name)
return -ENOMEM;
} else {
card->name = "AVS I2S ALC5640";
}
card->driver_name = "avs_rt5640";
card->long_name = card->name;
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5640_driver_ids[] = {
{
.name = "avs_rt5640",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids);
static struct platform_driver avs_rt5640_driver = {
.probe = avs_rt5640_probe,
.driver = {
.name = "avs_rt5640",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt5640_driver_ids,
};
module_platform_driver(avs_rt5640_driver);
MODULE_DESCRIPTION("Intel rt5640 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022-2023 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/clk.h>
#include <linux/input.h>
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/rt5663.h"
#include "../utils.h"
#define RT5663_CODEC_DAI "rt5663-aif"
struct rt5663_private {
struct snd_soc_jack jack;
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static const struct snd_soc_dapm_route card_routes[] = {
/* HP jack connectors */
{ "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" },
/* Mic jacks */
{ "IN1P", NULL, "Headset Mic" },
{ "IN1N", NULL, "Headset Mic" },
};
static const struct snd_soc_jack_pin card_headset_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct rt5663_private *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = &priv->jack;
num_pins = ARRAY_SIZE(card_headset_pins);
pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins,
sizeof(card_headset_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
pins, num_pins);
if (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);
snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, jack, NULL);
return 0;
}
static void avs_rt5663_codec_exit(struct snd_soc_pcm_runtime *runtime)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(runtime, 0)->component, NULL, NULL);
}
static int
avs_rt5663_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSPN to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_rt5663_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_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
/* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */
rt5663_sel_asrc_clk_src(codec_dai->component,
RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER,
RT5663_CLK_SEL_I2S1_ASRC);
ret = snd_soc_dai_set_sysclk(codec_dai, RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
return ret;
}
static const struct snd_soc_ops avs_rt5663_ops = {
.hw_params = avs_rt5663_hw_params,
};
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5663:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5663_CODEC_DAI);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt5663_codec_init;
dl->exit = avs_rt5663_codec_exit;
dl->be_hw_params_fixup = avs_rt5663_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->ops = &avs_rt5663_ops;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt5663_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct rt5663_private *priv;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!priv || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt5663";
} else {
card->driver_name = "avs_rt5663";
card->long_name = card->name = "AVS I2S ALC5663";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_routes;
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, priv);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5663_driver_ids[] = {
{
.name = "avs_rt5663",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids);
static struct platform_driver avs_rt5663_driver = {
.probe = avs_rt5663_probe,
.driver = {
.name = "avs_rt5663",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt5663_driver_ids,
};
module_platform_driver(avs_rt5663_driver);
MODULE_DESCRIPTION("Intel rt5663 machine driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/clk.h>
#include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/rt5682.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../common/soc-intel-quirks.h"
#include "../../../codecs/rt5682.h"
#include "../utils.h"
#define AVS_RT5682_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
#define AVS_RT5682_SSP_CODEC_MASK (GENMASK(2, 0))
#define AVS_RT5682_MCLK_EN BIT(3)
#define AVS_RT5682_MCLK_24MHZ BIT(4)
#define AVS_RT5682_CODEC_DAI_NAME "rt5682-aif1"
/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long avs_rt5682_quirk = AVS_RT5682_MCLK_EN | AVS_RT5682_SSP_CODEC(0);
static int avs_rt5682_quirk_cb(const struct dmi_system_id *id)
{
avs_rt5682_quirk = (unsigned long)id->driver_data;
return 1;
}
static const struct dmi_system_id avs_rt5682_quirk_table[] = {
{
.callback = avs_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"),
},
.driver_data = (void *)(AVS_RT5682_MCLK_EN |
AVS_RT5682_MCLK_24MHZ |
AVS_RT5682_SSP_CODEC(1)),
},
{
.callback = avs_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
},
.driver_data = (void *)(AVS_RT5682_MCLK_EN |
AVS_RT5682_SSP_CODEC(0)),
},
{}
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
/* HP jack connectors - unknown if we have jack detect */
{ "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" },
/* other jacks */
{ "IN1P", NULL, "Headset Mic" },
};
static const struct snd_soc_jack_pin card_jack_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
struct snd_soc_card *card = runtime->card;
struct snd_soc_jack_pin *pins;
struct snd_soc_jack *jack;
int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
num_pins = ARRAY_SIZE(card_jack_pins);
pins = devm_kmemdup_array(card->dev, card_jack_pins, num_pins,
sizeof(card_jack_pins[0]), GFP_KERNEL);
if (!pins)
return -ENOMEM;
/* Need to enable ASRC function for 24MHz mclk rate */
if ((avs_rt5682_quirk & AVS_RT5682_MCLK_EN) &&
(avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ)) {
rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER |
RT5682_AD_STEREO1_FILTER, RT5682_CLK_SEL_I2S1_ASRC);
}
ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
pins, num_pins);
if (ret) {
dev_err(card->dev, "Headset Jack creation failed: %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, "Headset Jack call-back failed: %d\n", ret);
return ret;
}
return 0;
};
static void avs_rt5682_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component, NULL, NULL);
}
static int
avs_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
int pll_source, freq_in, freq_out;
int ret;
if (avs_rt5682_quirk & AVS_RT5682_MCLK_EN) {
pll_source = RT5682_PLL1_S_MCLK;
if (avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ)
freq_in = 24000000;
else
freq_in = 19200000;
} else {
pll_source = RT5682_PLL1_S_BCLK1;
freq_in = params_rate(params) * 50;
}
freq_out = params_rate(params) * 512;
ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, pll_source, freq_in, freq_out);
if (ret < 0)
dev_err(runtime->dev, "Set PLL failed: %d\n", ret);
ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, freq_out, SND_SOC_CLOCK_IN);
if (ret < 0)
dev_err(runtime->dev, "Set sysclk failed: %d\n", ret);
/* slot_width should be equal or larger than data length. */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, params_width(params));
if (ret < 0)
dev_err(runtime->dev, "Set TDM slot failed: %d\n", ret);
return ret;
}
static const struct snd_soc_ops avs_rt5682_ops = {
.hw_params = avs_rt5682_hw_params,
};
static int
avs_rt5682_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSPN to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5682:00");
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, AVS_RT5682_CODEC_DAI_NAME);
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 1;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt5682_codec_init;
dl->exit = avs_rt5682_codec_exit;
dl->be_hw_params_fixup = avs_rt5682_be_fixup;
dl->ops = &avs_rt5682_ops;
dl->nonatomic = 1;
dl->no_pcm = 1;
*dai_link = dl;
return 0;
}
static int avs_card_suspend_pre(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME);
return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
}
static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, AVS_RT5682_CODEC_DAI_NAME);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
}
static int avs_rt5682_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
if (pdev->id_entry && pdev->id_entry->driver_data)
avs_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data;
dmi_check_system(avs_rt5682_quirk_table);
dev_dbg(dev, "avs_rt5682_quirk = %lx\n", avs_rt5682_quirk);
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!jack || !card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_rt5682";
} else {
card->driver_name = "avs_rt5682";
card->long_name = card->name = "AVS I2S ALC5682";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
card->resume_post = avs_card_resume_post;
card->dai_link = dai_link;
card->num_links = 1;
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
snd_soc_card_set_drvdata(card, jack);
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5682_driver_ids[] = {
{
.name = "avs_rt5682",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids);
static struct platform_driver avs_rt5682_driver = {
.probe = avs_rt5682_probe,
.driver = {
.name = "avs_rt5682",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_rt5682_driver_ids,
};
module_platform_driver(avs_rt5682_driver)
MODULE_DESCRIPTION("Intel rt5682 machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#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 "../../../codecs/nau8825.h"
#include "../utils.h"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
static struct snd_soc_codec_conf card_codec_conf[] = {
{
.dlc = COMP_CODEC_CONF("i2c-INT343B:00"),
.name_prefix = "Left",
},
{
.dlc = COMP_CODEC_CONF("i2c-INT343B:01"),
.name_prefix = "Right",
},
};
static const struct snd_kcontrol_new card_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Speaker"),
SOC_DAPM_PIN_SWITCH("Right Speaker"),
};
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_SPK("Left Speaker", NULL),
SND_SOC_DAPM_SPK("Right Speaker", NULL),
};
static const struct snd_soc_dapm_route card_base_routes[] = {
{"Left Speaker", NULL, "Left OUT"},
{"Right Speaker", NULL, "Right OUT"},
};
static int avs_ssm4567_codec_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
/* Slot 1 for left */
ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 0), 0x01, 0x01, 2, 48);
if (ret < 0)
return ret;
/* Slot 2 for right */
ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(runtime, 1), 0x02, 0x02, 2, 48);
if (ret < 0)
return ret;
return 0;
}
static int
avs_ssm4567_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params)
{
struct snd_interval *rate, *channels;
struct snd_mask *fmt;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 24 bit */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
struct snd_soc_dai_link **dai_link)
{
struct snd_soc_dai_link_component *platform;
struct snd_soc_dai_link *dl;
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
if (!dl || !platform)
return -ENOMEM;
dl->name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL);
if (!dl->name || !dl->cpus || !dl->codecs)
return -ENOMEM;
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:00");
dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssm4567-hifi");
dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, "i2c-INT343B:01");
dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssm4567-hifi");
if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name ||
!dl->codecs[1].name || !dl->codecs[1].dai_name)
return -ENOMEM;
platform->name = dev_name(dev);
dl->num_cpus = 1;
dl->num_codecs = 2;
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_ssm4567_codec_init;
dl->be_hw_params_fixup = avs_ssm4567_be_fixup;
dl->nonatomic = 1;
dl->no_pcm = 1;
dl->ignore_pmdown_time = 1;
*dai_link = dl;
return 0;
}
static int avs_ssm4567_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ssp_port, tdm_slot, ret;
mach = dev_get_platdata(dev);
pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
return ret;
ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
if (ret) {
dev_err(dev, "Failed to create dai link: %d", ret);
return ret;
}
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (pdata->obsolete_card_names) {
card->name = "avs_ssm4567";
} else {
card->driver_name = "avs_ssm4567";
card->long_name = card->name = "AVS I2S SSM4567";
}
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
card->num_links = 1;
card->codec_conf = card_codec_conf;
card->num_configs = ARRAY_SIZE(card_codec_conf);
card->controls = card_controls;
card->num_controls = ARRAY_SIZE(card_controls);
card->dapm_widgets = card_widgets;
card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
card->dapm_routes = card_base_routes;
card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
card->fully_routed = true;
return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_ssm4567_driver_ids[] = {
{
.name = "avs_ssm4567",
},
{},
};
MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids);
static struct platform_driver avs_ssm4567_driver = {
.probe = avs_ssm4567_probe,
.driver = {
.name = "avs_ssm4567",
.pm = &snd_soc_pm_ops,
},
.id_table = avs_ssm4567_driver_ids,
};
module_platform_driver(avs_ssm4567_driver)
MODULE_DESCRIPTION("Intel ssm4567 machine driver");
MODULE_LICENSE("GPL");