mirror of
https://github.com/torvalds/linux.git
synced 2024-11-12 15:11:50 +00:00
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
This commit is contained in:
commit
180bc41ad1
@ -7,8 +7,8 @@ codec/DSP interfaces.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, contains "fsl,vf610-sai" or
|
||||
"fsl,imx6sx-sai".
|
||||
- compatible : Compatible list, contains "fsl,vf610-sai",
|
||||
"fsl,imx6sx-sai" or "fsl,imx6ul-sai"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
@ -48,6 +48,11 @@ Required properties:
|
||||
receive data by following their own bit clocks and
|
||||
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:
|
||||
- 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
|
||||
|
@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS
|
||||
M: Timur Tabi <timur@tabi.org>
|
||||
M: Nicolin Chen <nicoleotsuka@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: linuxppc-dev@lists.ozlabs.org
|
||||
S: Maintained
|
||||
|
@ -447,5 +447,11 @@
|
||||
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
|
||||
#define IMX6UL_GPR1_ENET_CLK_DIR (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 */
|
||||
|
@ -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,
|
||||
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>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -26,18 +26,30 @@
|
||||
#include <sound/tlv.h>
|
||||
#include "es8328.h"
|
||||
|
||||
#define ES8328_SYSCLK_RATE_1X 11289600
|
||||
#define ES8328_SYSCLK_RATE_2X 22579200
|
||||
static const unsigned int rates_12288[] = {
|
||||
8000, 12000, 16000, 24000, 32000, 48000, 96000,
|
||||
};
|
||||
|
||||
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
|
||||
static struct {
|
||||
int rate;
|
||||
u8 ratio;
|
||||
} mclk_ratios[] = {
|
||||
{ 8000, 9 },
|
||||
{11025, 7 },
|
||||
{22050, 4 },
|
||||
{44100, 2 },
|
||||
static const int ratios_12288[] = {
|
||||
10, 7, 6, 4, 3, 2, 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list constraints_12288 = {
|
||||
.count = ARRAY_SIZE(rates_12288),
|
||||
.list = rates_12288,
|
||||
};
|
||||
|
||||
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 */
|
||||
@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
|
||||
"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_11025)
|
||||
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
||||
SNDRV_PCM_RATE_16000 | \
|
||||
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 regmap *regmap;
|
||||
struct clk *clk;
|
||||
int playback_fs;
|
||||
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];
|
||||
};
|
||||
|
||||
@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
|
||||
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,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
int i;
|
||||
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)
|
||||
reg = ES8328_DACCONTROL2;
|
||||
else
|
||||
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) &&
|
||||
(clk_rate != ES8328_SYSCLK_RATE_2X)) {
|
||||
dev_err(codec->dev,
|
||||
"%s: clock is running at %d Hz, not %d or %d Hz\n",
|
||||
__func__, clk_rate,
|
||||
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
|
||||
if (i == es8328->sysclk_constraints->count) {
|
||||
dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* find master mode MCLK to sampling frequency ratio */
|
||||
ratio = mclk_ratios[0].rate;
|
||||
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
|
||||
if (params_rate(params) <= mclk_ratios[i].rate)
|
||||
ratio = mclk_ratios[i].ratio;
|
||||
ratio = es8328->mclk_ratios[i];
|
||||
snd_soc_update_bits(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MCLKDIV2,
|
||||
es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
es8328->playback_fs = params_rate(params);
|
||||
es8328_set_deemph(codec);
|
||||
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) {
|
||||
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
|
||||
ES8328_DACCONTROL1_DACWL_MASK,
|
||||
wl << ES8328_DACCONTROL1_DACWL_SHIFT);
|
||||
|
||||
es8328->playback_fs = params_rate(params);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
u8 mode = ES8328_DACCONTROL1_DACWL_16;
|
||||
u8 dac_mode = 0;
|
||||
u8 adc_mode = 0;
|
||||
|
||||
/* set master/slave audio interface */
|
||||
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,
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -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)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_write(codec, ES8328_DACCONTROL1, mode);
|
||||
snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
|
||||
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
|
||||
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 */
|
||||
clk_rate = clk_get_rate(es8328->clk);
|
||||
if (clk_rate == ES8328_SYSCLK_RATE_1X)
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MSC);
|
||||
else
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MCLKDIV2 |
|
||||
ES8328_MASTERMODE_MSC);
|
||||
snd_soc_update_bits(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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 = {
|
||||
.startup = es8328_startup,
|
||||
.hw_params = es8328_hw_params,
|
||||
.digital_mute = es8328_mute,
|
||||
.set_sysclk = es8328_set_sysclk,
|
||||
.set_fmt = es8328_set_dai_fmt,
|
||||
};
|
||||
|
||||
@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
|
||||
.formats = ES8328_FORMATS,
|
||||
},
|
||||
.ops = &es8328_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int es8328_suspend(struct snd_soc_codec *codec)
|
||||
@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.max_register = ES8328_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_rw = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(es8328_regmap_config);
|
||||
|
||||
|
@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 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_SEQEN (1 << 3)
|
||||
#define ES8328_CONTROL1_SAMEFS (1 << 4)
|
||||
@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_ADCCONTROL1 0x09
|
||||
#define ES8328_ADCCONTROL2 0x0a
|
||||
#define ES8328_ADCCONTROL3 0x0b
|
||||
|
||||
#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_RATEMASK (0x1f << 0)
|
||||
|
||||
@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_ADCCONTROL14 0x16
|
||||
|
||||
#define ES8328_DACCONTROL1 0x17
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
|
||||
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_20 (1 << 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_DACWL_SHIFT 3
|
||||
#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
#include "imx-pcm.h"
|
||||
@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fsl_sai *sai;
|
||||
struct regmap *gpr;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
char tmp[8];
|
||||
int irq, ret, i;
|
||||
int index;
|
||||
|
||||
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
|
||||
if (!sai)
|
||||
@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *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->is_lsb_first = of_property_read_bool(np, "lsb-first");
|
||||
@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
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_tx.addr = res->start + FSL_SAI_TDR;
|
||||
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
|
||||
@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
static const struct of_device_id fsl_sai_ids[] = {
|
||||
{ .compatible = "fsl,vf610-sai", },
|
||||
{ .compatible = "fsl,imx6sx-sai", },
|
||||
{ .compatible = "fsl,imx6ul-sai", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
|
||||
|
@ -262,6 +262,7 @@ struct fsl_ssi_private {
|
||||
struct fsl_ssi_dbg dbg_stats;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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,
|
||||
* (online configuration)
|
||||
*/
|
||||
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_STCR, vals->stcr, vals->stcr);
|
||||
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
|
||||
} else {
|
||||
u32 sier;
|
||||
u32 srcr;
|
||||
@ -507,9 +530,41 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
|
||||
|
||||
config_done:
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
|
||||
@ -671,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
|
||||
if (IS_ERR(ssi_private->baudclk))
|
||||
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));
|
||||
|
||||
/* 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,
|
||||
else
|
||||
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;
|
||||
afreq = clkrate / (i + 1);
|
||||
|
||||
@ -1159,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
|
||||
.playback = {
|
||||
.stream_name = "CPU-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.channels_max = 32,
|
||||
.rates = FSLSSI_I2S_RATES,
|
||||
.formats = FSLSSI_I2S_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "CPU-Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.channels_max = 32,
|
||||
.rates = FSLSSI_I2S_RATES,
|
||||
.formats = FSLSSI_I2S_FORMATS,
|
||||
},
|
||||
@ -1403,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ssi_private->soc = of_id->data;
|
||||
ssi_private->dev = &pdev->dev;
|
||||
|
||||
sprop = of_get_property(np, "fsl,mode", NULL);
|
||||
if (sprop) {
|
||||
|
@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component(
|
||||
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)
|
||||
{
|
||||
struct snd_soc_component *component;
|
||||
@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_find_dai);
|
||||
|
||||
static bool soc_is_dai_link_bound(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
|
Loading…
Reference in New Issue
Block a user