Commit 180bc41a authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/es8328', 'asoc/topic/find-dai',...

Merge remote-tracking branches 'asoc/topic/es8328', 'asoc/topic/find-dai', 'asoc/topic/fsl', 'asoc/topic/fsl-sai' and 'asoc/topic/fsl-ssi' into asoc-next
...@@ -7,8 +7,8 @@ codec/DSP interfaces. ...@@ -7,8 +7,8 @@ codec/DSP interfaces.
Required properties: Required properties:
- compatible : Compatible list, contains "fsl,vf610-sai" or - compatible : Compatible list, contains "fsl,vf610-sai",
"fsl,imx6sx-sai". "fsl,imx6sx-sai" or "fsl,imx6ul-sai"
- reg : Offset and length of the register set for the device. - reg : Offset and length of the register set for the device.
...@@ -48,6 +48,11 @@ Required properties: ...@@ -48,6 +48,11 @@ Required properties:
receive data by following their own bit clocks and receive data by following their own bit clocks and
frame sync clocks separately. frame sync clocks separately.
Optional properties (for mx6ul):
- fsl,sai-mclk-direction-output: This is a boolean property. If present,
indicates that SAI will output the SAI MCLK clock.
Note: Note:
- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
default synchronous mode (sync Rx with Tx) will be used, which means both default synchronous mode (sync Rx with Tx) will be used, which means both
......
...@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS ...@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS
M: Timur Tabi <timur@tabi.org> M: Timur Tabi <timur@tabi.org>
M: Nicolin Chen <nicoleotsuka@gmail.com> M: Nicolin Chen <nicoleotsuka@gmail.com>
M: Xiubo Li <Xiubo.Lee@gmail.com> M: Xiubo Li <Xiubo.Lee@gmail.com>
R: Fabio Estevam <fabio.estevam@nxp.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: linuxppc-dev@lists.ozlabs.org L: linuxppc-dev@lists.ozlabs.org
S: Maintained S: Maintained
......
...@@ -447,5 +447,11 @@ ...@@ -447,5 +447,11 @@
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18) #define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
#define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17)
#define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17)
#define IMX6UL_GPR1_SAI1_MCLK_DIR (0x1 << 19)
#define IMX6UL_GPR1_SAI2_MCLK_DIR (0x1 << 20)
#define IMX6UL_GPR1_SAI3_MCLK_DIR (0x1 << 21)
#define IMX6UL_GPR1_SAI_MCLK_MASK (0x7 << 19)
#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \
IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR)
#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */ #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
...@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card, ...@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card,
int snd_soc_register_dai(struct snd_soc_component *component, int snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv); struct snd_soc_dai_driver *dai_drv);
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc);
#include <sound/soc-dai.h> #include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
......
...@@ -26,18 +26,30 @@ ...@@ -26,18 +26,30 @@
#include <sound/tlv.h> #include <sound/tlv.h>
#include "es8328.h" #include "es8328.h"
#define ES8328_SYSCLK_RATE_1X 11289600 static const unsigned int rates_12288[] = {
#define ES8328_SYSCLK_RATE_2X 22579200 8000, 12000, 16000, 24000, 32000, 48000, 96000,
};
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ static const int ratios_12288[] = {
static struct { 10, 7, 6, 4, 3, 2, 0,
int rate; };
u8 ratio;
} mclk_ratios[] = { static const struct snd_pcm_hw_constraint_list constraints_12288 = {
{ 8000, 9 }, .count = ARRAY_SIZE(rates_12288),
{11025, 7 }, .list = rates_12288,
{22050, 4 }, };
{44100, 2 },
static const unsigned int rates_11289[] = {
8018, 11025, 22050, 44100, 88200,
};
static const int ratios_11289[] = {
9, 7, 4, 2, 0,
};
static const struct snd_pcm_hw_constraint_list constraints_11289 = {
.count = ARRAY_SIZE(rates_11289),
.list = rates_11289,
}; };
/* regulator supplies for sgtl5000, VDDD is an optional external supply */ /* regulator supplies for sgtl5000, VDDD is an optional external supply */
...@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = { ...@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"HPVDD", "HPVDD",
}; };
#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ #define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_11025) SNDRV_PCM_RATE_16000 | \
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_8000)
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
struct es8328_priv { struct es8328_priv {
struct regmap *regmap; struct regmap *regmap;
struct clk *clk; struct clk *clk;
int playback_fs; int playback_fs;
bool deemph; bool deemph;
int mclkdiv2;
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
const int *mclk_ratios;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
}; };
...@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute) ...@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
mute ? ES8328_DACCONTROL3_DACMUTE : 0); mute ? ES8328_DACCONTROL3_DACMUTE : 0);
} }
static int es8328_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
if (es8328->sysclk_constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
es8328->sysclk_constraints);
return 0;
}
static int es8328_hw_params(struct snd_pcm_substream *substream, static int es8328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
int i; int i;
int reg; int reg;
u8 ratio; int wl;
int ratio;
if (!es8328->sysclk_constraints) {
dev_err(codec->dev, "No MCLK configured\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2; reg = ES8328_DACCONTROL2;
else else
reg = ES8328_ADCCONTROL5; reg = ES8328_ADCCONTROL5;
clk_rate = clk_get_rate(es8328->clk); for (i = 0; i < es8328->sysclk_constraints->count; i++)
if (es8328->sysclk_constraints->list[i] == params_rate(params))
break;
if ((clk_rate != ES8328_SYSCLK_RATE_1X) && if (i == es8328->sysclk_constraints->count) {
(clk_rate != ES8328_SYSCLK_RATE_2X)) { dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
dev_err(codec->dev, params_rate(params));
"%s: clock is running at %d Hz, not %d or %d Hz\n",
__func__, clk_rate,
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
return -EINVAL; return -EINVAL;
} }
/* find master mode MCLK to sampling frequency ratio */ ratio = es8328->mclk_ratios[i];
ratio = mclk_ratios[0].rate; snd_soc_update_bits(codec, ES8328_MASTERMODE,
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) ES8328_MASTERMODE_MCLKDIV2,
if (params_rate(params) <= mclk_ratios[i].rate) es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
ratio = mclk_ratios[i].ratio;
switch (params_width(params)) {
case 16:
wl = 3;
break;
case 18:
wl = 2;
break;
case 20:
wl = 1;
break;
case 24:
wl = 0;
break;
case 32:
wl = 4;
break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
ES8328_DACCONTROL1_DACWL_MASK,
wl << ES8328_DACCONTROL1_DACWL_SHIFT);
es8328->playback_fs = params_rate(params); es8328->playback_fs = params_rate(params);
es8328_set_deemph(codec); es8328_set_deemph(codec);
} } else
snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCWL_MASK,
wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
} }
static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int mclkdiv2 = 0;
switch (freq) {
case 0:
es8328->sysclk_constraints = NULL;
es8328->mclk_ratios = NULL;
break;
case 22579200:
mclkdiv2 = 1;
/* fallthru */
case 11289600:
es8328->sysclk_constraints = &constraints_11289;
es8328->mclk_ratios = ratios_11289;
break;
case 24576000:
mclkdiv2 = 1;
/* fallthru */
case 12288000:
es8328->sysclk_constraints = &constraints_12288;
es8328->mclk_ratios = ratios_12288;
break;
default:
return -EINVAL;
}
es8328->mclkdiv2 = mclkdiv2;
return 0;
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); u8 dac_mode = 0;
int clk_rate; u8 adc_mode = 0;
u8 mode = ES8328_DACCONTROL1_DACWL_16;
/* set master/slave audio interface */ /* set master/slave audio interface */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
...@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* interface format */ /* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
break; break;
case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_RIGHT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL; return -EINVAL;
snd_soc_write(codec, ES8328_DACCONTROL1, mode); snd_soc_update_bits(codec, ES8328_DACCONTROL1,
snd_soc_write(codec, ES8328_ADCCONTROL4, mode); ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
/* Master serial port mode, with BCLK generated automatically */ /* Master serial port mode, with BCLK generated automatically */
clk_rate = clk_get_rate(es8328->clk); snd_soc_update_bits(codec, ES8328_MASTERMODE,
if (clk_rate == ES8328_SYSCLK_RATE_1X) ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC);
else
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2 |
ES8328_MASTERMODE_MSC);
return 0; return 0;
} }
...@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, ...@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
} }
static const struct snd_soc_dai_ops es8328_dai_ops = { static const struct snd_soc_dai_ops es8328_dai_ops = {
.startup = es8328_startup,
.hw_params = es8328_hw_params, .hw_params = es8328_hw_params,
.digital_mute = es8328_mute, .digital_mute = es8328_mute,
.set_sysclk = es8328_set_sysclk,
.set_fmt = es8328_set_dai_fmt, .set_fmt = es8328_set_dai_fmt,
}; };
...@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = { ...@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
.formats = ES8328_FORMATS, .formats = ES8328_FORMATS,
}, },
.ops = &es8328_dai_ops, .ops = &es8328_dai_ops,
.symmetric_rates = 1,
}; };
static int es8328_suspend(struct snd_soc_codec *codec) static int es8328_suspend(struct snd_soc_codec *codec)
...@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = { ...@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
.val_bits = 8, .val_bits = 8,
.max_register = ES8328_REG_MAX, .max_register = ES8328_REG_MAX,
.cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
}; };
EXPORT_SYMBOL_GPL(es8328_regmap_config); EXPORT_SYMBOL_GPL(es8328_regmap_config);
......
...@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) #define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
#define ES8328_CONTROL1_ENREF (1 << 2) #define ES8328_CONTROL1_ENREF (1 << 2)
#define ES8328_CONTROL1_SEQEN (1 << 3) #define ES8328_CONTROL1_SEQEN (1 << 3)
#define ES8328_CONTROL1_SAMEFS (1 << 4) #define ES8328_CONTROL1_SAMEFS (1 << 4)
...@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL1 0x09 #define ES8328_ADCCONTROL1 0x09
#define ES8328_ADCCONTROL2 0x0a #define ES8328_ADCCONTROL2 0x0a
#define ES8328_ADCCONTROL3 0x0b #define ES8328_ADCCONTROL3 0x0b
#define ES8328_ADCCONTROL4 0x0c #define ES8328_ADCCONTROL4 0x0c
#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
#define ES8328_ADCCONTROL5 0x0d #define ES8328_ADCCONTROL5 0x0d
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
...@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL14 0x16 #define ES8328_ADCCONTROL14 0x16
#define ES8328_DACCONTROL1 0x17 #define ES8328_DACCONTROL1 0x17
#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3) #define ES8328_DACCONTROL1_DACWL_SHIFT 3
#define ES8328_DACCONTROL1_DACWL_20 (1 << 3) #define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include "fsl_sai.h" #include "fsl_sai.h"
#include "imx-pcm.h" #include "imx-pcm.h"
...@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct fsl_sai *sai; struct fsl_sai *sai;
struct regmap *gpr;
struct resource *res; struct resource *res;
void __iomem *base; void __iomem *base;
char tmp[8]; char tmp[8];
int irq, ret, i; int irq, ret, i;
int index;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai) if (!sai)
...@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->pdev = pdev; sai->pdev = pdev;
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") ||
of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai"))
sai->sai_on_imx = true; sai->sai_on_imx = true;
sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
...@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_sai_dai.symmetric_samplebits = 0; fsl_sai_dai.symmetric_samplebits = 0;
} }
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) {
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
if (IS_ERR(gpr)) {
dev_err(&pdev->dev, "cannot find iomuxc registers\n");
return PTR_ERR(gpr);
}
index = of_alias_get_id(np, "sai");
if (index < 0)
return index;
regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
MCLK_DIR(index));
}
sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
...@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
static const struct of_device_id fsl_sai_ids[] = { static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,vf610-sai", },
{ .compatible = "fsl,imx6sx-sai", }, { .compatible = "fsl,imx6sx-sai", },
{ .compatible = "fsl,imx6ul-sai", },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fsl_sai_ids); MODULE_DEVICE_TABLE(of, fsl_sai_ids);
......
...@@ -262,6 +262,7 @@ struct fsl_ssi_private { ...@@ -262,6 +262,7 @@ struct fsl_ssi_private {
struct fsl_ssi_dbg dbg_stats; struct fsl_ssi_dbg dbg_stats;
const struct fsl_ssi_soc_data *soc; const struct fsl_ssi_soc_data *soc;
struct device *dev;
}; };
/* /*
...@@ -400,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, ...@@ -400,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
} }
} }
/*
* Clear RX or TX FIFO to remove samples from the previous
* stream session which may be still present in the FIFO and
* may introduce bad samples and/or channel slipping.
*
* Note: The SOR is not documented in recent IMX datasheet, but
* is described in IMX51 reference manual at section 56.3.3.15.
*/
static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
bool is_rx)
{
if (is_rx) {
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR);
} else {
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
}
}
/* /*
* Calculate the bits that have to be disabled for the current stream that is * Calculate the bits that have to be disabled for the current stream that is
* getting disabled. This keeps the bits enabled that are necessary for the * getting disabled. This keeps the bits enabled that are necessary for the
...@@ -475,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -475,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
* (online configuration) * (online configuration)
*/ */
if (enable) { if (enable) {
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE);
regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
} else { } else {
u32 sier; u32 sier;
u32 srcr; u32 srcr;
...@@ -507,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -507,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
config_done: config_done:
/* Enabling of subunits is done after configuration */ /* Enabling of subunits is done after configuration */
if (enable) if (enable) {
if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
/*
* Be sure the Tx FIFO is filled when TE is set.
* Otherwise, there are some chances to start the
* playback with some void samples inserted first,
* generating a channel slip.
*
* First, SSIEN must be set, to let the FIFO be filled.
*
* Notes:
* - Limit this fix to the DMA case until FIQ cases can
* be tested.
* - Limit the length of the busy loop to not lock the
* system too long, even if 1-2 loops are sufficient
* in general.
*/
int i;
int max_loop = 100;
regmap_update_bits(regs, CCSR_SSI_SCR,
CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
for (i = 0; i < max_loop; i++) {
u32 sfcsr;
regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
if (CCSR_SSI_SFCSR_TFCNT0(sfcsr))
break;
}
if (i == max_loop) {
dev_err(ssi_private->dev,
"Timeout waiting TX FIFO filling\n");
}
}
regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
}
} }
...@@ -671,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -671,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
if (IS_ERR(ssi_private->baudclk)) if (IS_ERR(ssi_private->baudclk))
return -EINVAL; return -EINVAL;
/*
* Hardware limitation: The bclk rate must be
* never greater than 1/5 IPG clock rate
*/
if (freq * 5 > clk_get_rate(ssi_private->clk)) {
dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n");
return -EINVAL;
}
baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
/* It should be already enough to divide clock by setting pm alone */ /* It should be already enough to divide clock by setting pm alone */
...@@ -687,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -687,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
else else
clkrate = clk_round_rate(ssi_private->baudclk, tmprate); clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
/*
* Hardware limitation: The bclk rate must be
* never greater than 1/5 IPG clock rate
*/
if (clkrate * 5 > clk_get_rate(ssi_private->clk))
continue;
clkrate /= factor; clkrate /= factor;
afreq = clkrate / (i + 1); afreq = clkrate / (i + 1);
...@@ -1159,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { ...@@ -1159,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.playback = { .playback = {
.stream_name = "CPU-Playback", .stream_name = "CPU-Playback",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
.capture = { .capture = {
.stream_name = "CPU-Capture", .stream_name = "CPU-Capture",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
...@@ -1403,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1403,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
} }
ssi_private->soc = of_id->data; ssi_private->soc = of_id->data;
ssi_private->dev = &pdev->dev;
sprop = of_get_property(np, "fsl,mode", NULL); sprop = of_get_property(np, "fsl,mode", NULL);
if (sprop) { if (sprop) {
......
...@@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component( ...@@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component(
return NULL; return NULL;
} }
static struct snd_soc_dai *snd_soc_find_dai( /**
* snd_soc_find_dai - Find a registered DAI
*
* @dlc: name of the DAI and optional component info to match
*
* This function will search all regsitered components and their DAIs to
* find the DAI of the same name. The component's of_node and name
* should also match if being specified.
*
* Return: pointer of DAI, or NULL if not found.
*/
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc) const struct snd_soc_dai_link_component *dlc)
{ {
struct snd_soc_component *component; struct snd_soc_component *component;
...@@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai( ...@@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(snd_soc_find_dai);
static bool soc_is_dai_link_bound(struct snd_soc_card *card, static bool soc_is_dai_link_bound(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link) struct snd_soc_dai_link *dai_link)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment