All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] ASoC: add CSR SiRFSoC sound drivers
@ 2013-07-19 11:07 ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Workgroup.Linux, linux-arm-kernel, Barry Song

This patchset adds CSR SiRFSoC sound drivers including:
1. the platform DMA driver which will be shard by all DAI
2. the I2S CPU DAI driver
3. the USP-based PCM CPU DAI driver
4. CPU DAI and Codec driver for internal chip codec
5. the mach driver for EVB board using internal codec

Barry Song (1):
  arm: prima2: defconfig: enable sound components

Rongjun Ying (5):
  ASoC: sirf: add sirf platform driver which provides DMA
  ASoC: sirf: add I2S CPU DAI driver
  ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
  ASoC: sirf-inner: add mach driver for SiRFSoC internal codec

 arch/arm/configs/prima2_defconfig |  10 +-
 sound/soc/Kconfig                 |   1 +
 sound/soc/Makefile                |   1 +
 sound/soc/sirf/Kconfig            |  18 ++
 sound/soc/sirf/Makefile           |  11 +
 sound/soc/sirf/sirf-audio.h       | 266 ++++++++++++++++
 sound/soc/sirf/sirf-i2s.c         | 411 ++++++++++++++++++++++++
 sound/soc/sirf/sirf-inner.c       | 267 ++++++++++++++++
 sound/soc/sirf/sirf-pcm.c         | 221 +++++++++++++
 sound/soc/sirf/sirf-pcm.h         |  17 +
 sound/soc/sirf/sirf-soc-inner.c   | 653 ++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.c         | 481 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.h         | 276 ++++++++++++++++
 13 files changed, 2630 insertions(+), 3 deletions(-)
 create mode 100644 sound/soc/sirf/Kconfig
 create mode 100644 sound/soc/sirf/Makefile
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c
 create mode 100644 sound/soc/sirf/sirf-inner.c
 create mode 100644 sound/soc/sirf/sirf-pcm.c
 create mode 100644 sound/soc/sirf/sirf-pcm.h
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c
 create mode 100644 sound/soc/sirf/sirf-usp.c
 create mode 100644 sound/soc/sirf/sirf-usp.h

-- 
1.8.2.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 0/6] ASoC: add CSR SiRFSoC sound drivers
@ 2013-07-19 11:07 ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset adds CSR SiRFSoC sound drivers including:
1. the platform DMA driver which will be shard by all DAI
2. the I2S CPU DAI driver
3. the USP-based PCM CPU DAI driver
4. CPU DAI and Codec driver for internal chip codec
5. the mach driver for EVB board using internal codec

Barry Song (1):
  arm: prima2: defconfig: enable sound components

Rongjun Ying (5):
  ASoC: sirf: add sirf platform driver which provides DMA
  ASoC: sirf: add I2S CPU DAI driver
  ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
  ASoC: sirf-inner: add mach driver for SiRFSoC internal codec

 arch/arm/configs/prima2_defconfig |  10 +-
 sound/soc/Kconfig                 |   1 +
 sound/soc/Makefile                |   1 +
 sound/soc/sirf/Kconfig            |  18 ++
 sound/soc/sirf/Makefile           |  11 +
 sound/soc/sirf/sirf-audio.h       | 266 ++++++++++++++++
 sound/soc/sirf/sirf-i2s.c         | 411 ++++++++++++++++++++++++
 sound/soc/sirf/sirf-inner.c       | 267 ++++++++++++++++
 sound/soc/sirf/sirf-pcm.c         | 221 +++++++++++++
 sound/soc/sirf/sirf-pcm.h         |  17 +
 sound/soc/sirf/sirf-soc-inner.c   | 653 ++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.c         | 481 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.h         | 276 ++++++++++++++++
 13 files changed, 2630 insertions(+), 3 deletions(-)
 create mode 100644 sound/soc/sirf/Kconfig
 create mode 100644 sound/soc/sirf/Makefile
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c
 create mode 100644 sound/soc/sirf/sirf-inner.c
 create mode 100644 sound/soc/sirf/sirf-pcm.c
 create mode 100644 sound/soc/sirf/sirf-pcm.h
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c
 create mode 100644 sound/soc/sirf/sirf-usp.c
 create mode 100644 sound/soc/sirf/sirf-usp.h

-- 
1.8.2.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Rongjun Ying, Workgroup.Linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

this driver uses dmaengine APIs and provides DMA to the CPU DAIs
of I2S, USP and SiRF-soc-inner.
SiRFSoC has 3 audio DAIs: I2S, USP(Universal Serial Ports) and DAI
connected to soc-inner-codec, all of them will use the same DMA
driver here.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/Kconfig         |   1 +
 sound/soc/Makefile        |   1 +
 sound/soc/sirf/Kconfig    |   4 +
 sound/soc/sirf/Makefile   |   3 +
 sound/soc/sirf/sirf-pcm.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-pcm.h |  17 ++++
 6 files changed, 247 insertions(+)
 create mode 100644 sound/soc/sirf/Kconfig
 create mode 100644 sound/soc/sirf/Makefile
 create mode 100644 sound/soc/sirf/sirf-pcm.c
 create mode 100644 sound/soc/sirf/sirf-pcm.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 45eeaa9..2581e55 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -51,6 +51,7 @@ source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index bc02614..e15df84 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
