ASoC: Updates for v4.16

With the merge window having been delayed for another week here's
 another batch of updates that came in during that week.  There's a few
 important fixes in here, mainly a fix for I/O on a number of devices
 caused by some of the component rework and a fix for a potential issue
 if more than one component in a link provides compressed operations.
 The I/O fixes are particularly important as the problem causes a power
 regression on a number of OMAP platforms.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAlp65E4THGJyb29uaWVA
 a2VybmVsLm9yZwAKCRAk1otyXVSH0Oy1B/9XQd6twiSRfNKtbdujOyvjc8lpZ01n
 JYHVySXyL9ZqAnIblM7Or/iXVEtsAVcXgFegF3SLHAY6VF7DQ1pDolnxUtuXOxOj
 j+/2Y4wnDGCjXEr0tMoxrbNUIqlVZLCpPwsPo3vvVbr6sLLmQYVposNp2A2sK2bz
 uWm9E3Nr26Q0UctzjWQM5+AFHSouyL7zDPfBCoWkEToP7163w6r4JDr991KdNGwP
 Ac+5qjRUSldsn8WB2ngm8ioqbq+aOvsz2THYjG8gxrlQK+BWsyCDqF7f1d9GWse2
 7k+xZLrdJrVkBMOnpvOx/Y4KRfe9BAFZBZ3KRbi2IR++7TD3902xEX27
 =aIRm
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v4.16-5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound

Pull more ASoC updates from Mark Brown:
 "With the merge window having been delayed for another week here's
  another batch of updates that came in during that week.

  There's a few important fixes in here, mainly a fix for I/O on a
  number of devices caused by some of the component rework and a fix for
  a potential issue if more than one component in a link provides
  compressed operations. The I/O fixes are particularly important as the
  problem causes a power regression on a number of OMAP platforms"

* tag 'asoc-v4.16-5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound: (22 commits)
  ASoC: stm32: add of dependency for stm32 drivers
  ASoC: mt8173-rt5650: fix child-node lookup
  ASoC: dapm: fix debugfs read using path->connected
  ASoC: compress: Fixup error messages
  ASoC: compress: Remove some extraneous blank lines
  ASoC: compress: Correct handling of copy callback
  ASoC: Intel: kbl: Enable mclk and ssp sclk early
  ASoC: Intel: Skylake: Add extended I2S config blob support in Clock driver
  ASoC: Intel: Skylake: Add ssp clock driver
  ASoC: Fix twl4030 and 6040 regression by adding back read and write
  ASoC: sun8i-codec: Add ADC support for a33
  ASoC: rockchip: Use dummy_dai for rt5514 dsp dailink
  ASoC: soc-pcm: rename .pmdown_time to .use_pmdown_time for Component
  ASoC: ak4613: call dummy write for PW_MGMT1/3 when Playback
  ASoC: soc-pcm: don't call flush_delayed_work() many times in soc_pcm_private_free()
  ASoC: soc-core: snd_soc_rtdcom_lookup() cares component driver name
  ASoC: sam9x5_wm8731: Drop 'ASoC' prefix from error messages
  ASoC: sam9g20_wm8731: use dev_*() logging functions
  ASoC: max98373 Changed SPDX header in C++ comments style
  ASoC: dmic: Fix check of return value from read of 'num-channels'
  ...
This commit is contained in:
Linus Torvalds 2018-02-07 12:11:09 -08:00
commit 7590e37bda
29 changed files with 918 additions and 106 deletions

View File

@ -841,7 +841,7 @@ struct snd_soc_component_driver {
/* bits */ /* bits */
unsigned int idle_bias_on:1; unsigned int idle_bias_on:1;
unsigned int suspend_bias_off:1; unsigned int suspend_bias_off:1;
unsigned int pmdown_time:1; /* care pmdown_time at stop */ unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
unsigned int endianness:1; unsigned int endianness:1;
unsigned int non_legacy_dai_naming:1; unsigned int non_legacy_dai_naming:1;
}; };

View File

@ -110,16 +110,15 @@ static const struct snd_soc_dapm_route intercon[] = {
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct device *dev = rtd->dev;
int ret; int ret;
printk(KERN_DEBUG dev_dbg(dev, "%s called\n", __func__);
"at91sam9g20ek_wm8731 "
": at91sam9g20ek_wm8731_init() called\n");
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK, ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
MCLK_RATE, SND_SOC_CLOCK_IN); MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret; return ret;
} }
@ -179,21 +178,21 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
*/ */
mclk = clk_get(NULL, "pck0"); mclk = clk_get(NULL, "pck0");
if (IS_ERR(mclk)) { if (IS_ERR(mclk)) {
printk(KERN_ERR "ASoC: Failed to get MCLK\n"); dev_err(&pdev->dev, "Failed to get MCLK\n");
ret = PTR_ERR(mclk); ret = PTR_ERR(mclk);
goto err; goto err;
} }
pllb = clk_get(NULL, "pllb"); pllb = clk_get(NULL, "pllb");
if (IS_ERR(pllb)) { if (IS_ERR(pllb)) {
printk(KERN_ERR "ASoC: Failed to get PLLB\n"); dev_err(&pdev->dev, "Failed to get PLLB\n");
ret = PTR_ERR(pllb); ret = PTR_ERR(pllb);
goto err_mclk; goto err_mclk;
} }
ret = clk_set_parent(mclk, pllb); ret = clk_set_parent(mclk, pllb);
clk_put(pllb); clk_put(pllb);
if (ret != 0) { if (ret != 0) {
printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); dev_err(&pdev->dev, "Failed to set MCLK parent\n");
goto err_mclk; goto err_mclk;
} }
@ -236,7 +235,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);
if (ret) { if (ret) {
printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n"); dev_err(&pdev->dev, "snd_soc_register_card() failed\n");
} }
return ret; return ret;

View File

