diff options
author | Takashi Iwai <tiwai@suse.de> | 2020-08-03 14:41:43 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2020-08-03 14:41:43 +0200 |
commit | 103f528d3bc35d2b6e726a3fffd879e492d191c2 (patch) | |
tree | 2829604c2386f96e228fac7841e49906f698dfff /sound/soc/samsung | |
parent | 07c9983b567d0ef33aefc063299de95a987e12a8 (diff) | |
parent | 84569f329f7fcb40b7b1860f273b2909dabf2a2b (diff) |
Merge tag 'asoc-v5.9' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.9
The biggest changes here one again come from Mormioto-san who has
continued his dilligent work cleaning up long standing issues in the
APIs, it's particularly nice to see the transition from digital_mute()
to mute_stream() finally completed. There's also been a lot of work on
the x86 code again, this time a big focus has been on cleaning up some
issues identified by various static tests, and on the Freescale systems.
Otherwise the biggest thing has been a lot of driver additions:
- Convert users of digital_mute() to mute_stream().
- Simplify I/O helper functions.
- Add a helper for getting the RTD from a substream.
- Many, many fixes and cleanups to the x86 code.
- New drivers for Freescale MQS and i.MX6sx, Intel KeemBay I2S, Maxim
MAX98360A and MAX98373 Soundwire, several Mediatek boards, nVidia
Tegra 186 and 210, RealTek RL6231, Samsung Midas and Aries boards (some
of the first phones I worked on!) and TI J721e EVM.
Diffstat (limited to 'sound/soc/samsung')
24 files changed, 1306 insertions, 38 deletions
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 99a49248e966..1431be4ed054 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -77,7 +77,7 @@ config SND_SOC_SAMSUNG_S3C24XX_UDA134X config SND_SOC_SAMSUNG_SIMTEC tristate help - Internal node for common S3C24XX/Simtec suppor + Internal node for common S3C24XX/Simtec support. config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23 tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" @@ -212,4 +212,25 @@ config SND_SOC_SAMSUNG_TM2_WM5110 help Say Y if you want to add support for SoC audio on the TM2 board. +config SND_SOC_SAMSUNG_ARIES_WM8994 + tristate "SoC I2S Audio support for WM8994 on Aries" + depends on SND_SOC_SAMSUNG && MFD_WM8994 && IIO && EXTCON + select SND_SOC_BT_SCO + select SND_SOC_WM8994 + select SND_SAMSUNG_I2S + help + Say Y if you want to add support for SoC audio on Aries boards, + which has a WM8994 codec connected to a BT codec, a cellular + modem, and the Samsung I2S controller. Jack detection is done + via ADC, GPIOs, and an extcon device. Switching between the Mic + and TV-Out path is also handled. + +config SND_SOC_SAMSUNG_MIDAS_WM1811 + tristate "SoC I2S Audio support for Midas boards" + depends on SND_SOC_SAMSUNG + select SND_SAMSUNG_I2S + select SND_SOC_WM8994 + help + Say Y if you want to add support for SoC audio on the Midas boards. + endif #SND_SOC_SAMSUNG diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 8f5dfe20b9f1..398e843f388c 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -41,6 +41,8 @@ snd-soc-bells-objs := bells.o snd-soc-odroid-objs := odroid.o snd-soc-arndale-objs := arndale.o snd-soc-tm2-wm5110-objs := tm2_wm5110.o +snd-soc-aries-wm8994-objs := aries_wm8994.o +snd-soc-midas-wm1811-objs := midas_wm1811.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -64,3 +66,5 @@ obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o obj-$(CONFIG_SND_SOC_ARNDALE) += snd-soc-arndale.o obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o +obj-$(CONFIG_SND_SOC_SAMSUNG_ARIES_WM8994) += snd-soc-aries-wm8994.o +obj-$(CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811) += snd-soc-midas-wm1811.o diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c new file mode 100644 index 000000000000..0ac5956ba270 --- /dev/null +++ b/sound/soc/samsung/aries_wm8994.c @@ -0,0 +1,695 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <linux/extcon.h> +#include <linux/iio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/input-event-codes.h> +#include <linux/mfd/wm8994/registers.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "i2s.h" +#include "../codecs/wm8994.h" + +#define ARIES_MCLK1_FREQ 24000000 + +struct aries_wm8994_variant { + unsigned int modem_dai_fmt; + bool has_fm_radio; +}; + +struct aries_wm8994_data { + struct extcon_dev *usb_extcon; + struct regulator *reg_main_micbias; + struct regulator *reg_headset_micbias; + struct gpio_desc *gpio_headset_detect; + struct gpio_desc *gpio_headset_key; + struct gpio_desc *gpio_earpath_sel; + struct iio_channel *adc; + const struct aries_wm8994_variant *variant; +}; + +/* USB dock */ +static struct snd_soc_jack aries_dock; + +static struct snd_soc_jack_pin dock_pins[] = { + { + .pin = "LINE", + .mask = SND_JACK_LINEOUT, + }, +}; + +static int aries_extcon_notifier(struct notifier_block *this, + unsigned long connected, void *_cmd) +{ + if (connected) + snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT, + SND_JACK_LINEOUT); + else + snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT); + + return NOTIFY_DONE; +} + +static struct notifier_block aries_extcon_notifier_block = { + .notifier_call = aries_extcon_notifier, +}; + +/* Headset jack */ +static struct snd_soc_jack aries_headset; + +static struct snd_soc_jack_pin jack_pins[] = { + { + .pin = "HP", + .mask = SND_JACK_HEADPHONE, + }, { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static struct snd_soc_jack_zone headset_zones[] = { + { + .min_mv = 0, + .max_mv = 241, + .jack_type = SND_JACK_HEADPHONE, + }, { + .min_mv = 242, + .max_mv = 2980, + .jack_type = SND_JACK_HEADSET, + }, { + .min_mv = 2981, + .max_mv = UINT_MAX, + .jack_type = SND_JACK_HEADPHONE, + }, +}; + +static irqreturn_t headset_det_irq_thread(int irq, void *data) +{ + struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data; + int ret = 0; + int time_left_ms = 300; + int adc; + + while (time_left_ms > 0) { + if (!gpiod_get_value(priv->gpio_headset_detect)) { + snd_soc_jack_report(&aries_headset, 0, + SND_JACK_HEADSET); + gpiod_set_value(priv->gpio_earpath_sel, 0); + return IRQ_HANDLED; + } + msleep(20); + time_left_ms -= 20; + } + + /* Temporarily enable micbias and earpath selector */ + ret = regulator_enable(priv->reg_headset_micbias); + if (ret) + pr_err("%s failed to enable micbias: %d", __func__, ret); + + gpiod_set_value(priv->gpio_earpath_sel, 1); + + ret = iio_read_channel_processed(priv->adc, &adc); + if (ret < 0) { + /* failed to read ADC, so assume headphone */ + pr_err("%s failed to read ADC, assuming headphones", __func__); + snd_soc_jack_report(&aries_headset, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + } else { + snd_soc_jack_report(&aries_headset, + snd_soc_jack_get_type(&aries_headset, adc), + SND_JACK_HEADSET); + } + + ret = regulator_disable(priv->reg_headset_micbias); + if (ret) + pr_err("%s failed disable micbias: %d", __func__, ret); + + /* Disable earpath selector when no mic connected */ + if (!(aries_headset.status & SND_JACK_MICROPHONE)) + gpiod_set_value(priv->gpio_earpath_sel, 0); + + return IRQ_HANDLED; +} + +static int headset_button_check(void *data) +{ + struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data; + + /* Filter out keypresses when 4 pole jack not detected */ + if (gpiod_get_value_cansleep(priv->gpio_headset_key) && + aries_headset.status & SND_JACK_MICROPHONE) + return SND_JACK_BTN_0; + + return 0; +} + +static struct snd_soc_jack_gpio headset_button_gpio[] = { + { + .name = "Media Button", + .report = SND_JACK_BTN_0, + .debounce_time = 30, + .jack_status_check = headset_button_check, + }, +}; + +static int aries_spk_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; + int ret = 0; + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + component = asoc_rtd_to_codec(rtd, 0)->component; + + /** + * We have an odd setup - the SPKMODE pin is pulled up so + * we only have access to the left side SPK configs, + * but SPKOUTR isn't bridged so when playing back in + * stereo, we only get the left hand channel. The only + * option we're left with is to force the AIF into mono + * mode. + */ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_component_update_bits(component, + WM8994_AIF1_DAC1_FILTERS_1, + WM8994_AIF1DAC1_MONO, WM8994_AIF1DAC1_MONO); + break; + case SND_SOC_DAPM_PRE_PMD: + ret = snd_soc_component_update_bits(component, + WM8994_AIF1_DAC1_FILTERS_1, + WM8994_AIF1DAC1_MONO, 0); + break; + } + + return ret; +} + +static int aries_main_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(priv->reg_main_micbias); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(priv->reg_main_micbias); + break; + } + + return ret; +} + +static int aries_headset_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(priv->reg_headset_micbias); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(priv->reg_headset_micbias); + break; + } + + return ret; +} + +static const struct snd_kcontrol_new aries_controls[] = { + SOC_DAPM_PIN_SWITCH("Modem In"), + SOC_DAPM_PIN_SWITCH("Modem Out"), +}; + +static const struct snd_soc_dapm_widget aries_dapm_widgets[] = { + SND_SOC_DAPM_HP("HP", NULL), + + SND_SOC_DAPM_SPK("SPK", aries_spk_cfg), + SND_SOC_DAPM_SPK("RCV", NULL), + + SND_SOC_DAPM_LINE("LINE", NULL), + + SND_SOC_DAPM_MIC("Main Mic", aries_main_bias), + SND_SOC_DAPM_MIC("Headset Mic", aries_headset_bias), + + SND_SOC_DAPM_MIC("Bluetooth Mic", NULL), + SND_SOC_DAPM_SPK("Bluetooth SPK", NULL), + + SND_SOC_DAPM_LINE("Modem In", NULL), + SND_SOC_DAPM_LINE("Modem Out", NULL), + + /* This must be last as it is conditionally not used */ + SND_SOC_DAPM_LINE("FM In", NULL), +}; + +static int aries_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int pll_out; + int ret; + + /* AIF1CLK should be >=3MHz for optimal performance */ + if (params_width(params) == 24) + pll_out = params_rate(params) * 384; + else if (params_rate(params) == 8000 || params_rate(params) == 11025) + pll_out = params_rate(params) * 512; + else + pll_out = params_rate(params) * 256; + + ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + ARIES_MCLK1_FREQ, pll_out); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, + pll_out, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +static int aries_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int ret; + + /* Switch sysclk to MCLK1 */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, + ARIES_MCLK1_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* Stop PLL */ + ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + ARIES_MCLK1_FREQ, 0); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Main DAI operations + */ +static struct snd_soc_ops aries_ops = { + .hw_params = aries_hw_params, + .hw_free = aries_hw_free, +}; + +static int aries_baseband_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int pll_out; + int ret; + + pll_out = 8000 * 512; + + /* Set the codec FLL */ + ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1, + ARIES_MCLK1_FREQ, pll_out); + if (ret < 0) + return ret; + + /* Set the codec system clock */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2, + pll_out, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +static int aries_late_probe(struct snd_soc_card *card) +{ + struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card); + int ret, irq; + + ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT, + &aries_dock, dock_pins, ARRAY_SIZE(dock_pins)); + if (ret) + return ret; + + ret = devm_extcon_register_notifier(card->dev, + priv->usb_extcon, EXTCON_JACK_LINE_OUT, + &aries_extcon_notifier_block); + if (ret) + return ret; + + if (extcon_get_state(priv->usb_extcon, + EXTCON_JACK_LINE_OUT) > 0) + snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT, + SND_JACK_LINEOUT); + else + snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT); + + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &aries_headset, + jack_pins, ARRAY_SIZE(jack_pins)); + if (ret) + return ret; + + ret = snd_soc_jack_add_zones(&aries_headset, ARRAY_SIZE(headset_zones), + headset_zones); + if (ret) + return ret; + + irq = gpiod_to_irq(priv->gpio_headset_detect); + if (irq < 0) { + dev_err(card->dev, "Failed to map headset detect gpio to irq"); + return -EINVAL; + } + + ret = devm_request_threaded_irq(card->dev, irq, NULL, + headset_det_irq_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "headset_detect", priv); + if (ret) { + dev_err(card->dev, "Failed to request headset detect irq"); + return ret; + } + + headset_button_gpio[0].data = priv; + headset_button_gpio[0].desc = priv->gpio_headset_key; + + snd_jack_set_key(aries_headset.jack, SND_JACK_BTN_0, KEY_MEDIA); + + return snd_soc_jack_add_gpios(&aries_headset, + ARRAY_SIZE(headset_button_gpio), headset_button_gpio); +} + +static const struct snd_soc_pcm_stream baseband_params = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, +}; + +static const struct snd_soc_pcm_stream bluetooth_params = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 2, +}; + +static const struct snd_soc_dapm_widget aries_modem_widgets[] = { + SND_SOC_DAPM_INPUT("Modem RX"), + SND_SOC_DAPM_OUTPUT("Modem TX"), +}; + +static const struct snd_soc_dapm_route aries_modem_routes[] = { + { "Modem Capture", NULL, "Modem RX" }, + { "Modem TX", NULL, "Modem Playback" }, +}; + +static const struct snd_soc_component_driver aries_component = { + .name = "aries-audio", + .dapm_widgets = aries_modem_widgets, + .num_dapm_widgets = ARRAY_SIZE(aries_modem_widgets), + .dapm_routes = aries_modem_routes, + .num_dapm_routes = ARRAY_SIZE(aries_modem_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct snd_soc_dai_driver aries_ext_dai[] = { + { + .name = "Voice call", + .playback = { + .stream_name = "Modem Playback", + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 8000, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Modem Capture", + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 8000, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, +}; + +SND_SOC_DAILINK_DEFS(aif1, + DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(baseband, + DAILINK_COMP_ARRAY(COMP_CPU("Voice call")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2"))); + +SND_SOC_DAILINK_DEFS(bluetooth, + DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3"))); + +static struct snd_soc_dai_link aries_dai[] = { + { + .name = "WM8994 AIF1", + .stream_name = "HiFi", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ops = &aries_ops, + SND_SOC_DAILINK_REG(aif1), + }, + { + .name = "WM8994 AIF2", + .stream_name = "Baseband", + .init = &aries_baseband_init, + .params = &baseband_params, + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(baseband), + }, + { + .name = "WM8994 AIF3", + .stream_name = "Bluetooth", + .params = &bluetooth_params, + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(bluetooth), + }, +}; + +static struct snd_soc_card aries_card = { + .name = "ARIES", + .owner = THIS_MODULE, + .dai_link = aries_dai, + .num_links = ARRAY_SIZE(aries_dai), + .controls = aries_controls, + .num_controls = ARRAY_SIZE(aries_controls), + .dapm_widgets = aries_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aries_dapm_widgets), + .late_probe = aries_late_probe, +}; + +static const struct aries_wm8994_variant fascinate4g_variant = { + .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_IB_NF, + .has_fm_radio = false, +}; + +static const struct aries_wm8994_variant aries_variant = { + .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM + | SND_SOC_DAIFMT_IB_NF, + .has_fm_radio = true, +}; + +static const struct of_device_id samsung_wm8994_of_match[] = { + { + .compatible = "samsung,fascinate4g-wm8994", + .data = &fascinate4g_variant, + }, + { + .compatible = "samsung,aries-wm8994", + .data = &aries_variant, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); + +static int aries_audio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *cpu, *codec, *extcon_np; + struct device *dev = &pdev->dev; + struct snd_soc_card *card = &aries_card; + struct aries_wm8994_data *priv; + struct snd_soc_dai_link *dai_link; + const struct of_device_id *match; + int ret, i; + + if (!np) + return -EINVAL; + + card->dev = dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + + match = of_match_node(samsung_wm8994_of_match, np); + priv->variant = match->data; + + /* Remove FM widget if not present */ + if (!priv->variant->has_fm_radio) + card->num_dapm_widgets--; + + priv->reg_main_micbias = devm_regulator_get(dev, "main-micbias"); + if (IS_ERR(priv->reg_main_micbias)) { + dev_err(dev, "Failed to get main micbias regulator\n"); + return PTR_ERR(priv->reg_main_micbias); + } + + priv->reg_headset_micbias = devm_regulator_get(dev, "headset-micbias"); + if (IS_ERR(priv->reg_headset_micbias)) { + dev_err(dev, "Failed to get headset micbias regulator\n"); + return PTR_ERR(priv->reg_headset_micbias); + } + + priv->gpio_earpath_sel = devm_gpiod_get(dev, "earpath-sel", + GPIOD_OUT_LOW); + if (IS_ERR(priv->gpio_earpath_sel)) { + dev_err(dev, "Failed to get earpath selector gpio"); + return PTR_ERR(priv->gpio_earpath_sel); + } + + extcon_np = of_parse_phandle(np, "extcon", 0); + priv->usb_extcon = extcon_find_edev_by_node(extcon_np); + if (IS_ERR(priv->usb_extcon)) { + if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER) + dev_err(dev, "Failed to get extcon device"); + return PTR_ERR(priv->usb_extcon); + } + of_node_put(extcon_np); + + priv->adc = devm_iio_channel_get(dev, "headset-detect"); + if (IS_ERR(priv->adc)) { + if (PTR_ERR(priv->adc) != -EPROBE_DEFER) + dev_err(dev, "Failed to get ADC channel"); + return PTR_ERR(priv->adc); + } + if (priv->adc->channel->type != IIO_VOLTAGE) + return -EINVAL; + + priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key", + GPIOD_IN); + if (IS_ERR(priv->gpio_headset_key)) { + dev_err(dev, "Failed to get headset key gpio"); + return PTR_ERR(priv->gpio_headset_key); + } + + priv->gpio_headset_detect = devm_gpiod_get(dev, + "headset-detect", GPIOD_IN); + if (IS_ERR(priv->gpio_headset_detect)) { + dev_err(dev, "Failed to get headset detect gpio"); + return PTR_ERR(priv->gpio_headset_detect); + } + + /* Update card-name if provided through DT, else use default name */ + snd_soc_of_parse_card_name(card, "model"); + + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } + + aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt; + + cpu = of_get_child_by_name(dev->of_node, "cpu"); + if (!cpu) + return -EINVAL; + + codec = of_get_child_by_name(dev->of_node, "codec"); + if (!codec) + return -EINVAL; + + for_each_card_prelinks(card, i, dai_link) { + dai_link->codecs->of_node = of_parse_phandle(codec, + "sound-dai", 0); + if (!dai_link->codecs->of_node) { + ret = -EINVAL; + goto out; + } + } + + /* Set CPU and platform of_node for main DAI */ + aries_dai[0].cpus->of_node = of_parse_phandle(cpu, + "sound-dai", 0); + if (!aries_dai[0].cpus->of_node) { + ret = -EINVAL; + goto out; + } + + aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; + + /* Set CPU of_node for BT DAI */ + aries_dai[2].cpus->of_node = of_parse_phandle(cpu, + "sound-dai", 1); + if (!aries_dai[2].cpus->of_node) { + ret = -EINVAL; + goto out; + } + + ret = devm_snd_soc_register_component(dev, &aries_component, + aries_ext_dai, ARRAY_SIZE(aries_ext_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component: %d\n", ret); + goto out; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret) + dev_err(dev, "snd_soc_register_card() failed:%d\n", ret); + +out: + of_node_put(cpu); + of_node_put(codec); + + return ret; +} + +static struct platform_driver aries_audio_driver = { + .driver = { + .name = "aries-audio-wm8994", + .of_match_table = of_match_ptr(samsung_wm8994_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = aries_audio_probe, +}; + +module_platform_driver(aries_audio_driver); + +MODULE_DESCRIPTION("ALSA SoC ARIES WM8994"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:aries-audio-wm8994"); diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index c81ece78e036..28587375813a 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -20,7 +20,7 @@ static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int rfs, ret; @@ -55,7 +55,7 @@ static struct snd_soc_ops arndale_rt5631_ops = { static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int rfs, rclk; diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index 9139a1e7e200..b8f0057a0510 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -67,7 +67,7 @@ static int h1940_startup(struct snd_pcm_substream *substream) static int h1940_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int div; int ret; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f86e3028b402..80ecb5c7fed0 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -931,7 +931,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, { struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c index 30899016cf08..40a85f539509 100644 --- a/sound/soc/samsung/jive_wm8750.c +++ b/sound/soc/samsung/jive_wm8750.c @@ -32,7 +32,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { static int jive_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct s3c_i2sv2_rate_calc div; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index f4375c49f7f4..a1ff1400857e 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -104,7 +104,7 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, static int littlemill_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int ret; diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c new file mode 100644 index 000000000000..d03340ce49a2 --- /dev/null +++ b/sound/soc/samsung/midas_wm1811.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Midas audio support +// +// Copyright (C) 2018 Simon Shields <simon@lineageos.org> +// Copyright (C) 2020 Samsung Electronics Co., Ltd. + +#include <linux/clk.h> +#include <linux/mfd/wm8994/registers.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "i2s.h" +#include "../codecs/wm8994.h" + +/* + * The MCLK1 clock source is XCLKOUT with its mux set to the external fixed rate + * oscillator (XXTI). + */ +#define MCLK1_RATE 24000000U +#define MCLK2_RATE 32768U +#define DEFAULT_FLL1_RATE 11289600U + +struct midas_priv { + struct regulator *reg_mic_bias; + struct regulator *reg_submic_bias; + struct gpio_desc *gpio_fm_sel; + struct gpio_desc *gpio_lineout_sel; + unsigned int fll1_rate; + + struct snd_soc_jack headset_jack; +}; + +static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) +{ + struct snd_soc_card *card = rtd->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int ret; + + if (!rate) + rate = priv->fll1_rate; + /* + * If no new rate is requested, set FLL1 to a sane default for jack + * detection. + */ + if (!rate) + rate = DEFAULT_FLL1_RATE; + + if (rate != priv->fll1_rate && priv->fll1_rate) { + /* while reconfiguring, switch to MCLK2 for SYSCLK */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + MCLK2_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret); + return ret; + } + } + + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + MCLK1_RATE, rate); + if (ret < 0) { + dev_err(card->dev, "Failed to set FLL1 rate: %d\n", ret); + return ret; + } + priv->fll1_rate = rate; + + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, + priv->fll1_rate, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Failed to set SYSCLK source: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, 0, + SAMSUNG_I2S_OPCLK_PCLK); + if (ret < 0) { + dev_err(card->dev, "Failed to set OPCLK source: %d\n", ret); + return ret; + } + + return 0; +} + +static int midas_stop_fll1(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0); + int ret; + + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + MCLK2_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); + if (ret < 0) { + dev_err(card->dev, "Unable to stop FLL1: %d\n", ret); + return ret; + } + + priv->fll1_rate = 0; + + return 0; +} + +static int midas_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int pll_out; + + /* AIF1CLK should be at least 3MHz for "optimal performance" */ + if (params_rate(params) == 8000 || params_rate(params) == 11025) + pll_out = params_rate(params) * 512; + else + pll_out = params_rate(params) * 256; + + return midas_start_fll1(rtd, pll_out); +} + +static struct snd_soc_ops midas_aif1_ops = { + .hw_params = midas_aif1_hw_params, +}; + +/* + * We only have a single external speaker, so mix stereo data + * to a single mono stream. + */ +static int midas_ext_spkmode(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS, + WM8994_SPKMIXR_TO_SPKOUTL_MASK, + WM8994_SPKMIXR_TO_SPKOUTL); + break; + case SND_SOC_DAPM_POST_PMD: + ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS, + WM8994_SPKMIXR_TO_SPKOUTL_MASK, + 0); + break; + } + + return ret; +} + +static int midas_mic_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return regulator_enable(priv->reg_mic_bias); + case SND_SOC_DAPM_POST_PMD: + return regulator_disable(priv->reg_mic_bias); + } + + return 0; +} + +static int midas_submic_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return regulator_enable(priv->reg_submic_bias); + case SND_SOC_DAPM_POST_PMD: + return regulator_disable(priv->reg_submic_bias); + } + + return 0; +} + +static int midas_fm_set(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + + if (!priv->gpio_fm_sel) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + gpiod_set_value_cansleep(priv->gpio_fm_sel, 1); + break; + case SND_SOC_DAPM_POST_PMD: + gpiod_set_value_cansleep(priv->gpio_fm_sel, 0); + break; + } + + return 0; +} + +static int midas_line_set(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + + if (!priv->gpio_lineout_sel) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + gpiod_set_value_cansleep(priv->gpio_lineout_sel, 1); + break; + case SND_SOC_DAPM_POST_PMD: + gpiod_set_value_cansleep(priv->gpio_lineout_sel, 0); + break; + } + + return 0; +} + +static const struct snd_kcontrol_new midas_controls[] = { + SOC_DAPM_PIN_SWITCH("HP"), + + SOC_DAPM_PIN_SWITCH("SPK"), + SOC_DAPM_PIN_SWITCH("RCV"), + + SOC_DAPM_PIN_SWITCH("LINE"), + SOC_DAPM_PIN_SWITCH("HDMI"), + + SOC_DAPM_PIN_SWITCH("Main Mic"), + SOC_DAPM_PIN_SWITCH("Sub Mic"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + + SOC_DAPM_PIN_SWITCH("FM In"), +}; + +static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { + SND_SOC_DAPM_HP("HP", NULL), + + SND_SOC_DAPM_SPK("SPK", midas_ext_spkmode), + SND_SOC_DAPM_SPK("RCV", NULL), + + /* FIXME: toggle MAX77693 on i9300/i9305 */ + SND_SOC_DAPM_LINE("LINE", midas_line_set), + SND_SOC_DAPM_LINE("HDMI", NULL), + SND_SOC_DAPM_LINE("FM In", midas_fm_set), + + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias), + SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias), +}; + +static int midas_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, + &card->dai_link[0]); + struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0); + + if (dapm->dev != aif1_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + return midas_stop_fll1(rtd); + case SND_SOC_BIAS_PREPARE: + return midas_start_fll1(rtd, 0); + default: + break; + } + + return 0; +} + +static int midas_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, + &card->dai_link[0]); + struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0); + struct midas_priv *priv = snd_soc_card_get_drvdata(card); + int ret; + + /* Use MCLK2 as SYSCLK for boot */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MCLK2_RATE, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(aif1_dai->dev, "Failed to switch to MCLK2: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, + &priv->headset_jack, NULL, 0); + if (ret) + return ret; + + wm8958_mic_detect(aif1_dai->component, &priv->headset_jack, + NULL, NULL, NULL, NULL); + return 0; +} + +static struct snd_soc_dai_driver midas_ext_dai[] = { + { + .name = "Voice call", + .playback = { + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "Bluetooth", + .playback = { + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, +}; + +static const struct snd_soc_component_driver midas_component = { + .name = "midas-audio", +}; + +SND_SOC_DAILINK_DEFS(wm1811_hifi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(wm1811_voice, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(wm1811_bt, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link midas_dai[] = { + { + .name = "WM8994 AIF1", + .stream_name = "HiFi Primary", + .ops = &midas_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAILINK_REG(wm1811_hifi), + }, { + .name = "WM1811 Voice", + .stream_name = "Voice call", + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(wm1811_voice), + }, { + .name = "WM1811 BT", + .stream_name = "Bluetooth", + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(wm1811_bt), + }, +}; + +static struct snd_soc_card midas_card = { + .name = "Midas WM1811", + .owner = THIS_MODULE, + + .dai_link = midas_dai, + .num_links = ARRAY_SIZE(midas_dai), + .controls = midas_controls, + .num_controls = ARRAY_SIZE(midas_controls), + .dapm_widgets = midas_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(midas_dapm_widgets), + + .set_bias_level = midas_set_bias_level, + .late_probe = midas_late_probe, +}; + +static int midas_probe(struct platform_device *pdev) +{ + struct device_node *cpu_dai_node = NULL, *codec_dai_node = NULL; + struct device_node *cpu = NULL, *codec = NULL; + struct snd_soc_card *card = &midas_card; + struct device *dev = &pdev->dev; + static struct snd_soc_dai_link *dai_link; + struct midas_priv *priv; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + card->dev = dev; + + priv->reg_mic_bias = devm_regulator_get(dev, "mic-bias"); + if (IS_ERR(priv->reg_mic_bias)) { + dev_err(dev, "Failed to get mic bias regulator\n"); + return PTR_ERR(priv->reg_mic_bias); + } + + priv->reg_submic_bias = devm_regulator_get(dev, "submic-bias"); + if (IS_ERR(priv->reg_submic_bias)) { + dev_err(dev, "Failed to get submic bias regulator\n"); + return PTR_ERR(priv->reg_submic_bias); + } + + priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_fm_sel)) { + dev_err(dev, "Failed to get FM selection GPIO\n"); + return PTR_ERR(priv->gpio_fm_sel); + } + + priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_lineout_sel)) { + dev_err(dev, "Failed to get line out selection GPIO\n"); + return PTR_ERR(priv->gpio_lineout_sel); + } + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret < 0) { + dev_err(dev, "Card name is not specified\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } + + cpu = of_get_child_by_name(dev->of_node, "cpu"); + if (!cpu) + return -EINVAL; + + codec = of_get_child_by_name(dev->of_node, "codec"); + if (!codec) { + of_node_put(cpu); + return -EINVAL; + } + + cpu_dai_node = of_parse_phandle(cpu, "sound-dai", 0); + of_node_put(cpu); + if (!cpu_dai_node) { + dev_err(dev, "parsing cpu/sound-dai failed\n"); + of_node_put(codec); + return -EINVAL; + } + + codec_dai_node = of_parse_phandle(codec, "sound-dai", 0); + of_node_put(codec); + if (!codec_dai_node) { + dev_err(dev, "audio-codec property invalid/missing\n"); + ret = -EINVAL; + goto put_cpu_dai_node; + } + + for_each_card_prelinks(card, i, dai_link) { + dai_link->codecs->of_node = codec_dai_node; + dai_link->cpus->of_node = cpu_dai_node; + dai_link->platforms->of_node = cpu_dai_node; + } + + ret = devm_snd_soc_register_component(dev, &midas_component, + midas_ext_dai, ARRAY_SIZE(midas_ext_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component: %d\n", ret); + goto put_codec_dai_node; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) { + dev_err(dev, "Failed to register card: %d\n", ret); + goto put_codec_dai_node; + } + + return 0; + +put_codec_dai_node: + of_node_put(codec_dai_node); +put_cpu_dai_node: + of_node_put(cpu_dai_node); + return ret; +} + +static const struct of_device_id midas_of_match[] = { + { .compatible = "samsung,midas-audio" }, + { }, +}; +MODULE_DEVICE_TABLE(of, midas_of_match); + +static struct platform_driver midas_driver = { + .driver = { + .name = "midas-audio", + .of_match_table = midas_of_match, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = midas_probe, +}; +module_platform_driver(midas_driver); + +MODULE_AUTHOR("Simon Shields <simon@lineageos.org>"); +MODULE_DESCRIPTION("ASoC support for Midas"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index b7ce1da854ce..54317e0f68f8 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -25,7 +25,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int pll_out = 0, bclk = 0; @@ -99,7 +99,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* disable the PLL */ @@ -117,7 +117,7 @@ static struct snd_soc_ops neo1973_hifi_ops = { static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pcmdiv = 0; int ret = 0; @@ -154,7 +154,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); /* disable the PLL */ diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 6eda5af989fe..ca643a488c3c 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -35,7 +35,7 @@ static int odroid_card_fe_startup(struct snd_pcm_substream *substream) static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned long flags; int ret = 0; @@ -56,7 +56,7 @@ static const struct snd_soc_ops odroid_card_fe_ops = { static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned int pll_freq, rclk_freq, rfs; unsigned long flags; @@ -115,7 +115,7 @@ static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); unsigned long flags; diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index a5b1a12b3496..6f50c7b47326 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -104,8 +104,13 @@ /** * struct s3c_pcm_info - S3C PCM Controller information + * @lock: Spin lock * @dev: The parent device passed to use from the probe. * @regs: The pointer to the device register block. + * @sclk_per_fs: number of sclk per frame sync + * @idleclk: Whether to keep PCMSCLK enabled even when idle (no active xfer) + * @pclk: the PCLK_PCM (pcm) clock pointer + * @cclk: the SCLK_AUDIO (audio-bus) clock pointer * @dma_playback: DMA information for playback channel. * @dma_capture: DMA information for capture channel. */ @@ -211,7 +216,7 @@ static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on) static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; @@ -255,7 +260,7 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *socdai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = pcm->regs; struct clk *clk; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 3afe63c0923e..08f7c82aedb6 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -148,7 +148,7 @@ static int rx1950_spk_power(struct snd_soc_dapm_widget *w, static int rx1950_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int div; int ret; diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index 5e95c30fb2ba..ed21786104a1 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -379,7 +379,7 @@ static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai, static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct s3c_i2sv2_info *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); unsigned long irqs; diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c index fd2a4da086f3..3cddd11344ac 100644 --- a/sound/soc/samsung/s3c24xx_simtec.c +++ b/sound/soc/samsung/s3c24xx_simtec.c @@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(simtec_audio_init); static int simtec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index abb5c4713c53..6272070dcd92 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -49,7 +49,7 @@ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; @@ -101,7 +101,7 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); mutex_lock(&priv->clk_lock); @@ -118,7 +118,7 @@ static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream) static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 36bef136d57f..c95629becbc3 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -24,7 +24,7 @@ static struct snd_soc_card snd_soc_smartq; static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int clk = 0; diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index 776a270261bf..6f3eeb7bc834 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -100,7 +100,7 @@ static int set_audio_clock_rate(unsigned long epll_rate, static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned long pll_out, rclk_rate; int ret, ratio; diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 02074c34a2b2..ed753a2f202e 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -22,7 +22,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pll_out; int rfs, ret; diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index a9f345f19a8a..64a1a64656ab 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -44,7 +44,7 @@ static struct smdk_wm8994_data smdk_board_data = { static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int pll_out; int ret; diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 746930dde5d7..a01640576f71 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -43,7 +43,7 @@ static int smdk_wm8994_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned long mclk_freq; diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 40c5de8df0ff..07163f07c6d5 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -30,7 +30,7 @@ static int snow_card_hw_params(struct snd_pcm_substream *substream, static const unsigned int pll_rate[] = { 73728000U, 67737602U, 49152000U, 45158401U, 32768001U }; - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card); int bfs, psr, rfs, bitwidth; unsigned long int rclk; diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 759fc6644329..226c359892e9 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -70,9 +70,9 @@ * @clk_rate: Current clock rate for calcurate ratio. * @pclk: The peri-clock pointer for spdif master operation. * @sclk: The source clock pointer for making sync signals. - * @save_clkcon: Backup clkcon reg. in suspend. - * @save_con: Backup con reg. in suspend. - * @save_cstas: Backup cstas reg. in suspend. + * @saved_clkcon: Backup clkcon reg. in suspend. + * @saved_con: Backup con reg. in suspend. + * @saved_cstas: Backup cstas reg. in suspend. * @dma_playback: DMA information for playback channel. */ struct samsung_spdif_info { @@ -141,7 +141,7 @@ static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai, static int spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); unsigned long flags; @@ -177,7 +177,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *socdai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = spdif->regs; struct snd_dmaengine_dai_dma_data *dma_data; @@ -279,7 +279,7 @@ err: static void spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); void __iomem *regs = spdif->regs; u32 con, clkcon; diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 6dfd540e2d74..9300fef9bf26 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -92,7 +92,7 @@ static int tm2_stop_sysclk(struct snd_soc_card *card) static int tm2_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card); @@ -133,7 +133,7 @@ static struct snd_soc_ops tm2_aif1_ops = { static int tm2_aif2_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; unsigned int asyncclk_rate; int ret; @@ -187,7 +187,7 @@ static int tm2_aif2_hw_params(struct snd_pcm_substream *substream, static int tm2_aif2_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret; @@ -208,7 +208,7 @@ static struct snd_soc_ops tm2_aif2_ops = { static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int bfs; int bitwidth, ret; |