new file mode 100644
index 0000000..3678aed
--- /dev/null
+++ b/sound/soc/sirf/Kconfig
@@ -0,0 +1,4 @@
+config SND_SIRF_SOC
+	tristate "Platform DMA driver for the SiRF SoC chips"
+	depends on ARCH_SIRF && SND_SOC
+	select SND_SOC_DMAENGINE_PCM
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
new file mode 100644
index 0000000..f268b83
--- /dev/null
+++ b/sound/soc/sirf/Makefile
@@ -0,0 +1,3 @@
+snd-soc-sirf-objs := sirf-pcm.o
+
+obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
diff --git a/sound/soc/sirf/sirf-pcm.c b/sound/soc/sirf/sirf-pcm.c
new file mode 100644
index 0000000..d4cb897
--- /dev/null
+++ b/sound/soc/sirf/sirf-pcm.c
@@ -0,0 +1,221 @@
+/*
+ * ALSA PCM interface for the SiRF SoC
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/sirfsoc_dma.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "sirf-pcm.h"
+
+static struct snd_pcm_hardware sirf_pcm_hardware = {
+	.info                   = (SNDRV_PCM_INFO_MMAP
+			| SNDRV_PCM_INFO_MMAP_VALID
+			| SNDRV_PCM_INFO_INTERLEAVED
+			| SNDRV_PCM_INFO_BLOCK_TRANSFER
+			| SNDRV_PCM_INFO_RESUME
+			| SNDRV_PCM_INFO_PAUSE),
+	.formats                = (SNDRV_PCM_FMTBIT_S16_LE),
+	.rates                  = (SNDRV_PCM_RATE_48000),
+	.rate_min               = 512,
+	.rate_max               = 115200,
+	.channels_min           = 1,
+	.channels_max           = 2,
+	.buffer_bytes_max       = 64 * 1024,
+	.period_bytes_min       = 128,
+	.period_bytes_max       = 32 * 1024,
+	.periods_min            = 2,
+	.periods_max            = 2,
+};
+
+static int sirf_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sirf_pcm_dma_data *dma_data;
+	substream->runtime->hw = sirf_pcm_hardware;
+	snd_soc_set_runtime_hwparams(substream, &sirf_pcm_hardware);
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	return snd_dmaengine_pcm_open_request_chan(substream,
+			(dma_filter_fn)sirfsoc_dma_filter_id,
+			(void *)(dma_data->dma_req));
+}
+
+static int sirf_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sirf_pcm_dma_data *dma_data;
+	struct dma_slave_config config;
+	struct dma_chan *chan;
+	int err = 0;
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	chan = snd_dmaengine_pcm_get_chan(substream);
+	if (!chan)
+		return -EINVAL;
+
+	/* fills in addr_width and direction */
+	err = snd_hwparams_to_dma_slave_config(substream, params, &config);
+	if (err)
+		return err;
+
+	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+	config.src_addr = runtime->dma_addr;
+	config.dst_addr = runtime->dma_addr;
+	config.src_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
+	config.dst_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+	return dmaengine_slave_config(chan, &config);
+}
+
+static int sirf_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int sirf_pcm_mmap(struct snd_pcm_substream *substream,
+		struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return dma_mmap_coherent(substream->pcm->card->dev, vma,
+			runtime->dma_area,
+			runtime->dma_addr,
+			runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops sirf_pcm_ops = {
+	.open		= sirf_pcm_open,
+	.close		= snd_dmaengine_pcm_close_release_chan,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= sirf_pcm_hw_params,
+	.hw_free	= sirf_pcm_hw_free,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= snd_dmaengine_pcm_pointer,
+	.mmap		= sirf_pcm_mmap,
+};
+
+static void sirf_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static int sirf_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = sirf_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->bytes = size;
+
+	return 0;
+}
+
+static int sirf_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ret = sirf_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = sirf_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+
+	return 0;
+
+out:
+	sirf_pcm_free_dma_buffers(pcm);
+	return ret;
+}
+
+static struct snd_soc_platform_driver sirf_soc_platform = {
+	.ops		= &sirf_pcm_ops,
+	.pcm_new	= sirf_pcm_new,
+	.pcm_free	= sirf_pcm_free_dma_buffers,
+};
+
+static int sirf_pcm_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&pdev->dev,
+			&sirf_soc_platform);
+}
+
+static int sirf_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id sirf_pcm_of_match[] = {
+	{ .compatible = "sirf,pcm-audio", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_pcm_of_match);
+
+static struct platform_driver sirf_pcm_driver = {
+	.driver = {
+		.name = "sirf-pcm-audio",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_pcm_of_match,
+	},
+	.probe = sirf_pcm_probe,
+	.remove = sirf_pcm_remove,
+};
+module_platform_driver(sirf_pcm_driver);
+
+MODULE_DESCRIPTION("SiRF PCM audio interface driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-pcm.h b/sound/soc/sirf/sirf-pcm.h
new file mode 100644
index 0000000..c178016
--- /dev/null
+++ b/sound/soc/sirf/sirf-pcm.h
@@ -0,0 +1,17 @@
+/*
+ * SiRF pcm dma data struct
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __SIRF_PCM_H__
+#define __SIRF_PCM_H__
+
+struct sirf_pcm_dma_data {
+	char	*name;		/* Stream name */
+	int	dma_req;	/* DMA request line */
+};
+
+#endif
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

this driver uses dmaengine APIs and provides DMA to the CPU DAIs
of I2S, USP and SiRF-soc-inner.
SiRFSoC has 3 audio DAIs: I2S, USP(Universal Serial Ports) and DAI
connected to soc-inner-codec, all of them will use the same DMA
driver here.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/Kconfig         |   1 +
 sound/soc/Makefile        |   1 +
 sound/soc/sirf/Kconfig    |   4 +
 sound/soc/sirf/Makefile   |   3 +
 sound/soc/sirf/sirf-pcm.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-pcm.h |  17 ++++
 6 files changed, 247 insertions(+)
 create mode 100644 sound/soc/sirf/Kconfig
 create mode 100644 sound/soc/sirf/Makefile
 create mode 100644 sound/soc/sirf/sirf-pcm.c
 create mode 100644 sound/soc/sirf/sirf-pcm.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 45eeaa9..2581e55 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -51,6 +51,7 @@ source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index bc02614..e15df84 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
new file mode 100644
index 0000000..3678aed
--- /dev/null
+++ b/sound/soc/sirf/Kconfig
@@ -0,0 +1,4 @@
+config SND_SIRF_SOC
+	tristate "Platform DMA driver for the SiRF SoC chips"
+	depends on ARCH_SIRF && SND_SOC
+	select SND_SOC_DMAENGINE_PCM
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
new file mode 100644
index 0000000..f268b83
--- /dev/null
+++ b/sound/soc/sirf/Makefile
@@ -0,0 +1,3 @@
+snd-soc-sirf-objs := sirf-pcm.o
+
+obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
diff --git a/sound/soc/sirf/sirf-pcm.c b/sound/soc/sirf/sirf-pcm.c
new file mode 100644
index 0000000..d4cb897
--- /dev/null
+++ b/sound/soc/sirf/sirf-pcm.c
@@ -0,0 +1,221 @@
+/*
+ * ALSA PCM interface for the SiRF SoC
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/sirfsoc_dma.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "sirf-pcm.h"
+
+static struct snd_pcm_hardware sirf_pcm_hardware = {
+	.info                   = (SNDRV_PCM_INFO_MMAP
+			| SNDRV_PCM_INFO_MMAP_VALID
+			| SNDRV_PCM_INFO_INTERLEAVED
+			| SNDRV_PCM_INFO_BLOCK_TRANSFER
+			| SNDRV_PCM_INFO_RESUME
+			| SNDRV_PCM_INFO_PAUSE),
+	.formats                = (SNDRV_PCM_FMTBIT_S16_LE),
+	.rates                  = (SNDRV_PCM_RATE_48000),
+	.rate_min               = 512,
+	.rate_max               = 115200,
+	.channels_min           = 1,
+	.channels_max           = 2,
+	.buffer_bytes_max       = 64 * 1024,
+	.period_bytes_min       = 128,
+	.period_bytes_max       = 32 * 1024,
+	.periods_min            = 2,
+	.periods_max            = 2,
+};
+
+static int sirf_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sirf_pcm_dma_data *dma_data;
+	substream->runtime->hw = sirf_pcm_hardware;
+	snd_soc_set_runtime_hwparams(substream, &sirf_pcm_hardware);
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	return snd_dmaengine_pcm_open_request_chan(substream,
+			(dma_filter_fn)sirfsoc_dma_filter_id,
+			(void *)(dma_data->dma_req));
+}
+
+static int sirf_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sirf_pcm_dma_data *dma_data;
+	struct dma_slave_config config;
+	struct dma_chan *chan;
+	int err = 0;
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	chan = snd_dmaengine_pcm_get_chan(substream);
+	if (!chan)
+		return -EINVAL;
+
+	/* fills in addr_width and direction */
+	err = snd_hwparams_to_dma_slave_config(substream, params, &config);
+	if (err)
+		return err;
+
+	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+	config.src_addr = runtime->dma_addr;
+	config.dst_addr = runtime->dma_addr;
+	config.src_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
+	config.dst_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+	return dmaengine_slave_config(chan, &config);
+}
+
+static int sirf_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int sirf_pcm_mmap(struct snd_pcm_substream *substream,
+		struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return dma_mmap_coherent(substream->pcm->card->dev, vma,
+			runtime->dma_area,
+			runtime->dma_addr,
+			runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops sirf_pcm_ops = {
+	.open		= sirf_pcm_open,
+	.close		= snd_dmaengine_pcm_close_release_chan,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= sirf_pcm_hw_params,
+	.hw_free	= sirf_pcm_hw_free,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= snd_dmaengine_pcm_pointer,
+	.mmap		= sirf_pcm_mmap,
+};
+
+static void sirf_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static int sirf_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = sirf_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->bytes = size;
+
+	return 0;
+}
+
+static int sirf_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ret = sirf_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = sirf_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+
+	return 0;
+
+out:
+	sirf_pcm_free_dma_buffers(pcm);
+	return ret;
+}
+
+static struct snd_soc_platform_driver sirf_soc_platform = {
+	.ops		= &sirf_pcm_ops,
+	.pcm_new	= sirf_pcm_new,
+	.pcm_free	= sirf_pcm_free_dma_buffers,
+};
+
+static int sirf_pcm_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&pdev->dev,
+			&sirf_soc_platform);
+}
+
+static int sirf_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id sirf_pcm_of_match[] = {
+	{ .compatible = "sirf,pcm-audio", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_pcm_of_match);
+
+static struct platform_driver sirf_pcm_driver = {
+	.driver = {
+		.name = "sirf-pcm-audio",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_pcm_of_match,
+	},
+	.probe = sirf_pcm_probe,
+	.remove = sirf_pcm_remove,
+};
+module_platform_driver(sirf_pcm_driver);
+
+MODULE_DESCRIPTION("SiRF PCM audio interface driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-pcm.h b/sound/soc/sirf/sirf-pcm.h
new file mode 100644
index 0000000..c178016
--- /dev/null
+++ b/sound/soc/sirf/sirf-pcm.h
@@ -0,0 +1,17 @@
+/*
+ * SiRF pcm dma data struct
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __SIRF_PCM_H__
+#define __SIRF_PCM_H__
+
+struct sirf_pcm_dma_data {
+	char	*name;		/* Stream name */
+	int	dma_req;	/* DMA request line */
+};
+
+#endif
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Rongjun Ying, Workgroup.Linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

This patch adds I2S DAI driver for SiRFprima2 and SiRFatlas6. The hardware supports:
I2S bus specification compliant (Released by Philips Semiconductors in June, 1996)
Supports I2S master and I2S slave mode
Provides MCLK to I2S CODEC in I2S master mode
Supports 16-bit resolution playback
Supports 16-bit resolution record
Supports 2 channel record
Supports 2 channel and 6 channel mode playback
Frame length, left channel length and right channel length are all programmable
Supports two DMA channels for TXFIFO and RXFIFO
Supports floating mode (x_ac97_dout can be configured as input)

headfile sound/soc/sirf/sirf-audio.h will be shard by all I2S and soc-inner
DAIs.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 266 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 411 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 682 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 3678aed..d98acd4 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,3 +2,6 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_DMAENGINE_PCM
+
+config SND_SOC_SIRF_I2S
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index f268b83..9f754fe 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,3 +1,5 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-i2s-objs := sirf-i2s.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
diff --git a/sound/soc/sirf/sirf-audio.h b/sound/soc/sirf/sirf-audio.h
new file mode 100644
index 0000000..514c815
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,266 @@
+/*
+ * SiRF inner codec controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_INNER_AUDIO_CTRL_H
+#define _SIRF_INNER_AUDIO_CTRL_H
+
+#define AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_CTRL_TX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_TX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_CTRL_RX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_RX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_MODE_SEL			(0x0000)
+#define AUDIO_CTRL_AC97_CTRL			(0x0004)
+#define AUDIO_CTRL_AC97_CMD			(0x0008)
+#define AUDIO_CTRL_AC97_OP_STATUS		(0x000C)
+#define AUDIO_CTRL_AC97_RD_CODEC_REG		(0x0010)
+#define AUDIO_CTRL_TXSLOT_EN			(0x0014)
+#define AUDIO_CTRL_RXSLOT_EN			(0x0018)
+#define AUDIO_CTRL_AC97_AUX_SLOT_EN		(0x001c)
+#define AUDIO_CTRL_I2S_CTRL			(0x0020)
+#define AUDIO_CTRL_I2S_TX_RX_EN			(0x0024)
+
+#define AUDIO_CTRL_EXT_TXFIFO1_OP		(0x0040)
+#define AUDIO_CTRL_EXT_TXFIFO1_LEV_CHK		(0x0044)
+#define AUDIO_CTRL_EXT_TXFIFO1_STS		(0x0048)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT		(0x004C)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT_MSK		(0x0050)
+
+#define AUDIO_CTRL_EXT_TXFIFO2_OP		(0x0054)
+#define AUDIO_CTRL_EXT_TXFIFO2_LEV_CHK		(0x0058)
+#define AUDIO_CTRL_EXT_TXFIFO2_STS		(0x005C)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT		(0x0060)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT_MSK		(0x0064)
+
+#define AUDIO_CTRL_EXT_TXFIFO3_OP		(0x0068)
+#define AUDIO_CTRL_EXT_TXFIFO3_LEV_CHK		(0x006C)
+#define AUDIO_CTRL_EXT_TXFIFO3_STS		(0x0070)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT		(0x0074)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT_MSK		(0x0078)
+
+#define AUDIO_CTRL_EXT_TXFIFO4_OP		(0x007C)
+#define AUDIO_CTRL_EXT_TXFIFO4_LEV_CHK		(0x0080)
+#define AUDIO_CTRL_EXT_TXFIFO4_STS		(0x0084)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT		(0x0088)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT_MSK		(0x008C)
+
+#define AUDIO_CTRL_EXT_TXFIFO5_OP		(0x0090)
+#define AUDIO_CTRL_EXT_TXFIFO5_LEV_CHK		(0x0094)
+#define AUDIO_CTRL_EXT_TXFIFO5_STS		(0x0098)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT		(0x009C)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT_MSK		(0x00A0)
+
+#define AUDIO_CTRL_EXT_TXFIFO6_OP		(0x00A4)
+#define AUDIO_CTRL_EXT_TXFIFO6_LEV_CHK		(0x00A8)
+#define AUDIO_CTRL_EXT_TXFIFO6_STS		(0x00AC)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT		(0x00B0)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT_MSK		(0x00B4)
+
+#define AUDIO_CTRL_RXFIFO_OP			(0x00B8)
+#define AUDIO_CTRL_RXFIFO_LEV_CHK		(0x00BC)
+#define AUDIO_CTRL_RXFIFO_STS			(0x00C0)
+#define AUDIO_CTRL_RXFIFO_INT			(0x00C4)
+#define AUDIO_CTRL_RXFIFO_INT_MSK		(0x00C8)
+
+#define AUDIO_CTRL_AUXFIFO_OP			(0x00CC)
+#define AUDIO_CTRL_AUXFIFO_LEV_CHK		(0x00D0)
+#define AUDIO_CTRL_AUXFIFO_STS			(0x00D4)
+#define AUDIO_CTRL_AUXFIFO_INT			(0x00D8)
+#define AUDIO_CTRL_AUXFIFO_INT_MSK		(0x00DC)
+
+#define AUDIO_IC_CODEC_PWR			(0x00E0)
+#define AUDIO_IC_CODEC_CTRL0			(0x00E4)
+#define AUDIO_IC_CODEC_CTRL1			(0x00E8)
+#define AUDIO_IC_CODEC_CTRL2			(0x00EC)
+#define AUDIO_IC_CODEC_CTRL3			(0x00F0)
+
+#define AUDIO_CTRL_IC_CODEC_TX_CTRL		(0x00F4)
+#define AUDIO_CTRL_IC_CODEC_RX_CTRL		(0x00F8)
+
+#define AUDIO_CTRL_IC_TXFIFO_OP			(0x00FC)
+#define AUDIO_CTRL_IC_TXFIFO_LEV_CHK		(0x0100)
+#define AUDIO_CTRL_IC_TXFIFO_STS		(0x0104)
+#define AUDIO_CTRL_IC_TXFIFO_INT		(0x0108)
+#define AUDIO_CTRL_IC_TXFIFO_INT_MSK		(0x010C)
+
+#define AUDIO_CTRL_IC_RXFIFO_OP			(0x0110)
+#define AUDIO_CTRL_IC_RXFIFO_LEV_CHK		(0x0114)
+#define AUDIO_CTRL_IC_RXFIFO_STS		(0x0118)
+#define AUDIO_CTRL_IC_RXFIFO_INT		(0x011C)
+#define AUDIO_CTRL_IC_RXFIFO_INT_MSK		(0x0120)
+
+#define I2S_MODE				(1<<0)
+#define AC97_TX_SLOT3_WIDTH_MASK		(3<<1)
+#define AC97_TX_SLOT4_WIDTH_MASK		(3<<3)
+#define AC97_TX_SLOT6_WIDTH_MASK		(3<<5)
+#define AC97_TX_SLOT7_WIDTH_MASK		(3<<7)
+#define AC97_TX_SLOT8_WIDTH_MASK		(3<<9)
+#define AC97_TX_SLOT9_WIDTH_MASK		(3<<11)
+#define AC97_FIFO_SYNC_MASK			(3<<13)
+#define AC97_FIFO_SYNC_ALL			(1<<13)
+
+#define SYNC_START				(1<<0)
+#define AC97_START				(1<<1)
+#define WARM_WAKEUP				(1<<2)
+
+#define AC97_CMD_TYPE_MASK			(1<<7)
+#define AC97_CMD_ADDR_MASK			(0x7F)
+#define AC97_CMD_TYPE_WRITE			(0<<7)
+#define AC97_CMD_TYPE_READ			(1<<7)
+
+#define CMD_ISSUE_BIT				(1<<0)
+#define	RD_CMD_FINISH_BIT			(1<<1)
+#define	CODEC_READY_BIT				(1<<2)
+
+#define	AC97_RDBACK_ADDR_BITS			(0x7F)
+#define	AC97_RDBACK_DATA_BITS			(0xFF<<16)
+
+#define AC97_TX_SLOT3_EN			(1<<0)
+#define AC97_TX_SLOT4_EN			(1<<1)
+#define AC97_TX_SLOT6_EN			(1<<2)
+#define AC97_TX_SLOT7_EN			(1<<3)
+#define AC97_TX_SLOT8_EN			(1<<4)
+#define AC97_TX_SLOT9_EN			(1<<5)
+
+#define AC97_RX_SLOT3_EN			(1<<0)
+#define AC97_RX_SLOT4_EN			(1<<1)
+#define AC97_RX_SLOT5_EN			(1<<2)
+#define AC97_RX_SLOT6_EN			(1<<3)
+#define AC97_RX_SLOT7_EN			(1<<4)
+#define AC97_RX_SLOT8_EN			(1<<5)
+#define AC97_RX_SLOT9_EN			(1<<6)
+#define AC97_RX_SLOT10_EN			(1<<7)
+#define AC97_RX_SLOT11_EN			(1<<8)
+#define AC97_RX_SLOT12_EN			(1<<9)
+
+#define AC97_AUX_SLOT3_EN			(1<<0)
+#define AC97_AUX_SLOT4_EN			(1<<1)
+#define AC97_AUX_SLOT5_EN			(1<<2)
+#define AC97_AUX_SLOT6_EN			(1<<3)
+#define AC97_AUX_SLOT7_EN			(1<<4)
+#define AC97_AUX_SLOT8_EN			(1<<5)
+#define AC97_AUX_SLOT9_EN			(1<<6)
+#define AC97_AUX_SLOT10_EN			(1<<7)
+#define AC97_AUX_SLOT11_EN			(1<<8)
+#define AC97_AUX_SLOT12_EN			(1<<9)
+
+#define I2S_LOOP_BACK				(1<<3)
+#define	I2S_MCLK_DIV_SHIFT			15
+#define I2S_MCLK_DIV_MASK			(0x1FF<<I2S_MCLK_DIV_SHIFT)
+#define I2S_BITCLK_DIV_SHIFT			24
+#define I2S_BITCLK_DIV_MASK			(0xFF<<I2S_BITCLK_DIV_SHIFT)
+
+#define I2S_MCLK_EN				(1<<2)
+#define I2S_REF_CLK_SEL_EXT			(1<<3)
+#define I2S_DOUT_OE				(1<<4)
+#define i2s_R2X_LP_TO_TX0			(1<<30)
+#define i2s_R2X_LP_TO_TX1			(2<<30)
+#define i2s_R2X_LP_TO_TX2			(3<<30)
+
+#define AUDIO_FIFO_START		(1 << 0)
+#define AUDIO_FIFO_RESET		(1 << 1)
+
+#define AUDIO_FIFO_FULL			(1 << 0)
+#define AUDIO_FIFO_EMPTY		(1 << 1)
+#define AUDIO_FIFO_OFLOW		(1 << 2)
+#define AUDIO_FIFO_UFLOW		(1 << 3)
+
+#define I2S_RX_ENABLE			(1 << 0)
+#define I2S_TX_ENABLE			(1 << 1)
+
+/* Codec I2S Control Register defines */
+#define I2S_SLAVE_MODE			(1 << 0)
+#define I2S_SIX_CHANNELS		(1 << 1)
+#define I2S_L_CHAN_LEN_MASK		(0x1f << 4)
+#define I2S_FRAME_LEN_MASK		(0x3f << 9)
+
+#define AC97_WRITE_FRAME_VALID    0X10
+#define AC97_WRITE_SLOT1_VALID    0X08
+#define AC97_WRITE_SLOT2_VALID    0X04
+#define AC97_WRITE_SLOT3_VALID    0X02
+#define AC97_WRITE_SLOT4_VALID    0X01
+
+#define IC_TX_ENABLE		(0x03)
+#define IC_RX_ENABLE		(0x03)
+
+#define MICBIASEN		(1 << 3)
+
+#define IC_RDACEN		(1 << 0)
+#define IC_LDACEN		(1 << 1)
+#define IC_HSREN		(1 << 2)
+#define IC_HSLEN		(1 << 3)
+#define IC_SPEN			(1 << 4)
+#define IC_CPEN			(1 << 5)
+
+#define IC_HPRSELR		(1 << 6)
+#define IC_HPLSELR		(1 << 7)
+#define IC_HPRSELL		(1 << 8)
+#define IC_HPLSELL		(1 << 9)
+#define IC_SPSELR		(1 << 10)
+#define IC_SPSELL		(1 << 11)
+
+#define IC_MONOR		(1 << 12)
+#define IC_MONOL		(1 << 13)
+
+#define IC_RXOSRSEL		(1 << 28)
+#define IC_CPFREQ		(1 << 29)
+#define IC_HSINVEN		(1 << 30)
+
+#define IC_MICINREN		(1 << 0)
+#define IC_MICINLEN		(1 << 1)
+#define IC_MICIN1SEL		(1 << 2)
+#define IC_MICIN2SEL		(1 << 3)
+#define IC_MICDIFSEL		(1 << 4)
+#define	IC_LINEIN1SEL		(1 << 5)
+#define	IC_LINEIN2SEL		(1 << 6)
+#define	IC_RADCEN		(1 << 7)
+#define	IC_LADCEN		(1 << 8)
+#define	IC_ALM			(1 << 9)
+
+#define IC_DIGMICEN             (1 << 22)
+#define IC_DIGMICFREQ           (1 << 23)
+#define IC_ADC14B_12            (1 << 24)
+#define IC_FIRDAC_HSL_EN        (1 << 25)
+#define IC_FIRDAC_HSR_EN        (1 << 26)
+#define IC_FIRDAC_LOUT_EN       (1 << 27)
+#define IC_POR                  (1 << 28)
+#define IC_CODEC_CLK_EN         (1 << 29)
+#define IC_HP_3DB_BOOST         (1 << 30)
+
+#define IC_ADC_LEFT_GAIN_SHIFT	16
+#define IC_ADC_RIGHT_GAIN_SHIFT 10
+#define IC_ADC_GAIN_MASK	0x3F
+#define IC_MIC_MAX_GAIN		0x39
+
+#define IC_RXPGAR_MASK		0x3F
+#define IC_RXPGAR_SHIFT		14
+#define IC_RXPGAL_MASK		0x3F
+#define IC_RXPGAL_SHIFT		21
+#define IC_RXPGAR		0x7B
+#define IC_RXPGAL		0x7B
+
+#endif /*__SIRF_INNER_AUDIO_CTRL_H*/
diff --git a/sound/soc/sirf/sirf-i2s.c b/sound/soc/sirf/sirf-i2s.c
new file mode 100644
index 0000000..d0f81f9
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,411 @@
+/*
+ * SiRF I2S driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sirf-pcm.h"
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	struct pwm_device	*mclk_pwm;
+	u32			i2s_ctrl;
+	spinlock_t		lock;
+	int			master_mode;
+};
+
+static struct sirf_pcm_dma_data sirf_i2s_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(dai->dev);
+	snd_soc_dai_set_dma_data(dai, substream,
+		&sirf_i2s_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	clk_disable_unprepare(si2s->clk);
+}
+
+static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		} else {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_TX_ENABLE | I2S_MCLK_EN),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+		} else {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_RX_ENABLE | I2S_MCLK_EN),
+				si2s->base+AUDIO_CTRL_I2S_TX_RX_EN);
+
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_RXFIFO_OP);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	if (runtime->channels == 2)
+		ctrl &= ~I2S_SIX_CHANNELS;
+	else
+		ctrl |= I2S_SIX_CHANNELS;
+
+	writel(ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	return 0;
+}
+
+static int sirf_i2s_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	u32 left_len, frame_len;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		left_len = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		left_len = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		left_len = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		left_len = 32;
+		break;
+	default:
+		dev_err(dai->dev, "Format unsupported\n");
+		return -EINVAL;
+	}
+
+	frame_len = left_len * 2;
+	i2s_ctrl &= ~(I2S_L_CHAN_LEN_MASK | I2S_FRAME_LEN_MASK);
+	/* Fill the actual len - 1 */
+	i2s_ctrl |= ((frame_len - 1) << 9) | ((left_len - 1) << 4)
+		| (0 << 15) | (3 << 24);
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	return 0;
+}
+
+static int sirf_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		ctrl |= I2S_SLAVE_MODE;
+		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->master_mode = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		si2s->master_mode = 1;
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base + AUDIO_CTRL_MODE_SEL);
+		break;
+	default:
+		dev_err(dai->dev, "Only I2S format supported\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.prepare	= sirf_i2s_prepare,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.name		= "sirf-i2s",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF I2S Playback",
+		.channels_min = 2,
+		.channels_max = 6,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF I2S Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &sirfsoc_i2s_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int sirf_i2s_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	clk_disable_unprepare(si2s->clk);
+
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	return 0;
+}
+
+static int sirf_i2s_resume(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(&pdev->dev);
+	writel(readl(si2s->base+AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base+AUDIO_CTRL_MODE_SEL);
+	writel(si2s->i2s_ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+	writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+	writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#endif
+
+static const struct snd_soc_component_driver sirf_i2s_component = {
+	.name       = "sirf-i2s",
+};
+
+static int sirf_i2s_probe(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource mem_res;
+
+	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
+			GFP_KERNEL);
+	if (!si2s)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, si2s);
+
+	spin_lock_init(&si2s->lock);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+	sirf_i2s_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_i2s_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	ret = of_address_to_resource(pdev->dev.of_node, 0, &mem_res);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to get i2s memory resource.\n");
+		return ret;
+	}
+	si2s->base = devm_ioremap(&pdev->dev, mem_res.start,
+		resource_size(&mem_res));
+	if (!si2s->base)
+		return -ENOMEM;
+
+	si2s->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		ret = PTR_ERR(si2s->clk);
+		goto err;
+	}
+
+	/* i2s bus uses an internal PWM to generate MCLK */
+	si2s->mclk_pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->mclk_pwm)) {
+		dev_err(&pdev->dev, "unable to request PWM\n");
+		ret = PTR_ERR(si2s->mclk_pwm);
+		goto err_clk_put;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
+			&sirf_i2s_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(si2s->clk);
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	pwm_disable(si2s->mclk_pwm);
+	snd_soc_unregister_component(&pdev->dev);
+	clk_disable_unprepare(si2s->clk);
+
+	return 0;
+}
+
+static const struct of_device_id sirf_i2s_of_match[] = {
+	{ .compatible = "sirf,prima2-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_i2s_of_match);
+
+static struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+	.suspend = sirf_i2s_suspend,
+	.resume = sirf_i2s_resume,
+};
+
+module_platform_driver(sirf_i2s_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2S driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

This patch adds I2S DAI driver for SiRFprima2 and SiRFatlas6. The hardware supports:
I2S bus specification compliant (Released by Philips Semiconductors in June, 1996)
Supports I2S master and I2S slave mode
Provides MCLK to I2S CODEC in I2S master mode
Supports 16-bit resolution playback
Supports 16-bit resolution record
Supports 2 channel record
Supports 2 channel and 6 channel mode playback
Frame length, left channel length and right channel length are all programmable
Supports two DMA channels for TXFIFO and RXFIFO
Supports floating mode (x_ac97_dout can be configured as input)

headfile sound/soc/sirf/sirf-audio.h will be shard by all I2S and soc-inner
DAIs.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 266 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 411 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 682 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 3678aed..d98acd4 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,3 +2,6 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_DMAENGINE_PCM
+
+config SND_SOC_SIRF_I2S
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index f268b83..9f754fe 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,3 +1,5 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-i2s-objs := sirf-i2s.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
diff --git a/sound/soc/sirf/sirf-audio.h b/sound/soc/sirf/sirf-audio.h
new file mode 100644
index 0000000..514c815
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,266 @@
+/*
+ * SiRF inner codec controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_INNER_AUDIO_CTRL_H
+#define _SIRF_INNER_AUDIO_CTRL_H
+
+#define AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_CTRL_TX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_TX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_CTRL_RX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_RX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_MODE_SEL			(0x0000)
+#define AUDIO_CTRL_AC97_CTRL			(0x0004)
+#define AUDIO_CTRL_AC97_CMD			(0x0008)
+#define AUDIO_CTRL_AC97_OP_STATUS		(0x000C)
+#define AUDIO_CTRL_AC97_RD_CODEC_REG		(0x0010)
+#define AUDIO_CTRL_TXSLOT_EN			(0x0014)
+#define AUDIO_CTRL_RXSLOT_EN			(0x0018)
+#define AUDIO_CTRL_AC97_AUX_SLOT_EN		(0x001c)
+#define AUDIO_CTRL_I2S_CTRL			(0x0020)
+#define AUDIO_CTRL_I2S_TX_RX_EN			(0x0024)
+
+#define AUDIO_CTRL_EXT_TXFIFO1_OP		(0x0040)
+#define AUDIO_CTRL_EXT_TXFIFO1_LEV_CHK		(0x0044)
+#define AUDIO_CTRL_EXT_TXFIFO1_STS		(0x0048)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT		(0x004C)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT_MSK		(0x0050)
+
+#define AUDIO_CTRL_EXT_TXFIFO2_OP		(0x0054)
+#define AUDIO_CTRL_EXT_TXFIFO2_LEV_CHK		(0x0058)
+#define AUDIO_CTRL_EXT_TXFIFO2_STS		(0x005C)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT		(0x0060)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT_MSK		(0x0064)
+
+#define AUDIO_CTRL_EXT_TXFIFO3_OP		(0x0068)
+#define AUDIO_CTRL_EXT_TXFIFO3_LEV_CHK		(0x006C)
+#define AUDIO_CTRL_EXT_TXFIFO3_STS		(0x0070)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT		(0x0074)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT_MSK		(0x0078)
+
+#define AUDIO_CTRL_EXT_TXFIFO4_OP		(0x007C)
+#define AUDIO_CTRL_EXT_TXFIFO4_LEV_CHK		(0x0080)
+#define AUDIO_CTRL_EXT_TXFIFO4_STS		(0x0084)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT		(0x0088)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT_MSK		(0x008C)
+
+#define AUDIO_CTRL_EXT_TXFIFO5_OP		(0x0090)
+#define AUDIO_CTRL_EXT_TXFIFO5_LEV_CHK		(0x0094)
+#define AUDIO_CTRL_EXT_TXFIFO5_STS		(0x0098)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT		(0x009C)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT_MSK		(0x00A0)
+
+#define AUDIO_CTRL_EXT_TXFIFO6_OP		(0x00A4)
+#define AUDIO_CTRL_EXT_TXFIFO6_LEV_CHK		(0x00A8)
+#define AUDIO_CTRL_EXT_TXFIFO6_STS		(0x00AC)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT		(0x00B0)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT_MSK		(0x00B4)
+
+#define AUDIO_CTRL_RXFIFO_OP			(0x00B8)
+#define AUDIO_CTRL_RXFIFO_LEV_CHK		(0x00BC)
+#define AUDIO_CTRL_RXFIFO_STS			(0x00C0)
+#define AUDIO_CTRL_RXFIFO_INT			(0x00C4)
+#define AUDIO_CTRL_RXFIFO_INT_MSK		(0x00C8)
+
+#define AUDIO_CTRL_AUXFIFO_OP			(0x00CC)
+#define AUDIO_CTRL_AUXFIFO_LEV_CHK		(0x00D0)
+#define AUDIO_CTRL_AUXFIFO_STS			(0x00D4)
+#define AUDIO_CTRL_AUXFIFO_INT			(0x00D8)
+#define AUDIO_CTRL_AUXFIFO_INT_MSK		(0x00DC)
+
+#define AUDIO_IC_CODEC_PWR			(0x00E0)
+#define AUDIO_IC_CODEC_CTRL0			(0x00E4)
+#define AUDIO_IC_CODEC_CTRL1			(0x00E8)
+#define AUDIO_IC_CODEC_CTRL2			(0x00EC)
+#define AUDIO_IC_CODEC_CTRL3			(0x00F0)
+
+#define AUDIO_CTRL_IC_CODEC_TX_CTRL		(0x00F4)
+#define AUDIO_CTRL_IC_CODEC_RX_CTRL		(0x00F8)
+
+#define AUDIO_CTRL_IC_TXFIFO_OP			(0x00FC)
+#define AUDIO_CTRL_IC_TXFIFO_LEV_CHK		(0x0100)
+#define AUDIO_CTRL_IC_TXFIFO_STS		(0x0104)
+#define AUDIO_CTRL_IC_TXFIFO_INT		(0x0108)
+#define AUDIO_CTRL_IC_TXFIFO_INT_MSK		(0x010C)
+
+#define AUDIO_CTRL_IC_RXFIFO_OP			(0x0110)
+#define AUDIO_CTRL_IC_RXFIFO_LEV_CHK		(0x0114)
+#define AUDIO_CTRL_IC_RXFIFO_STS		(0x0118)
+#define AUDIO_CTRL_IC_RXFIFO_INT		(0x011C)
+#define AUDIO_CTRL_IC_RXFIFO_INT_MSK		(0x0120)
+
+#define I2S_MODE				(1<<0)
+#define AC97_TX_SLOT3_WIDTH_MASK		(3<<1)
+#define AC97_TX_SLOT4_WIDTH_MASK		(3<<3)
+#define AC97_TX_SLOT6_WIDTH_MASK		(3<<5)
+#define AC97_TX_SLOT7_WIDTH_MASK		(3<<7)
+#define AC97_TX_SLOT8_WIDTH_MASK		(3<<9)
+#define AC97_TX_SLOT9_WIDTH_MASK		(3<<11)
+#define AC97_FIFO_SYNC_MASK			(3<<13)
+#define AC97_FIFO_SYNC_ALL			(1<<13)
+
+#define SYNC_START				(1<<0)
+#define AC97_START				(1<<1)
+#define WARM_WAKEUP				(1<<2)
+
+#define AC97_CMD_TYPE_MASK			(1<<7)
+#define AC97_CMD_ADDR_MASK			(0x7F)
+#define AC97_CMD_TYPE_WRITE			(0<<7)
+#define AC97_CMD_TYPE_READ			(1<<7)
+
+#define CMD_ISSUE_BIT				(1<<0)
+#define	RD_CMD_FINISH_BIT			(1<<1)
+#define	CODEC_READY_BIT				(1<<2)
+
+#define	AC97_RDBACK_ADDR_BITS			(0x7F)
+#define	AC97_RDBACK_DATA_BITS			(0xFF<<16)
+
+#define AC97_TX_SLOT3_EN			(1<<0)
+#define AC97_TX_SLOT4_EN			(1<<1)
+#define AC97_TX_SLOT6_EN			(1<<2)
+#define AC97_TX_SLOT7_EN			(1<<3)
+#define AC97_TX_SLOT8_EN			(1<<4)
+#define AC97_TX_SLOT9_EN			(1<<5)
+
+#define AC97_RX_SLOT3_EN			(1<<0)
+#define AC97_RX_SLOT4_EN			(1<<1)
+#define AC97_RX_SLOT5_EN			(1<<2)
+#define AC97_RX_SLOT6_EN			(1<<3)
+#define AC97_RX_SLOT7_EN			(1<<4)
+#define AC97_RX_SLOT8_EN			(1<<5)
+#define AC97_RX_SLOT9_EN			(1<<6)
+#define AC97_RX_SLOT10_EN			(1<<7)
+#define AC97_RX_SLOT11_EN			(1<<8)
+#define AC97_RX_SLOT12_EN			(1<<9)
+
+#define AC97_AUX_SLOT3_EN			(1<<0)
+#define AC97_AUX_SLOT4_EN			(1<<1)
+#define AC97_AUX_SLOT5_EN			(1<<2)
+#define AC97_AUX_SLOT6_EN			(1<<3)
+#define AC97_AUX_SLOT7_EN			(1<<4)
+#define AC97_AUX_SLOT8_EN			(1<<5)
+#define AC97_AUX_SLOT9_EN			(1<<6)
+#define AC97_AUX_SLOT10_EN			(1<<7)
+#define AC97_AUX_SLOT11_EN			(1<<8)
+#define AC97_AUX_SLOT12_EN			(1<<9)
+
+#define I2S_LOOP_BACK				(1<<3)
+#define	I2S_MCLK_DIV_SHIFT			15
+#define I2S_MCLK_DIV_MASK			(0x1FF<<I2S_MCLK_DIV_SHIFT)
+#define I2S_BITCLK_DIV_SHIFT			24
+#define I2S_BITCLK_DIV_MASK			(0xFF<<I2S_BITCLK_DIV_SHIFT)
+
+#define I2S_MCLK_EN				(1<<2)
+#define I2S_REF_CLK_SEL_EXT			(1<<3)
+#define I2S_DOUT_OE				(1<<4)
+#define i2s_R2X_LP_TO_TX0			(1<<30)
+#define i2s_R2X_LP_TO_TX1			(2<<30)
+#define i2s_R2X_LP_TO_TX2			(3<<30)
+
+#define AUDIO_FIFO_START		(1 << 0)
+#define AUDIO_FIFO_RESET		(1 << 1)
+
+#define AUDIO_FIFO_FULL			(1 << 0)
+#define AUDIO_FIFO_EMPTY		(1 << 1)
+#define AUDIO_FIFO_OFLOW		(1 << 2)
+#define AUDIO_FIFO_UFLOW		(1 << 3)
+
+#define I2S_RX_ENABLE			(1 << 0)
+#define I2S_TX_ENABLE			(1 << 1)
+
+/* Codec I2S Control Register defines */
+#define I2S_SLAVE_MODE			(1 << 0)
+#define I2S_SIX_CHANNELS		(1 << 1)
+#define I2S_L_CHAN_LEN_MASK		(0x1f << 4)
+#define I2S_FRAME_LEN_MASK		(0x3f << 9)
+
+#define AC97_WRITE_FRAME_VALID    0X10
+#define AC97_WRITE_SLOT1_VALID    0X08
+#define AC97_WRITE_SLOT2_VALID    0X04
+#define AC97_WRITE_SLOT3_VALID    0X02
+#define AC97_WRITE_SLOT4_VALID    0X01
+
+#define IC_TX_ENABLE		(0x03)
+#define IC_RX_ENABLE		(0x03)
+
+#define MICBIASEN		(1 << 3)
+
+#define IC_RDACEN		(1 << 0)
+#define IC_LDACEN		(1 << 1)
+#define IC_HSREN		(1 << 2)
+#define IC_HSLEN		(1 << 3)
+#define IC_SPEN			(1 << 4)
+#define IC_CPEN			(1 << 5)
+
+#define IC_HPRSELR		(1 << 6)
+#define IC_HPLSELR		(1 << 7)
+#define IC_HPRSELL		(1 << 8)
+#define IC_HPLSELL		(1 << 9)
+#define IC_SPSELR		(1 << 10)
+#define IC_SPSELL		(1 << 11)
+
+#define IC_MONOR		(1 << 12)
+#define IC_MONOL		(1 << 13)
+
+#define IC_RXOSRSEL		(1 << 28)
+#define IC_CPFREQ		(1 << 29)
+#define IC_HSINVEN		(1 << 30)
+
+#define IC_MICINREN		(1 << 0)
+#define IC_MICINLEN		(1 << 1)
+#define IC_MICIN1SEL		(1 << 2)
+#define IC_MICIN2SEL		(1 << 3)
+#define IC_MICDIFSEL		(1 << 4)
+#define	IC_LINEIN1SEL		(1 << 5)
+#define	IC_LINEIN2SEL		(1 << 6)
+#define	IC_RADCEN		(1 << 7)
+#define	IC_LADCEN		(1 << 8)
+#define	IC_ALM			(1 << 9)
+
+#define IC_DIGMICEN             (1 << 22)
+#define IC_DIGMICFREQ           (1 << 23)
+#define IC_ADC14B_12            (1 << 24)
+#define IC_FIRDAC_HSL_EN        (1 << 25)
+#define IC_FIRDAC_HSR_EN        (1 << 26)
+#define IC_FIRDAC_LOUT_EN       (1 << 27)
+#define IC_POR                  (1 << 28)
+#define IC_CODEC_CLK_EN         (1 << 29)
+#define IC_HP_3DB_BOOST         (1 << 30)
+
+#define IC_ADC_LEFT_GAIN_SHIFT	16
+#define IC_ADC_RIGHT_GAIN_SHIFT 10
+#define IC_ADC_GAIN_MASK	0x3F
+#define IC_MIC_MAX_GAIN		0x39
+
+#define IC_RXPGAR_MASK		0x3F
+#define IC_RXPGAR_SHIFT		14
+#define IC_RXPGAL_MASK		0x3F
+#define IC_RXPGAL_SHIFT		21
+#define IC_RXPGAR		0x7B
+#define IC_RXPGAL		0x7B
+
+#endif /*__SIRF_INNER_AUDIO_CTRL_H*/
diff --git a/sound/soc/sirf/sirf-i2s.c b/sound/soc/sirf/sirf-i2s.c
new file mode 100644
index 0000000..d0f81f9
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,411 @@
+/*
+ * SiRF I2S driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sirf-pcm.h"
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	struct pwm_device	*mclk_pwm;
+	u32			i2s_ctrl;
+	spinlock_t		lock;
+	int			master_mode;
+};
+
+static struct sirf_pcm_dma_data sirf_i2s_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(dai->dev);
+	snd_soc_dai_set_dma_data(dai, substream,
+		&sirf_i2s_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	clk_disable_unprepare(si2s->clk);
+}
+
+static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		} else {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_TX_ENABLE | I2S_MCLK_EN),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+		} else {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_RX_ENABLE | I2S_MCLK_EN),
+				si2s->base+AUDIO_CTRL_I2S_TX_RX_EN);
+
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_RXFIFO_OP);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	if (runtime->channels == 2)
+		ctrl &= ~I2S_SIX_CHANNELS;
+	else
+		ctrl |= I2S_SIX_CHANNELS;
+
+	writel(ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	return 0;
+}
+
+static int sirf_i2s_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	u32 left_len, frame_len;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		left_len = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		left_len = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		left_len = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		left_len = 32;
+		break;
+	default:
+		dev_err(dai->dev, "Format unsupported\n");
+		return -EINVAL;
+	}
+
+	frame_len = left_len * 2;
+	i2s_ctrl &= ~(I2S_L_CHAN_LEN_MASK | I2S_FRAME_LEN_MASK);
+	/* Fill the actual len - 1 */
+	i2s_ctrl |= ((frame_len - 1) << 9) | ((left_len - 1) << 4)
+		| (0 << 15) | (3 << 24);
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	return 0;
+}
+
+static int sirf_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		ctrl |= I2S_SLAVE_MODE;
+		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->master_mode = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		si2s->master_mode = 1;
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base + AUDIO_CTRL_MODE_SEL);
+		break;
+	default:
+		dev_err(dai->dev, "Only I2S format supported\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.prepare	= sirf_i2s_prepare,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.name		= "sirf-i2s",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF I2S Playback",
+		.channels_min = 2,
+		.channels_max = 6,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF I2S Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &sirfsoc_i2s_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int sirf_i2s_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	clk_disable_unprepare(si2s->clk);
+
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	return 0;
+}
+
+static int sirf_i2s_resume(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(&pdev->dev);
+	writel(readl(si2s->base+AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base+AUDIO_CTRL_MODE_SEL);
+	writel(si2s->i2s_ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+	writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+	writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#endif
+
+static const struct snd_soc_component_driver sirf_i2s_component = {
+	.name       = "sirf-i2s",
+};
+
+static int sirf_i2s_probe(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource mem_res;
+
+	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
+			GFP_KERNEL);
+	if (!si2s)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, si2s);
+
+	spin_lock_init(&si2s->lock);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+	sirf_i2s_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_i2s_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	ret = of_address_to_resource(pdev->dev.of_node, 0, &mem_res);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to get i2s memory resource.\n");
+		return ret;
+	}
+	si2s->base = devm_ioremap(&pdev->dev, mem_res.start,
+		resource_size(&mem_res));
+	if (!si2s->base)
+		return -ENOMEM;
+
+	si2s->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		ret = PTR_ERR(si2s->clk);
+		goto err;
+	}
+
+	/* i2s bus uses an internal PWM to generate MCLK */
+	si2s->mclk_pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->mclk_pwm)) {
+		dev_err(&pdev->dev, "unable to request PWM\n");
+		ret = PTR_ERR(si2s->mclk_pwm);
+		goto err_clk_put;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
+			&sirf_i2s_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(si2s->clk);
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	pwm_disable(si2s->mclk_pwm);
+	snd_soc_unregister_component(&pdev->dev);
+	clk_disable_unprepare(si2s->clk);
+
+	return 0;
+}
+
+static const struct of_device_id sirf_i2s_of_match[] = {
+	{ .compatible = "sirf,prima2-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_i2s_of_match);
+
+static struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+	.suspend = sirf_i2s_suspend,
+	.resume = sirf_i2s_resume,
+};
+
+module_platform_driver(sirf_i2s_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2S driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Rongjun Ying, Workgroup.Linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

Universal Serial Ports (USP) can be used as PCM. this patch
adds support for this functionality.

The USP provides 128-byte data FIFO which supports DMA and I/O
modes. Here we use the common DMA driver.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig    |   3 +
 sound/soc/sirf/Makefile   |   2 +
 sound/soc/sirf/sirf-usp.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.h | 276 ++++++++++++++++++++++++++
 4 files changed, 762 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-usp.c
 create mode 100644 sound/soc/sirf/sirf-usp.h

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index d98acd4..3606614 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -5,3 +5,6 @@ config SND_SIRF_SOC
 
 config SND_SOC_SIRF_I2S
 	tristate
+
+config SND_SOC_SIRF_USP
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 9f754fe..630c9be 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,5 +1,7 @@
 snd-soc-sirf-objs := sirf-pcm.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
+snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
+obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
new file mode 100644
index 0000000..107aa4b
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.c
@@ -0,0 +1,481 @@
+/*
+ * SiRF USP audio transfer interface like I2S
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/soc.h>
+
+#include "sirf-usp.h"
+#include "sirf-pcm.h"
+
+#define FIFO_RESET  0
+#define FIFO_START	1
+#define FIFO_STOP	2
+
+#define AUDIO_WORD_SIZE 16
+
+struct sirf_usp {
+	void __iomem *base;
+	struct clk *clk;
+	u32 mode1_reg;
+	u32 mode2_reg;
+};
+
+static struct sirf_pcm_dma_data sirf_usp_pcm_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+static void sirf_usp_tx_fifo_op(struct sirf_usp *susp, int cmd)
+{
+	switch (cmd) {
+	case FIFO_RESET:
+		writel(USP_TX_FIFO_RESET, susp->base + USP_TX_FIFO_OP);
+		writel(0, susp->base + USP_TX_FIFO_OP);
+		break;
+	case FIFO_START:
+		writel(USP_TX_FIFO_START, susp->base + USP_TX_FIFO_OP);
+		break;
+	case FIFO_STOP:
+		writel(0, susp->base + USP_TX_FIFO_OP);
+		break;
+	}
+}
+
+static void sirf_usp_rx_fifo_op(struct sirf_usp *susp, int cmd)
+{
+	switch (cmd) {
+	case FIFO_RESET:
+		writel(USP_RX_FIFO_RESET, susp->base + USP_RX_FIFO_OP);
+		writel(0, susp->base + USP_RX_FIFO_OP);
+		break;
+	case FIFO_START:
+		writel(USP_RX_FIFO_START, susp->base + USP_RX_FIFO_OP);
+		break;
+	case FIFO_STOP:
+		writel(0, susp->base + USP_RX_FIFO_OP);
+		break;
+	}
+}
+
+static inline void sirf_usp_tx_enable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) | USP_TX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_tx_disable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) & ~USP_TX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_rx_enable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) | USP_RX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_rx_disable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) & ~USP_RX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static int sirf_usp_pcm_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	if (playback)
+		sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+	else
+		sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+
+	snd_soc_dai_set_dma_data(dai, substream,
+			&sirf_usp_pcm_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+	u32 val = readl(susp->base + USP_MODE2);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dev_info(dai->dev, "USP master mode is not supported.\n");
+		return -EINVAL;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		writel(readl(susp->base + USP_MODE1)
+			| USP_CLOCK_MODE_SLAVE, susp->base + USP_MODE1);
+		val |= (USP_TFS_CLK_SLAVE_MODE);
+		val |= (USP_RFS_CLK_SLAVE_MODE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(val, susp->base + USP_MODE2);
+
+	return 0;
+}
+
+static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+
+	u32 clk_rate = clk_get_rate(susp->clk);
+	u32 clk_div = (clk_rate/(2*rate)) - 1;
+	u32 clk_div_hi = (clk_div & 0xC00)>>10;
+	u32 clk_div_lo = (clk_div & 0x3FF);
+
+	writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
+		susp->base + USP_MODE2);
+	writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
+		susp->base + USP_TX_FRAME_CTRL);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = {
+	.startup        = sirf_usp_pcm_dai_startup,
+	.trigger  = sirf_usp_pcm_trigger,
+	.set_fmt = sirf_usp_pcm_set_dai_fmt,
+	.set_clkdiv = sirf_usp_pcm_divider,
+};
+
+static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
+	.name		= "sirf-usp-pcm",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF USP PCM Playback",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_48000
+			| SNDRV_PCM_RATE_44100
+			| SNDRV_PCM_RATE_32000
+			| SNDRV_PCM_RATE_22050
+			| SNDRV_PCM_RATE_16000
+			| SNDRV_PCM_RATE_11025
+			| SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF USP PCM Capture",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_48000
+			| SNDRV_PCM_RATE_44100
+			| SNDRV_PCM_RATE_32000
+			| SNDRV_PCM_RATE_22050
+			| SNDRV_PCM_RATE_16000
+			| SNDRV_PCM_RATE_11025
+			| SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_usp_pcm_dai_ops,
+};
+
+static void sirf_usp_controller_init(struct sirf_usp *susp)
+{
+	u32 val;
+
+	/* Configure RISC mode */
+	writel(readl(susp->base + USP_RISC_DSP_MODE) & ~USP_RISC_DSP_SEL,
+		susp->base + USP_RISC_DSP_MODE);
+
+	/* Disable all interrupts status */
+	writel(readl(susp->base + USP_INT_STATUS), susp->base + USP_INT_STATUS);
+
+	/* Configure DMA IO Length register */
+	writel(0, susp->base + USP_TX_DMA_IO_LEN);
+	writel(0, susp->base + USP_RX_DMA_IO_LEN);
+
+	/* Configure RX Frame Control */
+	val = (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_DATA_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_FRAME_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_SHIFTER_LEN_OFFSET;
+	val |= USP_SINGLE_SYNC_MODE;
+	writel(val, susp->base + USP_RX_FRAME_CTRL);
+
+	/* Configure TX Frame Control */
+	val = (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_DATA_LEN_OFFSET;
+	val |= 0<<USP_TXC_SYNC_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_FRAME_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_SHIFTER_LEN_OFFSET;
+	val |= USP_TXC_SLAVE_CLK_SAMPLE;
+	writel(val, susp->base + USP_TX_FRAME_CTRL);
+
+	/* Configure Mode2 register */
+	val = (1<<USP_RXD_DELAY_LEN_OFFSET) | (0<<USP_TXD_DELAY_LEN_OFFSET);
+	val &= ~USP_ENA_CTRL_MODE;
+	val &= ~USP_FRAME_CTRL_MODE;
+	val &= ~USP_TFS_SOURCE_MODE;
+	writel(val, susp->base + USP_MODE2);
+
+	/* Configure Mode1 register */
+	val = 0;
+	val |= USP_SYNC_MODE;
+	val |= USP_ENDIAN_CTRL_LSBF;
+	val |= USP_EN;
+	val |= USP_RXD_ACT_EDGE_FALLING;
+	val &= ~USP_TXD_ACT_EDGE_FALLING;
+	val |= USP_RFS_ACT_LEVEL_LOGIC1;
+	val |= USP_TFS_ACT_LEVEL_LOGIC1;
+	val |= USP_SCLK_IDLE_MODE_TOGGLE;
+	val |= USP_SCLK_IDLE_LEVEL_LOGIC1;
+	val &= ~USP_SCLK_PIN_MODE_IO;
+	val &= ~USP_RFS_PIN_MODE_IO;
+	val &= ~USP_TFS_PIN_MODE_IO;
+	val &= ~USP_RXD_PIN_MODE_IO;
+	val &= ~USP_TXD_PIN_MODE_IO;
+	val |= USP_TX_UFLOW_REPEAT_ZERO;
+	writel(val, susp->base + USP_MODE1);
+
+	/* Configure RX DMA IO Control register */
+	writel(0x0, susp->base + USP_RX_DMA_IO_CTRL);
+
+	/* Congiure RX FIFO Control register */
+	writel((USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) |
+		(USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET),
+		susp->base + USP_RX_FIFO_CTRL);
+
+	/* Congiure RX FIFO Level Check register */
+	writel(RX_FIFO_SC(0x04)|RX_FIFO_LC(0x0E)|RX_FIFO_HC(0x1B),
+		susp->base + USP_RX_FIFO_LEVEL_CHK);
+
+	/* Configure TX DMA IO Control register*/
+	writel(0x0, susp->base + USP_TX_DMA_IO_CTRL);
+
+	/* Configure TX FIFO Control register */
+	writel((USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) |
+		(USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET),
+		susp->base + USP_TX_FIFO_CTRL);
+
+	/* Congiure TX FIFO Level Check register */
+	writel(TX_FIFO_SC(0x1B)|TX_FIFO_LC(0x0E)|TX_FIFO_HC(0x04),
+		susp->base + USP_TX_FIFO_LEVEL_CHK);
+
+	/* Configure RX FIFO */
+	writel(USP_RX_FIFO_RESET, susp->base + USP_RX_FIFO_OP);
+	writel(0, susp->base + USP_RX_FIFO_OP);
+
+	/* Configure TX FIFO */
+	writel(USP_TX_FIFO_RESET, susp->base + USP_TX_FIFO_OP);
+	writel(0, susp->base + USP_TX_FIFO_OP);
+}
+
+static void sirf_usp_controller_uninit(struct sirf_usp *susp)
+{
+	/* Disable RX/TX */
+	writel(0, susp->base+USP_INT_ENABLE);
+	writel(0, susp->base + USP_TX_RX_ENABLE);
+}
+
+#ifdef CONFIG_PM
+static int sirf_usp_pcm_suspend(struct platform_device *pdev,
+	pm_message_t state)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+
+	susp->mode1_reg = readl(susp->base + USP_MODE1);
+	susp->mode2_reg = readl(susp->base + USP_MODE2);
+	sirf_usp_controller_uninit(susp);
+	clk_disable_unprepare(susp->clk);
+
+	return 0;
+}
+
+static int sirf_usp_pcm_resume(struct platform_device *pdev)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(susp->clk);
+	sirf_usp_controller_init(susp);
+
+	writel(susp->mode1_reg, susp->base + USP_MODE1);
+	writel(susp->mode2_reg, susp->base + USP_MODE2);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#endif
+
+static const struct snd_soc_component_driver sirf_usp_component = {
+	.name		= "sirf-usp",
+};
+
+static int sirf_usp_pcm_probe(struct platform_device *pdev)
+{
+	struct sirf_usp *susp;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource *mem_res;
+
+	susp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp),
+			GFP_KERNEL);
+	if (!susp)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, susp);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,usp-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,usp-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+	sirf_usp_pcm_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_usp_pcm_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	susp->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (susp->base == NULL)
+		return -ENOMEM;
+
+	susp->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(susp->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(susp->clk);
+	}
+	clk_prepare_enable(susp->clk);
+
+	sirf_usp_controller_init(susp);
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_usp_component,
+		&sirf_usp_pcm_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(susp->clk);
+	clk_put(susp->clk);
+	return ret;
+}
+
+static int sirf_usp_pcm_remove(struct platform_device *pdev)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+	snd_soc_unregister_component(&pdev->dev);
+	sirf_usp_controller_uninit(susp);
+	clk_disable_unprepare(susp->clk);
+	clk_put(susp->clk);
+
+	return 0;
+}
+
+static const struct of_device_id sirf_usp_pcm_of_match[] = {
+	{ .compatible = "sirf,prima2-usp-pcm", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match);
+
+static struct platform_driver sirf_usp_pcm_driver = {
+	.driver = {
+		.name = "sirf-usp-pcm",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_usp_pcm_of_match,
+	},
+	.probe = sirf_usp_pcm_probe,
+	.remove = sirf_usp_pcm_remove,
+	.suspend = sirf_usp_pcm_suspend,
+	.resume = sirf_usp_pcm_resume,
+};
+
+module_platform_driver(sirf_usp_pcm_driver);
+
+MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-usp.h b/sound/soc/sirf/sirf-usp.h
new file mode 100644
index 0000000..cb9085f
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.h
@@ -0,0 +1,276 @@
+/*
+ * arch/arm/mach-prima2/include/mach/sirfsoc_usp.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __SIRFSOC_USP__
+#define __SIRFSOC_USP__
+
+/* USP Registers */
+#define USP_MODE1		0x00
+#define USP_MODE2 		0x04
+#define USP_TX_FRAME_CTRL  	0x08
+#define USP_RX_FRAME_CTRL	0x0C
+#define USP_TX_RX_ENABLE	0x10
+#define USP_INT_ENABLE		0x14
+#define USP_INT_STATUS		0x18
+#define USP_PIN_IO_DATA		0x1C
+#define USP_RISC_DSP_MODE	0x20
+#define USP_AYSNC_PARAM_REG	0x24
+#define USP_IRDA_X_MODE_DIV	0x28
+#define USP_SM_CFG		0x2C
+#define USP_TX_DMA_IO_CTRL	0x100
+#define USP_TX_DMA_IO_LEN	0x104
+#define USP_TX_FIFO_CTRL	0x108
+#define USP_TX_FIFO_LEVEL_CHK	0x10C
+#define USP_TX_FIFO_OP		0x110
+#define USP_TX_FIFO_STATUS	0x114
+#define USP_TX_FIFO_DATA	0x118
+#define USP_RX_DMA_IO_CTRL	0x120
+#define USP_RX_DMA_IO_LEN	0x124
+#define USP_RX_FIFO_CTRL	0x128
+#define USP_RX_FIFO_LEVEL_CHK	0x12C
+#define USP_RX_FIFO_OP		0x130
+#define USP_RX_FIFO_STATUS	0x134
+#define USP_RX_FIFO_DATA	0x138
+
+/* USP MODE register-1 */
+#define USP_SYNC_MODE 			0x00000001
+#define USP_CLOCK_MODE_SLAVE 		0x00000002
+#define USP_LOOP_BACK_EN		0x00000004
+#define USP_HPSIR_EN			0x00000008
+#define USP_ENDIAN_CTRL_LSBF		0x00000010
+#define USP_EN				0x00000020
+#define USP_RXD_ACT_EDGE_FALLING	0x00000040
+#define USP_TXD_ACT_EDGE_FALLING	0x00000080
+#define USP_RFS_ACT_LEVEL_LOGIC1	0x00000100
+#define USP_TFS_ACT_LEVEL_LOGIC1	0x00000200
+#define USP_SCLK_IDLE_MODE_TOGGLE	0x00000400
+#define USP_SCLK_IDLE_LEVEL_LOGIC1	0x00000800
+#define USP_SCLK_PIN_MODE_IO	0x00001000
+#define USP_RFS_PIN_MODE_IO	0x00002000
+#define USP_TFS_PIN_MODE_IO	0x00004000
+#define USP_RXD_PIN_MODE_IO	0x00008000
+#define USP_TXD_PIN_MODE_IO	0x00010000
+#define USP_SCLK_IO_MODE_INPUT	0x00020000
+#define USP_RFS_IO_MODE_INPUT	0x00040000
+#define USP_TFS_IO_MODE_INPUT	0x00080000
+#define USP_RXD_IO_MODE_INPUT	0x00100000
+#define USP_TXD_IO_MODE_INPUT	0x00200000
+#define USP_IRDA_WIDTH_DIV_MASK	0x3FC00000
+#define USP_IRDA_WIDTH_DIV_OFFSET	0
+#define USP_IRDA_IDLE_LEVEL_HIGH	0x40000000
+#define USP_TX_UFLOW_REPEAT_ZERO	0x80000000
+#define USP_TX_ENDIAN_MODE		0x00000020
+#define USP_RX_ENDIAN_MODE		0x00000020
+
+/* USP Mode Register-2 */
+#define USP_RXD_DELAY_LEN_MASK		0x000000FF
+#define USP_RXD_DELAY_LEN_OFFSET	0
+
+#define USP_TXD_DELAY_LEN_MASK		0x0000FF00
+#define USP_TXD_DELAY_LEN_OFFSET	8
+
+#define USP_ENA_CTRL_MODE		0x00010000
+#define USP_FRAME_CTRL_MODE		0x00020000
+#define USP_TFS_SOURCE_MODE             0x00040000
+#define USP_TFS_MS_MODE                 0x00080000
+#define USP_CLK_DIVISOR_MASK		0x7FE00000
+#define USP_CLK_DIVISOR_OFFSET 		21
+
+#define USP_TFS_CLK_SLAVE_MODE		(1<<20)
+#define USP_RFS_CLK_SLAVE_MODE		(1<<19)
+
+#define USP_IRDA_DATA_WIDTH		0x80000000
+
+/* USP Transmit Frame Control Register */
+
+#define USP_TXC_DATA_LEN_MASK		0x000000FF
+#define USP_TXC_DATA_LEN_OFFSET		0
+
+#define USP_TXC_SYNC_LEN_MASK		0x0000FF00
+#define USP_TXC_SYNC_LEN_OFFSET		8
+
+#define USP_TXC_FRAME_LEN_MASK		0x00FF0000
+#define USP_TXC_FRAME_LEN_OFFSET 	16
+
+#define USP_TXC_SHIFTER_LEN_MASK	0x1F000000
+#define USP_TXC_SHIFTER_LEN_OFFSET	24
+
+#define USP_TXC_SLAVE_CLK_SAMPLE 	0x20000000
+
+#define USP_TXC_CLK_DIVISOR_MASK  	0xC0000000
+#define USP_TXC_CLK_DIVISOR_OFFSET 	30
+
+/* USP Receive Frame Control Register */
+
+#define USP_RXC_DATA_LEN_MASK 		0x000000FF
+#define USP_RXC_DATA_LEN_OFFSET		0
+
+#define USP_RXC_FRAME_LEN_MASK		0x0000FF00
+#define USP_RXC_FRAME_LEN_OFFSET  	8
+
+#define USP_RXC_SHIFTER_LEN_MASK 	0x001F0000
+#define USP_RXC_SHIFTER_LEN_OFFSET 	16
+
+#define USP_I2S_SYNC_CHG		0x00200000
+
+#define USP_RXC_CLK_DIVISOR_MASK  	0x0F000000
+#define USP_RXC_CLK_DIVISOR_OFFSET 	24
+#define USP_SINGLE_SYNC_MODE		0x00400000
+
+/* Tx - RX Enable Register */
+
+#define USP_RX_ENA		0x00000001
+#define USP_TX_ENA		0x00000002
+
+/* USP Interrupt Enable and status Register */
+#define USP_RX_DONE_INT			0x00000001
+#define USP_TX_DONE_INT			0x00000002
+#define USP_RX_OFLOW_INT		0x00000004
+#define USP_TX_UFLOW_INT		0x00000008
+#define USP_RX_IO_DMA_INT		0x00000010
+#define USP_TX_IO_DMA_INT		0x00000020
+#define USP_RXFIFO_FULL_INT		0x00000040
+#define USP_TXFIFO_EMPTY_INT		0x00000080
+#define USP_RXFIFO_THD_INT		0x00000100
+#define USP_TXFIFO_THD_INT		0x00000200
+#define USP_UART_FRM_ERR_INT		0x00000400
+#define USP_RX_TIMEOUT_INT 		0x00000800
+#define USP_TX_ALLOUT_INT  		0x00001000
+#define USP_RXD_BREAK_INT		0x00008000
+
+/* All possible TX interruots */
+#define USP_TX_INTERRUPT		(USP_TX_DONE_INT|USP_TX_UFLOW_INT|USP_TX_IO_DMA_INT|\
+					USP_TXFIFO_EMPTY_INT|USP_TXFIFO_THD_INT)
+/* All possible RX interruots */
+#define USP_RX_INTERRUPT		(USP_RX_DONE_INT|USP_RX_OFLOW_INT|USP_RX_IO_DMA_INT|\
+					USP_RXFIFO_FULL_INT|USP_RXFIFO_THD_INT|USP_RXFIFO_THD_INT|USP_RX_TIMEOUT_INT)
+
+#define USP_INT_ALL        0x1FFF
+
+/* USP Pin I/O Data Register */
+
+#define USP_RFS_PIN_VALUE_MASK	0x00000001
+#define USP_TFS_PIN_VALUE_MASK	0x00000002
+#define USP_RXD_PIN_VALUE_MASK	0x00000004
+#define USP_TXD_PIN_VALUE_MASK	0x00000008
+#define USP_SCLK_PIN_VALUE_MASK	0x00000010
+
+/* USP RISC/DSP Mode Register */
+#define USP_RISC_DSP_SEL	0x00000001
+
+/* USP ASYNC PARAMETER Register*/
+
+#define USP_ASYNC_TIMEOUT_MASK 	0x0000FFFF
+#define USP_ASYNC_TIMEOUT_OFFSET	0
+#define USP_ASYNC_TIMEOUT(x)	(((x)&USP_ASYNC_TIMEOUT_MASK)<<USP_ASYNC_TIMEOUT_OFFSET)
+
+#define USP_ASYNC_DIV2_MASK		0x003F0000
+#define USP_ASYNC_DIV2_OFFSET		16
+
+/* USP TX DMA I/O MODE Register */
+#define USP_TX_MODE_IO			0x00000001
+
+/* USP TX DMA I/O Length Register */
+#define USP_TX_DATA_LEN_MASK		0xFFFFFFFF
+#define USP_TX_DATA_LEN_OFFSET		0
+
+/* USP TX FIFO Control Register */
+#define USP_TX_FIFO_WIDTH_MASK		0x00000003
+#define USP_TX_FIFO_WIDTH_OFFSET	0
+
+#define USP_TX_FIFO_THD_MASK		0x000001FC
+#define USP_TX_FIFO_THD_OFFSET		2
+
+/* USP TX FIFO Level Check Register */
+#define USP_TX_FIFO_LEVEL_CHECK_MASK	0x1F
+#define USP_TX_FIFO_SC_OFFSET	0
+#define USP_TX_FIFO_LC_OFFSET	10
+#define USP_TX_FIFO_HC_OFFSET	20
+
+#define TX_FIFO_SC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_HC_OFFSET)
+
+/* USP TX FIFO Operation Register */
+#define USP_TX_FIFO_RESET		0x00000001
+#define USP_TX_FIFO_START		0x00000002
+
+/* USP TX FIFO Status Register */
+#define USP_TX_FIFO_LEVEL_MASK		0x0000007F
+#define USP_TX_FIFO_LEVEL_OFFSET	0
+
+#define USP_TX_FIFO_FULL 		0x00000080
+#define USP_TX_FIFO_EMPTY		0x00000100
+
+/* USP TX FIFO Data Register */
+#define USP_TX_FIFO_DATA_MASK		0xFFFFFFFF
+#define USP_TX_FIFO_DATA_OFFSET		0
+
+/* USP RX DMA I/O MODE Register */
+#define USP_RX_MODE_IO			0x00000001
+#define USP_RX_DMA_FLUSH		0x00000004
+
+/* USP RX DMA I/O Length Register */
+#define USP_RX_DATA_LEN_MASK		0xFFFFFFFF
+#define USP_RX_DATA_LEN_OFFSET		0
+
+/* USP RX FIFO Control Register */
+#define USP_RX_FIFO_WIDTH_MASK		0x00000003
+#define USP_RX_FIFO_WIDTH_OFFSET	0
+
+#define USP_RX_FIFO_THD_MASK		0x000001FC
+#define USP_RX_FIFO_THD_OFFSET		2
+
+/* USP RX FIFO Level Check Register */
+
+#define USP_RX_FIFO_LEVEL_CHECK_MASK	0x1F
+#define USP_RX_FIFO_SC_OFFSET	0
+#define USP_RX_FIFO_LC_OFFSET	10
+#define USP_RX_FIFO_HC_OFFSET	20
+
+#define RX_FIFO_SC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_HC_OFFSET)
+
+/* USP RX FIFO Operation Register */
+#define USP_RX_FIFO_RESET		0x00000001
+#define USP_RX_FIFO_START		0x00000002
+
+/* USP RX FIFO Status Register */
+
+#define USP_RX_FIFO_LEVEL_MASK		0x0000007F
+#define USP_RX_FIFO_LEVEL_OFFSET	0
+
+#define USP_RX_FIFO_FULL		0x00000080
+#define USP_RX_FIFO_EMPTY		0x00000100
+
+/* USP RX FIFO Data Register */
+
+#define USP_RX_FIFO_DATA_MASK		0xFFFFFFFF
+#define USP_RX_FIFO_DATA_OFFSET		0
+
+/*
+ * When rx thd irq occur, sender just disable tx empty irq,
+ * Remaining data in tx fifo wil also be sent out.
+ */
+#define USP_FIFO_SIZE           	128
+#define USP_TX_FIFO_THRESHOLD		(USP_FIFO_SIZE/2)
+#define USP_RX_FIFO_THRESHOLD		(USP_FIFO_SIZE/2)
+
+/* FIFO_WIDTH for the USP_TX_FIFO_CTRL and USP_RX_FIFO_CTRL registers */
+#define USP_FIFO_WIDTH_BYTE  0x00
+#define USP_FIFO_WIDTH_WORD  0x01
+#define USP_FIFO_WIDTH_DWORD 0x02
+
+#define USP_ASYNC_DIV2          16
+
+#define USP_PLUGOUT_RETRY_CNT	2
+
+#define USP_TX_RX_FIFO_WIDTH_DWORD    2
+
+#endif
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