@ -49,13 +49,13 @@ static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
struct device *dev = rtd->dev; struct device *dev = rtd->dev;
int ret; int ret;
dev_dbg(dev, "ASoC: %s called\n", __func__); dev_dbg(dev, "%s called\n", __func__);
/* set the codec system clock for DAC and ADC */ /* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
MCLK_RATE, SND_SOC_CLOCK_IN); MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret); dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret; return ret;
} }
@ -146,8 +146,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
ret = atmel_ssc_set_audio(priv->ssc_id); ret = atmel_ssc_set_audio(priv->ssc_id);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
"ASoC: Failed to set SSC %d for audio: %d\n",
ret, priv->ssc_id); ret, priv->ssc_id);
goto out; goto out;
} }
@ -157,12 +156,11 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);
if (ret) { if (ret) {
dev_err(&pdev->dev, dev_err(&pdev->dev, "Platform device allocation failed\n");
"ASoC: Platform device allocation failed\n");
goto out_put_audio; goto out_put_audio;
} }
dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__); dev_dbg(&pdev->dev, "%s ok\n", __func__);
return ret; return ret;

View File

@ -15,6 +15,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_device.h> #include <linux/of_device.h>
@ -95,6 +96,9 @@ struct ak4613_priv {
struct mutex lock; struct mutex lock;
const struct ak4613_interface *iface; const struct ak4613_interface *iface;
struct snd_pcm_hw_constraint_list constraint; struct snd_pcm_hw_constraint_list constraint;
struct work_struct dummy_write_work;
struct snd_soc_component *component;
unsigned int rate;
unsigned int sysclk; unsigned int sysclk;
unsigned int fmt; unsigned int fmt;
@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
default: default:
return -EINVAL; return -EINVAL;
} }
priv->rate = rate;
/* /*
* FIXME * FIXME
@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
return 0; return 0;
} }
static void ak4613_dummy_write(struct work_struct *work)
{
struct ak4613_priv *priv = container_of(work,
struct ak4613_priv,
dummy_write_work);
struct snd_soc_component *component = priv->component;
unsigned int mgmt1;
unsigned int mgmt3;
/*
* PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
*
* Note
*
* To avoid extra delay, we want to avoid preemption here,
* but we can't. Because it uses I2C access which is using IRQ
* and sleep. Thus, delay might be more than 5 LR clocks
* see also
* ak4613_dai_trigger()
*/
udelay(5000000 / priv->rate);
snd_soc_component_read(component, PW_MGMT1, &mgmt1);
snd_soc_component_read(component, PW_MGMT3, &mgmt3);
snd_soc_component_write(component, PW_MGMT1, mgmt1);
snd_soc_component_write(component, PW_MGMT3, mgmt3);
}
static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
/*
* FIXME
*
* PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
* from Power Down Release. Otherwise, Playback volume will be 0dB.
* To avoid complex multiple delay/dummy_write method from
* ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
* call it once here.
*
* But, unfortunately, we can't "write" here because here is atomic
* context (It uses I2C access for writing).
* Thus, use schedule_work() to switching to normal context
* immediately.
*
* Note
*
* Calling ak4613_dummy_write() function might be delayed.
* In such case, ak4613 volume might be temporarily 0dB when
* beggining of playback.
* see also
* ak4613_dummy_write()
*/
if ((cmd != SNDRV_PCM_TRIGGER_START) &&
(cmd != SNDRV_PCM_TRIGGER_RESUME))
return 0;
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
priv->component = &codec->component;
schedule_work(&priv->dummy_write_work);
return 0;
}
static const struct snd_soc_dai_ops ak4613_dai_ops = { static const struct snd_soc_dai_ops ak4613_dai_ops = {
.startup = ak4613_dai_startup, .startup = ak4613_dai_startup,
.shutdown = ak4613_dai_shutdown, .shutdown = ak4613_dai_shutdown,
.set_sysclk = ak4613_dai_set_sysclk, .set_sysclk = ak4613_dai_set_sysclk,
.set_fmt = ak4613_dai_set_fmt, .set_fmt = ak4613_dai_set_fmt,
.trigger = ak4613_dai_trigger,
.hw_params = ak4613_dai_hw_params, .hw_params = ak4613_dai_hw_params,
}; };
@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
priv->iface = NULL; priv->iface = NULL;
priv->cnt = 0; priv->cnt = 0;
priv->sysclk = 0; priv->sysclk = 0;
INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
mutex_init(&priv->lock); mutex_init(&priv->lock);

View File

