ASoC: Intel: avs: Add rt5640 board support

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

Small set of patches intoducing two low-impact improvements to rt5640
codec driver and then avs_rt5640 machine board driver. The board
behavior is based on existing boards such as avs_rt274.

In regard to the retry-device-verification change, there is number of
similar "fixes" found in the Realtek code in sound/soc/codecs. What I
provide here is verified with tests, the delay is not mentioned in the
spec I have access to.
This commit is contained in:
Mark Brown 2025-06-09 16:44:08 +01:00
commit 4388f5d214
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
6 changed files with 338 additions and 22 deletions

View File

@ -193,7 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
case RT5640_DUMMY2:
case RT5640_GCTL2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@ -325,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
case RT5640_HP_CALIB2:
case RT5640_SV_ZCD1:
case RT5640_SV_ZCD2:
case RT5640_DUMMY1:
case RT5640_DUMMY2:
case RT5640_GCTL1:
case RT5640_GCTL2:
case RT5640_DUMMY3:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
@ -423,7 +423,7 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
127, 0, adc_vol_tlv),
SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1,
SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1,
RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1),
SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
@ -1969,7 +1969,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
snd_soc_component_update_bits(component, RT5640_DUMMY1,
snd_soc_component_update_bits(component, RT5640_GCTL1,
0x1, 0x1);
snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
@ -1979,7 +1979,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
case SND_SOC_BIAS_OFF:
snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004);
snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100);
snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0);
snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0);
snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000);
snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
@ -2328,12 +2328,12 @@ static void rt5640_jack_work(struct work_struct *work)
jack_type |= SND_JACK_MICROPHONE;
/* headphone jack */
val = snd_soc_component_read(component, RT5640_DUMMY2);
val = snd_soc_component_read(component, RT5640_GCTL2);
hda_hp_plugged = !(val & (0x1 << 11));
dev_dbg(component->dev, "headphone jack status %d\n",
hda_hp_plugged);
snd_soc_component_update_bits(component, RT5640_DUMMY2,
snd_soc_component_update_bits(component, RT5640_GCTL2,
(0x1 << 10), !hda_hp_plugged << 10);
if (hda_hp_plugged)
@ -2504,7 +2504,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41);
snd_soc_component_write(component, RT5640_GCTL1, 0x3f41);
rt5640_set_ovcd_params(component);
@ -2519,7 +2519,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_IRQ_CTRL1,
RT5640_IRQ_JD_NOR);
else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(component, RT5640_DUMMY2,
snd_soc_component_update_bits(component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR | RT5640_JD2_EN);
} else {
@ -2527,7 +2527,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_IRQ_CTRL1,
RT5640_IRQ_JD_NOR | RT5640_JD_P_INV);
else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(component, RT5640_DUMMY2,
snd_soc_component_update_bits(component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK |
RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV |
@ -2596,7 +2596,7 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
/* Select JD2 for Headphone */
snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100);
snd_soc_component_update_bits(component, RT5640_GCTL2, 0x1100, 0x1100);
/* Selecting GPIO01 as an interrupt */
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
@ -2606,7 +2606,7 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
snd_soc_component_update_bits(component, RT5640_GCTL1, 0x400, 0x0);
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
@ -2668,7 +2668,7 @@ static int rt5640_probe(struct snd_soc_component *component)
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301);
snd_soc_component_update_bits(component, RT5640_GCTL1, 0x0301, 0x0301);
snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030);
snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
@ -2719,7 +2719,7 @@ static int rt5640_probe(struct snd_soc_component *component)
RT5640_IN_DF2, RT5640_IN_DF2);
if (device_property_read_bool(component->dev, "realtek,lout-differential"))
snd_soc_component_update_bits(component, RT5640_DUMMY1,
snd_soc_component_update_bits(component, RT5640_GCTL1,
RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF);
if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin",
@ -2829,12 +2829,12 @@ static int rt5640_resume(struct snd_soc_component *component)
if (rt5640->jack) {
if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
snd_soc_component_update_bits(component,
RT5640_DUMMY2, 0x1100, 0x1100);
RT5640_GCTL2, 0x1100, 0x1100);
} else {
if (rt5640->jd_inverted) {
if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(
component, RT5640_DUMMY2,
component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK |
RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR |
@ -2843,7 +2843,7 @@ static int rt5640_resume(struct snd_soc_component *component)
} else {
if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(
component, RT5640_DUMMY2,
component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK |
RT5640_JD2_P_MASK |
RT5640_JD2_MASK,
@ -3013,6 +3013,11 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
}
regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
if (val != RT5640_DEVICE_ID) {
usleep_range(60000, 100000);
regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
}
if (val != RT5640_DEVICE_ID) {
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5640/39\n", val);
@ -3026,7 +3031,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
regmap_update_bits(rt5640->regmap, RT5640_DUMMY1,
regmap_update_bits(rt5640->regmap, RT5640_GCTL1,
RT5640_MCLK_DET, RT5640_MCLK_DET);
rt5640->hp_mute = true;

View File

@ -139,8 +139,8 @@
#define RT5640_SV_ZCD1 0xd9
#define RT5640_SV_ZCD2 0xda
/* Dummy Register */
#define RT5640_DUMMY1 0xfa
#define RT5640_DUMMY2 0xfb
#define RT5640_GCTL1 0xfa
#define RT5640_GCTL2 0xfb
#define RT5640_DUMMY3 0xfc
@ -1986,7 +1986,7 @@
#define RT5640_M_MONO_ADC_R_SFT 12
#define RT5640_MCLK_DET (0x1 << 11)
/* General Control 1 (0xfb) */
/* General Control 2 (0xfb) */
#define RT5640_IRQ_JD2_MASK (0x1 << 12)
#define RT5640_IRQ_JD2_SFT 12
#define RT5640_IRQ_JD2_BP (0x0 << 12)

View File

@ -308,6 +308,33 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
},
.tplg_filename = "rt1308-tplg.bin",
},
{
.id = "10EC5640",
.uid = "1",
.drv_name = "avs_rt5640",
.mach_params = {
.i2s_link_mask = AVS_SSP(0),
},
.tplg_filename = "rt5640-tplg.bin",
},
{
.id = "10EC5640",
.uid = "3",
.drv_name = "avs_rt5640",
.mach_params = {
.i2s_link_mask = AVS_SSP(1),
},
.tplg_filename = "rt5640-tplg.bin",
},
{
.id = "10EC5640",
.uid = "2",
.drv_name = "avs_rt5640",
.mach_params = {
.i2s_link_mask = AVS_SSP(2),
},
.tplg_filename = "rt5640-tplg.bin",
},
{
.id = "ESSX8336",
.drv_name = "avs_es8336",

View File

@ -153,6 +153,18 @@ config SND_SOC_INTEL_AVS_MACH_RT5514
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

View File

@ -15,6 +15,7 @@ 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
@ -34,6 +35,7 @@ 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

View File

@ -0,0 +1,270 @@
// 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_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);
card->dapm.idle_bias_off = true;
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");