Universal Serial Ports (USP) can be used as PCM. this patch
adds support for this functionality.

The USP provides 128-byte data FIFO which supports DMA and I/O
modes. Here we use the common DMA driver.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig    |   3 +
 sound/soc/sirf/Makefile   |   2 +
 sound/soc/sirf/sirf-usp.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-usp.h | 276 ++++++++++++++++++++++++++
 4 files changed, 762 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-usp.c
 create mode 100644 sound/soc/sirf/sirf-usp.h

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index d98acd4..3606614 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -5,3 +5,6 @@ config SND_SIRF_SOC
 
 config SND_SOC_SIRF_I2S
 	tristate
+
+config SND_SOC_SIRF_USP
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 9f754fe..630c9be 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,5 +1,7 @@
 snd-soc-sirf-objs := sirf-pcm.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
+snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
+obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
new file mode 100644
index 0000000..107aa4b
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.c
@@ -0,0 +1,481 @@
+/*
+ * SiRF USP audio transfer interface like I2S
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/soc.h>
+
+#include "sirf-usp.h"
+#include "sirf-pcm.h"
+
+#define FIFO_RESET  0
+#define FIFO_START	1
+#define FIFO_STOP	2
+
+#define AUDIO_WORD_SIZE 16
+
+struct sirf_usp {
+	void __iomem *base;
+	struct clk *clk;
+	u32 mode1_reg;
+	u32 mode2_reg;
+};
+
+static struct sirf_pcm_dma_data sirf_usp_pcm_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+static void sirf_usp_tx_fifo_op(struct sirf_usp *susp, int cmd)
+{
+	switch (cmd) {
+	case FIFO_RESET:
+		writel(USP_TX_FIFO_RESET, susp->base + USP_TX_FIFO_OP);
+		writel(0, susp->base + USP_TX_FIFO_OP);
+		break;
+	case FIFO_START:
+		writel(USP_TX_FIFO_START, susp->base + USP_TX_FIFO_OP);
+		break;
+	case FIFO_STOP:
+		writel(0, susp->base + USP_TX_FIFO_OP);
+		break;
+	}
+}
+
+static void sirf_usp_rx_fifo_op(struct sirf_usp *susp, int cmd)
+{
+	switch (cmd) {
+	case FIFO_RESET:
+		writel(USP_RX_FIFO_RESET, susp->base + USP_RX_FIFO_OP);
+		writel(0, susp->base + USP_RX_FIFO_OP);
+		break;
+	case FIFO_START:
+		writel(USP_RX_FIFO_START, susp->base + USP_RX_FIFO_OP);
+		break;
+	case FIFO_STOP:
+		writel(0, susp->base + USP_RX_FIFO_OP);
+		break;
+	}
+}
+
+static inline void sirf_usp_tx_enable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) | USP_TX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_tx_disable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) & ~USP_TX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_rx_enable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) | USP_RX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static inline void sirf_usp_rx_disable(struct sirf_usp *susp)
+{
+	writel(readl(susp->base + USP_TX_RX_ENABLE) & ~USP_RX_ENA,
+			susp->base + USP_TX_RX_ENABLE);
+}
+
+static int sirf_usp_pcm_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	if (playback)
+		sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+	else
+		sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+
+	snd_soc_dai_set_dma_data(dai, substream,
+			&sirf_usp_pcm_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+	u32 val = readl(susp->base + USP_MODE2);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dev_info(dai->dev, "USP master mode is not supported.\n");
+		return -EINVAL;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		writel(readl(susp->base + USP_MODE1)
+			| USP_CLOCK_MODE_SLAVE, susp->base + USP_MODE1);
+		val |= (USP_TFS_CLK_SLAVE_MODE);
+		val |= (USP_RFS_CLK_SLAVE_MODE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(val, susp->base + USP_MODE2);
+
+	return 0;
+}
+
+static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_RESET);
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (playback) {
+			sirf_usp_tx_disable(susp);
+			sirf_usp_tx_fifo_op(susp, FIFO_STOP);
+		} else {
+			sirf_usp_rx_disable(susp);
+			sirf_usp_rx_fifo_op(susp, FIFO_STOP);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (playback) {
+			sirf_usp_tx_fifo_op(susp, FIFO_START);
+			sirf_usp_tx_enable(susp);
+		} else {
+			sirf_usp_rx_fifo_op(susp, FIFO_START);
+			sirf_usp_rx_enable(susp);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
+{
+	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
+
+	u32 clk_rate = clk_get_rate(susp->clk);
+	u32 clk_div = (clk_rate/(2*rate)) - 1;
+	u32 clk_div_hi = (clk_div & 0xC00)>>10;
+	u32 clk_div_lo = (clk_div & 0x3FF);
+
+	writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
+		susp->base + USP_MODE2);
+	writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
+		susp->base + USP_TX_FRAME_CTRL);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = {
+	.startup        = sirf_usp_pcm_dai_startup,
+	.trigger  = sirf_usp_pcm_trigger,
+	.set_fmt = sirf_usp_pcm_set_dai_fmt,
+	.set_clkdiv = sirf_usp_pcm_divider,
+};
+
+static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
+	.name		= "sirf-usp-pcm",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF USP PCM Playback",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_48000
+			| SNDRV_PCM_RATE_44100
+			| SNDRV_PCM_RATE_32000
+			| SNDRV_PCM_RATE_22050
+			| SNDRV_PCM_RATE_16000
+			| SNDRV_PCM_RATE_11025
+			| SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF USP PCM Capture",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_48000
+			| SNDRV_PCM_RATE_44100
+			| SNDRV_PCM_RATE_32000
+			| SNDRV_PCM_RATE_22050
+			| SNDRV_PCM_RATE_16000
+			| SNDRV_PCM_RATE_11025
+			| SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_usp_pcm_dai_ops,
+};
+
+static void sirf_usp_controller_init(struct sirf_usp *susp)
+{
+	u32 val;
+
+	/* Configure RISC mode */
+	writel(readl(susp->base + USP_RISC_DSP_MODE) & ~USP_RISC_DSP_SEL,
+		susp->base + USP_RISC_DSP_MODE);
+
+	/* Disable all interrupts status */
+	writel(readl(susp->base + USP_INT_STATUS), susp->base + USP_INT_STATUS);
+
+	/* Configure DMA IO Length register */
+	writel(0, susp->base + USP_TX_DMA_IO_LEN);
+	writel(0, susp->base + USP_RX_DMA_IO_LEN);
+
+	/* Configure RX Frame Control */
+	val = (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_DATA_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_FRAME_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_RXC_SHIFTER_LEN_OFFSET;
+	val |= USP_SINGLE_SYNC_MODE;
+	writel(val, susp->base + USP_RX_FRAME_CTRL);
+
+	/* Configure TX Frame Control */
+	val = (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_DATA_LEN_OFFSET;
+	val |= 0<<USP_TXC_SYNC_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_FRAME_LEN_OFFSET;
+	val |= (AUDIO_WORD_SIZE*2 - 1)<<USP_TXC_SHIFTER_LEN_OFFSET;
+	val |= USP_TXC_SLAVE_CLK_SAMPLE;
+	writel(val, susp->base + USP_TX_FRAME_CTRL);
+
+	/* Configure Mode2 register */
+	val = (1<<USP_RXD_DELAY_LEN_OFFSET) | (0<<USP_TXD_DELAY_LEN_OFFSET);
+	val &= ~USP_ENA_CTRL_MODE;
+	val &= ~USP_FRAME_CTRL_MODE;
+	val &= ~USP_TFS_SOURCE_MODE;
+	writel(val, susp->base + USP_MODE2);
+
+	/* Configure Mode1 register */
+	val = 0;
+	val |= USP_SYNC_MODE;
+	val |= USP_ENDIAN_CTRL_LSBF;
+	val |= USP_EN;
+	val |= USP_RXD_ACT_EDGE_FALLING;
+	val &= ~USP_TXD_ACT_EDGE_FALLING;
+	val |= USP_RFS_ACT_LEVEL_LOGIC1;
+	val |= USP_TFS_ACT_LEVEL_LOGIC1;
+	val |= USP_SCLK_IDLE_MODE_TOGGLE;
+	val |= USP_SCLK_IDLE_LEVEL_LOGIC1;
+	val &= ~USP_SCLK_PIN_MODE_IO;
+	val &= ~USP_RFS_PIN_MODE_IO;
+	val &= ~USP_TFS_PIN_MODE_IO;
+	val &= ~USP_RXD_PIN_MODE_IO;
+	val &= ~USP_TXD_PIN_MODE_IO;
+	val |= USP_TX_UFLOW_REPEAT_ZERO;
+	writel(val, susp->base + USP_MODE1);
+
+	/* Configure RX DMA IO Control register */
+	writel(0x0, susp->base + USP_RX_DMA_IO_CTRL);
+
+	/* Congiure RX FIFO Control register */
+	writel((USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) |
+		(USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET),
+		susp->base + USP_RX_FIFO_CTRL);
+
+	/* Congiure RX FIFO Level Check register */
+	writel(RX_FIFO_SC(0x04)|RX_FIFO_LC(0x0E)|RX_FIFO_HC(0x1B),
+		susp->base + USP_RX_FIFO_LEVEL_CHK);
+
+	/* Configure TX DMA IO Control register*/
+	writel(0x0, susp->base + USP_TX_DMA_IO_CTRL);
+
+	/* Configure TX FIFO Control register */
+	writel((USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) |
+		(USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET),
+		susp->base + USP_TX_FIFO_CTRL);
+
+	/* Congiure TX FIFO Level Check register */
+	writel(TX_FIFO_SC(0x1B)|TX_FIFO_LC(0x0E)|TX_FIFO_HC(0x04),
+		susp->base + USP_TX_FIFO_LEVEL_CHK);
+
+	/* Configure RX FIFO */
+	writel(USP_RX_FIFO_RESET, susp->base + USP_RX_FIFO_OP);
+	writel(0, susp->base + USP_RX_FIFO_OP);
+
+	/* Configure TX FIFO */
+	writel(USP_TX_FIFO_RESET, susp->base + USP_TX_FIFO_OP);
+	writel(0, susp->base + USP_TX_FIFO_OP);
+}
+
+static void sirf_usp_controller_uninit(struct sirf_usp *susp)
+{
+	/* Disable RX/TX */
+	writel(0, susp->base+USP_INT_ENABLE);
+	writel(0, susp->base + USP_TX_RX_ENABLE);
+}
+
+#ifdef CONFIG_PM
+static int sirf_usp_pcm_suspend(struct platform_device *pdev,
+	pm_message_t state)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+
+	susp->mode1_reg = readl(susp->base + USP_MODE1);
+	susp->mode2_reg = readl(susp->base + USP_MODE2);
+	sirf_usp_controller_uninit(susp);
+	clk_disable_unprepare(susp->clk);
+
+	return 0;
+}
+
+static int sirf_usp_pcm_resume(struct platform_device *pdev)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(susp->clk);
+	sirf_usp_controller_init(susp);
+
+	writel(susp->mode1_reg, susp->base + USP_MODE1);
+	writel(susp->mode2_reg, susp->base + USP_MODE2);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#endif
+
+static const struct snd_soc_component_driver sirf_usp_component = {
+	.name		= "sirf-usp",
+};
+
+static int sirf_usp_pcm_probe(struct platform_device *pdev)
+{
+	struct sirf_usp *susp;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource *mem_res;
+
+	susp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp),
+			GFP_KERNEL);
+	if (!susp)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, susp);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,usp-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,usp-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+	sirf_usp_pcm_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_usp_pcm_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	susp->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (susp->base == NULL)
+		return -ENOMEM;
+
+	susp->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(susp->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(susp->clk);
+	}
+	clk_prepare_enable(susp->clk);
+
+	sirf_usp_controller_init(susp);
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_usp_component,
+		&sirf_usp_pcm_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(susp->clk);
+	clk_put(susp->clk);
+	return ret;
+}
+
+static int sirf_usp_pcm_remove(struct platform_device *pdev)
+{
+	struct sirf_usp *susp = platform_get_drvdata(pdev);
+	snd_soc_unregister_component(&pdev->dev);
+	sirf_usp_controller_uninit(susp);
+	clk_disable_unprepare(susp->clk);
+	clk_put(susp->clk);
+
+	return 0;
+}
+
+static const struct of_device_id sirf_usp_pcm_of_match[] = {
+	{ .compatible = "sirf,prima2-usp-pcm", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match);
+
+static struct platform_driver sirf_usp_pcm_driver = {
+	.driver = {
+		.name = "sirf-usp-pcm",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_usp_pcm_of_match,
+	},
+	.probe = sirf_usp_pcm_probe,
+	.remove = sirf_usp_pcm_remove,
+	.suspend = sirf_usp_pcm_suspend,
+	.resume = sirf_usp_pcm_resume,
+};
+
+module_platform_driver(sirf_usp_pcm_driver);
+
+MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-usp.h b/sound/soc/sirf/sirf-usp.h
new file mode 100644
index 0000000..cb9085f
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.h
@@ -0,0 +1,276 @@
+/*
+ * arch/arm/mach-prima2/include/mach/sirfsoc_usp.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __SIRFSOC_USP__
+#define __SIRFSOC_USP__
+
+/* USP Registers */
+#define USP_MODE1		0x00
+#define USP_MODE2 		0x04
+#define USP_TX_FRAME_CTRL  	0x08
+#define USP_RX_FRAME_CTRL	0x0C
+#define USP_TX_RX_ENABLE	0x10
+#define USP_INT_ENABLE		0x14
+#define USP_INT_STATUS		0x18
+#define USP_PIN_IO_DATA		0x1C
+#define USP_RISC_DSP_MODE	0x20
+#define USP_AYSNC_PARAM_REG	0x24
+#define USP_IRDA_X_MODE_DIV	0x28
+#define USP_SM_CFG		0x2C
+#define USP_TX_DMA_IO_CTRL	0x100
+#define USP_TX_DMA_IO_LEN	0x104
+#define USP_TX_FIFO_CTRL	0x108
+#define USP_TX_FIFO_LEVEL_CHK	0x10C
+#define USP_TX_FIFO_OP		0x110
+#define USP_TX_FIFO_STATUS	0x114
+#define USP_TX_FIFO_DATA	0x118
+#define USP_RX_DMA_IO_CTRL	0x120
+#define USP_RX_DMA_IO_LEN	0x124
+#define USP_RX_FIFO_CTRL	0x128
+#define USP_RX_FIFO_LEVEL_CHK	0x12C
+#define USP_RX_FIFO_OP		0x130
+#define USP_RX_FIFO_STATUS	0x134
+#define USP_RX_FIFO_DATA	0x138
+
+/* USP MODE register-1 */
+#define USP_SYNC_MODE 			0x00000001
+#define USP_CLOCK_MODE_SLAVE 		0x00000002
+#define USP_LOOP_BACK_EN		0x00000004
+#define USP_HPSIR_EN			0x00000008
+#define USP_ENDIAN_CTRL_LSBF		0x00000010
+#define USP_EN				0x00000020
+#define USP_RXD_ACT_EDGE_FALLING	0x00000040
+#define USP_TXD_ACT_EDGE_FALLING	0x00000080
+#define USP_RFS_ACT_LEVEL_LOGIC1	0x00000100
+#define USP_TFS_ACT_LEVEL_LOGIC1	0x00000200
+#define USP_SCLK_IDLE_MODE_TOGGLE	0x00000400
+#define USP_SCLK_IDLE_LEVEL_LOGIC1	0x00000800
+#define USP_SCLK_PIN_MODE_IO	0x00001000
+#define USP_RFS_PIN_MODE_IO	0x00002000
+#define USP_TFS_PIN_MODE_IO	0x00004000
+#define USP_RXD_PIN_MODE_IO	0x00008000
+#define USP_TXD_PIN_MODE_IO	0x00010000
+#define USP_SCLK_IO_MODE_INPUT	0x00020000
+#define USP_RFS_IO_MODE_INPUT	0x00040000
+#define USP_TFS_IO_MODE_INPUT	0x00080000
+#define USP_RXD_IO_MODE_INPUT	0x00100000
+#define USP_TXD_IO_MODE_INPUT	0x00200000
+#define USP_IRDA_WIDTH_DIV_MASK	0x3FC00000
+#define USP_IRDA_WIDTH_DIV_OFFSET	0
+#define USP_IRDA_IDLE_LEVEL_HIGH	0x40000000
+#define USP_TX_UFLOW_REPEAT_ZERO	0x80000000
+#define USP_TX_ENDIAN_MODE		0x00000020
+#define USP_RX_ENDIAN_MODE		0x00000020
+
+/* USP Mode Register-2 */
+#define USP_RXD_DELAY_LEN_MASK		0x000000FF
+#define USP_RXD_DELAY_LEN_OFFSET	0
+
+#define USP_TXD_DELAY_LEN_MASK		0x0000FF00
+#define USP_TXD_DELAY_LEN_OFFSET	8
+
+#define USP_ENA_CTRL_MODE		0x00010000
+#define USP_FRAME_CTRL_MODE		0x00020000
+#define USP_TFS_SOURCE_MODE             0x00040000
+#define USP_TFS_MS_MODE                 0x00080000
+#define USP_CLK_DIVISOR_MASK		0x7FE00000
+#define USP_CLK_DIVISOR_OFFSET 		21
+
+#define USP_TFS_CLK_SLAVE_MODE		(1<<20)
+#define USP_RFS_CLK_SLAVE_MODE		(1<<19)
+
+#define USP_IRDA_DATA_WIDTH		0x80000000
+
+/* USP Transmit Frame Control Register */
+
+#define USP_TXC_DATA_LEN_MASK		0x000000FF
+#define USP_TXC_DATA_LEN_OFFSET		0
+
+#define USP_TXC_SYNC_LEN_MASK		0x0000FF00
+#define USP_TXC_SYNC_LEN_OFFSET		8
+
+#define USP_TXC_FRAME_LEN_MASK		0x00FF0000
+#define USP_TXC_FRAME_LEN_OFFSET 	16
+
+#define USP_TXC_SHIFTER_LEN_MASK	0x1F000000
+#define USP_TXC_SHIFTER_LEN_OFFSET	24
+
+#define USP_TXC_SLAVE_CLK_SAMPLE 	0x20000000
+
+#define USP_TXC_CLK_DIVISOR_MASK  	0xC0000000
+#define USP_TXC_CLK_DIVISOR_OFFSET 	30
+
+/* USP Receive Frame Control Register */
+
+#define USP_RXC_DATA_LEN_MASK 		0x000000FF
+#define USP_RXC_DATA_LEN_OFFSET		0
+
+#define USP_RXC_FRAME_LEN_MASK		0x0000FF00
+#define USP_RXC_FRAME_LEN_OFFSET  	8
+
+#define USP_RXC_SHIFTER_LEN_MASK 	0x001F0000
+#define USP_RXC_SHIFTER_LEN_OFFSET 	16
+
+#define USP_I2S_SYNC_CHG		0x00200000
+
+#define USP_RXC_CLK_DIVISOR_MASK  	0x0F000000
+#define USP_RXC_CLK_DIVISOR_OFFSET 	24
+#define USP_SINGLE_SYNC_MODE		0x00400000
+
+/* Tx - RX Enable Register */
+
+#define USP_RX_ENA		0x00000001
+#define USP_TX_ENA		0x00000002
+
+/* USP Interrupt Enable and status Register */
+#define USP_RX_DONE_INT			0x00000001
+#define USP_TX_DONE_INT			0x00000002
+#define USP_RX_OFLOW_INT		0x00000004
+#define USP_TX_UFLOW_INT		0x00000008
+#define USP_RX_IO_DMA_INT		0x00000010
+#define USP_TX_IO_DMA_INT		0x00000020
+#define USP_RXFIFO_FULL_INT		0x00000040
+#define USP_TXFIFO_EMPTY_INT		0x00000080
+#define USP_RXFIFO_THD_INT		0x00000100
+#define USP_TXFIFO_THD_INT		0x00000200
+#define USP_UART_FRM_ERR_INT		0x00000400
+#define USP_RX_TIMEOUT_INT 		0x00000800
+#define USP_TX_ALLOUT_INT  		0x00001000
+#define USP_RXD_BREAK_INT		0x00008000
+
+/* All possible TX interruots */
+#define USP_TX_INTERRUPT		(USP_TX_DONE_INT|USP_TX_UFLOW_INT|USP_TX_IO_DMA_INT|\
+					USP_TXFIFO_EMPTY_INT|USP_TXFIFO_THD_INT)
+/* All possible RX interruots */
+#define USP_RX_INTERRUPT		(USP_RX_DONE_INT|USP_RX_OFLOW_INT|USP_RX_IO_DMA_INT|\
+					USP_RXFIFO_FULL_INT|USP_RXFIFO_THD_INT|USP_RXFIFO_THD_INT|USP_RX_TIMEOUT_INT)
+
+#define USP_INT_ALL        0x1FFF
+
+/* USP Pin I/O Data Register */
+
+#define USP_RFS_PIN_VALUE_MASK	0x00000001
+#define USP_TFS_PIN_VALUE_MASK	0x00000002
+#define USP_RXD_PIN_VALUE_MASK	0x00000004
+#define USP_TXD_PIN_VALUE_MASK	0x00000008
+#define USP_SCLK_PIN_VALUE_MASK	0x00000010
+
+/* USP RISC/DSP Mode Register */
+#define USP_RISC_DSP_SEL	0x00000001
+
+/* USP ASYNC PARAMETER Register*/
+
+#define USP_ASYNC_TIMEOUT_MASK 	0x0000FFFF
+#define USP_ASYNC_TIMEOUT_OFFSET	0
+#define USP_ASYNC_TIMEOUT(x)	(((x)&USP_ASYNC_TIMEOUT_MASK)<<USP_ASYNC_TIMEOUT_OFFSET)
+
+#define USP_ASYNC_DIV2_MASK		0x003F0000
+#define USP_ASYNC_DIV2_OFFSET		16
+
+/* USP TX DMA I/O MODE Register */
+#define USP_TX_MODE_IO			0x00000001
+
+/* USP TX DMA I/O Length Register */
+#define USP_TX_DATA_LEN_MASK		0xFFFFFFFF
+#define USP_TX_DATA_LEN_OFFSET		0
+
+/* USP TX FIFO Control Register */
+#define USP_TX_FIFO_WIDTH_MASK		0x00000003
+#define USP_TX_FIFO_WIDTH_OFFSET	0
+
+#define USP_TX_FIFO_THD_MASK		0x000001FC
+#define USP_TX_FIFO_THD_OFFSET		2
+
+/* USP TX FIFO Level Check Register */
+#define USP_TX_FIFO_LEVEL_CHECK_MASK	0x1F
+#define USP_TX_FIFO_SC_OFFSET	0
+#define USP_TX_FIFO_LC_OFFSET	10
+#define USP_TX_FIFO_HC_OFFSET	20
+
+#define TX_FIFO_SC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)		(((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) << USP_TX_FIFO_HC_OFFSET)
+
+/* USP TX FIFO Operation Register */
+#define USP_TX_FIFO_RESET		0x00000001
+#define USP_TX_FIFO_START		0x00000002
+
+/* USP TX FIFO Status Register */
+#define USP_TX_FIFO_LEVEL_MASK		0x0000007F
+#define USP_TX_FIFO_LEVEL_OFFSET	0
+
+#define USP_TX_FIFO_FULL 		0x00000080
+#define USP_TX_FIFO_EMPTY		0x00000100
+
+/* USP TX FIFO Data Register */
+#define USP_TX_FIFO_DATA_MASK		0xFFFFFFFF
+#define USP_TX_FIFO_DATA_OFFSET		0
+
+/* USP RX DMA I/O MODE Register */
+#define USP_RX_MODE_IO			0x00000001
+#define USP_RX_DMA_FLUSH		0x00000004
+
+/* USP RX DMA I/O Length Register */
+#define USP_RX_DATA_LEN_MASK		0xFFFFFFFF
+#define USP_RX_DATA_LEN_OFFSET		0
+
+/* USP RX FIFO Control Register */
+#define USP_RX_FIFO_WIDTH_MASK		0x00000003
+#define USP_RX_FIFO_WIDTH_OFFSET	0
+
+#define USP_RX_FIFO_THD_MASK		0x000001FC
+#define USP_RX_FIFO_THD_OFFSET		2
+
+/* USP RX FIFO Level Check Register */
+
+#define USP_RX_FIFO_LEVEL_CHECK_MASK	0x1F
+#define USP_RX_FIFO_SC_OFFSET	0
+#define USP_RX_FIFO_LC_OFFSET	10
+#define USP_RX_FIFO_HC_OFFSET	20
+
+#define RX_FIFO_SC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)		(((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) << USP_RX_FIFO_HC_OFFSET)
+
+/* USP RX FIFO Operation Register */
+#define USP_RX_FIFO_RESET		0x00000001
+#define USP_RX_FIFO_START		0x00000002
+
+/* USP RX FIFO Status Register */
+
+#define USP_RX_FIFO_LEVEL_MASK		0x0000007F
+#define USP_RX_FIFO_LEVEL_OFFSET	0
+
+#define USP_RX_FIFO_FULL		0x00000080
+#define USP_RX_FIFO_EMPTY		0x00000100
+
+/* USP RX FIFO Data Register */
+
+#define USP_RX_FIFO_DATA_MASK		0xFFFFFFFF
+#define USP_RX_FIFO_DATA_OFFSET		0
+
+/*
+ * When rx thd irq occur, sender just disable tx empty irq,
+ * Remaining data in tx fifo wil also be sent out.
+ */
+#define USP_FIFO_SIZE           	128
+#define USP_TX_FIFO_THRESHOLD		(USP_FIFO_SIZE/2)
+#define USP_RX_FIFO_THRESHOLD		(USP_FIFO_SIZE/2)
+
+/* FIFO_WIDTH for the USP_TX_FIFO_CTRL and USP_RX_FIFO_CTRL registers */
+#define USP_FIFO_WIDTH_BYTE  0x00
+#define USP_FIFO_WIDTH_WORD  0x01
+#define USP_FIFO_WIDTH_DWORD 0x02
+
+#define USP_ASYNC_DIV2          16
+
+#define USP_PLUGOUT_RETRY_CNT	2
+
+#define USP_TX_RX_FIFO_WIDTH_DWORD    2
+
+#endif
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Rongjun Ying, Workgroup.Linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

