summaryrefslogtreecommitdiffstats
path: root/sound/soc/blackfin/bf5xx-i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/blackfin/bf5xx-i2s.c')
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c129
1 files changed, 109 insertions, 20 deletions
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index dd0c2a4f83a3..9a174fc47d39 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -42,6 +42,7 @@
#include <linux/gpio.h>
#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
struct bf5xx_i2s_port {
u16 tcr1;
@@ -49,6 +50,13 @@ struct bf5xx_i2s_port {
u16 tcr2;
u16 rcr2;
int configured;
+
+ unsigned int slots;
+ unsigned int tx_mask;
+ unsigned int rx_mask;
+
+ struct bf5xx_i2s_pcm_data tx_dma_data;
+ struct bf5xx_i2s_pcm_data rx_dma_data;
};
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -74,7 +82,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
- printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
+ dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n",
+ __func__);
ret = -EINVAL;
break;
}
@@ -88,7 +97,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
- printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
+ dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n",
+ __func__);
ret = -EINVAL;
break;
}
@@ -141,14 +151,14 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
}
@@ -162,18 +172,76 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
- pr_debug("%s enter\n", __func__);
+ dev_dbg(dai->dev, "%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_i2s->configured = 0;
}
+static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+ unsigned int tx_mapped = 0, rx_mapped = 0;
+ unsigned int slot;
+ int i;
+
+ if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+ (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+ return -EINVAL;
+
+ for (i = 0; i < tx_num; i++) {
+ slot = tx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(tx_mapped & (1 << slot)))) {
+ bf5xx_i2s->tx_dma_data.map[i] = slot;
+ tx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++) {
+ slot = rx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(rx_mapped & (1 << slot)))) {
+ bf5xx_i2s->rx_dma_data.map[i] = slot;
+ rx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+
+ if (slots % 8 != 0 || slots > 8)
+ return -EINVAL;
+
+ if (width != 32)
+ return -EINVAL;
+
+ bf5xx_i2s->slots = slots;
+ bf5xx_i2s->tx_mask = tx_mask;
+ bf5xx_i2s->rx_mask = rx_mask;
+
+ bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
+ bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
+
+ return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
+}
+
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
- pr_debug("%s : sport %d\n", __func__, dai->id);
+ dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
if (dai->capture_active)
sport_rx_stop(sport_handle);
@@ -188,23 +256,24 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
- pr_debug("%s : sport %d\n", __func__, dai->id);
+ dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
- pr_err("SPORT is busy!\n");
+ dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
- return 0;
+ return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
+ bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
}
#else
@@ -212,6 +281,23 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
#define bf5xx_i2s_resume NULL
#endif
+static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+ unsigned int i;
+
+ for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
+ bf5xx_i2s->tx_dma_data.map[i] = i;
+ bf5xx_i2s->rx_dma_data.map[i] = i;
+ }
+
+ dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
+ dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
+
+ return 0;
+}
+
#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
@@ -224,22 +310,25 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,
- .set_fmt = bf5xx_i2s_set_dai_fmt,
+ .shutdown = bf5xx_i2s_shutdown,
+ .hw_params = bf5xx_i2s_hw_params,
+ .set_fmt = bf5xx_i2s_set_dai_fmt,
+ .set_tdm_slot = bf5xx_i2s_set_tdm_slot,
+ .set_channel_map = bf5xx_i2s_set_channel_map,
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
+ .probe = bf5xx_i2s_dai_probe,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
- .channels_min = 1,
- .channels_max = 2,
+ .channels_min = 2,
+ .channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.capture = {
- .channels_min = 1,
- .channels_max = 2,
+ .channels_min = 2,
+ .channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.ops = &bf5xx_i2s_dai_ops,
@@ -255,7 +344,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
int ret;
/* configure SPORT for I2S */
- sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
+ sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
sizeof(struct bf5xx_i2s_port));
if (!sport_handle)
return -ENODEV;
@@ -264,7 +353,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
&bf5xx_i2s_dai, 1);
if (ret) {
- pr_err("Failed to register DAI: %d\n", ret);
+ dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
return ret;
}
@@ -276,7 +365,7 @@ static int bf5xx_i2s_remove(struct platform_device *pdev)
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
- pr_debug("%s enter\n", __func__);
+ dev_dbg(&pdev->dev, "%s enter\n", __func__);
snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);