[PATCH] ASoC: support pxa168 ssp in ASoC

Haojian Zhuang haojian.zhuang at marvell.com
Wed Mar 17 17:31:04 EDT 2010


Support pxa168 ssp in ASoC. The clock configuration and hw_params()
is different from pxa2xx ssp. Others are shared with pxa2xx ssp.

Signed-off-by: Haojian Zhuang <haojian.zhuang at marvell.com>
---
 sound/soc/pxa/Kconfig      |    6 +-
 sound/soc/pxa/Makefile     |    2 +
 sound/soc/pxa/pxa168-ssp.c |  318 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/pxa/pxa168-ssp.h |   31 +++++
 4 files changed, 356 insertions(+), 1 deletions(-)
 create mode 100644 sound/soc/pxa/pxa168-ssp.c
 create mode 100644 sound/soc/pxa/pxa168-ssp.h

diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 7be1d5f..286d52a 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,6 @@
 config SND_PXA2XX_SOC
 	tristate "SoC Audio for the Intel PXA2xx chip"
-	depends on ARCH_PXA
+	depends on ARCH_PXA || ARCH_MMP
 	select SND_PXA2XX_LIB
 	help
 	  Say Y or M if you want to add support for codecs attached to
@@ -25,6 +25,10 @@ config SND_PXA2XX_SOC_SSP
 	tristate
 	select PXA_SSP

+config SND_PXA168_SOC_SSP
+	tristate
+	select PXA_SSP
+
 config SND_PXA2XX_SOC_CORGI
 	tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
 	depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 33c1579..a74e6c9 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -3,11 +3,13 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o
 snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
 snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
 snd-soc-pxa2xx-ssp-objs := pxa-ssp.o pxa2xx-ssp.o
+snd-soc-pxa168-ssp-objs := pxa-ssp.o pxa168-ssp.o

 obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
 obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SSP) += snd-soc-pxa2xx-ssp.o
+obj-$(CONFIG_SND_PXA168_SOC_SSP) += snd-soc-pxa168-ssp.o

 # PXA Machine Support
 snd-soc-corgi-objs := corgi.o