there is an internal codec embedded in the SiRF SoC. this is not
a typical user scenerios of ASoC. but we can still get benefit by
sharing platform DMA codes instead of implementing a pure ALSA
driver.
This driver adds DAI drivers for this internal codec.

The features of  Internal Codec Controller include:
Support two channel 16-bit resolution playback with fix 48KHz sample rate
Support two channel 16-bit resolution record with fix 48KHz sample rate
Use dedicated Internal Codec TXFIFO and Internal Codec RXFIFO
Supports two DMA channels for Internal Codec TXFIFO and Internal Codec RXFIFO

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig          |   3 +
 sound/soc/sirf/Makefile         |   2 +
 sound/soc/sirf/sirf-soc-inner.c | 653 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 658 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 3606614..60b8857 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -6,5 +6,8 @@ config SND_SIRF_SOC
 config SND_SOC_SIRF_I2S
 	tristate
 
+config SND_SIRF_SOC_INNER
+	tristate
+
 config SND_SOC_SIRF_USP
 	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 630c9be..8517c67 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,7 +1,9 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
 snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
 obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-soc-inner.c b/sound/soc/sirf/sirf-soc-inner.c
new file mode 100644
index 0000000..96bf59b
--- /dev/null
+++ b/sound/soc/sirf/sirf-soc-inner.c
@@ -0,0 +1,653 @@
+/*
+ * SiRF inner audio codec driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.*
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc/sirfsoc_rtciobrg.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "sirf-audio.h"
+#include "sirf-pcm.h"
+
+struct sirf_soc_inner_audio_reg_bits {
+	u32 dig_mic_en_bits;
+	u32 dig_mic_freq_bits;
+	u32 adc14b_12_bits;
+	u32 firdac_hsl_en_bits;
+	u32 firdac_hsr_en_bits;
+	u32 firdac_lout_en_bits;
+	u32 por_bits;
+	u32 codec_clk_en_bits;
+	u32 hp_3db_boost_bits;
+	u32 adc_left_gain_shift;
+	u32 adc_right_gain_shift;
+	u32 adc_gain_mask;
+	u32 mic_max_gain;
+};
+
+struct sirf_soc_inner_audio {
+	void __iomem            *base;
+	unsigned int            playing;
+	struct clk              *clk;
+	spinlock_t              lock;
+	u32			sys_pwrc_reg_base;
+	struct sirf_soc_inner_audio_reg_bits *reg_bits;
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_prima2 = {
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 15, 10, 0x1f, 0x19,
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_atlas6 = {
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 16, 10, 0x3f, 0x39,
+};
+
+static int sirf_inner_control(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol,
+		int get, char *name)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	int i;
+	for (i = 0; i < card->num_controls; i++) {
+		if (!strcmp(card->controls[i].name, name)) {
+			if (card->controls[i].get && get)
+				return card->controls[i].get(kcontrol, ucontrol);
+			else if (card->controls[i].put && !get)
+				return card->controls[i].put(kcontrol, ucontrol);
+		}
+	}
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Speaker Out");
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
+
+	if (ucontrol->value.integer.value[0]) {
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+				(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+					IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					& ~(IC_SPEN | IC_SPSELR)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+
+	spin_unlock(&sinner_audio->lock);
+
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Headphone Out");
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Headphone Out");
+	if (ucontrol->value.integer.value[0])
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN | IC_HPRSELR
+				| IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	else
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR
+					| IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	spin_unlock(&sinner_audio->lock);
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_atlas6[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
+			0x3F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_prima2[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
+			0x1F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
+	u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
+	u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
+	u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+					| (1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+					(1 << sinner_audio->reg_bits->por_bits)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_HSINVEN)
+				& ~IC_MONOR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_RDACEN |
+				IC_LDACEN | IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+					(1 << sinner_audio->reg_bits->firdac_hsl_en_bits) |
+					(1 << sinner_audio->reg_bits->firdac_hsr_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		usleep_range(300, 1000);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					| IC_HSREN | IC_HSLEN),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		/* avoid break noise when sound loud, set RX gain -1dB */
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) &
+				~((IC_RXPGAR_MASK << IC_RXPGAR_SHIFT) |
+					(IC_RXPGAL_MASK << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+				((IC_RXPGAR << IC_RXPGAR_SHIFT) |
+				 (IC_RXPGAL << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+#define PWRC_PDN_CTRL_OFFSET    0
+#define AUDIO_POWER_EN_BIT      14
+		sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(
+			(sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET))
+			| (1 << AUDIO_POWER_EN_BIT),
+			sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+			(1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+			(1 << sinner_audio->reg_bits->por_bits),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
+			MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
+		usleep_range(300, 1000);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| IC_MICINREN | IC_MICINLEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_RADCEN |
+			IC_LADCEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_MICIN1SEL
+			| IC_MICDIFSEL) & (~IC_MICIN2SEL),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) &
+			~(adc_gain_mask << adc_left_gain_shift) &
+			~(adc_gain_mask << adc_right_gain_shift)) |
+			(mic_max_gain << adc_left_gain_shift) |
+			(mic_max_gain << adc_right_gain_shift),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+	return 0;
+}
+
+static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+}
+
+static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int sirf_inner_codec_trigger(struct snd_pcm_substream *substream,
+		int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL)
+				& ~IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = false;
+			writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_SPEN | IC_SPSELR | IC_HSLEN)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR | IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		} else {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL)
+				& ~IC_RX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(0, sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_INT_MSK);
+			writel(AUDIO_FIFO_START,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+			writel(IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = true;
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_SPEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN
+				| IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base
+				+ AUDIO_IC_CODEC_CTRL0);
+		} else {
+			/* unmask rx fifo interrupt */
+			writel(0, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_INT_MSK);
+
+				/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_START, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_OP);
+			/* mono capture from dacr*/
+			if (substream->runtime->channels == 1)
+				writel(0x01, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+			else
+				writel(IC_RX_ENABLE, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+struct snd_soc_dai_ops sirf_inner_codec_dai_ops = {
+	.startup = sirf_inner_codec_startup,
+	.hw_params = sirf_inner_codec_hw_params,
+	.shutdown = sirf_inner_codec_shutdown,
+	.trigger = sirf_inner_codec_trigger,
+};
+
+struct snd_soc_dai_driver sirf_inner_codec_dai = {
+	.name = "sirf-soc-inner",
+	.playback = {
+		.stream_name = "Audio Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Audio Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_inner_codec_dai_ops,
+};
+EXPORT_SYMBOL_GPL(sirf_inner_codec_dai);
+
+static int sirf_inner_codec_probe(struct snd_soc_codec *codec)
+{
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_prima2,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_prima2));
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_atlas6,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_atlas6));
+
+	return -EINVAL;
+}
+
+static int sirf_inner_codec_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static unsigned int sirf_inner_codec_reg_read(struct snd_soc_codec *codec,
+		unsigned int reg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	return readl(sinner_audio->base + reg);
+}
+
+static int sirf_inner_codec_reg_write(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int val)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	writel(val, sinner_audio->base + reg);
+	return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_device_sirf_inner_codec = {
+	.probe = sirf_inner_codec_probe,
+	.remove = sirf_inner_codec_remove,
+	.read = sirf_inner_codec_reg_read,
+	.write = sirf_inner_codec_reg_write,
+};
+
+static struct sirf_pcm_dma_data sirf_soc_inner_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	},
+};
+
+static int sirf_soc_inner_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	snd_soc_dai_set_dma_data(dai, substream,
+			&sirf_soc_inner_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static int sirf_soc_inner_dai_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct sirf_soc_inner_audio *sinner_audio =
+			snd_soc_dai_get_drvdata(dai);
+	if (playback) {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+	} else {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_soc_inner_dai_ops = {
+	.startup        = sirf_soc_inner_dai_startup,
+	.hw_params      = sirf_soc_inner_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver sirf_soc_inner_dai = {
+	.name		= "sirf-soc-inner",
+	.id			= 0,
+	.playback = {
+		.stream_name = "inner Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "inner Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_soc_inner_dai_ops,
+};
+
+static const struct snd_soc_component_driver sirf_soc_inner_component = {
+	.name		= "sirf-soc-inner",
+};
+
+static const struct of_device_id sirf_soc_inner_of_match[] = {
+	{ .compatible = "sirf,prima2-audio", .data = &sirf_soc_inner_audio_reg_bits_prima2 },
+	{ .compatible = "sirf,atlas6-audio", .data = &sirf_soc_inner_audio_reg_bits_atlas6 },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_soc_inner_of_match);
+
+static int sirf_soc_inner_probe(struct platform_device *pdev)
+{
+	int ret;
+	u32 rx_dma_ch, tx_dma_ch;
+	struct sirf_soc_inner_audio *sinner_audio;
+	struct resource *mem_res;
+	struct device_node *dn = NULL;
+	const struct of_device_id *match;
+
+	match = of_match_node(sirf_soc_inner_of_match, pdev->dev.of_node);
+
+	sinner_audio = devm_kzalloc(&pdev->dev,
+		sizeof(struct sirf_soc_inner_audio), GFP_KERNEL);
+	if (!sinner_audio)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, sinner_audio);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio capture dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio playback dma channel\n");
+		return ret;
+	}
+	sirf_soc_inner_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_soc_inner_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	dn = of_find_compatible_node(dn, NULL, "sirf,prima2-pwrc");
+	if (!dn) {
+		dev_err(&pdev->dev, "Failed to get sirf,prima2-pwrc  node!\n");
+		return -ENODEV;
+	}
+
+	ret = of_property_read_u32(dn, "reg", &sinner_audio->sys_pwrc_reg_base);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed tp get pwrc register base address\n");
+		return -EINVAL;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sinner_audio->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (sinner_audio->base == NULL)
+		return -ENOMEM;
+
+	sinner_audio->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sinner_audio->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(sinner_audio->clk);
+	}
+	clk_prepare_enable(sinner_audio->clk);
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_soc_inner_component,
+		&sirf_soc_inner_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	ret = snd_soc_register_codec(&(pdev->dev),
+			&soc_codec_device_sirf_inner_codec,
+			&sirf_inner_codec_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
+		goto err_com_unreg;
+	}
+
+	sinner_audio->reg_bits = (struct sirf_soc_inner_audio_reg_bits *)match->data;
+
+	spin_lock_init(&sinner_audio->lock);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+
+err_com_unreg:
+	snd_soc_unregister_component(&pdev->dev);
+err_clk_put:
+	clk_disable_unprepare(sinner_audio->clk);
+	return ret;
+}
+
+static int sirf_soc_inner_remove(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	snd_soc_unregister_codec(&(pdev->dev));
+	snd_soc_unregister_component(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int sirf_soc_inner_suspend(struct platform_device *pdev,
+		pm_message_t msg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	return 0;
+}
+
+static int sirf_soc_inner_resume(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(sinner_audio->clk);
+
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+}
+#else
+#define sirf_soc_inner_suspend NULL
+#define sirf_soc_inner_resume NULL
+#endif
+
+static struct platform_driver sirf_soc_inner_driver = {
+	.driver = {
+		.name = "sirf-soc-inner",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_soc_inner_of_match,
+	},
+	.probe = sirf_soc_inner_probe,
+	.remove = sirf_soc_inner_remove,
+	.suspend = sirf_soc_inner_suspend,
+	.resume = sirf_soc_inner_resume,
+};
+
+module_platform_driver(sirf_soc_inner_driver);
+
+MODULE_DESCRIPTION("SiRF SoC inner bus and codec driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

there is an internal codec embedded in the SiRF SoC. this is not
a typical user scenerios of ASoC. but we can still get benefit by
sharing platform DMA codes instead of implementing a pure ALSA
driver.
This driver adds DAI drivers for this internal codec.

The features of  Internal Codec Controller include:
Support two channel 16-bit resolution playback with fix 48KHz sample rate
Support two channel 16-bit resolution record with fix 48KHz sample rate
Use dedicated Internal Codec TXFIFO and Internal Codec RXFIFO
Supports two DMA channels for Internal Codec TXFIFO and Internal Codec RXFIFO

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig          |   3 +
 sound/soc/sirf/Makefile         |   2 +
 sound/soc/sirf/sirf-soc-inner.c | 653 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 658 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 3606614..60b8857 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -6,5 +6,8 @@ config SND_SIRF_SOC
 config SND_SOC_SIRF_I2S
 	tristate
 
+config SND_SIRF_SOC_INNER
+	tristate
+
 config SND_SOC_SIRF_USP
 	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 630c9be..8517c67 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,7 +1,9 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
 snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
 obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-soc-inner.c b/sound/soc/sirf/sirf-soc-inner.c
new file mode 100644
index 0000000..96bf59b
--- /dev/null
+++ b/sound/soc/sirf/sirf-soc-inner.c
@@ -0,0 +1,653 @@
+/*
+ * SiRF inner audio codec driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.*
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc/sirfsoc_rtciobrg.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "sirf-audio.h"
+#include "sirf-pcm.h"
+
+struct sirf_soc_inner_audio_reg_bits {
+	u32 dig_mic_en_bits;
+	u32 dig_mic_freq_bits;
+	u32 adc14b_12_bits;
+	u32 firdac_hsl_en_bits;
+	u32 firdac_hsr_en_bits;
+	u32 firdac_lout_en_bits;
+	u32 por_bits;
+	u32 codec_clk_en_bits;
+	u32 hp_3db_boost_bits;
+	u32 adc_left_gain_shift;
+	u32 adc_right_gain_shift;
+	u32 adc_gain_mask;
+	u32 mic_max_gain;
+};
+
+struct sirf_soc_inner_audio {
+	void __iomem            *base;
+	unsigned int            playing;
+	struct clk              *clk;
+	spinlock_t              lock;
+	u32			sys_pwrc_reg_base;
+	struct sirf_soc_inner_audio_reg_bits *reg_bits;
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_prima2 = {
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 15, 10, 0x1f, 0x19,
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_atlas6 = {
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 16, 10, 0x3f, 0x39,
+};
+
+static int sirf_inner_control(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol,
+		int get, char *name)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	int i;
+	for (i = 0; i < card->num_controls; i++) {
+		if (!strcmp(card->controls[i].name, name)) {
+			if (card->controls[i].get && get)
+				return card->controls[i].get(kcontrol, ucontrol);
+			else if (card->controls[i].put && !get)
+				return card->controls[i].put(kcontrol, ucontrol);
+		}
+	}
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Speaker Out");
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
+
+	if (ucontrol->value.integer.value[0]) {
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+				(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+					IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					& ~(IC_SPEN | IC_SPSELR)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+
+	spin_unlock(&sinner_audio->lock);
+
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Headphone Out");
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Headphone Out");
+	if (ucontrol->value.integer.value[0])
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN | IC_HPRSELR
+				| IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	else
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR
+					| IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	spin_unlock(&sinner_audio->lock);
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_atlas6[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
+			0x3F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_prima2[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
+			0x1F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
+	u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
+	u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
+	u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+					| (1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+					(1 << sinner_audio->reg_bits->por_bits)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_HSINVEN)
+				& ~IC_MONOR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_RDACEN |
+				IC_LDACEN | IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+					(1 << sinner_audio->reg_bits->firdac_hsl_en_bits) |
+					(1 << sinner_audio->reg_bits->firdac_hsr_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		usleep_range(300, 1000);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					| IC_HSREN | IC_HSLEN),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		/* avoid break noise when sound loud, set RX gain -1dB */
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) &
+				~((IC_RXPGAR_MASK << IC_RXPGAR_SHIFT) |
+					(IC_RXPGAL_MASK << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+				((IC_RXPGAR << IC_RXPGAR_SHIFT) |
+				 (IC_RXPGAL << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+#define PWRC_PDN_CTRL_OFFSET    0
+#define AUDIO_POWER_EN_BIT      14
+		sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(
+			(sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET))
+			| (1 << AUDIO_POWER_EN_BIT),
+			sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+			(1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+			(1 << sinner_audio->reg_bits->por_bits),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
+			MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
+		usleep_range(300, 1000);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| IC_MICINREN | IC_MICINLEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_RADCEN |
+			IC_LADCEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_MICIN1SEL
+			| IC_MICDIFSEL) & (~IC_MICIN2SEL),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) &
+			~(adc_gain_mask << adc_left_gain_shift) &
+			~(adc_gain_mask << adc_right_gain_shift)) |
+			(mic_max_gain << adc_left_gain_shift) |
+			(mic_max_gain << adc_right_gain_shift),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+	return 0;
+}
+
+static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+}
+
+static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int sirf_inner_codec_trigger(struct snd_pcm_substream *substream,
+		int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL)
+				& ~IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = false;
+			writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_SPEN | IC_SPSELR | IC_HSLEN)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR | IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		} else {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL)
+				& ~IC_RX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(0, sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_INT_MSK);
+			writel(AUDIO_FIFO_START,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+			writel(IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = true;
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_SPEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN
+				| IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base
+				+ AUDIO_IC_CODEC_CTRL0);
+		} else {
+			/* unmask rx fifo interrupt */
+			writel(0, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_INT_MSK);
+
+				/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_START, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_OP);
+			/* mono capture from dacr*/
+			if (substream->runtime->channels == 1)
+				writel(0x01, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+			else
+				writel(IC_RX_ENABLE, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+struct snd_soc_dai_ops sirf_inner_codec_dai_ops = {
+	.startup = sirf_inner_codec_startup,
+	.hw_params = sirf_inner_codec_hw_params,
+	.shutdown = sirf_inner_codec_shutdown,
+	.trigger = sirf_inner_codec_trigger,
+};
+
+struct snd_soc_dai_driver sirf_inner_codec_dai = {
+	.name = "sirf-soc-inner",
+	.playback = {
+		.stream_name = "Audio Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Audio Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_inner_codec_dai_ops,
+};
+EXPORT_SYMBOL_GPL(sirf_inner_codec_dai);
+
+static int sirf_inner_codec_probe(struct snd_soc_codec *codec)
+{
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_prima2,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_prima2));
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_atlas6,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_atlas6));
+
+	return -EINVAL;
+}
+
+static int sirf_inner_codec_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static unsigned int sirf_inner_codec_reg_read(struct snd_soc_codec *codec,
+		unsigned int reg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	return readl(sinner_audio->base + reg);
+}
+
+static int sirf_inner_codec_reg_write(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int val)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	writel(val, sinner_audio->base + reg);
+	return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_device_sirf_inner_codec = {
+	.probe = sirf_inner_codec_probe,
+	.remove = sirf_inner_codec_remove,
+	.read = sirf_inner_codec_reg_read,
+	.write = sirf_inner_codec_reg_write,
+};
+
+static struct sirf_pcm_dma_data sirf_soc_inner_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	},
+};
+
+static int sirf_soc_inner_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	snd_soc_dai_set_dma_data(dai, substream,
+			&sirf_soc_inner_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static int sirf_soc_inner_dai_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct sirf_soc_inner_audio *sinner_audio =
+			snd_soc_dai_get_drvdata(dai);
+	if (playback) {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+	} else {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_soc_inner_dai_ops = {
+	.startup        = sirf_soc_inner_dai_startup,
+	.hw_params      = sirf_soc_inner_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver sirf_soc_inner_dai = {
+	.name		= "sirf-soc-inner",
+	.id			= 0,
+	.playback = {
+		.stream_name = "inner Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "inner Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_soc_inner_dai_ops,
+};
+
+static const struct snd_soc_component_driver sirf_soc_inner_component = {
+	.name		= "sirf-soc-inner",
+};
+
+static const struct of_device_id sirf_soc_inner_of_match[] = {
+	{ .compatible = "sirf,prima2-audio", .data = &sirf_soc_inner_audio_reg_bits_prima2 },
+	{ .compatible = "sirf,atlas6-audio", .data = &sirf_soc_inner_audio_reg_bits_atlas6 },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_soc_inner_of_match);
+
+static int sirf_soc_inner_probe(struct platform_device *pdev)
+{
+	int ret;
+	u32 rx_dma_ch, tx_dma_ch;
+	struct sirf_soc_inner_audio *sinner_audio;
+	struct resource *mem_res;
+	struct device_node *dn = NULL;
+	const struct of_device_id *match;
+
+	match = of_match_node(sirf_soc_inner_of_match, pdev->dev.of_node);
+
+	sinner_audio = devm_kzalloc(&pdev->dev,
+		sizeof(struct sirf_soc_inner_audio), GFP_KERNEL);
+	if (!sinner_audio)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, sinner_audio);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio capture dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio playback dma channel\n");
+		return ret;
+	}
+	sirf_soc_inner_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_soc_inner_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	dn = of_find_compatible_node(dn, NULL, "sirf,prima2-pwrc");
+	if (!dn) {
+		dev_err(&pdev->dev, "Failed to get sirf,prima2-pwrc  node!\n");
+		return -ENODEV;
+	}
+
+	ret = of_property_read_u32(dn, "reg", &sinner_audio->sys_pwrc_reg_base);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed tp get pwrc register base address\n");
+		return -EINVAL;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sinner_audio->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (sinner_audio->base == NULL)
+		return -ENOMEM;
+
+	sinner_audio->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sinner_audio->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(sinner_audio->clk);
+	}
+	clk_prepare_enable(sinner_audio->clk);
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_soc_inner_component,
+		&sirf_soc_inner_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	ret = snd_soc_register_codec(&(pdev->dev),
+			&soc_codec_device_sirf_inner_codec,
+			&sirf_inner_codec_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
+		goto err_com_unreg;
+	}
+
+	sinner_audio->reg_bits = (struct sirf_soc_inner_audio_reg_bits *)match->data;
+
+	spin_lock_init(&sinner_audio->lock);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+
+err_com_unreg:
+	snd_soc_unregister_component(&pdev->dev);
+err_clk_put:
+	clk_disable_unprepare(sinner_audio->clk);
+	return ret;
+}
+
+static int sirf_soc_inner_remove(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	snd_soc_unregister_codec(&(pdev->dev));
+	snd_soc_unregister_component(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int sirf_soc_inner_suspend(struct platform_device *pdev,
+		pm_message_t msg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	return 0;
+}
+
+static int sirf_soc_inner_resume(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(sinner_audio->clk);
+
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+}
+#else
+#define sirf_soc_inner_suspend NULL
+#define sirf_soc_inner_resume NULL
+#endif
+
+static struct platform_driver sirf_soc_inner_driver = {
+	.driver = {
+		.name = "sirf-soc-inner",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_soc_inner_of_match,
+	},
+	.probe = sirf_soc_inner_probe,
+	.remove = sirf_soc_inner_remove,
+	.suspend = sirf_soc_inner_suspend,
+	.resume = sirf_soc_inner_resume,
+};
+
+module_platform_driver(sirf_soc_inner_driver);
+
+MODULE_DESCRIPTION("SiRF SoC inner bus and codec driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Rongjun Ying, Workgroup.Linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

This connects DMA, CPU DAI and Codec DAI together and works
as a mach driver.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig      |   7 +-
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-inner.c | 267 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/sirf/sirf-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 60b8857..ce3c025 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,7 +2,7 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_DMAENGINE_PCM
-
+	
 config SND_SOC_SIRF_I2S
 	tristate
 
@@ -11,3 +11,8 @@ config SND_SIRF_SOC_INNER
 
 config SND_SOC_SIRF_USP
 	tristate
+
+config SND_SIRF_INNER
+	tristate "SoC Audio support for SiRF inner codec of SiRF EVB"
+	depends on SND_SIRF_SOC
+	select SND_SIRF_SOC_INNER
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 8517c67..80abdf6 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,9 +1,11 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-inner-objs := sirf-inner.o
 snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
 snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_INNER) += snd-soc-sirf-inner.o
 obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
 obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-inner.c b/sound/soc/sirf/sirf-inner.c
new file mode 100644
index 0000000..a8f2765
--- /dev/null
+++ b/sound/soc/sirf/sirf-inner.c
@@ -0,0 +1,267 @@
+/*
+ * SiRF inner audio device driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+struct sirf_inner_card {
+	unsigned int            gpio_hp_pa;
+	unsigned int            gpio_spk_pa;
+	/*
+	 * Android platform uses switch gpio instead of jack.
+	 */
+#ifndef CONFIG_ANDROID
+	unsigned int            gpio_hp_detect;
+	struct snd_soc_jack     hp_jack;
+#endif
+};
+
+#ifndef CONFIG_ANDROID
+static int sirf_inner_jack_status_check(void);
+
+static struct snd_soc_jack_gpio hp_jack_gpios[] = {
+	{
+		.name = "hpdet-gpio",
+		.report = SND_JACK_HEADPHONE,
+		.debounce_time = 200,
+		.jack_status_check = sirf_inner_jack_status_check,
+	},
+};
+
+static int sirf_inner_jack_status_check(void)
+{
+	int spk_out = 0;
+	struct snd_soc_codec *codec = hp_jack_gpios[0].jack->codec;
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+	int hp_report = 0;
+
+	if (gpio_is_valid(sinner_card->gpio_hp_detect))
+		spk_out = gpio_get_value(sinner_card->gpio_hp_detect);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, !spk_out);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, spk_out);
+
+	if (!spk_out)
+		hp_report |= SND_JACK_HEADPHONE;
+
+	return hp_report;
+}
+
+static int sirf_inner_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+	int ret;
+	hp_jack_gpios[0].gpio = sinner_card->gpio_hp_detect;
+	ret = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+			&sinner_card->hp_jack);
+	if (ret)
+		return ret;
+	return snd_soc_jack_add_gpios(&sinner_card->hp_jack,
+			ARRAY_SIZE(hp_jack_gpios),
+			hp_jack_gpios);
+}
+#endif
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sirf_inner_dai_links[] = {
+	{
+		.name = "SiRF inner",
+		.stream_name = "SiRF inner",
+		.codec_dai_name = "sirf-soc-inner",
+#ifndef CONFIG_ANDROID
+		.init = sirf_inner_init,
+#endif
+	},
+};
+
+static int sirf_inner_headphone_out_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int hp_out = 0;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		hp_out = gpio_get_value(sinner_card->gpio_hp_pa);
+
+	*ucontrol->value.integer.value = hp_out;
+	return 0;
+}
+
+static int sirf_inner_headphone_out_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int is_hp_out = ucontrol->value.integer.value[0];
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, is_hp_out);
+
+	return 0;
+}
+
+static int sirf_inner_speaker_out_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int spk_out = 0;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		spk_out = gpio_get_value(sinner_card->gpio_spk_pa);
+
+	*ucontrol->value.integer.value = spk_out;
+	return 0;
+}
+
+static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int is_spk_out = ucontrol->value.integer.value[0];
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
+	return 0;
+}
+
+static int sirf_inner_speaker_out_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	return 0;
+}
+
+static int sirf_inner_headphone_out_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_sirf_inner_out_route_controls[] = {
+	{
+		.iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name           =       "Speaker Out",
+		.index          =       0,
+		.access         =       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info           =	sirf_inner_speaker_out_info,
+		.get            =       sirf_inner_speaker_out_get,
+		.put            =       sirf_inner_speaker_out_put,
+	}, {
+		.iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name           =       "Headphone Out",
+		.index          =       0,
+		.access         =       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info           =	sirf_inner_headphone_out_info,
+		.get            =       sirf_inner_headphone_out_get,
+		.put            =       sirf_inner_headphone_out_put,
+	},
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_sirf_inner_card = {
+	.name = "SiRF inner",
+	.owner = THIS_MODULE,
+	.dai_link = sirf_inner_dai_links,
+	.num_links = ARRAY_SIZE(sirf_inner_dai_links),
+	.controls = snd_sirf_inner_out_route_controls,
+	.num_controls = ARRAY_SIZE(snd_sirf_inner_out_route_controls),
+};
+
+static int sirf_inner_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_sirf_inner_card;
+	struct sirf_inner_card *sinner_card;
+	sinner_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_inner_card),
+			GFP_KERNEL);
+	if (sinner_card == NULL)
+		return -ENOMEM;
+
+	sirf_inner_dai_links[0].platform_of_node =
+		of_find_compatible_node(NULL, NULL, "sirf,pcm-audio");
+	sirf_inner_dai_links[0].cpu_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,inner-platform", 0);
+	sirf_inner_dai_links[0].codec_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,inner-codec", 0);
+	sinner_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node,
+			"spk-pa-gpios", 0);
+	sinner_card->gpio_hp_pa =  of_get_named_gpio(pdev->dev.of_node,
+			"hp-pa-gpios", 0);
+#ifndef CONFIG_ANDROID
+	sinner_card->gpio_hp_detect = of_get_named_gpio(pdev->dev.of_node,
+			"hp-switch-gpios", 0);
+#endif
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_request(sinner_card->gpio_spk_pa, "SPA_PA_SD");
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_request(sinner_card->gpio_hp_pa, "HP_PA_SD");
+
+	card->dev = &pdev->dev;
+	snd_soc_card_set_drvdata(card, sinner_card);
+	platform_set_drvdata(pdev, card);
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, 0);
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, 0);
+
+	return snd_soc_register_card(card);
+}
+
+static int sirf_inner_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_free(sinner_card->gpio_hp_pa);
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_free(sinner_card->gpio_spk_pa);
+
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static const struct of_device_id sirf_inner_of_match[] = {
+	{.compatible = "sirf,sirf-inner-audio", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sirf_inner_of_match);
+
+static struct platform_driver sirf_inner_driver = {
+	.driver = {
+		.name = "sirf-inner-audio",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_inner_of_match,
+	},
+	.probe = sirf_inner_probe,
+	.remove = sirf_inner_remove,
+};
+module_platform_driver(sirf_inner_driver);
+
+MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>");
+MODULE_DESCRIPTION("ALSA SoC SIRF inner AUDIO driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

This connects DMA, CPU DAI and Codec DAI together and works
as a mach driver.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 sound/soc/sirf/Kconfig      |   7 +-
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-inner.c | 267 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/sirf/sirf-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 60b8857..ce3c025 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,7 +2,7 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_DMAENGINE_PCM
-
+	
 config SND_SOC_SIRF_I2S
 	tristate
 
@@ -11,3 +11,8 @@ config SND_SIRF_SOC_INNER
 
 config SND_SOC_SIRF_USP
 	tristate
+
+config SND_SIRF_INNER
+	tristate "SoC Audio support for SiRF inner codec of SiRF EVB"
+	depends on SND_SIRF_SOC
+	select SND_SIRF_SOC_INNER
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 8517c67..80abdf6 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,9 +1,11 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-inner-objs := sirf-inner.o
 snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
 snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_INNER) += snd-soc-sirf-inner.o
 obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
 obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-inner.c b/sound/soc/sirf/sirf-inner.c
new file mode 100644
index 0000000..a8f2765
--- /dev/null
+++ b/sound/soc/sirf/sirf-inner.c
@@ -0,0 +1,267 @@
+/*
+ * SiRF inner audio device driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+struct sirf_inner_card {
+	unsigned int            gpio_hp_pa;
+	unsigned int            gpio_spk_pa;
+	/*
+	 * Android platform uses switch gpio instead of jack.
+	 */
+#ifndef CONFIG_ANDROID
+	unsigned int            gpio_hp_detect;
+	struct snd_soc_jack     hp_jack;
+#endif
+};
+
+#ifndef CONFIG_ANDROID
+static int sirf_inner_jack_status_check(void);
+
+static struct snd_soc_jack_gpio hp_jack_gpios[] = {
+	{
+		.name = "hpdet-gpio",
+		.report = SND_JACK_HEADPHONE,
+		.debounce_time = 200,
+		.jack_status_check = sirf_inner_jack_status_check,
+	},
+};
+
+static int sirf_inner_jack_status_check(void)
+{
+	int spk_out = 0;
+	struct snd_soc_codec *codec = hp_jack_gpios[0].jack->codec;
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+	int hp_report = 0;
+
+	if (gpio_is_valid(sinner_card->gpio_hp_detect))
+		spk_out = gpio_get_value(sinner_card->gpio_hp_detect);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, !spk_out);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, spk_out);
+
+	if (!spk_out)
+		hp_report |= SND_JACK_HEADPHONE;
+
+	return hp_report;
+}
+
+static int sirf_inner_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+	int ret;
+	hp_jack_gpios[0].gpio = sinner_card->gpio_hp_detect;
+	ret = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+			&sinner_card->hp_jack);
+	if (ret)
+		return ret;
+	return snd_soc_jack_add_gpios(&sinner_card->hp_jack,
+			ARRAY_SIZE(hp_jack_gpios),
+			hp_jack_gpios);
+}
+#endif
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sirf_inner_dai_links[] = {
+	{
+		.name = "SiRF inner",
+		.stream_name = "SiRF inner",
+		.codec_dai_name = "sirf-soc-inner",
+#ifndef CONFIG_ANDROID
+		.init = sirf_inner_init,
+#endif
+	},
+};
+
+static int sirf_inner_headphone_out_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int hp_out = 0;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		hp_out = gpio_get_value(sinner_card->gpio_hp_pa);
+
+	*ucontrol->value.integer.value = hp_out;
+	return 0;
+}
+
+static int sirf_inner_headphone_out_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int is_hp_out = ucontrol->value.integer.value[0];
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, is_hp_out);
+
+	return 0;
+}
+
+static int sirf_inner_speaker_out_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int spk_out = 0;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		spk_out = gpio_get_value(sinner_card->gpio_spk_pa);
+
+	*ucontrol->value.integer.value = spk_out;
+	return 0;
+}
+
+static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int is_spk_out = ucontrol->value.integer.value[0];
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
+	return 0;
+}
+
+static int sirf_inner_speaker_out_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	return 0;
+}
+
+static int sirf_inner_headphone_out_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_sirf_inner_out_route_controls[] = {
+	{
+		.iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name           =       "Speaker Out",
+		.index          =       0,
+		.access         =       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info           =	sirf_inner_speaker_out_info,
+		.get            =       sirf_inner_speaker_out_get,
+		.put            =       sirf_inner_speaker_out_put,
+	}, {
+		.iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name           =       "Headphone Out",
+		.index          =       0,
+		.access         =       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info           =	sirf_inner_headphone_out_info,
+		.get            =       sirf_inner_headphone_out_get,
+		.put            =       sirf_inner_headphone_out_put,
+	},
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_sirf_inner_card = {
+	.name = "SiRF inner",
+	.owner = THIS_MODULE,
+	.dai_link = sirf_inner_dai_links,
+	.num_links = ARRAY_SIZE(sirf_inner_dai_links),
+	.controls = snd_sirf_inner_out_route_controls,
+	.num_controls = ARRAY_SIZE(snd_sirf_inner_out_route_controls),
+};
+
+static int sirf_inner_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_sirf_inner_card;
+	struct sirf_inner_card *sinner_card;
+	sinner_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_inner_card),
+			GFP_KERNEL);
+	if (sinner_card == NULL)
+		return -ENOMEM;
+
+	sirf_inner_dai_links[0].platform_of_node =
+		of_find_compatible_node(NULL, NULL, "sirf,pcm-audio");
+	sirf_inner_dai_links[0].cpu_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,inner-platform", 0);
+	sirf_inner_dai_links[0].codec_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,inner-codec", 0);
+	sinner_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node,
+			"spk-pa-gpios", 0);
+	sinner_card->gpio_hp_pa =  of_get_named_gpio(pdev->dev.of_node,
+			"hp-pa-gpios", 0);
+#ifndef CONFIG_ANDROID
+	sinner_card->gpio_hp_detect = of_get_named_gpio(pdev->dev.of_node,
+			"hp-switch-gpios", 0);
+#endif
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_request(sinner_card->gpio_spk_pa, "SPA_PA_SD");
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_request(sinner_card->gpio_hp_pa, "HP_PA_SD");
+
+	card->dev = &pdev->dev;
+	snd_soc_card_set_drvdata(card, sinner_card);
+	platform_set_drvdata(pdev, card);
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_direction_output(sinner_card->gpio_hp_pa, 0);
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_direction_output(sinner_card->gpio_spk_pa, 0);
+
+	return snd_soc_register_card(card);
+}
+
+static int sirf_inner_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(sinner_card->gpio_hp_pa))
+		gpio_free(sinner_card->gpio_hp_pa);
+	if (gpio_is_valid(sinner_card->gpio_spk_pa))
+		gpio_free(sinner_card->gpio_spk_pa);
+
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static const struct of_device_id sirf_inner_of_match[] = {
+	{.compatible = "sirf,sirf-inner-audio", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sirf_inner_of_match);
+
+static struct platform_driver sirf_inner_driver = {
+	.driver = {
+		.name = "sirf-inner-audio",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_inner_of_match,
+	},
+	.probe = sirf_inner_probe,
+	.remove = sirf_inner_remove,
+};
+module_platform_driver(sirf_inner_driver);
+
+MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>");
+MODULE_DESCRIPTION("ALSA SoC SIRF inner AUDIO driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 6/6] arm: prima2: defconfig: enable sound components
  2013-07-19 11:07 ` Barry Song
@ 2013-07-19 11:07   ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: broonie, lgirdwood, alsa-devel
  Cc: Workgroup.Linux, linux-arm-kernel, Barry Song

Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 arch/arm/configs/prima2_defconfig | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig
index 002a1ce..07a87c8 100644
--- a/arch/arm/configs/prima2_defconfig
+++ b/arch/arm/configs/prima2_defconfig
@@ -1,4 +1,3 @@
-CONFIG_EXPERIMENTAL=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_RELAY=y
@@ -39,6 +38,11 @@ CONFIG_SPI=y
 CONFIG_SPI_SIRF=y
 CONFIG_SPI_SPIDEV=y
 # CONFIG_HWMON is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SIRF_SOC=y
+CONFIG_SND_SIRF_INNER=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_MASS_STORAGE=m
 CONFIG_MMC=y
@@ -59,12 +63,12 @@ CONFIG_ROMFS_FS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=y
-CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
 CONFIG_DEBUG_KERNEL=y
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_RT_MUTEXES=y
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_INFO=y
 CONFIG_CRC_CCITT=y
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 6/6] arm: prima2: defconfig: enable sound components
@ 2013-07-19 11:07   ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-19 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 arch/arm/configs/prima2_defconfig | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig
index 002a1ce..07a87c8 100644
--- a/arch/arm/configs/prima2_defconfig
+++ b/arch/arm/configs/prima2_defconfig
@@ -1,4 +1,3 @@
-CONFIG_EXPERIMENTAL=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_RELAY=y
@@ -39,6 +38,11 @@ CONFIG_SPI=y
 CONFIG_SPI_SIRF=y
 CONFIG_SPI_SPIDEV=y
 # CONFIG_HWMON is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SIRF_SOC=y
+CONFIG_SND_SIRF_INNER=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_MASS_STORAGE=m
 CONFIG_MMC=y
@@ -59,12 +63,12 @@ CONFIG_ROMFS_FS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=y
-CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
 CONFIG_DEBUG_KERNEL=y
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_RT_MUTEXES=y
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_INFO=y
 CONFIG_CRC_CCITT=y
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 15:08     ` Lars-Peter Clausen
  -1 siblings, 0 replies; 38+ messages in thread
From: Lars-Peter Clausen @ 2013-07-19 15:08 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, broonie,
	Barry Song, linux-arm-kernel

On 07/19/2013 01:07 PM, Barry Song wrote:
> From: Rongjun Ying <Rongjun.Ying@csr.com>
> 
> this driver uses dmaengine APIs and provides DMA to the CPU DAIs
> of I2S, USP and SiRF-soc-inner.
> SiRFSoC has 3 audio DAIs: I2S, USP(Universal Serial Ports) and DAI
> connected to soc-inner-codec, all of them will use the same DMA
> driver here.
> 

I think the bulk of the code here can be replaced by using the generic
dmaengine PCM driver. Have a look at e.g. mxs to see how that is done.

[...]
> +
> +static int sirf_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct sirf_pcm_dma_data *dma_data;
> +	substream->runtime->hw = sirf_pcm_hardware;
> +	snd_soc_set_runtime_hwparams(substream, &sirf_pcm_hardware);
> +
> +	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
> +
> +	return snd_dmaengine_pcm_open_request_chan(substream,
> +			(dma_filter_fn)sirfsoc_dma_filter_id,
> +			(void *)(dma_data->dma_req));

Since you are using devicetree on this platform use the devicetree to get a
handle to the DMA channel.

> +}
> +
> +static int sirf_pcm_hw_params(struct snd_pcm_substream *substream,
> +		struct snd_pcm_hw_params *params)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct sirf_pcm_dma_data *dma_data;
> +	struct dma_slave_config config;
> +	struct dma_chan *chan;
> +	int err = 0;
> +
> +	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> +	runtime->dma_bytes = params_buffer_bytes(params);
> +
> +	chan = snd_dmaengine_pcm_get_chan(substream);
> +	if (!chan)
> +		return -EINVAL;
> +
> +	/* fills in addr_width and direction */
> +	err = snd_hwparams_to_dma_slave_config(substream, params, &config);
> +	if (err)
> +		return err;
> +
> +	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +
> +	config.src_addr = runtime->dma_addr;
> +	config.dst_addr = runtime->dma_addr;
> +	config.src_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +	config.dst_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +
> +	return dmaengine_slave_config(chan, &config);


Nothing special in here, just use the generic
snd_dmaengine_pcm_prepare_slave_config().

> +}
> +
[...]

