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
29 changed files with 918 additions and 106 deletions
+1 -1
View File
@@ -841,7 +841,7 @@ struct snd_soc_component_driver {
/* bits */
unsigned int idle_bias_on: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 non_legacy_dai_naming:1;
};
+8 -9
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)
{
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;
+5 -7
View File
@@ -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;
+78
View File
@@ -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);
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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>
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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
+2
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 = {
.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,
+2
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 = {
.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,
+3
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
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
+1
View File
@@ -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.
+94 -1
View File
@@ -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);
}
+5
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
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
+31
View File
@@ -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 */
+1
View File
@@ -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,
+28 -11
View File
@@ -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;
i2s_config_ext = (struct skl_i2s_config_blob_ext *)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;
/* 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;
+429
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");
+38
View File
@@ -54,8 +54,46 @@ struct skl_clk_parent_src {
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 {
unsigned long rate;
union skl_clk_ctrl_ipc dma_ctl_ipc;
void *config;
};
+6
View File
@@ -38,6 +38,10 @@
/* D0I3C Register fields */
#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */
#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 {
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_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
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;

Some files were not shown because too many files have changed in this diff Show More