diff --git a/sound/soc/pxa/pxa168-ssp.c b/sound/soc/pxa/pxa168-ssp.c
new file mode 100644
index 0000000..2164359
--- /dev/null
+++ b/sound/soc/pxa/pxa168-ssp.c
@@ -0,0 +1,318 @@
+/*
+ * pxa168-ssp.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2009-2010 Marvell International Ltd.
+ * Author:
+ *	Haojian Zhuang <haojian.zhuang at marvell.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/regs-apbc.h>
+#include <mach/regs-apmu.h>
+#include <mach/regs-mpmu.h>
+#include <plat/ssp.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa168-ssp.h"
+#include "pxa-ssp.h"
+
+struct ssp_mclk {
+	unsigned int	rate;
+	unsigned int	format;
+	unsigned int	channel;
+	unsigned int	mclk;
+	unsigned int	mclk_denom;
+	unsigned int	mclk_num;
+	unsigned int	bclk;
+	unsigned int	bclk_denom;
+	unsigned int	bclk_num;
+};
+
+/*
+ * This table is used while CPU is clock master.
+ * MCLK = 312MHz * (ASYSCLK_DENOM + 1) / ASYSCLK_NUM
+ * BCLK = 2 * MCLK * (SSPSCLK_DENOM + 1) / SSPSCLK_NUM
+ */
+static const struct ssp_mclk mclk_conf[] = {
+	/* rate, fmt, chn, mclk,   den, num, bclk, den, num */
+	{96000, 16, 2, 12288000,  63, 1625, 3072000,  1,  2},
+	{96000, 16, 1, 12288000,  63, 1625, 3072000,  1,  8},
+	{88200, 16, 2, 11289600, 293, 8125, 2822400,  1,  2},
+	{88200, 16, 1, 11289600, 293, 8125, 2822400,  1,  8},
+	{48000, 16, 2, 12288000,  63, 1625, 1536000,  1,  4},
+	{48000, 16, 1, 12288000,  63, 1625, 1536000,  1, 16},
+	{44100, 16, 2, 11289600, 293, 8125, 1411200,  1,  4},
+	{44100, 16, 1, 11289600, 293, 8125, 1411200,  1, 16},
+	{32000, 16, 2, 12288000,  63, 1625, 1024000,  1,  6},
+	{32000, 16, 1, 12288000,  63, 1625, 1024000,  1, 24},
+	{22050, 16, 2, 11289600, 293, 8125,  705600,  1,  8},
+	{22050, 16, 1, 11289600, 293, 8125,  705600,  1, 32},
+	{16000, 16, 2, 12288000,  63, 1625,  512000,  1, 12},
+	{16000, 16, 1, 12288000,  63, 1625,  512000,  1, 48},
+	{11025, 16, 2, 11289600, 293, 8125,  352800,  1, 16},
+	{11025, 16, 1, 11289600, 293, 8125,  352800,  1, 64},
+	{ 8000, 16, 2, 12288000,  63, 1625,  256000,  1, 24},
+	{ 8000, 16, 1, 12288000,  63, 1625,  256000,  1, 96},
+};
+
+/* Seek the index of MCLK configuration table */
+int pxa168_seek_mclk_conf(int rate, int format, int channel)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mclk_conf); i++) {
+		if ((mclk_conf[i].rate == rate)
+			&& (mclk_conf[i].format == format)
+			&& (mclk_conf[i].channel == channel))
+			return i;
+	}
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(pxa168_seek_mclk_conf);
+
+/* Get the MCLK frequency */
+int pxa168_get_mclk(int i)
+{
+	if ((i < 0) || (i >= ARRAY_SIZE(mclk_conf)))
+		return -EINVAL;
+	return mclk_conf[i].mclk;
+}
+EXPORT_SYMBOL_GPL(pxa168_get_mclk);
+
+static void dump_registers(struct ssp_device *ssp)
+{
+	dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
+		ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1),
+		ssp_read_reg(ssp, SSTO));
+
+	dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x\n",
+		ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR));
+}
+
+/*
+ * Set the SSP ports SYSCLK only from Audio SYSCLK.
+ */
+static int pxa168_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
+				     unsigned int freq, int dir)
+{
+	struct ssp_priv *priv = cpu_dai->private_data;
+	struct ssp_device *ssp = priv->ssp;
+	unsigned int sscr0, data, asysdr, asspdr;
+
+	dev_dbg(&ssp->pdev->dev, "%s id: %d, clk_id %d, freq %u\n",
+		__func__, cpu_dai->id, clk_id, freq);
+
+	if ((clk_id != PXA168_ASYSCLK_MASTER)
+		&& (clk_id != PXA168_ASYSCLK_SLAVE)) {
+		dev_warn(&ssp->pdev->dev, "Wrong clk_id(%d) is specified\n",
+			clk_id);
+		return -EINVAL;
+	}
+
+	/* freq is the index of mclk_conf table */
+	if ((freq < 0) || (freq >= ARRAY_SIZE(mclk_conf))) {
+		dev_warn(&ssp->pdev->dev, "Wrong frequency index:%d\n", freq);
+		return -EINVAL;
+	}
+	asysdr = (mclk_conf[freq].mclk_num << 16)
+		| mclk_conf[freq].mclk_denom;
+	asspdr = 0;
+	/* If ASYSCLK is supplied by pxa168, ASSPDR should be configured. */
+	if (clk_id == PXA168_ASYSCLK_MASTER)
+		asspdr = (mclk_conf[freq].bclk_num << 16)
+			| mclk_conf[freq].bclk_denom;
+
+	ssp_disable(ssp);
+	clk_disable(ssp->clk);			/* SSP port internal clock */
+
+	/* clear ECS, NCS, MOD, ACS */
+	sscr0 = ssp_read_reg(ssp, SSCR0);
+	data = sscr0 & ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
+	if (sscr0 != data)
+		ssp_write_reg(ssp, SSCR0, data);
+
+	/* update divider register in MPMU */
+	__raw_writel(asysdr, MPMU_ASYSDR);
+	__raw_writel(asspdr, MPMU_ASSPDR);
+
+	clk_enable(ssp->clk);			/* SSP port internal clock */
+	ssp_enable(ssp);
+	return 0;
+}
+
+static int pxa168_ssp_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct ssp_priv *priv = cpu_dai->private_data;
+	struct ssp_device *ssp = priv->ssp;
+	int width = snd_pcm_format_physical_width(params_format(params));
+	int channels = params_channels(params);
+	int dma_16b = 0, stream_out, data_size;
+	u32 sscr0, sspsp;
+
+	/* generate correct DMA params */
+	if (cpu_dai->dma_data)
+		kfree(cpu_dai->dma_data);
+
+	if ((width == 16) && (params_channels(params) == 1))
+		dma_16b = 1;
+	stream_out = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+	cpu_dai->dma_data = ssp_get_dma_params(ssp, dma_16b, stream_out);
+
+	/* clear selected SSP bits */
+	sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
+
+	/* data_size should only be 16-bit or 32-bit because of DMA */
+	data_size = width * channels;
+	switch (data_size) {
+	case 16:
+		sscr0 |= SSCR0_DataSize(16);
+		break;
+	case 32:
+		sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
+		break;
+	}
+
+	ssp_disable(ssp);
+	sspsp = ssp_read_reg(ssp, SSPSP);
+	sspsp &= ~SSPSP_TIMING_MASK;
+	switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		/*
+		 * The polarity of frame sync should be inverted at here.
+		 *
+		 * In I2S format, frame sync is always inactive while
+		 * transfering data of left channel. So some additional
+		 * parameters like frame width, frame delay should be
+		 * configured. And more bit clocks in one frame cycle should
+		 * be reserved to meet the clock delay formula.
+		 *
+		 * If frame sync signal is inverted, frame width & frame
+		 * delay needn't be configured at here.
+		 */
+		sspsp |= SSPSP_SFRMWDTH(width);
+		if (channels == 1) {
+			sspsp |= SSPSP_DMYSTRT(1);
+			sspsp |= SSPSP_DMYSTOP((width - 1) & 0x3);
+			sspsp |= SSPSP_EDMYSTOP(((width - 1) >> 2) & 0x7);
+		} else if (channels == 2) {
+			if (width == 32) {
+				dev_err(&ssp->pdev->dev, "can't support %d-"
+					"data with %-channels in I2S mode\n",
+					width, channels);
+				return -EINVAL;
+			}
+			sspsp |= SSPSP_FSRT;
+		}
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		/* Right Justified mode doesn't support 32-bit data */
+		if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+			return -EINVAL;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		sspsp |= SSPSP_SFRMWDTH(width);
+		break;
+	}
+
+	/* update SSP register at the same time */
+	ssp_write_reg(ssp, SSCR0, sscr0);
+	ssp_write_reg(ssp, SSPSP, sspsp);
+	ssp_enable(ssp);
+
+	dump_registers(ssp);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops pxa168_ssp_dai_ops = {
+	.hw_params	= pxa168_ssp_hw_params,
+	.set_sysclk	= pxa168_ssp_set_dai_sysclk,
+};
+
+#define PXA168_SSP_RATES	SNDRV_PCM_RATE_8000_96000
+#define PXA168_SSP_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE |	\
+				SNDRV_PCM_FMTBIT_S24_LE |	\
+				SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PXA168_SSP_DAI(_id)					\
+{								\
+	.name	= "pxa168-ssp",					\
+	.id	= _id,						\
+	.playback = {						\
+		.channels_min = 1,				\
+		.channels_max = 2,				\
+		.rates = PXA168_SSP_RATES,			\
+		.formats = PXA168_SSP_FORMATS,			\
+	},							\
+	.capture = {						\
+		.channels_min = 1,				\
+		.channels_max = 2,				\
+		.rates = PXA168_SSP_RATES,			\
+		.formats = PXA168_SSP_FORMATS,			\
+	},							\
+	.ops = &pxa168_ssp_dai_ops,				\
+}
+
+struct snd_soc_dai pxa168_ssp_dai[] = {
+	PXA168_SSP_DAI(PXA168_DAI_SSP1),
+	PXA168_SSP_DAI(PXA168_DAI_SSP2),
+	PXA168_SSP_DAI(PXA168_DAI_SSP3),
+	PXA168_SSP_DAI(PXA168_DAI_SSP4),
+	PXA168_SSP_DAI(PXA168_DAI_SSP5),
+};
+EXPORT_SYMBOL_GPL(pxa168_ssp_dai);
+
+static int __init pxa168_ssp_init(void)
+{
+	struct snd_soc_dai *dai;
+	int i, ret;
+
+	for (i = 0; i < PXA168_DAI_SSP_MAX; i++) {
+		dai = &pxa168_ssp_dai[i];
+		ret = ssp_register_dai(dai);
+		if (ret)
+			return ret;
+	}
+	return ret;
+}
+module_init(pxa168_ssp_init);
+
+static void __exit pxa168_ssp_exit(void)
+{
+	struct snd_soc_dai *dai = NULL;
+	int i;
+
+	for (i = 0; i < PXA168_DAI_SSP_MAX; i++) {
+		dai = &pxa168_ssp_dai[i];
+		snd_soc_unregister_dai(dai);
+	}
+}
+module_exit(pxa168_ssp_exit);
+
+/* Module information */
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang at marvell.com>");
+MODULE_DESCRIPTION("PXA168 SSP SoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/pxa/pxa168-ssp.h b/sound/soc/pxa/pxa168-ssp.h
new file mode 100644
index 0000000..59b3cb9
--- /dev/null
+++ b/sound/soc/pxa/pxa168-ssp.h
@@ -0,0 +1,31 @@
+/*
+ * ASoC PXA168 SSP port support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PXA168_SSP_H
+#define _PXA168_SSP_H
+
+/* pxa DAI SSP IDs */
+enum {
+	PXA168_DAI_SSP1,
+	PXA168_DAI_SSP2,
+	PXA168_DAI_SSP3,
+	PXA168_DAI_SSP4,
+	PXA168_DAI_SSP5,
+	PXA168_DAI_SSP_MAX,
+};
+
+/* PXA168 SSP SYSCLK source */
+#define PXA168_ASYSCLK_MASTER		0	/* ASYSCLK master -- pxa168 */
+#define PXA168_ASYSCLK_SLAVE		1	/* ASYSCLK slave -- pxa168 */
+
+extern struct snd_soc_dai pxa168_ssp_dai[PXA168_DAI_SSP_MAX];
+
+extern int pxa168_seek_mclk_conf(int rate, int format, int channel);
+extern int pxa168_get_mclk(int i);
+
+#endif
-- 
1.5.6.5



More information about the linux-arm-kernel mailing list