static const struct of_device_id sirf_pcm_of_match[] = {
> +	{ .compatible = "sirf,pcm-audio", },
> +	{}
> +};

Since this is not a separate piece of hardware, but just the glue logic
between the DMA controller and the audio DAI it should not have it's own
devicetree node. Usually the the PCM device is registered by the DAI driver.
Again take at other platforms using the generic dmaengine PCM driver for
examples.

> +MODULE_DEVICE_TABLE(of, sirf_pcm_of_match);
> +
> +static struct platform_driver sirf_pcm_driver = {
> +	.driver = {
> +		.name = "sirf-pcm-audio",
> +		.owner = THIS_MODULE,
> +		.of_match_table = sirf_pcm_of_match,
> +	},
> +	.probe = sirf_pcm_probe,
> +	.remove = sirf_pcm_remove,
> +};
> +module_platform_driver(sirf_pcm_driver);
[...]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [alsa-devel] [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
@ 2013-07-19 15:08     ` Lars-Peter Clausen
  0 siblings, 0 replies; 38+ messages in thread
From: Lars-Peter Clausen @ 2013-07-19 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/19/2013 01:07 PM, Barry Song wrote:
> From: Rongjun Ying <Rongjun.Ying@csr.com>
> 
> this driver uses dmaengine APIs and provides DMA to the CPU DAIs
> of I2S, USP and SiRF-soc-inner.
> SiRFSoC has 3 audio DAIs: I2S, USP(Universal Serial Ports) and DAI
> connected to soc-inner-codec, all of them will use the same DMA
> driver here.
> 

I think the bulk of the code here can be replaced by using the generic
dmaengine PCM driver. Have a look at e.g. mxs to see how that is done.

[...]
> +
> +static int sirf_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct sirf_pcm_dma_data *dma_data;
> +	substream->runtime->hw = sirf_pcm_hardware;
> +	snd_soc_set_runtime_hwparams(substream, &sirf_pcm_hardware);
> +
> +	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
> +
> +	return snd_dmaengine_pcm_open_request_chan(substream,
> +			(dma_filter_fn)sirfsoc_dma_filter_id,
> +			(void *)(dma_data->dma_req));

Since you are using devicetree on this platform use the devicetree to get a
handle to the DMA channel.

> +}
> +
> +static int sirf_pcm_hw_params(struct snd_pcm_substream *substream,
> +		struct snd_pcm_hw_params *params)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct sirf_pcm_dma_data *dma_data;
> +	struct dma_slave_config config;
> +	struct dma_chan *chan;
> +	int err = 0;
> +
> +	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> +	runtime->dma_bytes = params_buffer_bytes(params);
> +
> +	chan = snd_dmaengine_pcm_get_chan(substream);
> +	if (!chan)
> +		return -EINVAL;
> +
> +	/* fills in addr_width and direction */
> +	err = snd_hwparams_to_dma_slave_config(substream, params, &config);
> +	if (err)
> +		return err;
> +
> +	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +
> +	config.src_addr = runtime->dma_addr;
> +	config.dst_addr = runtime->dma_addr;
> +	config.src_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +	config.dst_maxburst = DMA_SLAVE_BUSWIDTH_8_BYTES;
> +
> +	return dmaengine_slave_config(chan, &config);


Nothing special in here, just use the generic
snd_dmaengine_pcm_prepare_slave_config().

> +}
> +
[...]

static const struct of_device_id sirf_pcm_of_match[] = {
> +	{ .compatible = "sirf,pcm-audio", },
> +	{}
> +};

Since this is not a separate piece of hardware, but just the glue logic
between the DMA controller and the audio DAI it should not have it's own
devicetree node. Usually the the PCM device is registered by the DAI driver.
Again take at other platforms using the generic dmaengine PCM driver for
examples.

> +MODULE_DEVICE_TABLE(of, sirf_pcm_of_match);
> +
> +static struct platform_driver sirf_pcm_driver = {
> +	.driver = {
> +		.name = "sirf-pcm-audio",
> +		.owner = THIS_MODULE,
> +		.of_match_table = sirf_pcm_of_match,
> +	},
> +	.probe = sirf_pcm_probe,
> +	.remove = sirf_pcm_remove,
> +};
> +module_platform_driver(sirf_pcm_driver);
[...]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 16:12     ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 16:12 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 398 bytes --]

On Fri, Jul 19, 2013 at 07:07:17PM +0800, Barry Song wrote:

> index 0000000..3678aed
> --- /dev/null
> +++ b/sound/soc/sirf/Kconfig
> @@ -0,0 +1,4 @@
> +config SND_SIRF_SOC
> +	tristate "Platform DMA driver for the SiRF SoC chips"
> +	depends on ARCH_SIRF && SND_SOC
> +	select SND_SOC_DMAENGINE_PCM

Does this have any actual platform build dependencies or can this be
ARCH_SIRF || COMPILE_TEST?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
@ 2013-07-19 16:12     ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 16:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 19, 2013 at 07:07:17PM +0800, Barry Song wrote:

> index 0000000..3678aed
> --- /dev/null
> +++ b/sound/soc/sirf/Kconfig
> @@ -0,0 +1,4 @@
> +config SND_SIRF_SOC
> +	tristate "Platform DMA driver for the SiRF SoC chips"
> +	depends on ARCH_SIRF && SND_SOC
> +	select SND_SOC_DMAENGINE_PCM

Does this have any actual platform build dependencies or can this be
ARCH_SIRF || COMPILE_TEST?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130719/9274cadb/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 16:52     ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 16:52 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 2919 bytes --]

On Fri, Jul 19, 2013 at 07:07:18PM +0800, Barry Song wrote:

Pretty good overall - there's a few things below but should all be
fairly small.

> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +
> +	if (si2s->master_mode)
> +		pwm_enable(si2s->mclk_pwm);
> +	clk_prepare_enable(si2s->clk);
> +
> +	device_reset(dai->dev);
> +	snd_soc_dai_set_dma_data(dai, substream,
> +		&sirf_i2s_dai_dma_data[substream->stream]);
> +	return 0;
> +}

device_reset() sounds like it's not going to work for simultaneous
playback and capture.

> +static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
> +		int cmd, struct snd_soc_dai *dai)
> +{
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		spin_lock(&si2s->lock);
> +
> +		if (playback) {
> +			/* First start the FIFO, then enable the tx/rx */
> +			writel(AUDIO_FIFO_RESET,
> +				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
> +			mdelay(1);
> +			writel(AUDIO_FIFO_START,
> +				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
> +			mdelay(1);

Do we really need these 1ms delays?  They're very long for the
context...

> +static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
> +			struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
> +
> +	if (runtime->channels == 2)
> +		ctrl &= ~I2S_SIX_CHANNELS;
> +	else
> +		ctrl |= I2S_SIX_CHANNELS;

A switch statement for the number of channels would make me happier
here.

> +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
> +		ctrl |= I2S_SLAVE_MODE;
> +		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
> +		si2s->master_mode = 0;
> +		break;

Do we need locking on all these register updates?

> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		si2s->master_mode = 1;
> +		return -EINVAL;

Should this be undoing the work done for _CBM_CFM?

> +#ifdef CONFIG_PM
> +static int sirf_i2s_suspend(struct platform_device *pdev,
> +		pm_message_t state)
> +{
> +	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
> +
> +	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
> +
> +	clk_disable_unprepare(si2s->clk);
> +
> +	if (si2s->master_mode)
> +		pwm_disable(si2s->mclk_pwm);
> +	return 0;
> +}

Should these be runtime PM calls as well?  Seems like the bus could be
unclocked whenever audio is not playing.  If you need the clock to write
conifguraiton perhaps regmap-mmio could help?

> +	spin_lock_init(&si2s->lock);

What is this lock actually protecting?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-07-19 16:52     ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 19, 2013 at 07:07:18PM +0800, Barry Song wrote:

Pretty good overall - there's a few things below but should all be
fairly small.

> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +
> +	if (si2s->master_mode)
> +		pwm_enable(si2s->mclk_pwm);
> +	clk_prepare_enable(si2s->clk);
> +
> +	device_reset(dai->dev);
> +	snd_soc_dai_set_dma_data(dai, substream,
> +		&sirf_i2s_dai_dma_data[substream->stream]);
> +	return 0;
> +}

device_reset() sounds like it's not going to work for simultaneous
playback and capture.

> +static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
> +		int cmd, struct snd_soc_dai *dai)
> +{
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		spin_lock(&si2s->lock);
> +
> +		if (playback) {
> +			/* First start the FIFO, then enable the tx/rx */
> +			writel(AUDIO_FIFO_RESET,
> +				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
> +			mdelay(1);
> +			writel(AUDIO_FIFO_START,
> +				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
> +			mdelay(1);

Do we really need these 1ms delays?  They're very long for the
context...

> +static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
> +			struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
> +
> +	if (runtime->channels == 2)
> +		ctrl &= ~I2S_SIX_CHANNELS;
> +	else
> +		ctrl |= I2S_SIX_CHANNELS;

A switch statement for the number of channels would make me happier
here.

> +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
> +		ctrl |= I2S_SLAVE_MODE;
> +		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
> +		si2s->master_mode = 0;
> +		break;

Do we need locking on all these register updates?

> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		si2s->master_mode = 1;
> +		return -EINVAL;

Should this be undoing the work done for _CBM_CFM?

> +#ifdef CONFIG_PM
> +static int sirf_i2s_suspend(struct platform_device *pdev,
> +		pm_message_t state)
> +{
> +	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
> +
> +	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
> +
> +	clk_disable_unprepare(si2s->clk);
> +
> +	if (si2s->master_mode)
> +		pwm_disable(si2s->mclk_pwm);
> +	return 0;
> +}

Should these be runtime PM calls as well?  Seems like the bus could be
unclocked whenever audio is not playing.  If you need the clock to write
conifguraiton perhaps regmap-mmio could help?

> +	spin_lock_init(&si2s->lock);

What is this lock actually protecting?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130719/727ea2f8/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 18:13     ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:13 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1577 bytes --]

On Fri, Jul 19, 2013 at 07:07:19PM +0800, Barry Song wrote:

> +static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
> +{
> +	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
> +
> +	u32 clk_rate = clk_get_rate(susp->clk);
> +	u32 clk_div = (clk_rate/(2*rate)) - 1;
> +	u32 clk_div_hi = (clk_div & 0xC00)>>10;
> +	u32 clk_div_lo = (clk_div & 0x3FF);
> +
> +	writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
> +		susp->base + USP_MODE2);
> +	writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
> +		susp->base + USP_TX_FRAME_CTRL);
> +
> +	return 0;
> +}

I'd be happier if this did some validation on the supplied rate (for
example, checking that it's less than the master rate) and had arguments
specifying which clock was being affected (in case there's more in
future revisions).

Also what exactly is the rate being set - can the driver just figure
this out automatically from the sample rate?

> +#ifdef CONFIG_PM
> +static int sirf_usp_pcm_suspend(struct platform_device *pdev,
> +	pm_message_t state)
> +{
> +	struct sirf_usp *susp = platform_get_drvdata(pdev);
> +
> +	susp->mode1_reg = readl(susp->base + USP_MODE1);
> +	susp->mode2_reg = readl(susp->base + USP_MODE2);
> +	sirf_usp_controller_uninit(susp);
> +	clk_disable_unprepare(susp->clk);
> +
> +	return 0;
> +}

Can this be done as runtime PM as well?  Seems like it'd save power.

> +	susp->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(susp->clk)) {
> +		dev_err(&pdev->dev, "Get clock failed.\n");
> +		return PTR_ERR(susp->clk);
> +	}

devm_clk_get().

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
@ 2013-07-19 18:13     ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:13 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 19, 2013 at 07:07:19PM +0800, Barry Song wrote:

> +static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
> +{
> +	struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
> +
> +	u32 clk_rate = clk_get_rate(susp->clk);
> +	u32 clk_div = (clk_rate/(2*rate)) - 1;
> +	u32 clk_div_hi = (clk_div & 0xC00)>>10;
> +	u32 clk_div_lo = (clk_div & 0x3FF);
> +
> +	writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
> +		susp->base + USP_MODE2);
> +	writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
> +		susp->base + USP_TX_FRAME_CTRL);
> +
> +	return 0;
> +}

I'd be happier if this did some validation on the supplied rate (for
example, checking that it's less than the master rate) and had arguments
specifying which clock was being affected (in case there's more in
future revisions).

Also what exactly is the rate being set - can the driver just figure
this out automatically from the sample rate?

> +#ifdef CONFIG_PM
> +static int sirf_usp_pcm_suspend(struct platform_device *pdev,
> +	pm_message_t state)
> +{
> +	struct sirf_usp *susp = platform_get_drvdata(pdev);
> +
> +	susp->mode1_reg = readl(susp->base + USP_MODE1);
> +	susp->mode2_reg = readl(susp->base + USP_MODE2);
> +	sirf_usp_controller_uninit(susp);
> +	clk_disable_unprepare(susp->clk);
> +
> +	return 0;
> +}

Can this be done as runtime PM as well?  Seems like it'd save power.

> +	susp->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(susp->clk)) {
> +		dev_err(&pdev->dev, "Get clock failed.\n");
> +		return PTR_ERR(susp->clk);
> +	}

devm_clk_get().
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130719/e8735988/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 18:35     ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:35 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 4426 bytes --]

On Fri, Jul 19, 2013 at 07:07:20PM +0800, Barry Song wrote:

> there is an internal codec embedded in the SiRF SoC. this is not
> a typical user scenerios of ASoC. but we can still get benefit by
> sharing platform DMA codes instead of implementing a pure ALSA
> driver.
> This driver adds DAI drivers for this internal codec.

To be honest this shouldn't be too exciting from an ASoC point of view -
just a normal CODEC driver with some stub DAIs.  It looks like it might
benefit from regmap-mmio and DAPM.

> +static int sirf_inner_control(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol,
> +		int get, char *name)
> +{
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct snd_soc_card *card = codec->card;
> +	int i;
> +	for (i = 0; i < card->num_controls; i++) {
> +		if (!strcmp(card->controls[i].name, name)) {
> +			if (card->controls[i].get && get)
> +				return card->controls[i].get(kcontrol, ucontrol);
> +			else if (card->controls[i].put && !get)
> +				return card->controls[i].put(kcontrol, ucontrol);
> +		}
> +	}
> +	return 0;
> +}

What is all this about?  I don't quite understand what the goal is and
there's no comments.

> +static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
> +
> +	spin_lock(&sinner_audio->lock);
> +	sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
> +
> +	if (ucontrol->value.integer.value[0]) {
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
> +				| IC_RDACEN | IC_SPSELR,
> +				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
> +				(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
> +				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +
> +		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
> +					IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);

Is this possibly some supplies plus a power bit?

> +static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
> +	u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
> +	u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
> +	u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
> +	u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;

Would regmap_field help here?

> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
> +			(1 << sinner_audio->reg_bits->codec_clk_en_bits) |
> +			(1 << sinner_audio->reg_bits->por_bits),
> +			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +		msleep(50);
> +
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
> +			MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
> +		usleep_range(300, 1000);

This all looks a lot like a normal CODEC power up sequence that's been
open coded but could use DAPM?

> +static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +}
> +
> +static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
> +		struct snd_pcm_hw_params *params,
> +		struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}

Remove empty functions.

> +static int sirf_soc_inner_resume(struct platform_device *pdev)
> +{
> +	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
> +
> +	clk_prepare_enable(sinner_audio->clk);
> +
> +	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
> +		| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
> +		| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +	return 0;
> +}
> +#else
> +#define sirf_soc_inner_suspend NULL
> +#define sirf_soc_inner_resume NULL
> +#endif

Standard runtime PM question and this definitely does look like
regmap-mmio will help - you've essentially open coded some of it.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
@ 2013-07-19 18:35     ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 19, 2013 at 07:07:20PM +0800, Barry Song wrote:

> there is an internal codec embedded in the SiRF SoC. this is not
> a typical user scenerios of ASoC. but we can still get benefit by
> sharing platform DMA codes instead of implementing a pure ALSA
> driver.
> This driver adds DAI drivers for this internal codec.

To be honest this shouldn't be too exciting from an ASoC point of view -
just a normal CODEC driver with some stub DAIs.  It looks like it might
benefit from regmap-mmio and DAPM.

> +static int sirf_inner_control(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol,
> +		int get, char *name)
> +{
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct snd_soc_card *card = codec->card;
> +	int i;
> +	for (i = 0; i < card->num_controls; i++) {
> +		if (!strcmp(card->controls[i].name, name)) {
> +			if (card->controls[i].get && get)
> +				return card->controls[i].get(kcontrol, ucontrol);
> +			else if (card->controls[i].put && !get)
> +				return card->controls[i].put(kcontrol, ucontrol);
> +		}
> +	}
> +	return 0;
> +}

What is all this about?  I don't quite understand what the goal is and
there's no comments.

> +static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
> +
> +	spin_lock(&sinner_audio->lock);
> +	sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
> +
> +	if (ucontrol->value.integer.value[0]) {
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
> +				| IC_RDACEN | IC_SPSELR,
> +				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
> +				(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
> +				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +
> +		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
> +					IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);

Is this possibly some supplies plus a power bit?

> +static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
> +	u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
> +	u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
> +	u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
> +	u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;

Would regmap_field help here?

> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
> +			(1 << sinner_audio->reg_bits->codec_clk_en_bits) |
> +			(1 << sinner_audio->reg_bits->por_bits),
> +			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +		msleep(50);
> +
> +		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
> +			MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
> +		usleep_range(300, 1000);

This all looks a lot like a normal CODEC power up sequence that's been
open coded but could use DAPM?

> +static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +}
> +
> +static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
> +		struct snd_pcm_hw_params *params,
> +		struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}

Remove empty functions.

> +static int sirf_soc_inner_resume(struct platform_device *pdev)
> +{
> +	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
> +
> +	clk_prepare_enable(sinner_audio->clk);
> +
> +	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
> +		| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
> +		| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
> +	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
> +		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
> +	return 0;
> +}
> +#else
> +#define sirf_soc_inner_suspend NULL
> +#define sirf_soc_inner_resume NULL
> +#endif

Standard runtime PM question and this definitely does look like
regmap-mmio will help - you've essentially open coded some of it.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130719/2b23f3dd/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
  2013-07-19 11:07   ` Barry Song
@ 2013-07-19 18:51     ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:51 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1050 bytes --]