@ -113,7 +113,7 @@ static int dmic_dev_probe(struct platform_device *pdev)
if (pdev->dev.of_node) { if (pdev->dev.of_node) {
err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
if (err && (err != -ENOENT)) if (err && (err != -EINVAL))
return err; return err;
if (!err) { if (!err) {

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */ // SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2017, Maxim Integrated */ // Copyright (c) 2017, Maxim Integrated
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/i2c.h> #include <linux/i2c.h>

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */ // SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2017, Maxim Integrated */ // Copyright (c) 2017, Maxim Integrated
#ifndef _MAX98373_H #ifndef _MAX98373_H
#define _MAX98373_H #define _MAX98373_H

View File

@ -1392,7 +1392,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
ana_pwr |= SGTL5000_LINEREG_D_POWERUP; ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
dev_info(&client->dev, dev_info(&client->dev,
"Using internal LDO instead of VDDD: check ER1\n"); "Using internal LDO instead of VDDD: check ER1 erratum\n");
} else { } else {
/* using external LDO for VDDD /* using external LDO for VDDD
* Clear startup powerup and simple powerup * Clear startup powerup and simple powerup

View File

@ -2195,6 +2195,8 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
static const struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static const struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
.probe = twl4030_soc_probe, .probe = twl4030_soc_probe,
.remove = twl4030_soc_remove, .remove = twl4030_soc_remove,
.read = twl4030_read,
.write = twl4030_write,
.set_bias_level = twl4030_set_bias_level, .set_bias_level = twl4030_set_bias_level,
.idle_bias_off = true, .idle_bias_off = true,

View File

@ -1158,6 +1158,8 @@ static int twl6040_remove(struct snd_soc_codec *codec)
static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = { static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
.probe = twl6040_probe, .probe = twl6040_probe,
.remove = twl6040_remove, .remove = twl6040_remove,
.read = twl6040_read,
.write = twl6040_write,
.set_bias_level = twl6040_set_bias_level, .set_bias_level = twl6040_set_bias_level,
.suspend_bias_off = true, .suspend_bias_off = true,
.ignore_pmdown_time = true, .ignore_pmdown_time = true,

View File

@ -97,6 +97,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM
codec, then enable this option by saying Y or m. This is a codec, then enable this option by saying Y or m. This is a
recommended option recommended option
config SND_SOC_INTEL_SKYLAKE_SSP_CLK
tristate
config SND_SOC_INTEL_SKYLAKE config SND_SOC_INTEL_SKYLAKE
tristate "SKL/BXT/KBL/GLK/CNL... Platforms" tristate "SKL/BXT/KBL/GLK/CNL... Platforms"
depends on PCI && ACPI depends on PCI && ACPI

View File

@ -235,6 +235,7 @@ config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH
select SND_SOC_MAX98927 select SND_SOC_MAX98927
select SND_SOC_DMIC select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI select SND_SOC_HDAC_HDMI
select SND_SOC_INTEL_SKYLAKE_SSP_CLK
help help
This adds support for ASoC Onboard Codec I2S machine driver. This will This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for RT5663 + MAX98927. create an alsa sound card for RT5663 + MAX98927.

View File

@ -28,6 +28,9 @@
#include "../../codecs/rt5663.h" #include "../../codecs/rt5663.h"
#include "../../codecs/hdac_hdmi.h" #include "../../codecs/hdac_hdmi.h"
#include "../skylake/skl.h" #include "../skylake/skl.h"
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#define KBL_REALTEK_CODEC_DAI "rt5663-aif" #define KBL_REALTEK_CODEC_DAI "rt5663-aif"
#define KBL_MAXIM_CODEC_DAI "max98927-aif1" #define KBL_MAXIM_CODEC_DAI "max98927-aif1"
@ -48,6 +51,8 @@ struct kbl_hdmi_pcm {
struct kbl_rt5663_private { struct kbl_rt5663_private {
struct snd_soc_jack kabylake_headset; struct snd_soc_jack kabylake_headset;
struct list_head hdmi_pcm_list; struct list_head hdmi_pcm_list;
struct clk *mclk;
struct clk *sclk;
}; };
enum { enum {
@ -69,6 +74,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = {
SOC_DAPM_PIN_SWITCH("Right Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"),
}; };
static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct kbl_rt5663_private *priv = snd_soc_card_get_drvdata(card);
int ret = 0;
/*
* MCLK/SCLK need to be ON early for a successful synchronization of
* codec internal clock. And the clocks are turned off during
* POST_PMD after the stream is stopped.
*/
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable MCLK */
ret = clk_set_rate(priv->mclk, 24000000);
if (ret < 0) {
dev_err(card->dev, "Can't set rate for mclk, err: %d\n",
ret);
return ret;
}
ret = clk_prepare_enable(priv->mclk);
if (ret < 0) {
dev_err(card->dev, "Can't enable mclk, err: %d\n", ret);
return ret;
}
/* Enable SCLK */
ret = clk_set_rate(priv->sclk, 3072000);
if (ret < 0) {
dev_err(card->dev, "Can't set rate for sclk, err: %d\n",
ret);
clk_disable_unprepare(priv->mclk);
return ret;
}
ret = clk_prepare_enable(priv->sclk);
if (ret < 0) {
dev_err(card->dev, "Can't enable sclk, err: %d\n", ret);
clk_disable_unprepare(priv->mclk);
}
break;
case SND_SOC_DAPM_POST_PMD:
clk_disable_unprepare(priv->mclk);
clk_disable_unprepare(priv->sclk);
break;
default:
return 0;
}
return 0;
}
static const struct snd_soc_dapm_widget kabylake_widgets[] = { static const struct snd_soc_dapm_widget kabylake_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL),
@ -78,11 +138,14 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = {
SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL),
SND_SOC_DAPM_SPK("HDMI3", NULL), SND_SOC_DAPM_SPK("HDMI3", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
}; };
static const struct snd_soc_dapm_route kabylake_map[] = { static const struct snd_soc_dapm_route kabylake_map[] = {
/* HP jack connectors - unknown if we have jack detection */ /* HP jack connectors - unknown if we have jack detection */
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOL" },
{ "Headphone Jack", NULL, "HPOR" }, { "Headphone Jack", NULL, "HPOR" },
@ -91,6 +154,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = {
{ "Right Spk", NULL, "Right BE_OUT" }, { "Right Spk", NULL, "Right BE_OUT" },
/* other jacks */ /* other jacks */
{ "Headset Mic", NULL, "Platform Clock" },
{ "IN1P", NULL, "Headset Mic" }, { "IN1P", NULL, "Headset Mic" },
{ "IN1N", NULL, "Headset Mic" }, { "IN1N", NULL, "Headset Mic" },
{ "DMic", NULL, "SoC DMIC" }, { "DMic", NULL, "SoC DMIC" },
@ -901,6 +965,7 @@ static int kabylake_audio_probe(struct platform_device *pdev)
{ {
struct kbl_rt5663_private *ctx; struct kbl_rt5663_private *ctx;
struct skl_machine_pdata *pdata; struct skl_machine_pdata *pdata;
int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx) if (!ctx)
@ -919,6 +984,34 @@ static int kabylake_audio_probe(struct platform_device *pdev)
dmic_constraints = pdata->dmic_num == 2 ? dmic_constraints = pdata->dmic_num == 2 ?
&constraints_dmic_2ch : &constraints_dmic_channels; &constraints_dmic_2ch : &constraints_dmic_channels;
ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk");
if (IS_ERR(ctx->mclk)) {
ret = PTR_ERR(ctx->mclk);
if (ret == -ENOENT) {
dev_info(&pdev->dev,
"Failed to get ssp1_sclk, defer probe\n");
return -EPROBE_DEFER;
}
dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n",
ret);
return ret;
}
ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk");
if (IS_ERR(ctx->sclk)) {
ret = PTR_ERR(ctx->sclk);
if (ret == -ENOENT) {
dev_info(&pdev->dev,
"Failed to get ssp1_sclk, defer probe\n");
return -EPROBE_DEFER;
}
dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n",
ret);
return ret;
}
return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card); return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card);
} }

View File

@ -14,3 +14,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \
skl-sst-utils.o skl-sst-utils.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
#Skylake Clock device support
snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o

View File

