// SPDX-License-Identifier: GPL-2.0-only /* * bytcr_wm5102.c - ASoc Machine driver for Intel Baytrail platforms with a * Wolfson Microelectronics WM5102 codec * * Copyright (C) 2020 Hans de Goede <hdegoede@redhat.com> * Loosely based on bytcr_rt5640.c which is: * Copyright (C) 2014-2020 Intel Corp * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
*/
/* Note these values are pre-shifted for easy use of setting quirks */ enum {
BYT_WM5102_SPK_SPK_MAP = FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 0),
BYT_WM5102_SPK_HPOUT2_MAP = FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 1),
};
codec_dai = snd_soc_card_get_codec_dai(card, "wm5102-aif1"); if (!codec_dai) {
dev_err(card->dev, "Error codec DAI not found\n"); return -EIO;
}
if (SND_SOC_DAPM_EVENT_ON(event)) {
ret = clk_prepare_enable(priv->mclk); if (ret) {
dev_err(card->dev, "Error enabling MCLK: %d\n", ret); return ret;
}
ret = byt_wm5102_prepare_and_enable_pll1(codec_dai, 48000); if (ret) {
dev_err(card->dev, "Error setting codec sysclk: %d\n", ret); return ret;
}
} else { /* * The WM5102 has a separate 32KHz clock for jack-detect * so we can disable the PLL, followed by disabling the * platform clock which is the source-clock for the PLL.
*/
snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
clk_disable_unprepare(priv->mclk);
}
/* * The Headset Mix uses MICBIAS1 or 2 depending on if a CTIA/OMTP Headset * is connected, as the MICBIAS is applied after the CTIA/OMTP cross-switch.
*/
{"Headset Mic", NULL, "MICBIAS1"},
{"Headset Mic", NULL, "MICBIAS2"},
{"Internal Mic", NULL, "MICBIAS3"},
};
/* * The firmware might enable the clock at boot (this information * may or may not be reflected in the enable clock register). * To change the rate we must disable the clock first to cover these * cases. Due to common clock framework restrictions that do not allow * to disable a clock that has not been enabled, we need to enable * the clock first.
*/
ret = clk_prepare_enable(priv->mclk); if (!ret)
clk_disable_unprepare(priv->mclk);
ret = clk_set_rate(priv->mclk, priv->mclk_freq); if (ret) {
dev_err(card->dev, "Error setting MCLK rate: %d\n", ret); return ret;
}
/* The DSP will convert the FE rate to 48k, stereo */
rate->min = 48000;
rate->max = 48000;
channels->min = 2;
channels->max = 2;
if (quirk & BYT_WM5102_SSP2) { /* set SSP2 to 24-bit */
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
bits = 24;
} else { /* set SSP0 to 16-bit */
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
bits = 16;
}
/* * Default mode for SSP configuration is TDM 4 slot, override config * with explicit setting to I2S 2ch 16-bit. The word length is set with * dai_set_tdm_slot() since there is no other API exposed
*/
ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_BP_FP); if (ret) {
dev_err(rtd->dev, "Error setting format to I2S: %d\n", ret); return ret;
}
SND_SOC_DAILINK_DEF(ssp0_codec,
DAILINK_COMP_ARRAY(COMP_CODEC( /* * Note there is no need to overwrite the codec-name as is done in * other bytcr machine drivers, because the codec is a MFD child-dev.
*/ "wm5102-codec", "wm5102-aif1")));
},
[MERR_DPCM_DEEP_BUFFER] = {
.name = "Deep-Buffer Audio Port",
.stream_name = "Deep-Buffer Audio",
.nonatomic = true,
.dynamic = 1,
.playback_only = 1,
.ops = &byt_wm5102_aif1_ops,
SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
}, /* back ends */
{ /* * This dailink is updated dynamically to point to SSP0 or SSP2. * Yet its name is always kept as "SSP2-Codec" because the SOF * tplg files hardcode "SSP2-Codec" even in byt-foo-ssp0.tplg.
*/
.name = "SSP2-Codec",
.id = 0,
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBC_CFC,
.be_hw_params_fixup = byt_wm5102_codec_fixup,
.init = byt_wm5102_init,
SND_SOC_DAILINK_REG(ssp0_port, ssp0_codec, platform),
},
};
/* use space before codec name to simplify card ID, and simplify driver name */ #define SOF_CARD_NAME "bytcht wm5102"/* card name will be 'sof-bytcht wm5102' */ #define SOF_DRIVER_NAME "SOF"
#define CARD_NAME "bytcr-wm5102" #define DRIVER_NAME NULL /* card name will be used for driver name */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
/* Get MCLK */
priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) return dev_err_probe(dev, PTR_ERR(priv->mclk), "getting pmc_plt_clk_3\n");
/* * Get speaker VDD enable GPIO: * 1. Get codec-device-name * 2. Get codec-device * 3. Get GPIO from codec-device
*/
mach = dev->platform_data;
adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); if (adev) {
snprintf(codec_name, sizeof(codec_name), "spi-%s", acpi_dev_name(adev));
acpi_dev_put(adev);
} else { /* Special case for when the codec is missing from the DSTD */
strscpy(codec_name, "spi1.0", sizeof(codec_name));
}
codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, codec_name); if (!codec_dev) return -EPROBE_DEFER;
/* Note no devm_ here since we call gpiod_get on codec_dev rather then dev */
priv->spkvdd_en_gpio = gpiod_get(codec_dev, "wlf,spkvdd-ena", GPIOD_OUT_LOW);
put_device(codec_dev);
if (IS_ERR(priv->spkvdd_en_gpio)) {
ret = PTR_ERR(priv->spkvdd_en_gpio); /* * The spkvdd gpio-lookup is registered by: drivers/mfd/arizona-spi.c, * so -ENOENT means that arizona-spi hasn't probed yet.
*/ if (ret == -ENOENT)
ret = -EPROBE_DEFER;
if (soc_intel_is_cht()) { /* * CHT always uses SSP2 and 19.2 MHz; and * the one currently supported CHT design uses HPOUT2 as * speaker output and has the intmic on IN1L + hsmic on IN2L.
*/
quirk = BYT_WM5102_SSP2 | BYT_WM5102_MCLK_19_2MHZ |
BYT_WM5102_INTMIC_IN1L_HSMIC_IN2L |
BYT_WM5102_SPK_HPOUT2_MAP;
} if (quirk_override != -1) {
dev_info_once(dev, "Overriding quirk 0x%lx => 0x%x\n",
quirk, quirk_override);
quirk = quirk_override;
}
log_quirks(dev);
/* find index of codec dai */ for (i = 0; i < ARRAY_SIZE(byt_wm5102_dais); i++) { if (byt_wm5102_dais[i].num_codecs &&
!strcmp(byt_wm5102_dais[i].codecs->name, "wm5102-codec")) {
dai_index = i; break;
}
}
/* override platform name, if required */
byt_wm5102_card.dev = dev;
platform_name = mach->mach_params.platform;
ret = snd_soc_fixup_dai_links_platform_name(&byt_wm5102_card, platform_name); if (ret) goto out_put_gpio;
/* override SSP port, if required */ if (quirk & BYT_WM5102_SSP2)
byt_wm5102_dais[dai_index].cpus->dai_name = "ssp2-port";
/* set card and driver name and pm-ops */
sof_parent = snd_soc_acpi_sof_parent(dev); if (sof_parent) {
byt_wm5102_card.name = SOF_CARD_NAME;
byt_wm5102_card.driver_name = SOF_DRIVER_NAME;
dev->driver->pm = &snd_soc_pm_ops;
} else {
byt_wm5102_card.name = CARD_NAME;
byt_wm5102_card.driver_name = DRIVER_NAME;
}
snd_soc_card_set_drvdata(&byt_wm5102_card, priv);
ret = devm_snd_soc_register_card(dev, &byt_wm5102_card); if (ret) {
dev_err_probe(dev, ret, "registering card\n"); goto out_put_gpio;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.