On Fri, Jul 19, 2013 at 07:07:21PM +0800, Barry Song wrote:

> +#ifndef CONFIG_ANDROID
> +	unsigned int            gpio_hp_detect;
> +	struct snd_soc_jack     hp_jack;
> +#endif

Not for mainline.  I'd suggest setting it up for extcon anyway
especially if it's just a simple accessory, that should work for both
Android and mainline.

> +static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol)
> +{
> +	int is_spk_out = ucontrol->value.integer.value[0];
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct snd_soc_card *card = codec->card;
> +	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
> +
> +	if (gpio_is_valid(sinner_card->gpio_spk_pa))
> +		gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
> +	return 0;
> +}

Oh, right.  If this is doing what I think this is doing you want to use
DAPM within the CODEC then this can just become a pin switch or you can
hook in directly if you continue to use soc-jack - it has DAPM
integration.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
@ 2013-07-19 18:51     ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-19 18:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 19, 2013 at 07:07:21PM +0800, Barry Song wrote:

> +#ifndef CONFIG_ANDROID
> +	unsigned int            gpio_hp_detect;
> +	struct snd_soc_jack     hp_jack;
> +#endif

Not for mainline.  I'd suggest setting it up for extcon anyway
especially if it's just a simple accessory, that should work for both
Android and mainline.