@ -27,6 +27,12 @@
#define SKL_SHIFT(x) (ffs(x) - 1) #define SKL_SHIFT(x) (ffs(x) - 1)
#define SKL_MCLK_DIV_RATIO_MASK GENMASK(11, 0) #define SKL_MCLK_DIV_RATIO_MASK GENMASK(11, 0)
#define is_legacy_blob(x) (x.signature != 0xEE)
#define ext_to_legacy_blob(i2s_config_blob_ext) \
((struct skl_i2s_config_blob_legacy *) i2s_config_blob_ext)
#define get_clk_src(mclk, mask) \
((mclk.mdivctrl & mask) >> SKL_SHIFT(mask))
struct skl_i2s_config { struct skl_i2s_config {
u32 ssc0; u32 ssc0;
u32 ssc1; u32 ssc1;
@ -45,6 +51,24 @@ struct skl_i2s_config_mclk {
u32 mdivr; u32 mdivr;
}; };
struct skl_i2s_config_mclk_ext {
u32 mdivctrl;
u32 mdivr_count;
u32 mdivr[0];
} __packed;
struct skl_i2s_config_blob_signature {
u32 minor_ver : 8;
u32 major_ver : 8;
u32 resvdz : 8;
u32 signature : 8;
} __packed;
struct skl_i2s_config_blob_header {
struct skl_i2s_config_blob_signature sig;
u32 size;
};
/** /**
* struct skl_i2s_config_blob_legacy - Structure defines I2S Gateway * struct skl_i2s_config_blob_legacy - Structure defines I2S Gateway
* configuration legacy blob * configuration legacy blob
@ -61,4 +85,11 @@ struct skl_i2s_config_blob_legacy {
struct skl_i2s_config_mclk mclk; struct skl_i2s_config_mclk mclk;
}; };
struct skl_i2s_config_blob_ext {
u32 gtw_attr;
struct skl_i2s_config_blob_header hdr;
u32 tdm_ts_group[SKL_I2S_MAX_TIME_SLOTS];
struct skl_i2s_config i2s_cfg;
struct skl_i2s_config_mclk_ext mclk;
} __packed;
#endif /* __SOUND_SOC_SKL_I2S_H */ #endif /* __SOUND_SOC_SKL_I2S_H */

View File

@ -675,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
kfree(dma_ctrl); kfree(dma_ctrl);
return err; return err;
} }
EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control);
static void skl_setup_out_format(struct skl_sst *ctx, static void skl_setup_out_format(struct skl_sst *ctx,
struct skl_module_cfg *mconfig, struct skl_module_cfg *mconfig,

View File

@ -28,6 +28,7 @@ static guid_t osc_guid =
GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
{ {
acpi_handle handle; acpi_handle handle;
@ -287,6 +288,7 @@ void skl_nhlt_remove_sysfs(struct skl *skl)
static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks, static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
struct nhlt_fmt *fmt, u8 id) struct nhlt_fmt *fmt, u8 id)
{ {
struct skl_i2s_config_blob_ext *i2s_config_ext;
struct skl_i2s_config_blob_legacy *i2s_config; struct skl_i2s_config_blob_legacy *i2s_config;
struct skl_clk_parent_src *parent; struct skl_clk_parent_src *parent;
struct skl_ssp_clk *sclk, *sclkfs; struct skl_ssp_clk *sclk, *sclkfs;
@ -347,12 +349,18 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
/* Fill rate and parent for sclk/sclkfs */ /* Fill rate and parent for sclk/sclkfs */
if (!present) { if (!present) {
/* MCLK Divider Source Select */ i2s_config_ext = (struct skl_i2s_config_blob_ext *)
i2s_config = (struct skl_i2s_config_blob_legacy *)
fmt->fmt_config[0].config.caps; fmt->fmt_config[0].config.caps;
clk_src = ((i2s_config->mclk.mdivctrl)
& SKL_MNDSS_DIV_CLK_SRC_MASK) >> /* MCLK Divider Source Select */
SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK); if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
i2s_config = ext_to_legacy_blob(i2s_config_ext);
clk_src = get_clk_src(i2s_config->mclk,
SKL_MNDSS_DIV_CLK_SRC_MASK);
} else {
clk_src = get_clk_src(i2s_config_ext->mclk,
SKL_MNDSS_DIV_CLK_SRC_MASK);
}
parent = skl_get_parent_clk(clk_src); parent = skl_get_parent_clk(clk_src);
@ -378,6 +386,7 @@ static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk, static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
struct nhlt_fmt *fmt, u8 id) struct nhlt_fmt *fmt, u8 id)
{ {
struct skl_i2s_config_blob_ext *i2s_config_ext;
struct skl_i2s_config_blob_legacy *i2s_config; struct skl_i2s_config_blob_legacy *i2s_config;
struct nhlt_specific_cfg *fmt_cfg; struct nhlt_specific_cfg *fmt_cfg;
struct skl_clk_parent_src *parent; struct skl_clk_parent_src *parent;
@ -385,13 +394,21 @@ static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
u8 clk_src; u8 clk_src;
fmt_cfg = &fmt->fmt_config[0].config; fmt_cfg = &fmt->fmt_config[0].config;
i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps; i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps;
/* MCLK Divider Source Select */ /* MCLK Divider Source Select and divider */
clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >> if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK); i2s_config = ext_to_legacy_blob(i2s_config_ext);
clk_src = get_clk_src(i2s_config->mclk,
clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK; SKL_MCLK_DIV_CLK_SRC_MASK);
clkdiv = i2s_config->mclk.mdivr &
SKL_MCLK_DIV_RATIO_MASK;
} else {
clk_src = get_clk_src(i2s_config_ext->mclk,
SKL_MCLK_DIV_CLK_SRC_MASK);
clkdiv = i2s_config_ext->mclk.mdivr[0] &
SKL_MCLK_DIV_RATIO_MASK;
}
/* bypass divider */ /* bypass divider */
div_ratio = 1; div_ratio = 1;

View File

@ -0,0 +1,429 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2015-17 Intel Corporation
/*
* skl-ssp-clk.c - ASoC skylake ssp clock driver
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include "skl.h"
#include "skl-ssp-clk.h"
#include "skl-topology.h"
#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw)
struct skl_clk_parent {
struct clk_hw *hw;
struct clk_lookup *lookup;
};
struct skl_clk {
struct clk_hw hw;
struct clk_lookup *lookup;
unsigned long rate;
struct skl_clk_pdata *pdata;
u32 id;
};
struct skl_clk_data {
struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
struct skl_clk *clk[SKL_MAX_CLK_CNT];
u8 avail_clk_cnt;
};
static int skl_get_clk_type(u32 index)
{
switch (index) {
case 0 ... (SKL_SCLK_OFS - 1):
return SKL_MCLK;
case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
return SKL_SCLK;
case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
return SKL_SCLK_FS;
default:
return -EINVAL;
}
}
static int skl_get_vbus_id(u32 index, u8 clk_type)
{
switch (clk_type) {
case SKL_MCLK:
return index;
case SKL_SCLK:
return index - SKL_SCLK_OFS;
case SKL_SCLK_FS:
return index - SKL_SCLKFS_OFS;
default:
return -EINVAL;
}
}
static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
{
struct nhlt_fmt_cfg *fmt_cfg;
union skl_clk_ctrl_ipc *ipc;
struct wav_fmt *wfmt;
if (!rcfg)
return;
ipc = &rcfg->dma_ctl_ipc;
if (clk_type == SKL_SCLK_FS) {
fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
wfmt = &fmt_cfg->fmt_ext.fmt;
/* Remove TLV Header size */
ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
sizeof(struct skl_tlv_hdr);
ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
ipc->sclk_fs.valid_bit_depth =
fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
ipc->sclk_fs.number_of_channels = wfmt->channels;
} else {
ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
/* Remove TLV Header size */
ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
sizeof(struct skl_tlv_hdr);
}
}
/* Sends dma control IPC to turn the clock ON/OFF */
static int skl_send_clk_dma_control(struct skl *skl,
struct skl_clk_rate_cfg_table *rcfg,
u32 vbus_id, u8 clk_type,
bool enable)
{
struct nhlt_specific_cfg *sp_cfg;
u32 i2s_config_size, node_id = 0;
struct nhlt_fmt_cfg *fmt_cfg;
union skl_clk_ctrl_ipc *ipc;
void *i2s_config = NULL;
u8 *data, size;
int ret;
if (!rcfg)
return -EIO;
ipc = &rcfg->dma_ctl_ipc;
fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
sp_cfg = &fmt_cfg->config;
if (clk_type == SKL_SCLK_FS) {
ipc->sclk_fs.hdr.type =
enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
data = (u8 *)&ipc->sclk_fs;
size = sizeof(struct skl_dmactrl_sclkfs_cfg);
} else {
/* 1 to enable mclk, 0 to enable sclk */
if (clk_type == SKL_SCLK)
ipc->mclk.mclk = 0;
else
ipc->mclk.mclk = 1;
ipc->mclk.keep_running = enable;
ipc->mclk.warm_up_over = enable;
ipc->mclk.clk_stop_over = !enable;
data = (u8 *)&ipc->mclk;
size = sizeof(struct skl_dmactrl_mclk_cfg);
}
i2s_config_size = sp_cfg->size + size;
i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
if (!i2s_config)
return -ENOMEM;
/* copy blob */
memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
/* copy additional dma controls information */
memcpy(i2s_config + sp_cfg->size, data, size);
node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config,
i2s_config_size, node_id);
kfree(i2s_config);
return ret;
}
static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
struct skl_clk_rate_cfg_table *rcfg,
unsigned long rate)
{
int i;
for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
if (rcfg[i].rate == rate)
return &rcfg[i];
}
return NULL;
}
static int skl_clk_change_status(struct skl_clk *clkdev,
bool enable)
{
struct skl_clk_rate_cfg_table *rcfg;
int vbus_id, clk_type;
clk_type = skl_get_clk_type(clkdev->id);
if (clk_type < 0)
return clk_type;
vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
if (vbus_id < 0)
return vbus_id;
rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
clkdev->rate);
if (!rcfg)
return -EINVAL;
return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
vbus_id, clk_type, enable);
}
static int skl_clk_prepare(struct clk_hw *hw)
{
struct skl_clk *clkdev = to_skl_clk(hw);
return skl_clk_change_status(clkdev, true);
}
static void skl_clk_unprepare(struct clk_hw *hw)
{
struct skl_clk *clkdev = to_skl_clk(hw);
skl_clk_change_status(clkdev, false);
}
static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct skl_clk *clkdev = to_skl_clk(hw);
struct skl_clk_rate_cfg_table *rcfg;
int clk_type;
if (!rate)
return -EINVAL;
rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
rate);
if (!rcfg)
return -EINVAL;
clk_type = skl_get_clk_type(clkdev->id);
if (clk_type < 0)
return clk_type;
skl_fill_clk_ipc(rcfg, clk_type);
clkdev->rate = rate;
return 0;
}
static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct skl_clk *clkdev = to_skl_clk(hw);
if (clkdev->rate)
return clkdev->rate;
return 0;
}
/* Not supported by clk driver. Implemented to satisfy clk fw */
long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return rate;
}
/*
* prepare/unprepare are used instead of enable/disable as IPC will be sent
* in non-atomic context.
*/
static const struct clk_ops skl_clk_ops = {
.prepare = skl_clk_prepare,
.unprepare = skl_clk_unprepare,
.set_rate = skl_clk_set_rate,
.round_rate = skl_clk_round_rate,
.recalc_rate = skl_clk_recalc_rate,
};
static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
unsigned int id)
{
while (id--) {
clkdev_drop(pclk[id].lookup);
clk_hw_unregister_fixed_rate(pclk[id].hw);
}
}
static void unregister_src_clk(struct skl_clk_data *dclk)
{
u8 cnt = dclk->avail_clk_cnt;
while (cnt--)
clkdev_drop(dclk->clk[cnt]->lookup);
}
static int skl_register_parent_clks(struct device *dev,
struct skl_clk_parent *parent,
struct skl_clk_parent_src *pclk)
{
int i, ret;
for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
/* Register Parent clock */
parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
pclk[i].parent_name, 0, pclk[i].rate);
if (IS_ERR(parent[i].hw)) {
ret = PTR_ERR(parent[i].hw);
goto err;
}
parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
NULL);
if (!parent[i].lookup) {
clk_hw_unregister_fixed_rate(parent[i].hw);
ret = -ENOMEM;
goto err;
}
}
return 0;
err:
unregister_parent_src_clk(parent, i);
return ret;
}
/* Assign fmt_config to clk_data */
static struct skl_clk *register_skl_clk(struct device *dev,
struct skl_ssp_clk *clk,
struct skl_clk_pdata *clk_pdata, int id)
{
struct clk_init_data init;
struct skl_clk *clkdev;
int ret;
clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
if (!clkdev)
return ERR_PTR(-ENOMEM);
init.name = clk->name;
init.ops = &skl_clk_ops;
init.flags = CLK_SET_RATE_GATE;
init.parent_names = &clk->parent_name;
init.num_parents = 1;
clkdev->hw.init = &init;
clkdev->pdata = clk_pdata;
clkdev->id = id;
ret = devm_clk_hw_register(dev, &clkdev->hw);
if (ret) {
clkdev = ERR_PTR(ret);
return clkdev;
}
clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
if (!clkdev->lookup)
clkdev = ERR_PTR(-ENOMEM);
return clkdev;
}
static int skl_clk_dev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device *parent_dev = dev->parent;
struct skl_clk_parent_src *parent_clks;
struct skl_clk_pdata *clk_pdata;
struct skl_clk_data *data;
struct skl_ssp_clk *clks;
int ret, i;
clk_pdata = dev_get_platdata(&pdev->dev);
parent_clks = clk_pdata->parent_clks;
clks = clk_pdata->ssp_clks;
if (!parent_clks || !clks)
return -EIO;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* Register Parent clock */
ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
if (ret < 0)
return ret;
for (i = 0; i < clk_pdata->num_clks; i++) {
/*
* Only register valid clocks
* i.e. for which nhlt entry is present.
*/
if (clks[i].rate_cfg[0].rate == 0)
continue;
data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i);
if (IS_ERR(data->clk[i])) {
ret = PTR_ERR(data->clk[i]);
goto err_unreg_skl_clk;
}
data->avail_clk_cnt++;
}
platform_set_drvdata(pdev, data);
return 0;
err_unreg_skl_clk:
unregister_src_clk(data);
unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
return ret;
}
static int skl_clk_dev_remove(struct platform_device *pdev)
{
struct skl_clk_data *data;
data = platform_get_drvdata(pdev);
unregister_src_clk(data);
unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
return 0;
}
static struct platform_driver skl_clk_driver = {
.driver = {
.name = "skl-ssp-clk",
},
.probe = skl_clk_dev_probe,
.remove = skl_clk_dev_remove,
};
module_platform_driver(skl_clk_driver);
MODULE_DESCRIPTION("Skylake clock driver");
MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl-ssp-clk");

