summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorOla Lilja <ola.o.lilja@stericsson.com>2011-12-07 13:29:06 +0100
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:05:03 +0200
commit89becb08b9dffd67d543c086254c53c1a98bb909 (patch)
treea560c7b756523ff8c0d7725901a1c7d2d5e400b8 /sound
parent600afc22730e30beda77fed6b16722b86b00908a (diff)
ASoC: Ux500: Move i2s into ASoC and remove old i2s-layer
The old multi-client i2s driver-layer located in drivers/misc/i2s is removed and the MSP-operation for i2s is moved into the file ux500_msp_i2s.c in the Ux500 ASoC-folder. ST-Ericsson ID: - ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I11c9021bb7b2385afba9a3e658b5bef7fe9fdb68 Signed-off-by: Ola Lilja <ola.o.lilja@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41704
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/ux500/Kconfig2
-rw-r--r--sound/soc/ux500/Makefile35
-rwxr-xr-xsound/soc/ux500/u5500.c10
-rw-r--r--sound/soc/ux500/u8500.c12
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c141
-rw-r--r--[-rwxr-xr-x]sound/soc/ux500/ux500_msp_dai.h5
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c1013
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h41
-rw-r--r--sound/soc/ux500/ux500_pcm.c2
9 files changed, 1158 insertions, 103 deletions
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
index 0078beb72ec..1a25fb6a0d6 100644
--- a/sound/soc/ux500/Kconfig
+++ b/sound/soc/ux500/Kconfig
@@ -4,7 +4,7 @@
menuconfig SND_SOC_UX500
bool "SoC Audio support for Ux500 platform"
- depends on SND_SOC && STM_I2S && STM_MSP_I2S
+ depends on SND_SOC
default n
help
Say Y if you want to add support for the Ux500 platform.
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
index 255c0ecefdb..262e44a2812 100644
--- a/sound/soc/ux500/Makefile
+++ b/sound/soc/ux500/Makefile
@@ -1,47 +1,46 @@
# Ux500 Platform Support
ifdef CONFIG_SND_SOC_UX500_DEBUG
+CFLAGS_u8500.o := -DDEBUG
CFLAGS_ux500_pcm.o := -DDEBUG
CFLAGS_ux500_msp_dai.o := -DDEBUG
CFLAGS_ux500_ab3550.o := -DDEBUG
CFLAGS_ux500_ab8500.o := -DDEBUG
CFLAGS_ux500_av8100.o := -DDEBUG
CFLAGS_ux500_cg29xx.o := -DDEBUG
+CFLAGS_ux500_msp_i2s.o := -DDEBUG
endif
-ifdef CONFIG_SND_SOC_UX500_AB3550
-snd-soc-ux500-ab3550-objs := ux500_ab3550.o
-obj-$(CONFIG_SND_SOC_UX500_AB3550) += ux500_ab3550.o
-endif
-
-ifdef CONFIG_SND_SOC_UX500_AB5500
-snd-soc-ux500-ab5500-objs := ux500_ab5500.o
-obj-$(CONFIG_SND_SOC_UX500_AB5500) += ux500_ab5500.o
+ifdef CONFIG_UX500_SOC_DBX500
+snd-soc-ux500-platform-objs := ux500_pcm.o ux500_msp_dai.o ux500_msp_i2s.o
+obj-y += snd-soc-ux500-platform.o
endif
ifdef CONFIG_SND_SOC_UX500_AB8500
-snd-soc-ux500-ab8500-objs := ux500_ab8500.o
-obj-$(CONFIG_SND_SOC_UX500_AB8500) += ux500_ab8500.o
+snd-soc-ux500-machine-objs += ux500_ab8500.o
endif
ifdef CONFIG_SND_SOC_UX500_AV8100
-snd-soc-ux500-av8100-objs := ux500_av8100.o
-obj-$(CONFIG_SND_SOC_UX500_AV8100) += ux500_av8100.o
+snd-soc-ux500-machine-objs += ux500_av8100.o
endif
ifdef CONFIG_SND_SOC_UX500_CG29XX
-snd-soc-ux500-cg29xx-objs := ux500_cg29xx.o
-obj-$(CONFIG_SND_SOC_UX500_CG29XX) += ux500_cg29xx.o
+snd-soc-ux500-machine-objs += ux500_cg29xx.o
+endif
+
+ifdef CONFIG_SND_SOC_UX500_AB5500
+snd-soc-ux500-machine-objs += ux500_ab5500.o
endif
-snd-soc-ux500-objs := ux500_pcm.o ux500_msp_dai.o
+obj-y += snd-soc-ux500-machine.o
ifdef CONFIG_UX500_SOC_DB8500
-snd-soc-ux500-objs += u8500.o
+snd-soc-u8500-objs := u8500.o
+obj-y += snd-soc-u8500.o
endif
ifdef CONFIG_UX500_SOC_DB5500
-snd-soc-ux500-objs += u5500.o
+snd-soc-u5500-objs := u5500.o
+obj-y += snd-soc-u5500.o
endif
-obj-$(CONFIG_UX500_SOC_DBX500) += snd-soc-ux500.o
diff --git a/sound/soc/ux500/u5500.c b/sound/soc/ux500/u5500.c
index 1d17830c1c7..6787daa9de5 100755
--- a/sound/soc/ux500/u5500.c
+++ b/sound/soc/ux500/u5500.c
@@ -66,7 +66,7 @@ struct snd_soc_dai_link u5500_dai_links[] = {
{
.name = "ab5500_0",
.stream_name = "ab5500_0",
- .cpu_dai_name = "i2s.0",
+ .cpu_dai_name = "ux500-msp-i2s.0",
.codec_dai_name = "ab5500-codec-dai.0",
.platform_name = "ux500-pcm.0",
.codec_name = "ab5500-codec.0",
@@ -83,7 +83,7 @@ struct snd_soc_dai_link u5500_dai_links[] = {
{
.name = "cg29xx_0",
.stream_name = "cg29xx_0",
- .cpu_dai_name = "i2s.1",
+ .cpu_dai_name = "ux500-msp-i2s.1",
.codec_dai_name = "cg29xx-codec-dai.0",
.platform_name = "ux500-pcm.0",
.codec_name = "cg29xx-codec.0",
@@ -93,7 +93,7 @@ struct snd_soc_dai_link u5500_dai_links[] = {
{
.name = "cg29xx_1",
.stream_name = "cg29xx_1",
- .cpu_dai_name = "i2s.0",
+ .cpu_dai_name = "ux500-msp-i2s.0",
.codec_dai_name = "cg29xx-codec-dai.1",
.platform_name = "ux500-pcm.0",
.codec_name = "cg29xx-codec.0",
@@ -104,7 +104,7 @@ struct snd_soc_dai_link u5500_dai_links[] = {
{
.name = "ab5500_1",
.stream_name = "ab5500_1",
- .cpu_dai_name = "i2s.1",
+ .cpu_dai_name = "ux500-msp-i2s.1",
.codec_dai_name = "ab5500-codec-dai.1",
.platform_name = "ux500-pcm.0",
.codec_name = "ab5500-codec.0",
@@ -121,7 +121,7 @@ struct snd_soc_dai_link u5500_dai_links[] = {
{
.name = "hdmi",
.stream_name = "hdmi",
- .cpu_dai_name = "i2s.2",
+ .cpu_dai_name = "ux500-msp-i2s.2",
.codec_dai_name = "av8100-codec-dai",
.platform_name = "ux500-pcm.0",
.codec_name = "av8100-codec.0",
diff --git a/sound/soc/ux500/u8500.c b/sound/soc/ux500/u8500.c
index c27409f1009..516008fcc6a 100644
--- a/sound/soc/ux500/u8500.c
+++ b/sound/soc/ux500/u8500.c
@@ -77,7 +77,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "hdmi",
.stream_name = "hdmi",
- .cpu_dai_name = "i2s.2",
+ .cpu_dai_name = "ux500-msp-i2s.2",
.codec_dai_name = "av8100-codec-dai",
.platform_name = "ux500-pcm.0",
.codec_name = "av8100-codec.0",
@@ -89,7 +89,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "ab3550_0",
.stream_name = "ab3550_0",
- .cpu_dai_name = "i2s.0",
+ .cpu_dai_name = "ux500-msp-i2s.0",
.codec_dai_name = "ab3550-codec-dai.0",
.platform_name = "ux500-pcm.0",
.codec_name = "ab3550-codec.11",
@@ -99,7 +99,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "ab3550_1",
.stream_name = "ab3550_1",
- .cpu_dai_name = "i2s.1",
+ .cpu_dai_name = "ux500-msp-i2s.1",
.codec_dai_name = "ab3550-codec-dai.1",
.platform_name = "ux500-pcm.0",
.codec_name = "ab3550-codec.11",
@@ -111,7 +111,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "ab8500_0",
.stream_name = "ab8500_0",
- .cpu_dai_name = "i2s.1",
+ .cpu_dai_name = "ux500-msp-i2s.1",
.codec_dai_name = "ab8500-codec-dai.0",
.platform_name = "ux500-pcm.0",
.codec_name = "ab8500-codec.0",
@@ -121,7 +121,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "ab8500_1",
.stream_name = "ab8500_1",
- .cpu_dai_name = "i2s.3",
+ .cpu_dai_name = "ux500-msp-i2s.3",
.codec_dai_name = "ab8500-codec-dai.1",
.platform_name = "ux500-pcm.0",
.codec_name = "ab8500-codec.0",
@@ -133,7 +133,7 @@ struct snd_soc_dai_link u8500_dai_links[] = {
{
.name = "cg29xx_0",
.stream_name = "cg29xx_0",
- .cpu_dai_name = "i2s.0",
+ .cpu_dai_name = "ux500-msp-i2s.0",
.codec_dai_name = "cg29xx-codec-dai.1",
.platform_name = "ux500-pcm.0",
.codec_name = "cg29xx-codec.0",
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 6cdc96734ed..2af5bf14193 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
* Author: Ola Lilja <ola.o.lilja@stericsson.com>,
* Roger Nilsson <roger.xr.nilsson@stericsson.com>
@@ -14,21 +14,22 @@
#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/dma.h>
#include <linux/bitops.h>
+#include <linux/platform_device.h>
+
#include <mach/hardware.h>
#include <mach/msp.h>
-#include <linux/i2s/i2s.h>
-#include <asm/mach-types.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
+
+#include "ux500_msp_i2s.h"
#include "ux500_msp_dai.h"
#include "ux500_pcm.h"
static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
{
- .i2s = NULL,
+ .msp_i2s_drvdata = NULL,
.fmt = 0,
.slots = 1,
.tx_mask = 0x01,
@@ -41,7 +42,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
.master_clk = UX500_MSP_INTERNAL_CLOCK_FREQ,
},
{
- .i2s = NULL,
+ .msp_i2s_drvdata = NULL,
.fmt = 0,
.slots = 1,
.tx_mask = 0x01,
@@ -54,7 +55,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
.master_clk = UX500_MSP1_INTERNAL_CLOCK_FREQ,
},
{
- .i2s = NULL,
+ .msp_i2s_drvdata = NULL,
.fmt = 0,
.slots = 1,
.tx_mask = 0x01,
@@ -67,7 +68,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
.master_clk = UX500_MSP_INTERNAL_CLOCK_FREQ,
},
{
- .i2s = NULL,
+ .msp_i2s_drvdata = NULL,
.fmt = 0,
.slots = 1,
.tx_mask = 0x01,
@@ -84,14 +85,14 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = {
bool ux500_msp_dai_i2s_get_underrun_status(int dai_idx)
{
struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
- int status = i2s_hw_status(drvdata->i2s->controller);
+ int status = ux500_msp_i2s_hw_status(drvdata->msp_i2s_drvdata);
return (bool)(status & TRANSMIT_UNDERRUN_ERR_INT);
}
dma_addr_t ux500_msp_dai_i2s_get_pointer(int dai_idx, int stream_id)
{
struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
- return i2s_get_pointer(drvdata->i2s->controller,
+ return ux500_msp_i2s_get_pointer(drvdata->msp_i2s_drvdata,
(stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
I2S_DIRECTION_TX :
I2S_DIRECTION_RX);
@@ -105,7 +106,6 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr,
{
struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx];
struct i2s_message message;
- struct i2s_device *i2s_dev;
int ret = 0;
bool playback_req_valid =
(drvdata->playback_active &&
@@ -128,9 +128,6 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr,
return ret;
}
- i2s_dev = drvdata->i2s;
-
- message.i2s_transfer_mode = I2S_TRANSFER_MODE_CYCLIC_DMA;
message.i2s_direction = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
I2S_DIRECTION_TX :
I2S_DIRECTION_RX;
@@ -138,7 +135,7 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr,
message.buf_len = period_cnt * period_len;
message.period_len = period_len;
- ret = i2s_transfer(i2s_dev->controller, &message);
+ ret = ux500_msp_i2s_transfer(drvdata->msp_i2s_drvdata, &message);
if (ret < 0) {
pr_err("%s: Error: i2s_transfer failed. MSP index: %d\n",
__func__,
@@ -197,7 +194,7 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
else
drvdata->capture_active = false;
- if (i2s_cleanup(drvdata->i2s->controller,
+ if (ux500_msp_i2s_close(drvdata->msp_i2s_drvdata,
mode_playback ? DISABLE_TRANSMIT : DISABLE_RECEIVE)) {
pr_err("%s: Error: MSP %d (%s): Unable to close i2s.\n",
__func__,
@@ -468,7 +465,7 @@ static void ux500_msp_dai_compile_msp_config(struct snd_pcm_substream *substream
msp_config->work_mode = MSP_DMA_MODE;
msp_config->frame_freq = rate;
- printk(KERN_INFO "%s: input_clock_freq = %u, frame_freq = %u.\n",
+ pr_debug("%s: input_clock_freq = %u, frame_freq = %u.\n",
__func__, msp_config->input_clock_freq, msp_config->frame_freq);
/* To avoid division by zero in I2S-driver (i2s_setup) */
prot_desc->total_clocks_for_one_frame = 1;
@@ -548,9 +545,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
runtime->rate,
&msp_config);
- ret = i2s_setup(drvdata->i2s->controller, &msp_config);
+ ret = ux500_msp_i2s_open(drvdata->msp_i2s_drvdata, &msp_config);
if (ret < 0) {
- pr_err("%s: Error: i2s_setup failed (ret = %d)!\n", __func__, ret);
+ pr_err("%s: Error: msp_setup failed (ret = %d)!\n", __func__, ret);
goto cleanup;
}
@@ -764,11 +761,11 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
int ret = 0;
struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai->id];
- pr_debug("%s: MSP %d (%s): Enter (chip_select = %d, cmd = %d).\n",
+ pr_debug("%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n",
__func__,
dai->id,
stream_str(substream),
- (int)drvdata->i2s->chip_select,
+ (int)drvdata->msp_i2s_drvdata->id,
cmd);
switch (cmd) {
@@ -796,7 +793,7 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
{
- .name = "ux500-msp.0",
+ .name = "ux500-msp-i2s.0",
.id = 0,
.suspend = NULL,
.resume = NULL,
@@ -826,7 +823,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
},
},
{
- .name = "ux500-msp.1",
+ .name = "ux500-msp-i2s.1",
.id = 1,
.suspend = NULL,
.resume = NULL,
@@ -856,7 +853,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
},
},
{
- .name = "ux500-msp.2",
+ .name = "ux500-msp-i2s.2",
.id = 2,
.suspend = NULL,
.resume = NULL,
@@ -886,7 +883,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
},
},
{
- .name = "ux500-msp.3",
+ .name = "ux500-msp-i2s.3",
.id = 3,
.suspend = NULL,
.resume = NULL,
@@ -918,84 +915,90 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
};
EXPORT_SYMBOL(ux500_msp_dai_drv);
-static int ux500_msp_drv_probe(struct i2s_device *i2s_dev)
+static int ux500_msp_drv_probe(struct platform_device *pdev)
{
- int ret = 0;
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata;
struct ux500_platform_drvdata *drvdata;
- int msp_idx = i2s_dev->chip_select;
+ struct msp_i2s_platform_data *platform_data;
+ int id;
+ int ret = 0;
- pr_info("%s: Enter (idx: %d, dev-name: %s, drv-name: %s).\n",
- __func__,
- msp_idx,
- dev_name(&i2s_dev->dev),
- i2s_dev->dev.driver->name);
+ pr_err("%s: Enter (pdev->name = %s).\n", __func__, pdev->name);
- drvdata = &platform_drvdata[msp_idx];
- drvdata->i2s = i2s_dev;
+ platform_data = (struct msp_i2s_platform_data *)pdev->dev.platform_data;
+ msp_i2s_drvdata = ux500_msp_i2s_init(pdev, platform_data);
+ if (!msp_i2s_drvdata) {
+ pr_err("%s: ERROR: ux500_msp_i2s_init failed!", __func__);
+ return -EINVAL;
+ }
- try_module_get(i2s_dev->controller->dev.parent->driver->owner);
- i2s_set_drvdata(i2s_dev, drvdata);
+ id = msp_i2s_drvdata->id;
+ drvdata = &platform_drvdata[id];
+ drvdata->msp_i2s_drvdata = msp_i2s_drvdata;
- pr_debug("%s: Register MSP %d.\n", __func__, msp_idx);
- ret = snd_soc_register_dai(&i2s_dev->dev, &ux500_msp_dai_drv[msp_idx]);
+ pr_info("%s: Registering ux500-msp-dai SoC CPU-DAI.\n", __func__);
+ ret = snd_soc_register_dai(&pdev->dev, &ux500_msp_dai_drv[id]);
if (ret < 0) {
- pr_err("Error: %s: Failed to register MSP %d.\n", __func__, msp_idx);
+ pr_err("Error: %s: Failed to register MSP %d.\n", __func__, id);
return ret;
}
return ret;
}
-static int ux500_msp_drv_remove(struct i2s_device *i2s_dev)
+static int ux500_msp_drv_remove(struct platform_device *pdev)
{
- struct ux500_platform_drvdata *drvdata = i2s_get_drvdata(i2s_dev);
- int msp_idx = i2s_dev->chip_select;
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev);
+ struct ux500_platform_drvdata *drvdata = &platform_drvdata[msp_i2s_drvdata->id];
- pr_info("%s: Enter (idx: %d, dev-name: %s, drv-name: %s).\n",
- __func__,
- msp_idx,
- dev_name(&i2s_dev->dev),
- i2s_dev->dev.driver->name);
+ pr_info("%s: Unregister ux500-msp-dai ASoC CPU-DAI.\n", __func__);
+ snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
- drvdata->i2s = NULL;
- i2s_set_drvdata(i2s_dev, NULL);
+ ux500_msp_i2s_exit(msp_i2s_drvdata);
+ drvdata->msp_i2s_drvdata = NULL;
- pr_debug("%s: Calling module_put.\n", __func__);
- module_put(i2s_dev->controller->dev.parent->driver->owner);
+ return 0;
+}
- pr_debug("%s: Unregister ux500-pcm SoC platform driver.\n", __func__);
- snd_soc_unregister_dais(&i2s_dev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+int ux500_msp_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev);
- return 0;
+ pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name);
+
+ return ux500_msp_i2s_suspend(msp_i2s_drvdata);
}
-static const struct i2s_device_id dev_id_table[] = {
- { "i2s_device.0", 0, 0 },
- { "i2s_device.1", 1, 0 },
- { "i2s_device.2", 2, 0 },
- { "i2s_device.3", 3, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2s, dev_id_table);
+int ux500_msp_drv_resume(struct platform_device *pdev)
+{
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev);
+
+ pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name);
+
+ return ux500_msp_i2s_resume(msp_i2s_drvdata);
+}
-static struct i2s_driver i2sdrv_i2s = {
+static struct platform_driver msp_i2s_driver = {
.driver = {
- .name = "i2s",
+ .name = "ux500-msp-i2s",
.owner = THIS_MODULE,
},
.probe = ux500_msp_drv_probe,
- .remove = __devexit_p(ux500_msp_drv_remove),
- .id_table = dev_id_table,
+ .remove = ux500_msp_drv_remove,
+ .suspend = ux500_msp_drv_suspend,
+ .resume = ux500_msp_drv_resume,
};
static int __init ux500_msp_init(void)
{
- return i2s_register_driver(&i2sdrv_i2s);
+ pr_info("%s: Register ux500-msp-dai platform driver.\n", __func__);
+ return platform_driver_register(&msp_i2s_driver);
}
static void __exit ux500_msp_exit(void)
{
- i2s_unregister_driver(&i2sdrv_i2s);
+ pr_info("%s: Unregister ux500-msp-dai platform driver.\n", __func__);
+ platform_driver_unregister(&msp_i2s_driver);
}
module_init(ux500_msp_init);
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
index cff6749760d..c44894526f2 100755..100644
--- a/sound/soc/ux500/ux500_msp_dai.h
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
* Author: Ola Lilja <ola.o.lilja@stericsson.com>,
* Roger Nilsson <roger.xr.nilsson@stericsson.com>
@@ -17,7 +17,6 @@
#include <linux/types.h>
#include <linux/spinlock.h>
-#include <linux/i2s/i2s.h>
#include <mach/msp.h>
#define UX500_NBR_OF_DAI 4
@@ -54,7 +53,7 @@ enum ux500_msp_clock_id {
};
struct ux500_platform_drvdata {
- struct i2s_device *i2s;
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata;
unsigned int fmt;
unsigned int tx_mask;
unsigned int rx_mask;
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
new file mode 100644
index 00000000000..1fa7a947fe1
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * Sandeep Kaushik <sandeep.kaushik@st.com>
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/msp.h>
+
+#include "ux500_msp_i2s.h"
+
+ /* Protocol desciptors */
+static const struct msp_protocol_desc prot_descs[] = {
+ I2S_PROTOCOL_DESC,
+ PCM_PROTOCOL_DESC,
+ PCM_COMPAND_PROTOCOL_DESC,
+ AC97_PROTOCOL_DESC,
+ SPI_MASTER_PROTOCOL_DESC,
+ SPI_SLAVE_PROTOCOL_DESC,
+};
+
+static void ux500_msp_i2s_set_prot_desc_tx(struct msp *msp,
+ struct msp_protocol_desc *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->tx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->tx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->tx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->tx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protocol_desc->tx_element_length_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protocol_desc->tx_element_length_2);
+ if (protocol_desc->tx_element_length_1 ==
+ protocol_desc->tx_element_length_2) {
+ msp->actual_data_size = protocol_desc->tx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->tx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protocol_desc->tx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->tx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->tx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->compression_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_TCF);
+}
+
+static void ux500_msp_i2s_set_prot_desc_rx(struct msp *msp,
+ struct msp_protocol_desc *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->rx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->rx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->rx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->rx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(protocol_desc->rx_element_length_1);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(protocol_desc->rx_element_length_2);
+ if (protocol_desc->rx_element_length_1 ==
+ protocol_desc->rx_element_length_2) {
+ msp->actual_data_size = protocol_desc->rx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->rx_data_delay);
+ temp_reg |= MSP_SET_ENDIANNES_BIT(protocol_desc->rx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->rx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->rx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->expansion_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ writel(temp_reg, msp->registers + MSP_RCF);
+
+}
+
+static int ux500_msp_i2s_configure_protocol(struct msp *msp,
+ struct msp_config *config)
+{
+ int direction;
+ struct msp_protocol_desc *protocol_desc;
+ enum msp_data_size data_size;
+ u32 temp_reg = 0;
+
+ data_size = config->data_size;
+ msp->def_elem_len = config->def_elem_len;
+ direction = config->direction;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ pr_err("%s: ERROR: Invalid protocol!\n", __func__);
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&prot_descs[config->protocol];
+ } else {
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
+ pr_err("%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
+ __func__, data_size);
+ return -EINVAL;
+ }
+
+ switch (direction) {
+ case MSP_TRANSMIT_MODE:
+ ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size);
+ break;
+ case MSP_RECEIVE_MODE:
+ ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size);
+ break;
+ case MSP_BOTH_T_R_MODE:
+ ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size);
+ ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size);
+ break;
+ default:
+ pr_err("%s: ERROR: Invalid direction requested (direction = %d)!\n",
+ __func__, direction);
+ return -EINVAL;
+ }
+
+ /* The below code is needed for both Rx and Tx path. Can't separate them. */
+ temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+ temp_reg |= MSP_TX_CLKPOL_BIT(~protocol_desc->tx_clock_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+ temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+ temp_reg |= MSP_RX_CLKPOL_BIT(protocol_desc->rx_clock_pol);
+ writel(temp_reg, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+static int ux500_msp_i2s_configure_clock(struct msp *msp, struct msp_config *config)
+{
+ u32 reg_val_GCR;
+ u32 frame_per = 0;
+ u32 sck_div = 0;
+ u32 frame_width = 0;
+ u32 temp_reg = 0;
+ u32 bit_clock = 0;
+ struct msp_protocol_desc *protocol_desc = NULL;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
+
+ if (config->default_protocol_desc)
+ protocol_desc =
+ (struct msp_protocol_desc *)&prot_descs[config->protocol];
+ else
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+
+ switch (config->protocol) {
+ case MSP_PCM_PROTOCOL:
+ case MSP_PCM_COMPAND_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div = config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_I2S_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div = config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_AC97_PROTOCOL:
+ /* Not supported */
+ pr_err("%s: ERROR: AC97 protocol not supported!\n", __func__);
+ return -ENOSYS;
+ default:
+ pr_err("%s: ERROR: Unknown protocol (%d)!\n",
+ __func__,
+ config->protocol);
+ return -EINVAL;
+ }
+
+ temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+ temp_reg |= FRAME_WIDTH_BITS(frame_width);
+ temp_reg |= FRAME_PERIOD_BITS(frame_per);
+ writel(temp_reg, msp->registers + MSP_SRG);
+
+ bit_clock = (config->input_clock_freq)/(sck_div + 1);
+ /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP
+ * Only consider OPP 100% when bit-clock is used, i.e. MSP master mode
+ */
+ if ((bit_clock > 19200000) && ((config->tx_clock_sel != 0) || (config->rx_clock_sel != 0))) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 100);
+ msp->vape_opp_constraint = 1;
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+
+ /* Enable clock */
+ udelay(100);
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR);
+ udelay(100);
+
+ return 0;
+}
+
+static int ux500_msp_i2s_configure_multichannel(struct msp *msp, struct msp_config *config)
+{
+ struct msp_protocol_desc *protocol_desc;
+ struct msp_multichannel_config *mcfg;
+ u32 reg_val_MCR;
+
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ pr_err("%s: ERROR: Invalid protocol (%d)!\n",
+ __func__,
+ config->protocol);
+ return -EINVAL;
+ }
+ protocol_desc = (struct msp_protocol_desc *)
+ &prot_descs[config->protocol];
+ } else {
+ protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ mcfg = &config->multichannel_config;
+ if (mcfg->tx_multichannel_enable) {
+ if (protocol_desc->tx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->tx_multichannel_enable ? 1 << TMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->tx_channel_0_enable,
+ msp->registers + MSP_TCE0);
+ writel(mcfg->tx_channel_1_enable,
+ msp->registers + MSP_TCE1);
+ writel(mcfg->tx_channel_2_enable,
+ msp->registers + MSP_TCE2);
+ writel(mcfg->tx_channel_3_enable,
+ msp->registers + MSP_TCE3);
+ } else {
+ pr_err("%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
+ __func__, protocol_desc->tx_phase_mode);
+ return -EINVAL;
+ }
+ }
+ if (mcfg->rx_multichannel_enable) {
+ if (protocol_desc->rx_phase_mode == MSP_SINGLE_PHASE) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->rx_multichannel_enable ? 1 << RMCEN_BIT : 0),
+ msp->registers + MSP_MCR);
+ writel(mcfg->rx_channel_0_enable,
+ msp->registers + MSP_RCE0);
+ writel(mcfg->rx_channel_1_enable,
+ msp->registers + MSP_RCE1);
+ writel(mcfg->rx_channel_2_enable,
+ msp->registers + MSP_RCE2);
+ writel(mcfg->rx_channel_3_enable,
+ msp->registers + MSP_RCE3);
+ } else {
+ pr_err("%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
+ __func__, protocol_desc->rx_phase_mode);
+ return -EINVAL;
+ }
+ if (mcfg->rx_comparison_enable_mode) {
+ reg_val_MCR = readl(msp->registers + MSP_MCR);
+ writel(reg_val_MCR |
+ (mcfg->rx_comparison_enable_mode << RCMPM_BIT),
+ msp->registers + MSP_MCR);
+
+ writel(mcfg->comparison_mask,
+ msp->registers + MSP_RCM);
+ writel(mcfg->comparison_value,
+ msp->registers + MSP_RCV);
+
+ }
+ }
+
+ return 0;
+}
+
+void ux500_msp_i2s_configure_dma(struct msp *msp, struct msp_config *config)
+{
+ struct stedma40_chan_cfg *rx_dma_info = msp->dma_cfg_rx;
+ struct stedma40_chan_cfg *tx_dma_info = msp->dma_cfg_tx;
+ dma_cap_mask_t mask;
+ u16 word_width;
+ bool rx_active, tx_active;
+
+ if (msp->tx_pipeid != NULL) {
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+
+ switch (config->data_size) {
+ case MSP_DATA_BITS_32:
+ word_width = STEDMA40_WORD_WIDTH;
+ break;
+ case MSP_DATA_BITS_16:
+ word_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case MSP_DATA_BITS_8:
+ word_width = STEDMA40_BYTE_WIDTH;
+ break;
+ default:
+ word_width = STEDMA40_WORD_WIDTH;
+ pr_warn("%s: Unknown data-size (%d)! Assuming 32 bits.\n",
+ __func__, config->data_size);
+ }
+
+ rx_active = (config->direction == MSP_RECEIVE_MODE ||
+ config->direction == MSP_BOTH_T_R_MODE);
+ tx_active = (config->direction == MSP_TRANSMIT_MODE ||
+ config->direction == MSP_BOTH_T_R_MODE);
+
+ if (rx_active) {
+ rx_dma_info->src_info.data_width = word_width;
+ rx_dma_info->dst_info.data_width = word_width;
+ }
+ if (tx_active) {
+ tx_dma_info->src_info.data_width = word_width;
+ tx_dma_info->dst_info.data_width = word_width;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ if (rx_active)
+ msp->rx_pipeid = dma_request_channel(mask, stedma40_filter, rx_dma_info);
+
+ if (tx_active)
+ msp->tx_pipeid = dma_request_channel(mask, stedma40_filter, tx_dma_info);
+}
+
+static int ux500_msp_i2s_dma_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ dma_cookie_t status_submit;
+ int direction, enable_bit;
+ u32 reg_val_GCR;
+ struct dma_chan *pipeid;
+ struct dma_async_tx_descriptor *cdesc;
+
+ if (msg->i2s_direction == I2S_DIRECTION_TX) {
+ direction = DMA_TO_DEVICE;
+ pipeid = msp->tx_pipeid;
+ enable_bit = TX_ENABLE;
+ pr_debug("%s: Direction: TX\n", __func__);
+ } else {
+ direction = DMA_FROM_DEVICE;
+ pipeid = msp->rx_pipeid;
+ enable_bit = RX_ENABLE;
+ pr_debug("%s: Direction: RX\n", __func__);
+ }
+
+ pr_debug("%s: msg->buf_addr = %p\n", __func__, (void *)msg->buf_addr);
+ pr_debug("%s: buf_len = %d\n", __func__, msg->buf_len);
+ pr_debug("%s: perios_len = %d\n", __func__, msg->period_len);
+
+ /* setup the cyclic description */
+ cdesc = pipeid->device->device_prep_dma_cyclic(pipeid,
+ msg->buf_addr,
+ msg->buf_len,
+ msg->period_len,
+ direction);
+ if (IS_ERR(cdesc)) {
+ pr_err("%s: ERROR: device_prep_dma_cyclic failed (%ld)!\n",
+ __func__,
+ PTR_ERR(cdesc));
+ return -EINVAL;
+ }
+
+ /* Submit to the dma */
+ if (msg->i2s_direction == I2S_DIRECTION_TX) {
+ cdesc->callback = msp->xfer_data.tx_handler;
+ cdesc->callback_param = msp->xfer_data.tx_callback_data;
+ } else {
+ cdesc->callback = msp->xfer_data.rx_handler;
+ cdesc->callback_param = msp->xfer_data.rx_callback_data;
+ }
+ status_submit = dmaengine_submit(cdesc);
+ if (dma_submit_error(status_submit)) {
+ pr_err("%s: ERROR: dmaengine_submit failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Start the dma */
+ dma_async_issue_pending(pipeid);
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config)
+{
+ int status = 0;
+ u32 reg_val_DMACR, reg_val_GCR;
+
+ if (config->work_mode != MSP_DMA_MODE) {
+ pr_err("%s: ERROR: Only DMA-mode is supported (msp->work_mode = %d)\n",
+ __func__,
+ msp->work_mode);
+ return -EINVAL;
+ }
+ msp->work_mode = config->work_mode;
+
+ /* Check msp state whether in RUN or CONFIGURED Mode */
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ if (msp->plat_init) {
+ status = msp->plat_init();
+ if (status) {
+ pr_err("%s: ERROR: Failed to init MSP (%d)!\n",
+ __func__,
+ status);
+ return status;
+ }
+ }
+ }
+
+ /* Configure msp with protocol dependent settings */
+ ux500_msp_i2s_configure_protocol(msp, config);
+ ux500_msp_i2s_configure_clock(msp, config);
+ if (config->multichannel_configured == 1) {
+ status = ux500_msp_i2s_configure_multichannel(msp, config);
+ if (status)
+ pr_warn("%s: WARN: ux500_msp_i2s_configure_multichannel failed (%d)!\n",
+ __func__, status);
+ }
+
+ /* Make sure the correct DMA-directions are configured */
+ if ((config->direction == MSP_RECEIVE_MODE) ||
+ (config->direction == MSP_BOTH_T_R_MODE))
+ if (!msp->dma_cfg_rx) {
+ pr_err("%s: ERROR: MSP RX-mode is not configured!", __func__);
+ return -EINVAL;
+ }
+ if ((config->direction == MSP_TRANSMIT_MODE) ||
+ (config->direction == MSP_BOTH_T_R_MODE))
+ if (!msp->dma_cfg_tx) {
+ pr_err("%s: ERROR: MSP TX-mode is not configured!", __func__);
+ return -EINVAL;
+ }
+
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ writel(reg_val_DMACR | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.tx_callback_data = config->tx_callback_data;
+
+ break;
+ case MSP_RECEIVE_MODE:
+ writel(reg_val_DMACR | RX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.rx_callback_data = config->rx_callback_data;
+
+ break;
+ case MSP_BOTH_T_R_MODE:
+ writel(reg_val_DMACR | RX_DMA_ENABLE | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data = config->tx_callback_data;
+ msp->xfer_data.rx_callback_data = config->rx_callback_data;
+
+ break;
+ default:
+ pr_err("%s: ERROR: Illegal MSP direction (config->direction = %d)!",
+ __func__,
+ config->direction);
+ if (msp->plat_exit)
+ msp->plat_exit();
+ return -EINVAL;
+ }
+ ux500_msp_i2s_configure_dma(msp, config);
+
+ msp->transfer = ux500_msp_i2s_dma_xfer;
+
+ writel(config->iodelay, msp->registers + MSP_IODLY);
+
+ /* Enable frame generation logic */
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
+
+ return status;
+}
+
+static void flush_fifo_rx(struct msp *msp)
+{
+ u32 reg_val_DR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) {
+ reg_val_DR = readl(msp->registers + MSP_DR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+static void flush_fifo_tx(struct msp *msp)
+{
+ u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR;
+ u32 limit = 32;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR);
+ writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR);
+
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) {
+ reg_val_TSTDR = readl(msp->registers + MSP_TSTDR);
+ reg_val_FLR = readl(msp->registers + MSP_FLR);
+ }
+ writel(0x0, msp->registers + MSP_ITCR);
+ writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config)
+{
+ struct msp *msp = drvdata->msp;
+ u32 old_reg, new_reg, mask;
+ int res;
+
+ if (in_interrupt()) {
+ pr_err("%s: ERROR: Open called in interrupt context!\n", __func__);
+ return -1;
+ }
+
+ /* Two simultanous configuring msp is avoidable */
+ down(&msp->lock);
+
+ /* Don't enable regulator if its msp1 or msp3 */
+ if (!(msp->reg_enabled) && msp->id != MSP_1_I2S_CONTROLLER
+ && msp->id != MSP_3_I2S_CONTROLLER) {
+ res = regulator_enable(drvdata->reg_vape);
+ if (res != 0) {
+ pr_err("%s: Failed to enable regulator!\n", __func__);
+ up(&msp->lock);
+ return res;
+ }
+ msp->reg_enabled = 1;
+ }
+
+ switch (msp->users) {
+ case 0:
+ clk_enable(msp->clk);
+ msp->direction = msp_config->direction;
+ break;
+ case 1:
+ if (msp->direction == MSP_BOTH_T_R_MODE ||
+ msp_config->direction == msp->direction ||
+ msp_config->direction == MSP_BOTH_T_R_MODE) {
+ pr_warn("%s: WARN: MSP is in use (direction = %d)!\n",
+ __func__, msp_config->direction);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->direction = MSP_BOTH_T_R_MODE;
+ break;
+ default:
+ pr_warn("%s: MSP in use in (both directions)!\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->users++;
+
+ /* First do the global config register */
+ mask =
+ RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FRAME_SYNC_MASK |
+ TX_FRAME_SYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+ RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+ LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+ new_reg = (msp_config->tx_clock_sel | msp_config->rx_clock_sel |
+ msp_config->rx_frame_sync_pol | msp_config->tx_frame_sync_pol |
+ msp_config->rx_frame_sync_sel | msp_config->tx_frame_sync_sel |
+ msp_config->rx_fifo_config | msp_config->tx_fifo_config |
+ msp_config->srg_clock_sel | msp_config->loopback_enable |
+ msp_config->tx_data_enable);
+
+ old_reg = readl(msp->registers + MSP_GCR);
+ old_reg &= ~mask;
+ new_reg |= old_reg;
+ writel(new_reg, msp->registers + MSP_GCR);
+
+ if (ux500_msp_i2s_enable(msp, msp_config) != 0) {
+ pr_err("%s: ERROR: ux500_msp_i2s_enable failed!\n", __func__);
+ return -EBUSY;
+ }
+ if (msp_config->loopback_enable & 0x80)
+ msp->loopback_enable = 1;
+
+ /* Flush FIFOs */
+ flush_fifo_tx(msp);
+ flush_fifo_rx(msp);
+
+ msp->msp_state = MSP_STATE_CONFIGURED;
+ up(&msp->lock);
+ return 0;
+}
+
+static void func_notify_timer(unsigned long data)
+{
+ struct msp *msp = (struct msp *)data;
+ if (msp->polling_flag) {
+ msp->msp_io_error = 1;
+ pr_err("%s: ERROR: Polling timeout!\n", __func__);
+ del_timer(&msp->notify_timer);
+ }
+}
+
+int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message)
+{
+ struct msp *msp = drvdata->msp;
+ int status = 0;
+
+ if (!message || (msp->msp_state == MSP_STATE_IDLE)) {
+ pr_err("%s: ERROR: i2s_message == NULL!\n", __func__);
+ return -EINVAL;
+ }
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ pr_err("%s: ERROR: MSP in idle-state!\n", __func__);
+ return -EPERM;
+ }
+
+ msp->msp_state = MSP_STATE_RUN;
+ if (msp->transfer)
+ status = msp->transfer(msp, message);
+
+ if (msp->msp_state == MSP_STATE_RUN)
+ msp->msp_state = MSP_STATE_CONFIGURED;
+
+ return status;
+}
+
+static void ux500_msp_i2s_disable_rx(struct msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(RECEIVE_SERVICE_INT | RECEIVE_OVERRUN_ERROR_INT),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.rxbytes = 0;
+ msp->xfer_data.message.rx_offset = 0;
+ msp->xfer_data.message.rxdata = NULL;
+ msp->read = NULL;
+}
+
+static void ux500_msp_i2s_disable_tx(struct msp *msp)
+{
+ u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR);
+ reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+ writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR);
+ reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+ writel(reg_val_IMSC &
+ ~(TRANSMIT_SERVICE_INT | TRANSMIT_UNDERRUN_ERR_INT),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.txbytes = 0;
+ msp->xfer_data.message.tx_offset = 0;
+ msp->xfer_data.message.txdata = NULL;
+ msp->write = NULL;
+}
+
+static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag flag)
+{
+ u32 reg_val_GCR;
+ int status = 0;
+
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ if (!(reg_val_GCR & (TX_ENABLE | RX_ENABLE)))
+ return 0;
+
+ if (flag == DISABLE_ALL || flag == DISABLE_TRANSMIT) {
+ if (msp->tx_pipeid != NULL) {
+ dmaengine_terminate_all(msp->tx_pipeid);
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+ }
+ if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) {
+ if (msp->rx_pipeid != NULL) {
+ dmaengine_terminate_all(msp->rx_pipeid);
+ dma_release_channel(msp->rx_pipeid);
+ msp->rx_pipeid = NULL;
+ }
+ }
+
+ if (flag == DISABLE_TRANSMIT)
+ ux500_msp_i2s_disable_tx(msp);
+ else if (flag == DISABLE_RECEIVE)
+ ux500_msp_i2s_disable_rx(msp);
+ else {
+ reg_val_GCR = readl(msp->registers + MSP_GCR);
+ writel(reg_val_GCR | LOOPBACK_MASK,
+ msp->registers + MSP_GCR);
+
+ /* Flush TX-FIFO */
+ flush_fifo_tx(msp);
+
+ /* Disable TX-channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+
+ /* Flush RX-FIFO */
+ flush_fifo_rx(msp);
+
+ /* Disable Loopback and Receive channel */
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(RX_ENABLE | LOOPBACK_MASK))),
+ msp->registers + MSP_GCR);
+
+ ux500_msp_i2s_disable_tx(msp);
+ ux500_msp_i2s_disable_rx(msp);
+
+ }
+
+ /* disable sample rate and frame generators */
+ if (flag == DISABLE_ALL) {
+ msp->msp_state = MSP_STATE_IDLE;
+ writel((readl(msp->registers + MSP_GCR) &
+ (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+ memset(&msp->xfer_data, 0, sizeof(struct trans_data));
+ if (msp->plat_exit)
+ status = msp->plat_exit();
+ if (status)
+ pr_warn("%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
+ __func__, status);
+ msp->transfer = NULL;
+ writel(0, msp->registers + MSP_GCR);
+ writel(0, msp->registers + MSP_TCF);
+ writel(0, msp->registers + MSP_RCF);
+ writel(0, msp->registers + MSP_DMACR);
+ writel(0, msp->registers + MSP_SRG);
+ writel(0, msp->registers + MSP_MCR);
+ writel(0, msp->registers + MSP_RCM);
+ writel(0, msp->registers + MSP_RCV);
+ writel(0, msp->registers + MSP_TCE0);
+ writel(0, msp->registers + MSP_TCE1);
+ writel(0, msp->registers + MSP_TCE2);
+ writel(0, msp->registers + MSP_TCE3);
+ writel(0, msp->registers + MSP_RCE0);
+ writel(0, msp->registers + MSP_RCE1);
+ writel(0, msp->registers + MSP_RCE2);
+ writel(0, msp->registers + MSP_RCE3);
+ }
+
+ return status;
+}
+
+int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag)
+{
+ struct msp *msp = drvdata->msp;
+ int status = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ down(&msp->lock);
+
+ if (msp->users == 0) {
+ pr_err("%s: ERROR: MSP already closed!\n", __func__);
+ status = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s: msp->users = %d, flag = %d\n", __func__, msp->users, flag);
+
+ /* We need to call it twice for DISABLE_ALL*/
+ msp->users = flag == DISABLE_ALL ? 0 : msp->users - 1;
+ if (msp->users)
+ status = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, flag);
+ else {
+ status = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, DISABLE_ALL);
+ clk_disable(msp->clk);
+ if (msp->reg_enabled) {
+ status = regulator_disable(drvdata->reg_vape);
+ msp->reg_enabled = 0;
+ }
+ if (status != 0) {
+ pr_err("%s: ERROR: Failed to disable regulator (%d)!\n",
+ __func__, status);
+ clk_enable(msp->clk);
+ goto end;
+ }
+ }
+ if (status)
+ goto end;
+ if (msp->users)
+ msp->direction = flag == DISABLE_TRANSMIT ?
+ MSP_RECEIVE_MODE : MSP_TRANSMIT_MODE;
+
+ if (msp->vape_opp_constraint == 1) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+end:
+ up(&msp->lock);
+ return status;
+
+}
+
+int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ struct msp *msp = drvdata->msp;
+ int status;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ status = readl(msp->registers + MSP_RIS) & 0xee;
+ if (status)
+ writel(status, msp->registers + MSP_ICR);
+
+ return status;
+}
+
+dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata,
+ enum i2s_direction_t i2s_direction)
+{
+ struct msp *msp = drvdata->msp;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ return (i2s_direction == I2S_DIRECTION_TX) ?
+ stedma40_get_src_addr(msp->tx_pipeid) :
+ stedma40_get_dst_addr(msp->rx_pipeid);
+}
+
+struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
+ struct msp_i2s_platform_data *platform_data)
+{
+ struct ux500_msp_i2s_drvdata *msp_i2s_drvdata;
+ int irq;
+ struct resource *res = NULL;
+ struct i2s_controller *i2s_cont;
+ struct msp *msp;
+
+ pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name);
+
+ msp_i2s_drvdata = kzalloc(sizeof(struct ux500_msp_i2s_drvdata), GFP_KERNEL);
+ msp_i2s_drvdata->msp = kzalloc(sizeof(struct msp), GFP_KERNEL);
+ msp = msp_i2s_drvdata->msp;
+
+ msp->id = platform_data->id;
+ msp_i2s_drvdata->id = msp->id;
+ pr_debug("msp_i2s_drvdata->id = %d\n", msp_i2s_drvdata->id);
+
+ msp->plat_init = platform_data->msp_i2s_init;
+ msp->plat_exit = platform_data->msp_i2s_exit;
+ msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+ msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+ sema_init(&msp->lock, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_err("%s: ERROR: Unable to get resource!\n", __func__);
+ goto free_msp;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto free_msp;
+ msp->irq = irq;
+
+ msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ if (msp->registers == NULL)
+ goto free_msp;
+
+ msp_i2s_drvdata->reg_vape = regulator_get(NULL, "v-ape");
+ if (IS_ERR(msp_i2s_drvdata->reg_vape)) {
+ pr_err("%s: ERROR: Failed to get Vape supply (%d)!\n",
+ __func__, (int)PTR_ERR(msp_i2s_drvdata->reg_vape));
+ goto free_irq;
+ }
+ dev_set_drvdata(&pdev->dev, msp_i2s_drvdata);
+
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
+ msp->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(msp->clk)) {
+ pr_err("%s: ERROR: clk_get failed (%d)!\n",
+ __func__, (int)PTR_ERR(msp->clk));
+ goto free_irq;
+ }
+
+ init_timer(&msp->notify_timer);
+ msp->notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ msp->notify_timer.function = func_notify_timer;
+ msp->notify_timer.data = (unsigned long)msp;
+
+ msp->rx_pipeid = NULL;
+ msp->tx_pipeid = NULL;
+ msp->read = NULL;
+ msp->write = NULL;
+ msp->transfer = NULL;
+ msp->msp_state = MSP_STATE_IDLE;
+ msp->loopback_enable = 0;
+
+ /* I2S Controller is allocated and added in I2S controller class. */
+ i2s_cont = kzalloc(sizeof(*i2s_cont), GFP_KERNEL);
+ if (!i2s_cont) {
+ pr_err("%s: ERROR: Failed to allocate struct i2s_cont (kzalloc)!\n",
+ __func__);
+ goto del_timer;
+ }
+ i2s_cont->dev.parent = &pdev->dev;
+ i2s_cont->data = (void *)msp;
+ i2s_cont->id = (s16)msp->id;
+ snprintf(i2s_cont->name,
+ sizeof(i2s_cont->name),
+ "ux500-msp-i2s.%04x",
+ msp->id);
+ pr_debug("I2S device-name :%s\n", i2s_cont->name);
+ msp->i2s_cont = i2s_cont;
+
+ return msp_i2s_drvdata;
+
+del_timer:
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+free_irq:
+ iounmap(msp->registers);
+free_msp:
+ kfree(msp);
+ return NULL;
+}
+
+int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ struct msp *msp = drvdata->msp;
+ int status = 0;
+
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ device_unregister(&msp->i2s_cont->dev);
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+ iounmap(msp->registers);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
+ regulator_put(drvdata->reg_vape);
+ kfree(msp);
+
+ return status;
+}
+
+int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ struct msp *msp = drvdata->msp;
+
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ down(&msp->lock);
+ if (msp->users > 0) {
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ up(&msp->lock);
+
+ return 0;
+}
+
+int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata)
+{
+ pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id);
+
+ return 0;
+}
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
new file mode 100644
index 00000000000..db88d0ca5de
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+
+#ifndef UX500_MSP_I2S_H
+#define UX500_MSP_I2S_H
+
+#include <linux/platform_device.h>
+#include <mach/msp.h>
+
+struct ux500_msp_i2s_drvdata {
+ int id;
+ struct msp *msp;
+ struct regulator *reg_vape;
+};
+
+struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev,
+ struct msp_i2s_platform_data *platform_data);
+int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata);
+int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config);
+int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag);
+int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message);
+int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata);
+dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata,
+ enum i2s_direction_t i2s_direction);
+
+int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata);
+int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata);
+
+#endif
+
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 0161ffdf3f4..29b3f5e0ffb 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -272,7 +272,7 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
private->msp_id,
stream_id);
if (ret) {
- pr_err("%s: Failed to configure sg-list!\n", __func__);
+ pr_err("%s: Failed to configure I2S!\n", __func__);
return -EINVAL;
}
break;