> +static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol)
> +{
> +	int is_spk_out = ucontrol->value.integer.value[0];
> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> +	struct snd_soc_card *card = codec->card;
> +	struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
> +
> +	if (gpio_is_valid(sinner_card->gpio_spk_pa))
> +		gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
> +	return 0;
> +}

Oh, right.  If this is doing what I think this is doing you want to use
DAPM within the CODEC then this can just become a pin switch or you can
hook in directly if you continue to use soc-jack - it has DAPM
integration.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130719/df529a7f/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
  2013-07-19 16:12     ` Mark Brown
@ 2013-07-22  0:06       ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-22  0:06 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:17PM +0800, Barry Song wrote:
>
>> index 0000000..3678aed
>> --- /dev/null
>> +++ b/sound/soc/sirf/Kconfig
>> @@ -0,0 +1,4 @@
>> +config SND_SIRF_SOC
>> +     tristate "Platform DMA driver for the SiRF SoC chips"
>> +     depends on ARCH_SIRF && SND_SOC
>> +     select SND_SOC_DMAENGINE_PCM
>
> Does this have any actual platform build dependencies or can this be
> ARCH_SIRF || COMPILE_TEST?

it depends on SIRF as it is specific to SiRFSoC, but i think it would
be good to have " || COMPILE_TEST"

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA
@ 2013-07-22  0:06       ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-22  0:06 UTC (permalink / raw)
  To: linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:17PM +0800, Barry Song wrote:
>
>> index 0000000..3678aed
>> --- /dev/null
>> +++ b/sound/soc/sirf/Kconfig
>> @@ -0,0 +1,4 @@
>> +config SND_SIRF_SOC
>> +     tristate "Platform DMA driver for the SiRF SoC chips"
>> +     depends on ARCH_SIRF && SND_SOC
>> +     select SND_SOC_DMAENGINE_PCM
>
> Does this have any actual platform build dependencies or can this be
> ARCH_SIRF || COMPILE_TEST?

it depends on SIRF as it is specific to SiRFSoC, but i think it would
be good to have " || COMPILE_TEST"

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-07-19 16:52     ` Mark Brown
@ 2013-07-22  9:07       ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-22  9:07 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:18PM +0800, Barry Song wrote:
>
> Pretty good overall - there's a few things below but should all be
> fairly small.
>
>> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +
>> +     if (si2s->master_mode)
>> +             pwm_enable(si2s->mclk_pwm);
>> +     clk_prepare_enable(si2s->clk);
>> +
>> +     device_reset(dai->dev);
>> +     snd_soc_dai_set_dma_data(dai, substream,
>> +             &sirf_i2s_dai_dma_data[substream->stream]);
>> +     return 0;
>> +}
>
> device_reset() sounds like it's not going to work for simultaneous
> playback and capture.

right. i'll have rongjun to check whether this reset is necessary or
it can be moved to other place.
as here playback can break capture if we start a playback when we are
capturing here.