View File

@ -54,8 +54,46 @@ struct skl_clk_parent_src {
const char *parent_name; const char *parent_name;
}; };
struct skl_tlv_hdr {
u32 type;
u32 size;
};
struct skl_dmactrl_mclk_cfg {
struct skl_tlv_hdr hdr;
/* DMA Clk TLV params */
u32 clk_warm_up:16;
u32 mclk:1;
u32 warm_up_over:1;
u32 rsvd0:14;
u32 clk_stop_delay:16;
u32 keep_running:1;
u32 clk_stop_over:1;
u32 rsvd1:14;
};
struct skl_dmactrl_sclkfs_cfg {
struct skl_tlv_hdr hdr;
/* DMA SClk&FS TLV params */
u32 sampling_frequency;
u32 bit_depth;
u32 channel_map;
u32 channel_config;
u32 interleaving_style;
u32 number_of_channels : 8;
u32 valid_bit_depth : 8;
u32 sample_type : 8;
u32 reserved : 8;
};
union skl_clk_ctrl_ipc {
struct skl_dmactrl_mclk_cfg mclk;
struct skl_dmactrl_sclkfs_cfg sclk_fs;
};
struct skl_clk_rate_cfg_table { struct skl_clk_rate_cfg_table {
unsigned long rate; unsigned long rate;
union skl_clk_ctrl_ipc dma_ctl_ipc;
void *config; void *config;
}; };

View File

@ -38,6 +38,10 @@
/* D0I3C Register fields */ /* D0I3C Register fields */
#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ #define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */
#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */ #define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
#define SKL_MAX_DMACTRL_CFG 18
#define DMA_CLK_CONTROLS 1
#define DMA_TRANSMITION_START 2
#define DMA_TRANSMITION_STOP 3
struct skl_dsp_resource { struct skl_dsp_resource {
u32 max_mcps; u32 max_mcps;
@ -147,6 +151,8 @@ int skl_nhlt_create_sysfs(struct skl *skl);
void skl_nhlt_remove_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl);
void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks); void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id); struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
u32 caps_size, u32 node_id);
struct skl_module_cfg; struct skl_module_cfg;

