aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/atmel/atmel-i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/atmel/atmel-i2s.c')
-rw-r--r--sound/soc/atmel/atmel-i2s.c82
1 files changed, 51 insertions, 31 deletions
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
index bbe2b638abb5..762199faf872 100644
--- a/sound/soc/atmel/atmel-i2s.c
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -163,11 +163,14 @@ struct atmel_i2s_gck_param {
#define I2S_MCK_12M288 12288000UL
#define I2S_MCK_11M2896 11289600UL
+#define I2S_MCK_6M144 6144000UL
/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
static const struct atmel_i2s_gck_param gck_params[] = {
+ /* mck = 6.144Mhz */
+ { 8000, I2S_MCK_6M144, 1, 47}, /* mck = 768 fs */
+
/* mck = 12.288MHz */
- { 8000, I2S_MCK_12M288, 0, 47}, /* mck = 1536 fs */
{ 16000, I2S_MCK_12M288, 1, 47}, /* mck = 768 fs */
{ 24000, I2S_MCK_12M288, 3, 63}, /* mck = 512 fs */
{ 32000, I2S_MCK_12M288, 3, 47}, /* mck = 384 fs */
@@ -200,6 +203,7 @@ struct atmel_i2s_dev {
unsigned int fmt;
const struct atmel_i2s_gck_param *gck_param;
const struct atmel_i2s_caps *caps;
+ int clk_use_no;
};
static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
@@ -321,9 +325,16 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
{
struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- unsigned int mr = 0;
+ unsigned int mr = 0, mr_mask;
int ret;
+ mr_mask = ATMEL_I2SC_MR_FORMAT_MASK | ATMEL_I2SC_MR_MODE_MASK |
+ ATMEL_I2SC_MR_DATALENGTH_MASK;
+ if (is_playback)
+ mr_mask |= ATMEL_I2SC_MR_TXMONO;
+ else
+ mr_mask |= ATMEL_I2SC_MR_RXMONO;
+
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mr |= ATMEL_I2SC_MR_FORMAT_I2S;
@@ -334,8 +345,8 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
/* codec is slave, so cpu is master */
mr |= ATMEL_I2SC_MR_MODE_MASTER;
ret = atmel_i2s_get_gck_param(dev, params_rate(params));
@@ -343,7 +354,7 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return ret;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_BC_FC:
/* codec is master, so cpu is slave */
mr |= ATMEL_I2SC_MR_MODE_SLAVE;
dev->gck_param = NULL;
@@ -402,7 +413,7 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- return regmap_write(dev->regmap, ATMEL_I2SC_MR, mr);
+ return regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
}
static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
@@ -495,29 +506,32 @@ static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
/* If master starts, enable the audio clock. */
- if (is_master && mck_enabled)
- err = atmel_i2s_switch_mck_generator(dev, true);
- if (err)
- return err;
+ if (is_master && mck_enabled) {
+ if (!dev->clk_use_no) {
+ err = atmel_i2s_switch_mck_generator(dev, true);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no++;
+ }
err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
if (err)
return err;
/* If master stops, disable the audio clock. */
- if (is_master && !mck_enabled)
- err = atmel_i2s_switch_mck_generator(dev, false);
+ if (is_master && !mck_enabled) {
+ if (dev->clk_use_no == 1) {
+ err = atmel_i2s_switch_mck_generator(dev, false);
+ if (err)
+ return err;
+ }
+ dev->clk_use_no--;
+ }
return err;
}
-static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
- .prepare = atmel_i2s_prepare,
- .trigger = atmel_i2s_trigger,
- .hw_params = atmel_i2s_hw_params,
- .set_fmt = atmel_i2s_set_dai_fmt,
-};
-
static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
@@ -526,8 +540,15 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+ .probe = atmel_i2s_dai_probe,
+ .prepare = atmel_i2s_prepare,
+ .trigger = atmel_i2s_trigger,
+ .hw_params = atmel_i2s_hw_params,
+ .set_fmt = atmel_i2s_set_dai_fmt,
+};
+
static struct snd_soc_dai_driver atmel_i2s_dai = {
- .probe = atmel_i2s_dai_probe,
.playback = {
.channels_min = 1,
.channels_max = 2,
@@ -541,11 +562,13 @@ static struct snd_soc_dai_driver atmel_i2s_dai = {
.formats = ATMEL_I2S_FORMATS,
},
.ops = &atmel_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
static const struct snd_soc_component_driver atmel_i2s_component = {
- .name = "atmel-i2s",
+ .name = "atmel-i2s",
+ .legacy_dai_naming = 1,
};
static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
@@ -563,8 +586,8 @@ static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
err = PTR_ERR(muxclk);
if (err == -EPROBE_DEFER)
return -EPROBE_DEFER;
- dev_warn(dev->dev,
- "failed to get the I2S clock control: %d\n", err);
+ dev_dbg(dev->dev,
+ "failed to get the I2S clock control: %d\n", err);
return 0;
}
@@ -595,7 +618,7 @@ static int atmel_i2s_probe(struct platform_device *pdev)
struct regmap *regmap;
void __iomem *base;
int irq;
- int err = -ENXIO;
+ int err;
unsigned int pcm_flags = 0;
unsigned int version;
@@ -610,8 +633,7 @@ static int atmel_i2s_probe(struct platform_device *pdev)
dev->caps = match->data;
/* Map I/O registers. */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -698,19 +720,17 @@ static int atmel_i2s_probe(struct platform_device *pdev)
return 0;
}
-static int atmel_i2s_remove(struct platform_device *pdev)
+static void atmel_i2s_remove(struct platform_device *pdev)
{
struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
clk_disable_unprepare(dev->pclk);
-
- return 0;
}
static struct platform_driver atmel_i2s_driver = {
.driver = {
.name = "atmel_i2s",
- .of_match_table = of_match_ptr(atmel_i2s_dt_ids),
+ .of_match_table = atmel_i2s_dt_ids,
},
.probe = atmel_i2s_probe,
.remove = atmel_i2s_remove,