>
>> +static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
>> +             int cmd, struct snd_soc_dai *dai)
>> +{
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +     int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_START:
>> +     case SNDRV_PCM_TRIGGER_RESUME:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +             spin_lock(&si2s->lock);
>> +
>> +             if (playback) {
>> +                     /* First start the FIFO, then enable the tx/rx */
>> +                     writel(AUDIO_FIFO_RESET,
>> +                             si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
>> +                     mdelay(1);
>> +                     writel(AUDIO_FIFO_START,
>> +                             si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
>> +                     mdelay(1);
>
> Do we really need these 1ms delays?  They're very long for the
> context...

the hardware does need a delay to be stable as said by hardware guys

>
>> +static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
>> +                     struct snd_soc_dai *dai)
>> +{
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +     u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +
>> +     if (runtime->channels == 2)
>> +             ctrl &= ~I2S_SIX_CHANNELS;
>> +     else
>> +             ctrl |= I2S_SIX_CHANNELS;
>
> A switch statement for the number of channels would make me happier
> here.

here there is some hardware set to explain.

>
>> +     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>> +     case SND_SOC_DAIFMT_CBM_CFM:
>> +             ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +             ctrl |= I2S_SLAVE_MODE;
>> +             writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +             si2s->master_mode = 0;
>> +             break;
>
> Do we need locking on all these register updates?

yes if considering here it is happening read-modify-write.

>
>> +     case SND_SOC_DAIFMT_CBS_CFS:
>> +             si2s->master_mode = 1;
>> +             return -EINVAL;
>
> Should this be undoing the work done for _CBM_CFM?

it seems it need to rollback some registers. i'll have rongjun to check that.

>
>> +#ifdef CONFIG_PM
>> +static int sirf_i2s_suspend(struct platform_device *pdev,
>> +             pm_message_t state)
>> +{
>> +     struct sirf_i2s *si2s = platform_get_drvdata(pdev);
>> +
>> +     si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
>> +
>> +     clk_disable_unprepare(si2s->clk);
>> +
>> +     if (si2s->master_mode)
>> +             pwm_disable(si2s->mclk_pwm);
>> +     return 0;
>> +}
>
> Should these be runtime PM calls as well?  Seems like the bus could be
> unclocked whenever audio is not playing.  If you need the clock to write
> conifguraiton perhaps regmap-mmio could help?

i will make the runtime PM happen for this and other drivers on sirf platform.

>
>> +     spin_lock_init(&si2s->lock);
>
> What is this lock actually protecting?

sirf_i2s_trigger().

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-07-22  9:07       ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-22  9:07 UTC (permalink / raw)
  To: linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:18PM +0800, Barry Song wrote:
>
> Pretty good overall - there's a few things below but should all be
> fairly small.
>
>> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +
>> +     if (si2s->master_mode)
>> +             pwm_enable(si2s->mclk_pwm);
>> +     clk_prepare_enable(si2s->clk);
>> +
>> +     device_reset(dai->dev);
>> +     snd_soc_dai_set_dma_data(dai, substream,
>> +             &sirf_i2s_dai_dma_data[substream->stream]);
>> +     return 0;
>> +}
>
> device_reset() sounds like it's not going to work for simultaneous
> playback and capture.

right. i'll have rongjun to check whether this reset is necessary or
it can be moved to other place.
as here playback can break capture if we start a playback when we are
capturing here.

>
>> +static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
>> +             int cmd, struct snd_soc_dai *dai)
>> +{
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +     int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_START:
>> +     case SNDRV_PCM_TRIGGER_RESUME:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +             spin_lock(&si2s->lock);
>> +
>> +             if (playback) {
>> +                     /* First start the FIFO, then enable the tx/rx */
>> +                     writel(AUDIO_FIFO_RESET,
>> +                             si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
>> +                     mdelay(1);
>> +                     writel(AUDIO_FIFO_START,
>> +                             si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
>> +                     mdelay(1);
>
> Do we really need these 1ms delays?  They're very long for the
> context...

the hardware does need a delay to be stable as said by hardware guys

>
>> +static int sirf_i2s_prepare(struct snd_pcm_substream *substream,
>> +                     struct snd_soc_dai *dai)
>> +{
>> +     struct snd_pcm_runtime *runtime = substream->runtime;
>> +     struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
>> +     u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +
>> +     if (runtime->channels == 2)
>> +             ctrl &= ~I2S_SIX_CHANNELS;
>> +     else
>> +             ctrl |= I2S_SIX_CHANNELS;
>
> A switch statement for the number of channels would make me happier
> here.

here there is some hardware set to explain.

>
>> +     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>> +     case SND_SOC_DAIFMT_CBM_CFM:
>> +             ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +             ctrl |= I2S_SLAVE_MODE;
>> +             writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
>> +             si2s->master_mode = 0;
>> +             break;
>
> Do we need locking on all these register updates?

yes if considering here it is happening read-modify-write.

>
>> +     case SND_SOC_DAIFMT_CBS_CFS:
>> +             si2s->master_mode = 1;
>> +             return -EINVAL;
>
> Should this be undoing the work done for _CBM_CFM?

it seems it need to rollback some registers. i'll have rongjun to check that.

>
>> +#ifdef CONFIG_PM
>> +static int sirf_i2s_suspend(struct platform_device *pdev,
>> +             pm_message_t state)
>> +{
>> +     struct sirf_i2s *si2s = platform_get_drvdata(pdev);
>> +
>> +     si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
>> +
>> +     clk_disable_unprepare(si2s->clk);
>> +
>> +     if (si2s->master_mode)
>> +             pwm_disable(si2s->mclk_pwm);
>> +     return 0;
>> +}
>
> Should these be runtime PM calls as well?  Seems like the bus could be
> unclocked whenever audio is not playing.  If you need the clock to write
> conifguraiton perhaps regmap-mmio could help?

i will make the runtime PM happen for this and other drivers on sirf platform.

>
>> +     spin_lock_init(&si2s->lock);
>
> What is this lock actually protecting?

sirf_i2s_trigger().

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
  2013-07-19 18:51     ` Mark Brown
@ 2013-07-25  8:57       ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  8:57 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:21PM +0800, Barry Song wrote:
>
>> +#ifndef CONFIG_ANDROID
>> +     unsigned int            gpio_hp_detect;
>> +     struct snd_soc_jack     hp_jack;
>> +#endif
>
> Not for mainline.  I'd suggest setting it up for extcon anyway
> especially if it's just a simple accessory, that should work for both
> Android and mainline.

sounds good as if CONFIG_ANDROID is enabled without
CONFIG_ANDROID_SWITCH, /sys/class/switch/* will be symbolically linked
to /sys/class/extcon/, so that they are still compatible with legacy
userspace processes.

>
>> +static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     int is_spk_out = ucontrol->value.integer.value[0];
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct snd_soc_card *card = codec->card;
>> +     struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
>> +
>> +     if (gpio_is_valid(sinner_card->gpio_spk_pa))
>> +             gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
>> +     return 0;
>> +}
>
> Oh, right.  If this is doing what I think this is doing you want to use
> DAPM within the CODEC then this can just become a pin switch or you can
> hook in directly if you continue to use soc-jack - it has DAPM
> integration.

ok. here it is more of machine stuff than codec stuff, so i think we
move to snd_soc_jack_gpio.

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
@ 2013-07-25  8:57       ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  8:57 UTC (permalink / raw)
  To: linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:21PM +0800, Barry Song wrote:
>
>> +#ifndef CONFIG_ANDROID
>> +     unsigned int            gpio_hp_detect;
>> +     struct snd_soc_jack     hp_jack;
>> +#endif
>
> Not for mainline.  I'd suggest setting it up for extcon anyway
> especially if it's just a simple accessory, that should work for both
> Android and mainline.

sounds good as if CONFIG_ANDROID is enabled without
CONFIG_ANDROID_SWITCH, /sys/class/switch/* will be symbolically linked
to /sys/class/extcon/, so that they are still compatible with legacy
userspace processes.

>
>> +static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     int is_spk_out = ucontrol->value.integer.value[0];
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct snd_soc_card *card = codec->card;
>> +     struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
>> +
>> +     if (gpio_is_valid(sinner_card->gpio_spk_pa))
>> +             gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out);
>> +     return 0;
>> +}
>
> Oh, right.  If this is doing what I think this is doing you want to use
> DAPM within the CODEC then this can just become a pin switch or you can
> hook in directly if you continue to use soc-jack - it has DAPM
> integration.

ok. here it is more of machine stuff than codec stuff, so i think we
move to snd_soc_jack_gpio.

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
  2013-07-19 18:35     ` Mark Brown
@ 2013-07-25  9:11       ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  9:11 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:20PM +0800, Barry Song wrote:
>
>> there is an internal codec embedded in the SiRF SoC. this is not
>> a typical user scenerios of ASoC. but we can still get benefit by
>> sharing platform DMA codes instead of implementing a pure ALSA
>> driver.
>> This driver adds DAI drivers for this internal codec.
>
> To be honest this shouldn't be too exciting from an ASoC point of view -
> just a normal CODEC driver with some stub DAIs.  It looks like it might
> benefit from regmap-mmio and DAPM.

the driver was a legacy pure alsa driver as it is not soc+external
codec+mach framework.
so it has many legacy codes. i agree we can move to regmap-mmio and asoc dapm.

>
>> +static int sirf_inner_control(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol,
>> +             int get, char *name)
>> +{
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct snd_soc_card *card = codec->card;
>> +     int i;
>> +     for (i = 0; i < card->num_controls; i++) {
>> +             if (!strcmp(card->controls[i].name, name)) {
>> +                     if (card->controls[i].get && get)
>> +                             return card->controls[i].get(kcontrol, ucontrol);
>> +                     else if (card->controls[i].put && !get)
>> +                             return card->controls[i].put(kcontrol, ucontrol);
>> +             }
>> +     }
>> +     return 0;
>> +}
>
> What is all this about?  I don't quite understand what the goal is and
> there's no comments.

it is a common kcontrol get/set function which will be called by all
of sirf_inner_snd_speaker_set, sirf_inner_snd_headphone_get,
sirf_inner_snd_headphone_set, sirf_inner_snd_speaker_get. after we
move to DAPM, it should be useless.

>
>> +static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
>> +
>> +     spin_lock(&sinner_audio->lock);
>> +     sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
>> +
>> +     if (ucontrol->value.integer.value[0]) {
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
>> +                             | IC_RDACEN | IC_SPSELR,
>> +                             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
>> +                             (1 << sinner_audio->reg_bits->firdac_lout_en_bits),
>> +                             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +
>> +             writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
>> +                                     IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>
> Is this possibly some supplies plus a power bit?
>
>> +static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +     struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
>> +     u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
>> +     u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
>> +     u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
>> +     u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;
>
> Would regmap_field help here?
>
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
>> +                     (1 << sinner_audio->reg_bits->codec_clk_en_bits) |
>> +                     (1 << sinner_audio->reg_bits->por_bits),
>> +                     sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +             msleep(50);
>> +
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
>> +                     MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
>> +             usleep_range(300, 1000);
>
> This all looks a lot like a normal CODEC power up sequence that's been
> open coded but could use DAPM?
>
>> +static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +}
>> +
>> +static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
>> +             struct snd_pcm_hw_params *params,
>> +             struct snd_soc_dai *dai)
>> +{
>> +     return 0;
>> +}
>
> Remove empty functions.

ok.

>
>> +static int sirf_soc_inner_resume(struct platform_device *pdev)
>> +{
>> +     struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
>> +
>> +     clk_prepare_enable(sinner_audio->clk);
>> +
>> +     writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
>> +             | (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +     writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
>> +             | (1 << sinner_audio->reg_bits->adc14b_12_bits)),
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +     writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +     writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +     return 0;
>> +}
>> +#else
>> +#define sirf_soc_inner_suspend NULL
>> +#define sirf_soc_inner_resume NULL
>> +#endif
>
> Standard runtime PM question and this definitely does look like
> regmap-mmio will help - you've essentially open coded some of it.

we can use runtime PM to handle clock disable/enable here. audio path
widgets, codec and speaker widgets will be handled by DAPM.

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs
@ 2013-07-25  9:11       ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  9:11 UTC (permalink / raw)
  To: linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:20PM +0800, Barry Song wrote:
>
>> there is an internal codec embedded in the SiRF SoC. this is not
>> a typical user scenerios of ASoC. but we can still get benefit by
>> sharing platform DMA codes instead of implementing a pure ALSA
>> driver.
>> This driver adds DAI drivers for this internal codec.
>
> To be honest this shouldn't be too exciting from an ASoC point of view -
> just a normal CODEC driver with some stub DAIs.  It looks like it might
> benefit from regmap-mmio and DAPM.

the driver was a legacy pure alsa driver as it is not soc+external
codec+mach framework.
so it has many legacy codes. i agree we can move to regmap-mmio and asoc dapm.

>
>> +static int sirf_inner_control(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol,
>> +             int get, char *name)
>> +{
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct snd_soc_card *card = codec->card;
>> +     int i;
>> +     for (i = 0; i < card->num_controls; i++) {
>> +             if (!strcmp(card->controls[i].name, name)) {
>> +                     if (card->controls[i].get && get)
>> +                             return card->controls[i].get(kcontrol, ucontrol);
>> +                     else if (card->controls[i].put && !get)
>> +                             return card->controls[i].put(kcontrol, ucontrol);
>> +             }
>> +     }
>> +     return 0;
>> +}
>
> What is all this about?  I don't quite understand what the goal is and
> there's no comments.

it is a common kcontrol get/set function which will be called by all
of sirf_inner_snd_speaker_set, sirf_inner_snd_headphone_get,
sirf_inner_snd_headphone_set, sirf_inner_snd_speaker_get. after we
move to DAPM, it should be useless.

>
>> +static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
>> +             struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
>> +
>> +     spin_lock(&sinner_audio->lock);
>> +     sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
>> +
>> +     if (ucontrol->value.integer.value[0]) {
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
>> +                             | IC_RDACEN | IC_SPSELR,
>> +                             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
>> +                             (1 << sinner_audio->reg_bits->firdac_lout_en_bits),
>> +                             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +
>> +             writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
>> +                                     IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>
> Is this possibly some supplies plus a power bit?
>
>> +static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +     struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
>> +     u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
>> +     u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
>> +     u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
>> +     u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;
>
> Would regmap_field help here?
>
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
>> +                     (1 << sinner_audio->reg_bits->codec_clk_en_bits) |
>> +                     (1 << sinner_audio->reg_bits->por_bits),
>> +                     sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +             msleep(50);
>> +
>> +             writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
>> +                     MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
>> +             usleep_range(300, 1000);
>
> This all looks a lot like a normal CODEC power up sequence that's been
> open coded but could use DAPM?
>
>> +static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
>> +             struct snd_soc_dai *dai)
>> +{
>> +}
>> +
>> +static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
>> +             struct snd_pcm_hw_params *params,
>> +             struct snd_soc_dai *dai)
>> +{
>> +     return 0;
>> +}
>
> Remove empty functions.

ok.

>
>> +static int sirf_soc_inner_resume(struct platform_device *pdev)
>> +{
>> +     struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
>> +
>> +     clk_prepare_enable(sinner_audio->clk);
>> +
>> +     writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
>> +             | (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +     writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
>> +             | (1 << sinner_audio->reg_bits->adc14b_12_bits)),
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
>> +     writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +     writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
>> +             sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
>> +     return 0;
>> +}
>> +#else
>> +#define sirf_soc_inner_suspend NULL
>> +#define sirf_soc_inner_resume NULL
>> +#endif
>
> Standard runtime PM question and this definitely does look like
> regmap-mmio will help - you've essentially open coded some of it.

we can use runtime PM to handle clock disable/enable here. audio path
widgets, codec and speaker widgets will be handled by DAPM.

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  2013-07-19 18:13     ` Mark Brown
@ 2013-07-25  9:32       ` Barry Song
  -1 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  9:32 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:19PM +0800, Barry Song wrote:
>
>> +static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
>> +{
>> +     struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
>> +
>> +     u32 clk_rate = clk_get_rate(susp->clk);
>> +     u32 clk_div = (clk_rate/(2*rate)) - 1;
>> +     u32 clk_div_hi = (clk_div & 0xC00)>>10;
>> +     u32 clk_div_lo = (clk_div & 0x3FF);
>> +
>> +     writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
>> +             susp->base + USP_MODE2);
>> +     writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
>> +             susp->base + USP_TX_FRAME_CTRL);
>> +
>> +     return 0;
>> +}
>
> I'd be happier if this did some validation on the supplied rate (for
> example, checking that it's less than the master rate) and had arguments
> specifying which clock was being affected (in case there's more in
> future revisions).

sounds good.

>
> Also what exactly is the rate being set - can the driver just figure
> this out automatically from the sample rate?

here it is generating the right pcm clock by an internal DIVISOR in USP module.

For USP_MODE2:
30:21 (R/W) USP_CLK_DIVISOR 10’h0 USP serial clock divider

For USP_TX_FRAME_CTRL:
31:30 (R/W) USP_CLK_DIVISOR 2’h0 This is the two bit [11:10] of
USP_CLK_DIVISOR in USP_MODE2

for figuring this out automatically from the sample rate, do you mean
a lookup table for common sample rate?

>
>> +#ifdef CONFIG_PM
>> +static int sirf_usp_pcm_suspend(struct platform_device *pdev,
>> +     pm_message_t state)
>> +{
>> +     struct sirf_usp *susp = platform_get_drvdata(pdev);
>> +
>> +     susp->mode1_reg = readl(susp->base + USP_MODE1);
>> +     susp->mode2_reg = readl(susp->base + USP_MODE2);
>> +     sirf_usp_controller_uninit(susp);
>> +     clk_disable_unprepare(susp->clk);
>> +
>> +     return 0;
>> +}
>
> Can this be done as runtime PM as well?  Seems like it'd save power.

agree.

>
>> +     susp->clk = clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(susp->clk)) {
>> +             dev_err(&pdev->dev, "Get clock failed.\n");
>> +             return PTR_ERR(susp->clk);
>> +     }
>
> devm_clk_get().

agree. missed this one.

-barry

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
@ 2013-07-25  9:32       ` Barry Song
  0 siblings, 0 replies; 38+ messages in thread
From: Barry Song @ 2013-07-25  9:32 UTC (permalink / raw)
  To: linux-arm-kernel

2013/7/20 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 19, 2013 at 07:07:19PM +0800, Barry Song wrote:
>
>> +static int sirf_usp_pcm_divider(struct snd_soc_dai *dai, int div_id, int rate)
>> +{
>> +     struct sirf_usp *susp = snd_soc_dai_get_drvdata(dai);
>> +
>> +     u32 clk_rate = clk_get_rate(susp->clk);
>> +     u32 clk_div = (clk_rate/(2*rate)) - 1;
>> +     u32 clk_div_hi = (clk_div & 0xC00)>>10;
>> +     u32 clk_div_lo = (clk_div & 0x3FF);
>> +
>> +     writel((clk_div_lo<<21) | readl(susp->base + USP_MODE2),
>> +             susp->base + USP_MODE2);
>> +     writel((clk_div_hi<<30) | readl(susp->base + USP_TX_FRAME_CTRL),
>> +             susp->base + USP_TX_FRAME_CTRL);
>> +
>> +     return 0;
>> +}
>
> I'd be happier if this did some validation on the supplied rate (for
> example, checking that it's less than the master rate) and had arguments
> specifying which clock was being affected (in case there's more in
> future revisions).

sounds good.

>
> Also what exactly is the rate being set - can the driver just figure
> this out automatically from the sample rate?

here it is generating the right pcm clock by an internal DIVISOR in USP module.

For USP_MODE2:
30:21 (R/W) USP_CLK_DIVISOR 10?h0 USP serial clock divider

For USP_TX_FRAME_CTRL:
31:30 (R/W) USP_CLK_DIVISOR 2?h0 This is the two bit [11:10] of
USP_CLK_DIVISOR in USP_MODE2

for figuring this out automatically from the sample rate, do you mean
a lookup table for common sample rate?

>
>> +#ifdef CONFIG_PM
>> +static int sirf_usp_pcm_suspend(struct platform_device *pdev,
>> +     pm_message_t state)
>> +{
>> +     struct sirf_usp *susp = platform_get_drvdata(pdev);
>> +
>> +     susp->mode1_reg = readl(susp->base + USP_MODE1);
>> +     susp->mode2_reg = readl(susp->base + USP_MODE2);
>> +     sirf_usp_controller_uninit(susp);
>> +     clk_disable_unprepare(susp->clk);
>> +
>> +     return 0;
>> +}
>
> Can this be done as runtime PM as well?  Seems like it'd save power.

agree.

>
>> +     susp->clk = clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(susp->clk)) {
>> +             dev_err(&pdev->dev, "Get clock failed.\n");
>> +             return PTR_ERR(susp->clk);
>> +     }
>
> devm_clk_get().

agree. missed this one.

-barry

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
  2013-07-25  9:32       ` Barry Song
@ 2013-07-25 10:24         ` Mark Brown
  -1 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-25 10:24 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, Workgroup.Linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 825 bytes --]

On Thu, Jul 25, 2013 at 05:32:12PM +0800, Barry Song wrote:
> 2013/7/20 Mark Brown <broonie@kernel.org>:

> > Also what exactly is the rate being set - can the driver just figure
> > this out automatically from the sample rate?

> here it is generating the right pcm clock by an internal DIVISOR in USP module.

> For USP_MODE2:
> 30:21 (R/W) USP_CLK_DIVISOR 10’h0 USP serial clock divider

> For USP_TX_FRAME_CTRL:
> 31:30 (R/W) USP_CLK_DIVISOR 2’h0 This is the two bit [11:10] of
> USP_CLK_DIVISOR in USP_MODE2

> for figuring this out automatically from the sample rate, do you mean
> a lookup table for common sample rate?

That or just calculating it - if you know the input rate and know the
output rate you need then for a simple divisor it should be possible to
calculate the required value.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP
@ 2013-07-25 10:24         ` Mark Brown
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Brown @ 2013-07-25 10:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 25, 2013 at 05:32:12PM +0800, Barry Song wrote:
> 2013/7/20 Mark Brown <broonie@kernel.org>:

> > Also what exactly is the rate being set - can the driver just figure
> > this out automatically from the sample rate?

> here it is generating the right pcm clock by an internal DIVISOR in USP module.

> For USP_MODE2:
> 30:21 (R/W) USP_CLK_DIVISOR 10?h0 USP serial clock divider

> For USP_TX_FRAME_CTRL:
> 31:30 (R/W) USP_CLK_DIVISOR 2?h0 This is the two bit [11:10] of
> USP_CLK_DIVISOR in USP_MODE2

> for figuring this out automatically from the sample rate, do you mean
> a lookup table for common sample rate?

That or just calculating it - if you know the input rate and know the
output rate you need then for a simple divisor it should be possible to
calculate the required value.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130725/9d324ca6/attachment.sig>

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2013-07-25 10:24 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-19 11:07 [PATCH 0/6] ASoC: add CSR SiRFSoC sound drivers Barry Song
2013-07-19 11:07 ` Barry Song
2013-07-19 11:07 ` [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 15:08   ` Lars-Peter Clausen
2013-07-19 15:08     ` [alsa-devel] " Lars-Peter Clausen
2013-07-19 16:12   ` Mark Brown
2013-07-19 16:12     ` Mark Brown
2013-07-22  0:06     ` Barry Song
2013-07-22  0:06       ` Barry Song
2013-07-19 11:07 ` [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 16:52   ` Mark Brown
2013-07-19 16:52     ` Mark Brown
2013-07-22  9:07     ` Barry Song
2013-07-22  9:07       ` Barry Song
2013-07-19 11:07 ` [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:13   ` Mark Brown
2013-07-19 18:13     ` Mark Brown
2013-07-25  9:32     ` Barry Song
2013-07-25  9:32       ` Barry Song
2013-07-25 10:24       ` Mark Brown
2013-07-25 10:24         ` Mark Brown
2013-07-19 11:07 ` [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:35   ` Mark Brown
2013-07-19 18:35     ` Mark Brown
2013-07-25  9:11     ` Barry Song
2013-07-25  9:11       ` Barry Song
2013-07-19 11:07 ` [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:51   ` Mark Brown
2013-07-19 18:51     ` Mark Brown
2013-07-25  8:57     ` Barry Song
2013-07-25  8:57       ` Barry Song
2013-07-19 11:07 ` [PATCH 6/6] arm: prima2: defconfig: enable sound components Barry Song
2013-07-19 11:07   ` Barry Song

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.