View File

@ -1405,9 +1405,24 @@ static int mt2701_afe_runtime_resume(struct device *dev)
return mt2701_afe_enable_clock(afe); return mt2701_afe_enable_clock(afe);
} }
static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) static int mt2701_afe_add_component(struct mtk_base_afe *afe)
{ {
struct snd_soc_component *component; struct snd_soc_component *component;
component = kzalloc(sizeof(*component), GFP_KERNEL);
if (!component)
return -ENOMEM;
component->regmap = afe->regmap;
return snd_soc_add_component(afe->dev, component,
&mt2701_afe_pcm_dai_component,
mt2701_afe_pcm_dais,
ARRAY_SIZE(mt2701_afe_pcm_dais));
}
static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe; struct mtk_base_afe *afe;
struct mt2701_afe_private *afe_priv; struct mt2701_afe_private *afe_priv;
struct device *dev; struct device *dev;
@ -1477,12 +1492,6 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
= &mt2701_i2s_data[i][I2S_IN]; = &mt2701_i2s_data[i][I2S_IN];
} }
component = kzalloc(sizeof(*component), GFP_KERNEL);
if (!component)
return -ENOMEM;
component->regmap = afe->regmap;
afe->mtk_afe_hardware = &mt2701_afe_hardware; afe->mtk_afe_hardware = &mt2701_afe_hardware;
afe->memif_fs = mt2701_memif_fs; afe->memif_fs = mt2701_memif_fs;
afe->irq_fs = mt2701_irq_fs; afe->irq_fs = mt2701_irq_fs;
@ -1495,7 +1504,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
ret = mt2701_init_clock(afe); ret = mt2701_init_clock(afe);
if (ret) { if (ret) {
dev_err(dev, "init clock error\n"); dev_err(dev, "init clock error\n");
goto err_init_clock; return ret;
} }
platform_set_drvdata(pdev, afe); platform_set_drvdata(pdev, afe);
@ -1514,10 +1523,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
goto err_platform; goto err_platform;
} }
ret = snd_soc_add_component(dev, component, ret = mt2701_afe_add_component(afe);
&mt2701_afe_pcm_dai_component,
mt2701_afe_pcm_dais,
ARRAY_SIZE(mt2701_afe_pcm_dais));
if (ret) { if (ret) {
dev_warn(dev, "err_dai_component\n"); dev_warn(dev, "err_dai_component\n");
goto err_dai_component; goto err_dai_component;
@ -1531,8 +1537,6 @@ err_platform:
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
err_pm_disable: err_pm_disable:
pm_runtime_disable(dev); pm_runtime_disable(dev);
err_init_clock:
kfree(component);
return ret; return ret;
} }

View File

