diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-07 12:11:09 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-07 12:11:09 -0800 |
commit | 7590e37bdaeec25ae325f4ba450be13e2aac6c8d (patch) | |
tree | 8249846959a09148a467269c65756d4c278d4f07 /sound | |
parent | 7e6127c1240ed569cdda2a67c8f03836f9f28c05 (diff) | |
parent | 3d0a352a552e6cdf052e8baf66f1fe6418c122fe (diff) |
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'
...
Diffstat (limited to 'sound')
28 files changed, 918 insertions, 106 deletions
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index d7469cdd90dc..98f93e79c654 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -110,16 +110,15 @@ static const struct snd_soc_dapm_route intercon[] = { static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct device *dev = rtd->dev; int ret; - printk(KERN_DEBUG - "at91sam9g20ek_wm8731 " - ": at91sam9g20ek_wm8731_init() called\n"); + dev_dbg(dev, "%s called\n", __func__); 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) { - printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); + dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret); return ret; } @@ -179,21 +178,21 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) */ mclk = clk_get(NULL, "pck0"); 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); goto err; } pllb = clk_get(NULL, "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); goto err_mclk; } ret = clk_set_parent(mclk, pllb); clk_put(pllb); 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; } @@ -236,7 +235,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { - printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n"); + dev_err(&pdev->dev, "snd_soc_register_card() failed\n"); } return ret; diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c index ccdf547f4d8c..e6c303ab869d 100644 --- a/sound/soc/atmel/sam9x5_wm8731.c +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -49,13 +49,13 @@ static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd) struct device *dev = rtd->dev; 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 */ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, MCLK_RATE, SND_SOC_CLOCK_IN); 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; } @@ -146,8 +146,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) ret = atmel_ssc_set_audio(priv->ssc_id); if (ret != 0) { - dev_err(&pdev->dev, - "ASoC: Failed to set SSC %d for audio: %d\n", + dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n", ret, priv->ssc_id); goto out; } @@ -157,12 +156,11 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { - dev_err(&pdev->dev, - "ASoC: Platform device allocation failed\n"); + dev_err(&pdev->dev, "Platform device allocation failed\n"); goto out_put_audio; } - dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__); + dev_dbg(&pdev->dev, "%s ok\n", __func__); return ret; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index b95bb8b52e51..3d1cf4784e87 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -15,6 +15,7 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of_device.h> @@ -95,6 +96,9 @@ struct ak4613_priv { struct mutex lock; const struct ak4613_interface *iface; 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 fmt; @@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + priv->rate = rate; /* * FIXME @@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec, 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 = { .startup = ak4613_dai_startup, .shutdown = ak4613_dai_shutdown, .set_sysclk = ak4613_dai_set_sysclk, .set_fmt = ak4613_dai_set_fmt, + .trigger = ak4613_dai_trigger, .hw_params = ak4613_dai_hw_params, }; @@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, priv->iface = NULL; priv->cnt = 0; priv->sysclk = 0; + INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write); mutex_init(&priv->lock); diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index c88f974ebe3e..cf83c423394d 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -113,7 +113,7 @@ static int dmic_dev_probe(struct platform_device *pdev) if (pdev->dev.of_node) { err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); - if (err && (err != -ENOENT)) + if (err && (err != -EINVAL)) return err; if (!err) { diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index 31b0864583e8..562e88765129 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2017, Maxim Integrated */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated #include <linux/acpi.h> #include <linux/i2c.h> diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h index d0b359d0cf8c..f6a37aa02f26 100644 --- a/sound/soc/codecs/max98373.h +++ b/sound/soc/codecs/max98373.h @@ -1,5 +1,6 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2017, Maxim Integrated */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated + #ifndef _MAX98373_H #define _MAX98373_H diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 633cdcfc933d..e1ab5537d27a 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1392,7 +1392,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, ana_pwr |= SGTL5000_LINEREG_D_POWERUP; dev_info(&client->dev, - "Using internal LDO instead of VDDD: check ER1\n"); + "Using internal LDO instead of VDDD: check ER1 erratum\n"); } else { /* using external LDO for VDDD * Clear startup powerup and simple powerup diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 8798182959c1..e4d7f397d361 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -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 = { .probe = twl4030_soc_probe, .remove = twl4030_soc_remove, + .read = twl4030_read, + .write = twl4030_write, .set_bias_level = twl4030_set_bias_level, .idle_bias_off = true, diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 3b895b4b451c..573a523ed0b3 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1158,6 +1158,8 @@ static int twl6040_remove(struct snd_soc_codec *codec) static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = { .probe = twl6040_probe, .remove = twl6040_remove, + .read = twl6040_read, + .write = twl6040_write, .set_bias_level = twl6040_set_bias_level, .suspend_bias_off = true, .ignore_pmdown_time = true, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index f2c9e8c5970a..ceb105cbd461 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -97,6 +97,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM codec, then enable this option by saying Y or m. This is a recommended option +config SND_SOC_INTEL_SKYLAKE_SSP_CLK + tristate + config SND_SOC_INTEL_SKYLAKE tristate "SKL/BXT/KBL/GLK/CNL... Platforms" depends on PCI && ACPI diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d4e103615f51..fefb1ee9fec6 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -235,6 +235,7 @@ config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH select SND_SOC_MAX98927 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_SKYLAKE_SSP_CLK help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for RT5663 + MAX98927. diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index bf7014ca486f..f5df6bca3156 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -28,6 +28,9 @@ #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.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_MAXIM_CODEC_DAI "max98927-aif1" @@ -48,6 +51,8 @@ struct kbl_hdmi_pcm { struct kbl_rt5663_private { struct snd_soc_jack kabylake_headset; struct list_head hdmi_pcm_list; + struct clk *mclk; + struct clk *sclk; }; enum { @@ -69,6 +74,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = { 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[] = { SND_SOC_DAPM_HP("Headphone Jack", 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("HDMI2", 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[] = { /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "Platform Clock" }, { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -91,6 +154,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "Right Spk", NULL, "Right BE_OUT" }, /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, { "IN1P", NULL, "Headset Mic" }, { "IN1N", NULL, "Headset Mic" }, { "DMic", NULL, "SoC DMIC" }, @@ -901,6 +965,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_rt5663_private *ctx; struct skl_machine_pdata *pdata; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); if (!ctx) @@ -919,6 +984,34 @@ static int kabylake_audio_probe(struct platform_device *pdev) dmic_constraints = pdata->dmic_num == 2 ? &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); } diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index d1ccbecd141f..86f6e1d801af 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -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 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 diff --git a/sound/soc/intel/skylake/skl-i2s.h b/sound/soc/intel/skylake/skl-i2s.h index dcf819bc688f..ad0a1bbca13c 100644 --- a/sound/soc/intel/skylake/skl-i2s.h +++ b/sound/soc/intel/skylake/skl-i2s.h @@ -27,6 +27,12 @@ #define SKL_SHIFT(x) (ffs(x) - 1) #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 { u32 ssc0; u32 ssc1; @@ -45,6 +51,24 @@ struct skl_i2s_config_mclk { 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 * configuration legacy blob @@ -61,4 +85,11 @@ struct skl_i2s_config_blob_legacy { 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 */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8cbf080c38b3..60d76adade43 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -675,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, kfree(dma_ctrl); return err; } +EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control); static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 3b1d2b828c1b..b9b140275be0 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -28,6 +28,7 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); + struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { 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, 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_clk_parent_src *parent; 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 */ if (!present) { - /* MCLK Divider Source Select */ - i2s_config = (struct skl_i2s_config_blob_legacy *) + i2s_config_ext = (struct skl_i2s_config_blob_ext *) fmt->fmt_config[0].config.caps; - clk_src = ((i2s_config->mclk.mdivctrl) - & SKL_MNDSS_DIV_CLK_SRC_MASK) >> - SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK); + + /* MCLK Divider Source Select */ + 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); @@ -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, struct nhlt_fmt *fmt, u8 id) { + struct skl_i2s_config_blob_ext *i2s_config_ext; struct skl_i2s_config_blob_legacy *i2s_config; struct nhlt_specific_cfg *fmt_cfg; 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; fmt_cfg = &fmt->fmt_config[0].config; - i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps; - - /* MCLK Divider Source Select */ - clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >> - SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK); - - clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK; + i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps; + + /* MCLK Divider Source Select and divider */ + 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_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 */ div_ratio = 1; diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c new file mode 100644 index 000000000000..7fbddf5e3b00 --- /dev/null +++ b/sound/soc/intel/skylake/skl-ssp-clk.c @@ -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; |