@ -272,15 +272,10 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
} }
mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node; mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node;
if (of_find_node_by_name(platform_node, "codec-capture")) { np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
np = of_get_child_by_name(pdev->dev.of_node, "codec-capture"); if (np) {
if (!np) {
dev_err(&pdev->dev,
"%s: Can't find codec-capture DT node\n",
__func__);
return -EINVAL;
}
ret = snd_soc_of_get_dai_name(np, &codec_capture_dai); ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
of_node_put(np);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"%s codec_capture_dai name fail %d\n", "%s codec_capture_dai name fail %d\n",

View File

@ -368,7 +368,8 @@ static const struct snd_soc_dai_link rockchip_dais[] = {
[DAILINK_RT5514_DSP] = { [DAILINK_RT5514_DSP] = {
.name = "RT5514 DSP", .name = "RT5514 DSP",
.stream_name = "Wake on Voice", .stream_name = "Wake on Voice",
.codec_dai_name = "rt5514-dsp-cpu-dai", .codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
}, },
}; };
@ -529,7 +530,18 @@ static int rockchip_sound_of_parse_dais(struct device *dev,
if (index < 0) if (index < 0)
continue; continue;
np_cpu = (index == DAILINK_CDNDP) ? np_cpu1 : np_cpu0; switch (index) {
case DAILINK_CDNDP:
np_cpu = np_cpu1;
break;
case DAILINK_RT5514_DSP:
np_cpu = np_codec;
break;
default:
np_cpu = np_cpu0;
break;
}
if (!np_cpu) { if (!np_cpu) {
dev_err(dev, "Missing 'rockchip,cpu' for %s\n", dev_err(dev, "Missing 'rockchip,cpu' for %s\n",
rockchip_dais[index].name); rockchip_dais[index].name);
@ -539,7 +551,8 @@ static int rockchip_sound_of_parse_dais(struct device *dev,
dai = &card->dai_link[card->num_links++]; dai = &card->dai_link[card->num_links++];
*dai = rockchip_dais[index]; *dai = rockchip_dais[index];
dai->codec_of_node = np_codec; if (!dai->codec_name)
dai->codec_of_node = np_codec;
dai->platform_of_node = np_cpu; dai->platform_of_node = np_cpu;
dai->cpu_of_node = np_cpu; dai->cpu_of_node = np_cpu;

View File

@ -40,7 +40,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) {
ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); ret = cpu_dai->driver->cops->startup(cstream, cpu_dai);
if (ret < 0) { if (ret < 0) {
dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", dev_err(cpu_dai->dev,
"Compress ASoC: can't open interface %s: %d\n",
cpu_dai->name, ret); cpu_dai->name, ret);
goto out; goto out;
} }
@ -49,8 +50,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) {
ret = platform->driver->compr_ops->open(cstream); ret = platform->driver->compr_ops->open(cstream);
if (ret < 0) { if (ret < 0) {
pr_err("compress asoc: can't open platform %s\n", dev_err(platform->dev,
platform->component.name); "Compress ASoC: can't open platform %s: %d\n",
platform->component.name, ret);
goto plat_err; goto plat_err;
} }
} }
@ -68,8 +70,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
__ret = component->driver->compr_ops->open(cstream); __ret = component->driver->compr_ops->open(cstream);
if (__ret < 0) { if (__ret < 0) {
pr_err("compress asoc: can't open platform %s\n", dev_err(component->dev,
component->name); "Compress ASoC: can't open platform %s: %d\n",
component->name, __ret);
ret = __ret; ret = __ret;
} }
} }
@ -79,7 +82,9 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
ret = rtd->dai_link->compr_ops->startup(cstream); ret = rtd->dai_link->compr_ops->startup(cstream);
if (ret < 0) { if (ret < 0) {
pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name); dev_err(rtd->dev,
"Compress ASoC: %s startup failed: %d\n",
rtd->dai_link->name, ret);
goto machine_err; goto machine_err;
} }
} }
@ -139,18 +144,19 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) {
ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); ret = cpu_dai->driver->cops->startup(cstream, cpu_dai);
if (ret < 0) { if (ret < 0) {
dev_err(cpu_dai->dev, "Compress ASoC: can't open interface %s: %d\n", dev_err(cpu_dai->dev,
"Compress ASoC: can't open interface %s: %d\n",
cpu_dai->name, ret); cpu_dai->name, ret);
goto out; goto out;
} }
} }
if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) {
ret = platform->driver->compr_ops->open(cstream); ret = platform->driver->compr_ops->open(cstream);
if (ret < 0) { if (ret < 0) {
pr_err("compress asoc: can't open platform %s\n", dev_err(platform->dev,
platform->component.name); "Compress ASoC: can't open platform %s: %d\n",
platform->component.name, ret);
goto plat_err; goto plat_err;
} }
} }
@ -168,8 +174,9 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
__ret = component->driver->compr_ops->open(cstream); __ret = component->driver->compr_ops->open(cstream);
if (__ret < 0) { if (__ret < 0) {
pr_err("compress asoc: can't open platform %s\n", dev_err(component->dev,
component->name); "Compress ASoC: can't open platform %s: %d\n",
component->name, __ret);
ret = __ret; ret = __ret;
} }
} }
@ -179,7 +186,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) {
ret = fe->dai_link->compr_ops->startup(cstream); ret = fe->dai_link->compr_ops->startup(cstream);
if (ret < 0) { if (ret < 0) {
pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); pr_err("Compress ASoC: %s startup failed: %d\n",
fe->dai_link->name, ret);
goto machine_err; goto machine_err;
} }
} }
@ -190,7 +198,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (ret < 0) if (ret < 0)
goto fe_err; goto fe_err;
else if (ret == 0) else if (ret == 0)
dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n",
fe->dai_link->name, stream ? "capture" : "playback"); fe->dai_link->name, stream ? "capture" : "playback");
/* calculate valid and active FE <-> BE dpcms */ /* calculate valid and active FE <-> BE dpcms */
@ -265,10 +273,11 @@ static void close_delayed_work(struct work_struct *work)
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", dev_dbg(rtd->dev,
codec_dai->driver->playback.stream_name, "Compress ASoC: pop wq checking: %s status: %s waiting: %s\n",
codec_dai->playback_active ? "active" : "inactive", codec_dai->driver->playback.stream_name,
rtd->pop_wait ? "yes" : "no"); codec_dai->playback_active ? "active" : "inactive",
rtd->pop_wait ? "yes" : "no");
/* are we waiting on this codec DAI stream */ /* are we waiting on this codec DAI stream */
if (rtd->pop_wait == 1) { if (rtd->pop_wait == 1) {
@ -307,7 +316,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
if (!codec_dai->active) if (!codec_dai->active)
codec_dai->rate = 0; codec_dai->rate = 0;
if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
rtd->dai_link->compr_ops->shutdown(cstream); rtd->dai_link->compr_ops->shutdown(cstream);
@ -376,7 +384,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
ret = dpcm_be_dai_hw_free(fe, stream); ret = dpcm_be_dai_hw_free(fe, stream);
if (ret < 0) if (ret < 0)
dev_err(fe->dev, "compressed hw_free failed %d\n", ret); dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret);
ret = dpcm_be_dai_shutdown(fe, stream); ret = dpcm_be_dai_shutdown(fe, stream);
@ -460,7 +468,6 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger)
cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
@ -944,7 +951,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
struct snd_soc_platform *platform = rtd->platform; struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_component *component; struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_rtdcom_list *rtdcom;
int ret = 0, __ret; int ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@ -965,10 +972,10 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
!component->driver->compr_ops->copy) !component->driver->compr_ops->copy)
continue; continue;
__ret = component->driver->compr_ops->copy(cstream, buf, count); ret = component->driver->compr_ops->copy(cstream, buf, count);
if (__ret < 0) break;
ret = __ret;
} }
err: err:
mutex_unlock(&rtd->pcm_mutex); mutex_unlock(&rtd->pcm_mutex);
return ret; return ret;
@ -1108,7 +1115,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
int playback = 0, capture = 0; int playback = 0, capture = 0;
if (rtd->num_codecs > 1) { if (rtd->num_codecs > 1) {
dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n"); dev_err(rtd->card->dev,
"Compress ASoC: Multicodec not supported\n");
return -EINVAL; return -EINVAL;
} }
@ -1126,8 +1134,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
* should be set, check for that (xor) * should be set, check for that (xor)
*/ */
if (playback + capture != 1) { if (playback + capture != 1) {
dev_err(rtd->card->dev, "Invalid direction for compress P %d, C %d\n", dev_err(rtd->card->dev,
playback, capture); "Compress ASoC: Invalid direction for P %d, C %d\n",
playback, capture);
return -EINVAL; return -EINVAL;
} }
@ -1155,8 +1164,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
rtd->dai_link->dpcm_playback, rtd->dai_link->dpcm_playback,
rtd->dai_link->dpcm_capture, &be_pcm); rtd->dai_link->dpcm_capture, &be_pcm);
if (ret < 0) { if (ret < 0) {
dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", dev_err(rtd->card->dev,
rtd->dai_link->name); "Compress ASoC: can't create compressed for %s: %d\n",
rtd->dai_link->name, ret);
goto compr_err; goto compr_err;
} }
@ -1199,8 +1209,9 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
new_name, compr); new_name, compr);
if (ret < 0) { if (ret < 0) {
component = rtd->codec_dai->component; component = rtd->codec_dai->component;
pr_err("compress asoc: can't create compress for codec %s\n", dev_err(component->dev,
component->name); "Compress ASoC: can't create compress for codec %s: %d\n",
component->name, ret);
goto compr_err; goto compr_err;
} }
@ -1210,8 +1221,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
rtd->compr = compr; rtd->compr = compr;
compr->private_data = rtd; compr->private_data = rtd;
printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, dev_info(rtd->card->dev, "Compress ASoC: %s <-> %s mapping ok\n",
cpu_dai->name); codec_dai->name, cpu_dai->name);
return ret; return ret;
compr_err: compr_err:

View File

@ -553,9 +553,17 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
{ {
struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_rtdcom_list *rtdcom;
if (!driver_name)
return NULL;
for_each_rtdcom(rtd, rtdcom) { for_each_rtdcom(rtd, rtdcom) {
if ((rtdcom->component->driver->name == driver_name) || const char *component_name = rtdcom->component->driver->name;
strcmp(rtdcom->component->driver->name, driver_name) == 0)
if (!component_name)
continue;
if ((component_name == driver_name) ||
strcmp(component_name, driver_name) == 0)
return rtdcom->component; return rtdcom->component;
} }

View File

@ -2026,7 +2026,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
snd_soc_dapm_for_each_direction(dir) { snd_soc_dapm_for_each_direction(dir) {
rdir = SND_SOC_DAPM_DIR_REVERSE(dir); rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
snd_soc_dapm_widget_for_each_path(w, dir, p) { snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->connected && !p->connected(w, p->node[rdir])) if (p->connected && !p->connected(p->source, p->sink))
continue; continue;
if (!p->connect) if (!p->connect)

View File

@ -144,7 +144,7 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
for_each_rtdcom(rtd, rtdcom) { for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component; component = rtdcom->component;
ignore &= !component->driver->pmdown_time; ignore &= !component->driver->use_pmdown_time;
} }
/* this will be removed */ /* this will be removed */
@ -2831,10 +2831,9 @@ static void soc_pcm_private_free(struct snd_pcm *pcm)
struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_component *component; struct snd_soc_component *component;
/* need to sync the delayed work before releasing resources */
flush_delayed_work(&rtd->delayed_work);
for_each_rtdcom(rtd, rtdcom) { for_each_rtdcom(rtd, rtdcom) {
/* need to sync the delayed work before releasing resources */
flush_delayed_work(&rtd->delayed_work);
component = rtdcom->component; component = rtdcom->component;
if (component->pcm_free) if (component->pcm_free)

View File

@ -2,7 +2,7 @@ menu "STMicroelectronics STM32 SOC audio support"
config SND_SOC_STM32_SAI config SND_SOC_STM32_SAI
tristate "STM32 SAI interface (Serial Audio Interface) support" tristate "STM32 SAI interface (Serial Audio Interface) support"
depends on ARCH_STM32 || COMPILE_TEST depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on SND_SOC depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO select REGMAP_MMIO
@ -11,7 +11,7 @@ config SND_SOC_STM32_SAI
config SND_SOC_STM32_I2S config SND_SOC_STM32_I2S
tristate "STM32 I2S interface (SPI/I2S block) support" tristate "STM32 I2S interface (SPI/I2S block) support"
depends on ARCH_STM32 || COMPILE_TEST depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on SND_SOC depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO select REGMAP_MMIO
@ -20,7 +20,7 @@ config SND_SOC_STM32_I2S
config SND_SOC_STM32_SPDIFRX config SND_SOC_STM32_SPDIFRX
tristate "STM32 S/PDIF receiver (SPDIFRX) support" tristate "STM32 S/PDIF receiver (SPDIFRX) support"
depends on ARCH_STM32 || COMPILE_TEST depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on SND_SOC depends on SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO select REGMAP_MMIO

View File

@ -37,9 +37,11 @@
#define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0 #define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0
#define SUN8I_MOD_CLK_ENA 0x010 #define SUN8I_MOD_CLK_ENA 0x010
#define SUN8I_MOD_CLK_ENA_AIF1 15 #define SUN8I_MOD_CLK_ENA_AIF1 15
#define SUN8I_MOD_CLK_ENA_ADC 3
#define SUN8I_MOD_CLK_ENA_DAC 2 #define SUN8I_MOD_CLK_ENA_DAC 2
#define SUN8I_MOD_RST_CTL 0x014 #define SUN8I_MOD_RST_CTL 0x014
#define SUN8I_MOD_RST_CTL_AIF1 15 #define SUN8I_MOD_RST_CTL_AIF1 15
#define SUN8I_MOD_RST_CTL_ADC 3
#define SUN8I_MOD_RST_CTL_DAC 2 #define SUN8I_MOD_RST_CTL_DAC 2
#define SUN8I_SYS_SR_CTRL 0x018 #define SUN8I_SYS_SR_CTRL 0x018
#define SUN8I_SYS_SR_CTRL_AIF1_FS 12 #define SUN8I_SYS_SR_CTRL_AIF1_FS 12
@ -54,9 +56,25 @@
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4) #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2
#define SUN8I_AIF1_ADCDAT_CTRL 0x044
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA 15
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA 14
#define SUN8I_AIF1_DACDAT_CTRL 0x048 #define SUN8I_AIF1_DACDAT_CTRL 0x048
#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15
#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14
#define SUN8I_AIF1_MXR_SRC 0x04c
#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L 15
#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL 14
#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL 13
#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR 12
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9
#define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8
#define SUN8I_ADC_DIG_CTRL 0x100
#define SUN8I_ADC_DIG_CTRL_ENDA 15
#define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2
#define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1
#define SUN8I_DAC_DIG_CTRL 0x120 #define SUN8I_DAC_DIG_CTRL 0x120
#define SUN8I_DAC_DIG_CTRL_ENDA 15 #define SUN8I_DAC_DIG_CTRL_ENDA 15
#define SUN8I_DAC_MXR_SRC 0x130 #define SUN8I_DAC_MXR_SRC 0x130
@ -338,10 +356,30 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
}; };
static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
SUN8I_AIF1_MXR_SRC,
SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L,
SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC,
SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL,
SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
SUN8I_AIF1_MXR_SRC,
SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL,
SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
SUN8I_AIF1_MXR_SRC,
SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR,
SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
};
static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
/* Digital parts of the DACs */ /* Digital parts of the DACs and ADC */
SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
0, NULL, 0), 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA,
0, NULL, 0),
/* Analog DAC AIF */ /* Analog DAC AIF */
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0, SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0,
@ -351,17 +389,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
/* DAC Mixers */ /* Analog ADC AIF */
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0),
SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0),
/* DAC and ADC Mixers */
SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls), sun8i_dac_mixer_controls),
SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls), sun8i_dac_mixer_controls),
SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_input_mixer_controls),
SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_input_mixer_controls),
/* Clocks */ /* Clocks */
SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA,
SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL, SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL, SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
@ -378,6 +430,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL, SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Mic", NULL),
}; };
static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
@ -387,11 +445,16 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "RST AIF1", NULL, "AIF1 PLL" }, { "RST AIF1", NULL, "AIF1 PLL" },
{ "MODCLK AFI1", NULL, "RST AIF1" }, { "MODCLK AFI1", NULL, "RST AIF1" },
{ "DAC", NULL, "MODCLK AFI1" }, { "DAC", NULL, "MODCLK AFI1" },
{ "ADC", NULL, "MODCLK AFI1" },
{ "RST DAC", NULL, "SYSCLK" }, { "RST DAC", NULL, "SYSCLK" },
{ "MODCLK DAC", NULL, "RST DAC" }, { "MODCLK DAC", NULL, "RST DAC" },
{ "DAC", NULL, "MODCLK DAC" }, { "DAC", NULL, "MODCLK DAC" },
{ "RST ADC", NULL, "SYSCLK" },
{ "MODCLK ADC", NULL, "RST ADC" },
{ "ADC", NULL, "MODCLK ADC" },
/* DAC Routes */ /* DAC Routes */
{ "AIF1 Slot 0 Right", NULL, "DAC" }, { "AIF1 Slot 0 Right", NULL, "DAC" },
{ "AIF1 Slot 0 Left", NULL, "DAC" }, { "AIF1 Slot 0 Left", NULL, "DAC" },
@ -401,6 +464,12 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
"AIF1 Slot 0 Left"}, "AIF1 Slot 0 Left"},
{ "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
"AIF1 Slot 0 Right"}, "AIF1 Slot 0 Right"},
/* ADC routes */
{ "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
"AIF1 Slot 0 Left ADC" },
{ "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
"AIF1 Slot 0 Right ADC" },
}; };
static const struct snd_soc_dai_ops sun8i_codec_dai_ops = { static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
@ -418,6 +487,15 @@ static struct snd_soc_dai_driver sun8i_codec_dai = {
.rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
}, },
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.sig_bits = 24,
},
/* pcm operations */ /* pcm operations */
.ops = &sun8i_codec_dai_ops, .ops = &sun8i_codec_dai_ops,
}; };