* [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
[not found] <1275505397-16758-1-git-send-email-lars@metafoo.de>
@ 2010-06-02 19:12 ` Lars-Peter Clausen
2010-06-03 5:45 ` [alsa-devel] " Wan ZongShun
` (2 more replies)
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
2010-06-02 19:15 ` [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver Lars-Peter Clausen
2 siblings, 3 replies; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-02 19:12 UTC (permalink / raw)
To: Ralf Baechle
Cc: linux-mips, alsa-devel, Lars-Peter Clausen, Mark Brown,
linux-kernel, Liam Girdwood
This patch adds support for the JZ4740 internal codec.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Liam Girdwood <lrg@slimlogic.co.uk>
Cc: alsa-devel@alsa-project.org
---
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/jz4740-codec.h | 20 ++
4 files changed, 528 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/codecs/jz4740-codec.c
create mode 100644 sound/soc/codecs/jz4740-codec.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 31ac553..b8008df 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4671 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS4270 if I2C
+ select SND_SOC_JZ4740 if SOC_JZ4740
select SND_SOC_MAX9877 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_PCM3008
@@ -269,6 +270,9 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
+config SND_SOC_JZ4740_CODEC
+ tristate
+
# Amp
config SND_SOC_MAX9877
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 91429ea..4c7ee31 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-jz4740-codec-objs := jz4740-codec.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
new file mode 100644
index 0000000..6e4b741
--- /dev/null
+++ b/sound/soc/codecs/jz4740-codec.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+
+#define JZ4740_REG_CODEC_1 0x0
+#define JZ4740_REG_CODEC_2 0x1
+
+#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
+#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
+#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
+#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
+#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
+#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
+#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
+#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
+#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
+#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
+#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
+#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
+#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
+#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
+#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
+#define JZ4740_CODEC_1_SUSPEND BIT(1)
+#define JZ4740_CODEC_1_RESET BIT(0)
+
+#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
+#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
+#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
+#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
+#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
+#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
+#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
+#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
+#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
+
+struct jz4740_codec {
+ void __iomem *base;
+ struct resource *mem;
+
+ uint32_t reg_cache[2];
+ struct snd_soc_codec codec;
+};
+
+static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
+{
+ return container_of(codec, struct jz4740_codec, codec);
+}
+
+static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+ return readl(jz4740_codec->base + (reg << 2));
+}
+
+static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+ jz4740_codec->reg_cache[reg] = val;
+
+ writel(val, jz4740_codec->base + (reg << 2));
+ return 0;
+}
+
+static const struct snd_kcontrol_new jz4740_codec_controls[] = {
+ SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
+ SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+ SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
+ SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
+ SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
+ SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
+ SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
+
+ SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
+ jz4740_codec_output_controls,
+ ARRAY_SIZE(jz4740_codec_output_controls)),
+
+ SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+ jz4740_codec_input_controls,
+ ARRAY_SIZE(jz4740_codec_input_controls)),
+ SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("LIN"),
+ SND_SOC_DAPM_INPUT("RIN"),
+};
+
+static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
+
+ {"Line Input", NULL, "LIN"},
+ {"Line Input", NULL, "RIN"},
+
+ {"Input Mixer", "Line Capture Switch", "Line Input"},
+ {"Input Mixer", "Mic Capture Switch", "MIC"},
+
+ {"ADC", NULL, "Input Mixer"},
+
+ {"Output Mixer", "Bypass Switch", "Input Mixer"},
+ {"Output Mixer", "DAC Switch", "DAC"},
+
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+};
+
+static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ uint32_t val;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0;
+ break;
+ case 11025:
+ val = 1;
+ break;
+ case 12000:
+ val = 2;
+ break;
+ case 16000:
+ val = 3;
+ break;
+ case 22050:
+ val = 4;
+ break;
+ case 24000:
+ val = 5;
+ break;
+ case 32000:
+ val = 6;
+ break;
+ case 44100:
+ val = 7;
+ break;
+ case 48000:
+ val = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
+
+ return 0;
+}
+
+static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
+ .hw_params = jz4740_codec_hw_params,
+ .set_fmt = jz4740_codec_set_fmt,
+};
+
+struct snd_soc_dai jz4740_codec_dai = {
+ .name = "jz-codec",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ },
+ .ops = &jz4740_codec_dai_ops,
+ .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(jz4740_codec_dai);
+
+static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+ udelay(2);
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+ }
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_DISABLE |
+ JZ4740_CODEC_1_VREF_AMP_DISABLE |
+ JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT,
+ 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT,
+ JZ4740_CODEC_1_VREF_LOW_CURRENT |
+ JZ4740_CODEC_1_VREF_HIGH_CURRENT);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
+ JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+
+static struct snd_soc_codec *jz4740_codec_codec;
+
+static int jz4740_codec_dev_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = jz4740_codec_codec;
+
+ BUG_ON(!codec);
+
+ socdev->card->codec = codec;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_add_controls(codec, jz4740_codec_controls,
+ ARRAY_SIZE(jz4740_codec_controls));
+
+ snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
+ ARRAY_SIZE(jz4740_codec_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
+ ARRAY_SIZE(jz4740_codec_dapm_routes));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int jz4740_codec_dev_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
+ .probe = jz4740_codec_dev_probe,
+ .remove = jz4740_codec_dev_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
+
+static int __devinit jz4740_codec_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_codec *jz4740_codec;
+ struct snd_soc_codec *codec;
+
+ jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
+
+ if (!jz4740_codec)
+ return -ENOMEM;
+
+ jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!jz4740_codec->mem) {
+ dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
+ ret = -ENOENT;
+ goto err_free_codec;
+ }
+
+ jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
+ resource_size(jz4740_codec->mem), pdev->name);
+
+ if (!jz4740_codec->mem) {
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ ret = -EBUSY;
+ goto err_free_codec;
+ }
+
+ jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+
+ if (!jz4740_codec->base) {
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ ret = -EBUSY;
+ goto err_release_mem_region;
+ }
+
+ jz4740_codec_dai.dev = &pdev->dev;
+
+ codec = &jz4740_codec->codec;
+
+ codec->dev = &pdev->dev;
+ codec->name = "jz-codec";
+ codec->owner = THIS_MODULE;
+
+ codec->read = jz4740_codec_read;
+ codec->write = jz4740_codec_write;
+ codec->set_bias_level = jz4740_codec_set_bias_level;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+
+ codec->dai = &jz4740_codec_dai;
+ codec->num_dai = 1;
+
+ codec->reg_cache = jz4740_codec->reg_cache;
+ codec->reg_cache_size = 2;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ jz4740_codec_codec = codec;
+
+ snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
+
+
+ platform_set_drvdata(pdev, jz4740_codec);
+ ret = snd_soc_register_codec(codec);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register codec\n");
+ goto err_iounmap;
+ }
+
+ ret = snd_soc_register_dai(&jz4740_codec_dai);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register codec dai\n");
+ goto err_unregister_codec;
+ }
+
+ jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+err_unregister_codec:
+ snd_soc_unregister_codec(codec);
+err_iounmap:
+ iounmap(jz4740_codec->base);
+err_release_mem_region:
+ release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+err_free_codec:
+ kfree(jz4740_codec);
+
+ return ret;
+}
+
+static int __devexit jz4740_codec_remove(struct platform_device *pdev)
+{
+ struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&jz4740_codec_dai);
+ snd_soc_unregister_codec(&jz4740_codec->codec);
+
+ iounmap(jz4740_codec->base);
+ release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(jz4740_codec);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_codec_driver = {
+ .probe = jz4740_codec_probe,
+ .remove = __devexit_p(jz4740_codec_remove),
+ .driver = {
+ .name = "jz4740-codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_codec_init(void)
+{
+ return platform_driver_register(&jz4740_codec_driver);
+}
+module_init(jz4740_codec_init);
+
+static void __exit jz4740_codec_exit(void)
+{
+ platform_driver_unregister(&jz4740_codec_driver);
+}
+module_exit(jz4740_codec_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jz-codec");
diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
new file mode 100644
index 0000000..b5a0691
--- /dev/null
+++ b/sound/soc/codecs/jz4740-codec.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
+#define __SND_SOC_CODECS_JZ4740_CODEC_H__
+
+extern struct snd_soc_dai jz4740_codec_dai;
+extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
+
+#endif
--
1.5.6.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
[not found] <1275505397-16758-1-git-send-email-lars@metafoo.de>
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
@ 2010-06-02 19:12 ` Lars-Peter Clausen
2010-06-03 3:36 ` Wan ZongShun
` (2 more replies)
2010-06-02 19:15 ` [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver Lars-Peter Clausen
2 siblings, 3 replies; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-02 19:12 UTC (permalink / raw)
To: Ralf Baechle
Cc: linux-mips, alsa-devel, Lars-Peter Clausen, Mark Brown,
linux-kernel, Liam Girdwood
This patch adds ASoC support for JZ4740 SoCs I2S module.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Liam Girdwood <lrg@slimlogic.co.uk>
Cc: alsa-devel@alsa-project.org
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/jz4740/Kconfig | 13 +
sound/soc/jz4740/Makefile | 9 +
sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
sound/soc/jz4740/jz4740-i2s.h | 18 ++
sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
sound/soc/jz4740/jz4740-pcm.h | 22 ++
8 files changed, 982 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/jz4740/Kconfig
create mode 100644 sound/soc/jz4740/Makefile
create mode 100644 sound/soc/jz4740/jz4740-i2s.c
create mode 100644 sound/soc/jz4740/jz4740-i2s.h
create mode 100644 sound/soc/jz4740/jz4740-pcm.c
create mode 100644 sound/soc/jz4740/jz4740-pcm.h
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index b1749bc..5a7a724 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/jz4740/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 1470141..fdbe74d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += jz4740/
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
new file mode 100644
index 0000000..39df949
--- /dev/null
+++ b/sound/soc/jz4740/Kconfig
@@ -0,0 +1,13 @@
+config SND_JZ4740_SOC
+ tristate "SoC Audio for Ingenic JZ4740 SoC"
+ depends on SOC_JZ4740 && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Jz4740 AC97, I2S or SSP interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_JZ4740_SOC_I2S
+ depends on SND_JZ4740_SOC
+ tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
+ help
+ Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
new file mode 100644
index 0000000..1be8d19
--- /dev/null
+++ b/sound/soc/jz4740/Makefile
@@ -0,0 +1,9 @@
+#
+# Jz4740 Platform Support
+#
+snd-soc-jz4740-objs := jz4740-pcm.o
+snd-soc-jz4740-i2s-objs := jz4740-i2s.o
+
+obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
+obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
new file mode 100644
index 0000000..2b139fd
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "jz4740-i2s.h"
+#include "jz4740-pcm.h"
+
+#define JZ_REG_AIC_CONF 0x00
+#define JZ_REG_AIC_CTRL 0x04
+#define JZ_REG_AIC_I2S_FMT 0x10
+#define JZ_REG_AIC_FIFO_STATUS 0x14
+#define JZ_REG_AIC_I2S_STATUS 0x1c
+#define JZ_REG_AIC_CLK_DIV 0x30
+#define JZ_REG_AIC_FIFO 0x34
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
+#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
+#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
+#define JZ_AIC_CONF_I2S BIT(4)
+#define JZ_AIC_CONF_RESET BIT(3)
+#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
+#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
+#define JZ_AIC_CONF_ENABLE BIT(0)
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
+#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
+#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
+#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
+#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
+#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
+#define JZ_AIC_CTRL_FLUSH BIT(8)
+#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
+#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
+#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
+#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
+#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
+#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
+#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
+
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
+#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
+#define JZ_AIC_I2S_FMT_MSB BIT(0)
+
+#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
+
+#define JZ_AIC_CLK_DIV_MASK 0xf
+
+struct jz4740_i2s {
+ struct resource *mem;
+ void __iomem *base;
+ dma_addr_t phys_base;
+
+ struct clk *clk_aic;
+ struct clk *clk_i2s;
+
+ struct jz4740_pcm_config pcm_config_playback;
+ struct jz4740_pcm_config pcm_config_capture;
+};
+
+static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
+ unsigned int reg)
+{
+ return readl(i2s->base + reg);
+}
+
+static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
+ unsigned int reg, uint32_t value)
+{
+ writel(value, i2s->base + reg);
+}
+
+static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
+{
+ return dai->private_data;
+}
+
+static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf, ctrl;
+
+ if (dai->active)
+ return 0;
+
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+ ctrl |= JZ_AIC_CTRL_FLUSH;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ clk_enable(i2s->clk_i2s);
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf |= JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ return 0;
+}
+
+static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ if (!dai->active)
+ return;
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf &= ~JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ clk_disable(i2s->clk_i2s);
+}
+
+
+static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ uint32_t ctrl;
+ uint32_t mask;
+
+ if (playback)
+ mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
+ else
+ mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ctrl |= mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ctrl &= ~mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ return 0;
+}
+
+
+static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+ uint32_t format = 0;
+ uint32_t conf;
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+
+ conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
+ format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_MSB:
+ format |= JZ_AIC_I2S_FMT_MSB;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+ jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
+
+ return 0;
+}
+
+static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ enum jz4740_dma_width dma_width;
+ struct jz4740_pcm_config *pcm_config;
+ unsigned int sample_size;
+ uint32_t ctrl;
+
+ ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ sample_size = 0;
+ dma_width = JZ4740_DMA_WIDTH_8BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S16:
+ sample_size = 1;
+ dma_width = JZ4740_DMA_WIDTH_16BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (playback) {
+ ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
+ ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
+ } else {
+ ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
+ ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
+ }
+
+ switch (params_channels(params)) {
+ case 2:
+ break;
+ case 1:
+ if (playback) {
+ ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
+ break;
+ }
+ default: /* Falltrough */
+ return -EINVAL;
+ }
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+ if (playback) {
+ pcm_config = &i2s->pcm_config_playback;
+ pcm_config->dma_config.dst_width = dma_width;
+ } else {
+ pcm_config = &i2s->pcm_config_capture;
+ pcm_config->dma_config.src_width = dma_width;
+ }
+
+
+ snd_soc_dai_set_dma_data(dai, substream, pcm_config);
+
+ return 0;
+}
+
+static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+ switch (div_id) {
+ case JZ4740_I2S_BIT_CLK:
+ if (div & 1 || div > 16)
+ return -EINVAL;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ int ret = 0;
+ struct clk *parent;
+
+ switch (clk_id) {
+ case JZ4740_I2S_CLKSRC_EXT:
+ parent = clk_get(NULL, "ext");
+ clk_set_parent(i2s->clk_i2s, parent);
+ break;
+ case JZ4740_I2S_CLKSRC_PLL:
+ parent = clk_get(NULL, "pll half");
+ clk_set_parent(i2s->clk_i2s, parent);
+ ret = clk_set_rate(i2s->clk_i2s, freq);
+ break;
+ default:
+ return -EINVAL;
+ }
+ clk_put(parent);
+
+ return ret;
+}
+
+static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ if (dai->active) {
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf &= ~JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ clk_disable(i2s->clk_i2s);
+ }
+
+ clk_disable(i2s->clk_aic);
+
+ return 0;
+}
+
+static int jz4740_i2s_resume(struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ clk_enable(i2s->clk_aic);
+
+ if (dai->active) {
+ clk_enable(i2s->clk_i2s);
+
+ conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+ conf |= JZ_AIC_CONF_ENABLE;
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ }
+
+ return 0;
+}
+
+static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+ uint32_t conf;
+
+ conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
+ (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
+ JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+ JZ_AIC_CONF_I2S |
+ JZ_AIC_CONF_INTERNAL_CODEC;
+
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
+ jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+ return 0;
+}
+
+
+static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
+ .startup = jz4740_i2s_startup,
+ .shutdown = jz4740_i2s_shutdown,
+ .trigger = jz4740_i2s_trigger,
+ .hw_params = jz4740_i2s_hw_params,
+ .set_fmt = jz4740_i2s_set_fmt,
+ .set_clkdiv = jz4740_i2s_set_clkdiv,
+ .set_sysclk = jz4740_i2s_set_sysclk,
+};
+
+#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+struct snd_soc_dai jz4740_i2s_dai = {
+ .name = "jz4740-i2s",
+ .probe = jz4740_i2s_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = JZ4740_I2S_FMTS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = JZ4740_I2S_FMTS,
+ },
+ .symmetric_rates = 1,
+ .ops = &jz4740_i2s_dai_ops,
+ .suspend = jz4740_i2s_suspend,
+ .resume = jz4740_i2s_resume,
+};
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
+
+static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
+{
+ struct jz4740_dma_config *dma_config;
+
+ /* Playback */
+ dma_config = &i2s->pcm_config_playback.dma_config;
+ dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+ dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
+ dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
+ dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+ i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+
+ /* Capture */
+ dma_config = &i2s->pcm_config_capture.dma_config;
+ dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+ dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
+ dma_config->flags = JZ4740_DMA_DST_AUTOINC;
+ dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+ i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+}
+
+static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
+{
+ struct jz4740_i2s *i2s;
+ int ret;
+
+ i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
+
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!i2s->mem) {
+ ret = -ENOENT;
+ goto err_free;
+ }
+
+ i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
+ pdev->name);
+
+ if (!i2s->mem) {
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
+
+ if (!i2s->base) {
+ ret = -EBUSY;
+ goto err_release_mem_region;
+ }
+
+ i2s->phys_base = i2s->mem->start;
+
+ i2s->clk_aic = clk_get(&pdev->dev, "aic");
+ if (IS_ERR(i2s->clk_aic)) {
+ ret = PTR_ERR(i2s->clk_aic);
+ goto err_iounmap;
+ }
+
+ i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
+ if (IS_ERR(i2s->clk_i2s)) {
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err_iounmap;
+ }
+
+ clk_enable(i2s->clk_aic);
+
+ jz4740_i2c_init_pcm_config(i2s);
+
+ jz4740_i2s_dai.private_data = i2s;
+ ret = snd_soc_register_dai(&jz4740_i2s_dai);
+
+ platform_set_drvdata(pdev, i2s);
+
+ return 0;
+
+err_iounmap:
+ iounmap(i2s->base);
+err_release_mem_region:
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+err_free:
+ kfree(i2s);
+
+ return ret;
+}
+
+static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
+{
+ struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&jz4740_i2s_dai);
+
+ clk_disable(i2s->clk_aic);
+ clk_put(i2s->clk_i2s);
+ clk_put(i2s->clk_aic);
+
+ iounmap(i2s->base);
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(i2s);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_i2s_driver = {
+ .probe = jz4740_i2s_dev_probe,
+ .remove = __devexit_p(jz4740_i2s_dev_remove),
+ .driver = {
+ .name = "jz4740-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_i2s_init(void)
+{
+ return platform_driver_register(&jz4740_i2s_driver);
+}
+module_init(jz4740_i2s_init);
+
+static void __exit jz4740_i2s_exit(void)
+{
+ platform_driver_unregister(&jz4740_i2s_driver);
+}
+module_exit(jz4740_i2s_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-i2s");
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
new file mode 100644
index 0000000..da22ed8
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.h
@@ -0,0 +1,18 @@
+/*
+ * 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 _JZ4740_I2S_H
+#define _JZ4740_I2S_H
+
+/* I2S clock source */
+#define JZ4740_I2S_CLKSRC_EXT 0
+#define JZ4740_I2S_CLKSRC_PLL 1
+
+#define JZ4740_I2S_BIT_CLK 0
+
+extern struct snd_soc_dai jz4740_i2s_dai;
+
+#endif
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
new file mode 100644
index 0000000..fd1c203
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-jz4740/dma.h>
+#include "jz4740-pcm.h"
+
+struct jz4740_runtime_data {
+ unsigned int dma_period;
+ dma_addr_t dma_start;
+ dma_addr_t dma_pos;
+ dma_addr_t dma_end;
+
+ struct jz4740_dma_chan *dma;
+
+ dma_addr_t fifo_addr;
+};
+
+/* identify hardware playback capabilities */
+static const struct snd_pcm_hardware jz4740_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .period_bytes_min = 16,
+ .period_bytes_max = 2 * PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 128,
+ .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
+ .fifo_size = 32,
+};
+
+static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
+{
+ unsigned int count;
+
+ if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
+ count = prtd->dma_end - prtd->dma_pos;
+ else
+ count = prtd->dma_period;
+
+ jz4740_dma_disable(prtd->dma);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
+ jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
+ } else {
+ jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
+ jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
+ }
+
+ jz4740_dma_set_transfer_count(prtd->dma, count);
+
+ jz4740_dma_enable(prtd->dma);
+
+ prtd->dma_pos += prtd->dma_period;
+ if (prtd->dma_pos >= prtd->dma_end)
+ prtd->dma_pos = prtd->dma_start;
+}
+
+static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
+ void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ snd_pcm_period_elapsed(substream);
+
+ jz4740_pcm_start_transfer(prtd, substream->stream);
+}
+
+static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct jz4740_pcm_config *config;
+
+ config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
+ if (!prtd->dma) {
+ const char *dma_channel_name;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_channel_name = "PCM Playback";
+ else
+ dma_channel_name = "PCM Capture";
+
+ prtd->dma = jz4740_dma_request(substream, dma_channel_name);
+ }
+
+ if (!prtd->dma)
+ return -EBUSY;
+
+ jz4740_dma_configure(prtd->dma, &config->dma_config);
+ prtd->fifo_addr = config->fifo_addr;
+
+ jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->dma_period = params_period_bytes(params);
+ prtd->dma_start = runtime->dma_addr;
+ prtd->dma_pos = prtd->dma_start;
+ prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
+
+ return 0;
+}
+
+static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ if (prtd->dma) {
+ jz4740_dma_free(prtd->dma);
+ prtd->dma = NULL;
+ }
+
+ return 0;
+}
+
+static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ if (!prtd->dma)
+ return 0;
+
+ prtd->dma_pos = prtd->dma_start;
+
+ return ret;
+}
+
+static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ jz4740_pcm_start_transfer(prtd, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ jz4740_dma_disable(prtd->dma);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+ unsigned long count, pos;
+ snd_pcm_uframes_t offset;
+ struct jz4740_dma_chan *dma = prtd->dma;
+
+ count = jz4740_dma_get_residue(dma);
+ if (prtd->dma_pos == prtd->dma_start)
+ pos = prtd->dma_end - prtd->dma_start - count;
+ else
+ pos = prtd->dma_pos - prtd->dma_start - count;
+
+ offset = bytes_to_frames(runtime, pos);
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int jz4740_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd;
+
+ snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
+ prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
+
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ return 0;
+}
+
+static int jz4740_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct jz4740_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static struct snd_pcm_ops jz4740_pcm_ops = {
+ .open = jz4740_pcm_open,
+ .close = jz4740_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = jz4740_pcm_hw_params,
+ .hw_free = jz4740_pcm_hw_free,
+ .prepare = jz4740_pcm_prepare,
+ .trigger = jz4740_pcm_trigger,
+ .pointer = jz4740_pcm_pointer,
+ .mmap = jz4740_pcm_mmap,
+};
+
+static int jz4740_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 = jz4740_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void jz4740_pcm_free(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_noncoherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
+
+int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &jz4740_pcm_dmamask;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dai->playback.channels_min) {
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto err;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+struct snd_soc_platform jz4740_soc_platform = {
+ .name = "jz4740-pcm",
+ .pcm_ops = &jz4740_pcm_ops,
+ .pcm_new = jz4740_pcm_new,
+ .pcm_free = jz4740_pcm_free,
+};
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
+
+static int __init jz4740_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&jz4740_soc_platform);
+}
+module_init(jz4740_soc_platform_init);
+
+static void __exit jz4740_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&jz4740_soc_platform);
+}
+module_exit(jz4740_soc_platform_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
new file mode 100644
index 0000000..e3f221e
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * 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 _JZ4740_PCM_H
+#define _JZ4740_PCM_H
+
+#include <linux/dma-mapping.h>
+#include <asm/mach-jz4740/dma.h>
+
+/* platform data */
+extern struct snd_soc_platform jz4740_soc_platform;
+
+struct jz4740_pcm_config {
+ struct jz4740_dma_config dma_config;
+ phys_addr_t fifo_addr;
+};
+
+#endif
--
1.5.6.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver
[not found] <1275505397-16758-1-git-send-email-lars@metafoo.de>
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
@ 2010-06-02 19:15 ` Lars-Peter Clausen
2010-06-03 17:57 ` Mark Brown
2 siblings, 1 reply; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-02 19:15 UTC (permalink / raw)
To: Ralf Baechle
Cc: linux-mips, alsa-devel, Lars-Peter Clausen, Mark Brown,
linux-kernel, Liam Girdwood
This patch adds ASoC support for the qi_lb60 board.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Liam Girdwood <lrg@slimlogic.co.uk>
Cc: alsa-devel@alsa-project.org
---
sound/soc/jz4740/Kconfig | 8 ++
sound/soc/jz4740/Makefile | 4 +
sound/soc/jz4740/qi_lb60.c | 175 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 187 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/jz4740/qi_lb60.c
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 39df949..aaa116f 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -11,3 +11,11 @@ config SND_JZ4740_SOC_I2S
tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
+
+config SND_JZ4740_SOC_QI_LB60
+ tristate "SoC Audio support for Qi Hardware Ben Nanonote"
+ depends on SND_JZ4740_SOC && JZ4740_QI_LB60
+ select SND_JZ4740_SOC_I2S
+ select SND_SOC_JZ4740_CODEC
+ help
+ Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4740 QI_LB60 board.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
index 1be8d19..be873c1 100644
--- a/sound/soc/jz4740/Makefile
+++ b/sound/soc/jz4740/Makefile
@@ -7,3 +7,7 @@ snd-soc-jz4740-i2s-objs := jz4740-i2s.o
obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+# Jz4740 Machine Support
+snd-soc-qi-lb60-objs := qi_lb60.o
+
+obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
new file mode 100644
index 0000000..c3f1f91
--- /dev/null
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+
+#include "../codecs/jz4740-codec.h"
+#include "jz4740-pcm.h"
+#include "jz4740-i2s.h"
+
+
+#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
+#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
+
+static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *ctrl, int event)
+{
+ int on = 0;
+ if (event & SND_SOC_DAPM_POST_PMU)
+ on = 1;
+ else if (event & SND_SOC_DAPM_PRE_PMD)
+ on = 0;
+
+ gpio_set_value(QI_LB60_SND_GPIO, on);
+ gpio_set_value(QI_LB60_AMP_GPIO, on);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget qi_lb60_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route qi_lb60_routes[] = {
+ {"Mic", NULL, "MIC"},
+ {"Speaker", NULL, "LOUT"},
+ {"Speaker", NULL, "ROUT"},
+};
+
+#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
+ SND_SOC_DAIFMT_NB_NF | \
+ SND_SOC_DAIFMT_CBM_CFM)
+
+static int qi_lb60_codec_init(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = codec->socdev->card->dai_link->codec_dai;
+
+ snd_soc_dapm_nc_pin(codec, "LIN");
+ snd_soc_dapm_nc_pin(codec, "RIN");
+
+ ret = snd_soc_dai_set_fmt(codec_dai, QI_LB60_DAIFMT);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set codec dai format: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets));
+
+ snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link qi_lb60_dai = {
+ .name = "jz4740-codec",
+ .stream_name = "jz4740-codec",
+ .cpu_dai = &jz4740_i2s_dai,
+ .codec_dai = &jz4740_codec_dai,
+ .init = qi_lb60_codec_init,
+};
+
+static struct snd_soc_card qi_lb60 = {
+ .name = "QI LB60",
+ .dai_link = &qi_lb60_dai,
+ .num_links = 1,
+ .platform = &jz4740_soc_platform,
+};
+
+static struct snd_soc_device qi_lb60_snd_devdata = {
+ .card = &qi_lb60,
+ .codec_dev = &soc_codec_dev_jz4740_codec,
+};
+
+static struct platform_device *qi_lb60_snd_device;
+
+static int __init qi_lb60_init(void)
+{
+ int ret;
+
+ qi_lb60_snd_device = platform_device_alloc("soc-audio", -1);
+
+ if (!qi_lb60_snd_device)
+ return -ENOMEM;
+
+
+ ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
+ QI_LB60_SND_GPIO, ret);
+ goto err_device_put;
+ }
+
+ ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
+ QI_LB60_AMP_GPIO, ret);
+ goto err_gpio_free_snd;
+ }
+
+ gpio_direction_output(JZ_GPIO_PORTB(29), 0);
+ gpio_direction_output(JZ_GPIO_PORTD(4), 0);
+
+ platform_set_drvdata(qi_lb60_snd_device, &qi_lb60_snd_devdata);
+ qi_lb60_snd_devdata.dev = &qi_lb60_snd_device->dev;
+ ret = platform_device_add(qi_lb60_snd_device);
+ if (ret) {
+ pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret);
+ goto err_unset_pdata;
+ }
+
+ return 0;
+
+err_unset_pdata:
+ platform_set_drvdata(qi_lb60_snd_device, NULL);
+/*err_gpio_free_amp:*/
+ gpio_free(QI_LB60_AMP_GPIO);
+err_gpio_free_snd:
+ gpio_free(QI_LB60_SND_GPIO);
+err_device_put:
+ platform_device_put(qi_lb60_snd_device);
+
+ return ret;
+}
+module_init(qi_lb60_init);
+
+static void __exit qi_lb60_exit(void)
+{
+ gpio_free(QI_LB60_AMP_GPIO);
+ gpio_free(QI_LB60_SND_GPIO);
+ platform_device_unregister(qi_lb60_snd_device);
+}
+module_exit(qi_lb60_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
+MODULE_LICENSE("GPL v2");
--
1.5.6.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
@ 2010-06-03 3:36 ` Wan ZongShun
2010-06-03 12:48 ` Liam Girdwood
2010-06-03 17:55 ` Mark Brown
2 siblings, 0 replies; 22+ messages in thread
From: Wan ZongShun @ 2010-06-03 3:36 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle,
Liam Girdwood
Hi,
I have some minor comments below:
> This patch adds ASoC support for JZ4740 SoCs I2S module.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: alsa-devel@alsa-project.org
> ---
> sound/soc/Kconfig | 1 +
> sound/soc/Makefile | 1 +
> sound/soc/jz4740/Kconfig | 13 +
> sound/soc/jz4740/Makefile | 9 +
> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-i2s.h | 18 ++
> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-pcm.h | 22 ++
> 8 files changed, 982 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/jz4740/Kconfig
> create mode 100644 sound/soc/jz4740/Makefile
> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>
> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> index b1749bc..5a7a724 100644
> --- a/sound/soc/Kconfig
> +++ b/sound/soc/Kconfig
> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
> source "sound/soc/s6000/Kconfig"
> source "sound/soc/sh/Kconfig"
> source "sound/soc/txx9/Kconfig"
> +source "sound/soc/jz4740/Kconfig"
>
> # Supported codecs
> source "sound/soc/codecs/Kconfig"
> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
> index 1470141..fdbe74d 100644
> --- a/sound/soc/Makefile
> +++ b/sound/soc/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
> obj-$(CONFIG_SND_SOC) += s6000/
> obj-$(CONFIG_SND_SOC) += sh/
> obj-$(CONFIG_SND_SOC) += txx9/
> +obj-$(CONFIG_SND_SOC) += jz4740/
should keep the Kconfig and Makefile sorted, this helps avoid merge
issues.
except above issue, Also, I see many warning infos when using checkpatch tool,as following:
WARNING: line over 80 characters
#358: FILE: sound/soc/jz4740/jz4740-i2s.c:204:
+ conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
WARNING: line over 80 characters
#536: FILE: sound/soc/jz4740/jz4740-i2s.c:382:
+static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable
#587: FILE: sound/soc/jz4740/jz4740-i2s.c:433:
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
WARNING: line over 80 characters
#813: FILE: sound/soc/jz4740/jz4740-pcm.c:61:
+static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable
#1086: FILE: sound/soc/jz4740/jz4740-pcm.c:334:
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
total: 0 errors, 5 warnings, 991 lines checked
> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
> new file mode 100644
> index 0000000..39df949
> --- /dev/null
> +++ b/sound/soc/jz4740/Kconfig
> @@ -0,0 +1,13 @@
> +config SND_JZ4740_SOC
> + tristate "SoC Audio for Ingenic JZ4740 SoC"
> + depends on SOC_JZ4740 && SND_SOC
> + help
> + Say Y or M if you want to add support for codecs attached to
> + the Jz4740 AC97, I2S or SSP interface. You will also need
> + to select the audio interfaces to support below.
> +
> +config SND_JZ4740_SOC_I2S
> + depends on SND_JZ4740_SOC
> + tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
> + help
> + Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
> diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
> new file mode 100644
> index 0000000..1be8d19
> --- /dev/null
> +++ b/sound/soc/jz4740/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Jz4740 Platform Support
> +#
> +snd-soc-jz4740-objs := jz4740-pcm.o
> +snd-soc-jz4740-i2s-objs := jz4740-i2s.o
> +
> +obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
> +obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
> +
> diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
> new file mode 100644
> index 0000000..2b139fd
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/initval.h>
> +
> +#include "jz4740-i2s.h"
> +#include "jz4740-pcm.h"
> +
> +#define JZ_REG_AIC_CONF 0x00
> +#define JZ_REG_AIC_CTRL 0x04
> +#define JZ_REG_AIC_I2S_FMT 0x10
> +#define JZ_REG_AIC_FIFO_STATUS 0x14
> +#define JZ_REG_AIC_I2S_STATUS 0x1c
> +#define JZ_REG_AIC_CLK_DIV 0x30
> +#define JZ_REG_AIC_FIFO 0x34
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
> +#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
> +#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
> +#define JZ_AIC_CONF_I2S BIT(4)
> +#define JZ_AIC_CONF_RESET BIT(3)
> +#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
> +#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
> +#define JZ_AIC_CONF_ENABLE BIT(0)
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
> +#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
> +#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
> +#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
> +#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
> +#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
> +#define JZ_AIC_CTRL_FLUSH BIT(8)
> +#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
> +#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
> +#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
> +#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
> +#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
> +#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
> +#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
> +
> +#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
> +#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
> +#define JZ_AIC_I2S_FMT_MSB BIT(0)
> +
> +#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
> +
> +#define JZ_AIC_CLK_DIV_MASK 0xf
> +
> +struct jz4740_i2s {
> + struct resource *mem;
> + void __iomem *base;
> + dma_addr_t phys_base;
> +
> + struct clk *clk_aic;
> + struct clk *clk_i2s;
> +
> + struct jz4740_pcm_config pcm_config_playback;
> + struct jz4740_pcm_config pcm_config_capture;
> +};
> +
> +static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
> + unsigned int reg)
> +{
> + return readl(i2s->base + reg);
> +}
> +
> +static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
> + unsigned int reg, uint32_t value)
> +{
> + writel(value, i2s->base + reg);
> +}
> +
> +static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
> +{
> + return dai->private_data;
> +}
> +
> +static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf, ctrl;
> +
> + if (dai->active)
> + return 0;
> +
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> + ctrl |= JZ_AIC_CTRL_FLUSH;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (!dai->active)
> + return;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> +}
> +
> +
> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> + uint32_t ctrl;
> + uint32_t mask;
> +
> + if (playback)
> + mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
> + else
> + mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ctrl |= mask;
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ctrl &= ~mask;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + return 0;
> +}
> +
> +
> +static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + uint32_t format = 0;
> + uint32_t conf;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> +
> + conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBS_CFS:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
> + format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFS:
> + conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBS_CFM:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_MSB:
> + format |= JZ_AIC_I2S_FMT_MSB;
> + break;
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> + enum jz4740_dma_width dma_width;
> + struct jz4740_pcm_config *pcm_config;
> + unsigned int sample_size;
> + uint32_t ctrl;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + sample_size = 0;
> + dma_width = JZ4740_DMA_WIDTH_8BIT;
> + break;
> + case SNDRV_PCM_FORMAT_S16:
> + sample_size = 1;
> + dma_width = JZ4740_DMA_WIDTH_16BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (playback) {
> + ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
> + } else {
> + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
> + }
> +
> + switch (params_channels(params)) {
> + case 2:
> + break;
> + case 1:
> + if (playback) {
> + ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
> + break;
> + }
> + default: /* Falltrough */
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + if (playback) {
> + pcm_config = &i2s->pcm_config_playback;
> + pcm_config->dma_config.dst_width = dma_width;
> + } else {
> + pcm_config = &i2s->pcm_config_capture;
> + pcm_config->dma_config.src_width = dma_width;
> + }
> +
> +
> + snd_soc_dai_set_dma_data(dai, substream, pcm_config);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + int ret = 0;
> + struct clk *parent;
> +
> + switch (clk_id) {
> + case JZ4740_I2S_CLKSRC_EXT:
> + parent = clk_get(NULL, "ext");
> + clk_set_parent(i2s->clk_i2s, parent);
> + break;
> + case JZ4740_I2S_CLKSRC_PLL:
> + parent = clk_get(NULL, "pll half");
> + clk_set_parent(i2s->clk_i2s, parent);
> + ret = clk_set_rate(i2s->clk_i2s, freq);
> + break;
> + default:
> + return -EINVAL;
> + }
> + clk_put(parent);
> +
> + return ret;
> +}
> +
> +static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_resume(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + clk_enable(i2s->clk_aic);
> +
> + if (dai->active) {
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
> + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
> + JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
> + JZ_AIC_CONF_I2S |
> + JZ_AIC_CONF_INTERNAL_CODEC;
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
> + .startup = jz4740_i2s_startup,
> + .shutdown = jz4740_i2s_shutdown,
> + .trigger = jz4740_i2s_trigger,
> + .hw_params = jz4740_i2s_hw_params,
> + .set_fmt = jz4740_i2s_set_fmt,
> + .set_clkdiv = jz4740_i2s_set_clkdiv,
> + .set_sysclk = jz4740_i2s_set_sysclk,
> +};
> +
> +#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
> + SNDRV_PCM_FMTBIT_S16_LE)
> +
> +struct snd_soc_dai jz4740_i2s_dai = {
> + .name = "jz4740-i2s",
> + .probe = jz4740_i2s_probe,
> + .playback = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .capture = {
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .symmetric_rates = 1,
> + .ops = &jz4740_i2s_dai_ops,
> + .suspend = jz4740_i2s_suspend,
> + .resume = jz4740_i2s_resume,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
> +
> +static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
> +{
> + struct jz4740_dma_config *dma_config;
> +
> + /* Playback */
> + dma_config = &i2s->pcm_config_playback.dma_config;
> + dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
> + dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +
> + /* Capture */
> + dma_config = &i2s->pcm_config_capture.dma_config;
> + dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
> + dma_config->flags = JZ4740_DMA_DST_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +}
> +
> +static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s;
> + int ret;
> +
> + i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
> +
> + if (!i2s)
> + return -ENOMEM;
> +
> + i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!i2s->mem) {
> + ret = -ENOENT;
> + goto err_free;
> + }
> +
> + i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
> + pdev->name);
> +
> + if (!i2s->mem) {
> + ret = -EBUSY;
> + goto err_free;
> + }
> +
> + i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
> +
> + if (!i2s->base) {
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + i2s->phys_base = i2s->mem->start;
> +
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
> +
> + clk_enable(i2s->clk_aic);
> +
> + jz4740_i2c_init_pcm_config(i2s);
> +
> + jz4740_i2s_dai.private_data = i2s;
> + ret = snd_soc_register_dai(&jz4740_i2s_dai);
> +
> + platform_set_drvdata(pdev, i2s);
> +
> + return 0;
> +
> +err_iounmap:
> + iounmap(i2s->base);
> +err_release_mem_region:
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +err_free:
> + kfree(i2s);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_i2s_dai);
> +
> + clk_disable(i2s->clk_aic);
> + clk_put(i2s->clk_i2s);
> + clk_put(i2s->clk_aic);
> +
> + iounmap(i2s->base);
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(i2s);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_i2s_driver = {
> + .probe = jz4740_i2s_dev_probe,
> + .remove = __devexit_p(jz4740_i2s_dev_remove),
> + .driver = {
> + .name = "jz4740-i2s",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_i2s_init(void)
> +{
> + return platform_driver_register(&jz4740_i2s_driver);
> +}
> +module_init(jz4740_i2s_init);
> +
> +static void __exit jz4740_i2s_exit(void)
> +{
> + platform_driver_unregister(&jz4740_i2s_driver);
> +}
> +module_exit(jz4740_i2s_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:jz4740-i2s");
> diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
> new file mode 100644
> index 0000000..da22ed8
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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 _JZ4740_I2S_H
> +#define _JZ4740_I2S_H
> +
> +/* I2S clock source */
> +#define JZ4740_I2S_CLKSRC_EXT 0
> +#define JZ4740_I2S_CLKSRC_PLL 1
> +
> +#define JZ4740_I2S_BIT_CLK 0
> +
> +extern struct snd_soc_dai jz4740_i2s_dai;
> +
> +#endif
> diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
> new file mode 100644
> index 0000000..fd1c203
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.c
> @@ -0,0 +1,350 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/mach-jz4740/dma.h>
> +#include "jz4740-pcm.h"
> +
> +struct jz4740_runtime_data {
> + unsigned int dma_period;
> + dma_addr_t dma_start;
> + dma_addr_t dma_pos;
> + dma_addr_t dma_end;
> +
> + struct jz4740_dma_chan *dma;
> +
> + dma_addr_t fifo_addr;
> +};
> +
> +/* identify hardware playback capabilities */
> +static const struct snd_pcm_hardware jz4740_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_MMAP_VALID |
> + SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BLOCK_TRANSFER,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> +
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .channels_min = 1,
> + .channels_max = 2,
> + .period_bytes_min = 16,
> + .period_bytes_max = 2 * PAGE_SIZE,
> + .periods_min = 2,
> + .periods_max = 128,
> + .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
> + .fifo_size = 32,
> +};
> +
> +static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
> +{
> + unsigned int count;
> +
> + if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
> + count = prtd->dma_end - prtd->dma_pos;
> + else
> + count = prtd->dma_period;
> +
> + jz4740_dma_disable(prtd->dma);
> +
> + if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
> + } else {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
> + }
> +
> + jz4740_dma_set_transfer_count(prtd->dma, count);
> +
> + jz4740_dma_enable(prtd->dma);
> +
> + prtd->dma_pos += prtd->dma_period;
> + if (prtd->dma_pos >= prtd->dma_end)
> + prtd->dma_pos = prtd->dma_start;
> +}
> +
> +static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
> + void *dev_id)
> +{
> + struct snd_pcm_substream *substream = dev_id;
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + snd_pcm_period_elapsed(substream);
> +
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> +}
> +
> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct jz4740_pcm_config *config;
> +
> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
> + if (!prtd->dma) {
> + const char *dma_channel_name;
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + dma_channel_name = "PCM Playback";
> + else
> + dma_channel_name = "PCM Capture";
> +
> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
> + }
> +
> + if (!prtd->dma)
> + return -EBUSY;
> +
> + jz4740_dma_configure(prtd->dma, &config->dma_config);
> + prtd->fifo_addr = config->fifo_addr;
> +
> + jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
> +
> + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> + runtime->dma_bytes = params_buffer_bytes(params);
> +
> + prtd->dma_period = params_period_bytes(params);
> + prtd->dma_start = runtime->dma_addr;
> + prtd->dma_pos = prtd->dma_start;
> + prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> +
> + snd_pcm_set_runtime_buffer(substream, NULL);
> + if (prtd->dma) {
> + jz4740_dma_free(prtd->dma);
> + prtd->dma = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> + int ret = 0;
> +
> + if (!prtd->dma)
> + return 0;
> +
> + prtd->dma_pos = prtd->dma_start;
> +
> + return ret;
> +}
> +
> +static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + int ret = 0;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + jz4740_dma_disable(prtd->dma);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + unsigned long count, pos;
> + snd_pcm_uframes_t offset;
> + struct jz4740_dma_chan *dma = prtd->dma;
> +
> + count = jz4740_dma_get_residue(dma);
> + if (prtd->dma_pos == prtd->dma_start)
> + pos = prtd->dma_end - prtd->dma_start - count;
> + else
> + pos = prtd->dma_pos - prtd->dma_start - count;
> +
> + offset = bytes_to_frames(runtime, pos);
> + if (offset >= runtime->buffer_size)
> + offset = 0;
> +
> + return offset;
> +}
> +
> +static int jz4740_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd;
> +
> + snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
> + prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
> +
> + if (prtd == NULL)
> + return -ENOMEM;
> +
> + runtime->private_data = prtd;
> + return 0;
> +}
> +
> +static int jz4740_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + kfree(prtd);
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
> + struct vm_area_struct *vma)
> +{
> + return remap_pfn_range(vma, vma->vm_start,
> + substream->dma_buffer.addr >> PAGE_SHIFT,
> + vma->vm_end - vma->vm_start, vma->vm_page_prot);
> +}
> +
> +static struct snd_pcm_ops jz4740_pcm_ops = {
> + .open = jz4740_pcm_open,
> + .close = jz4740_pcm_close,
> + .ioctl = snd_pcm_lib_ioctl,
> + .hw_params = jz4740_pcm_hw_params,
> + .hw_free = jz4740_pcm_hw_free,
> + .prepare = jz4740_pcm_prepare,
> + .trigger = jz4740_pcm_trigger,
> + .pointer = jz4740_pcm_pointer,
> + .mmap = jz4740_pcm_mmap,
> +};
> +
> +static int jz4740_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 = jz4740_pcm_hardware.buffer_bytes_max;
> +
> + buf->dev.type = SNDRV_DMA_TYPE_DEV;
> + buf->dev.dev = pcm->card->dev;
> + buf->private_data = NULL;
> +
> + buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
> + &buf->addr, GFP_KERNEL);
> + if (!buf->area)
> + return -ENOMEM;
> +
> + buf->bytes = size;
> +
> + return 0;
> +}
> +
> +static void jz4740_pcm_free(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_noncoherent(pcm->card->dev, buf->bytes,
> + buf->area, buf->addr);
> + buf->area = NULL;
> + }
> +}
> +
> +static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
> +
> +int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> + struct snd_pcm *pcm)
> +{
> + int ret = 0;
> +
> + if (!card->dev->dma_mask)
> + card->dev->dma_mask = &jz4740_pcm_dmamask;
> +
> + if (!card->dev->coherent_dma_mask)
> + card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + if (dai->playback.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_PLAYBACK);
> + if (ret)
> + goto err;
> + }
> +
> + if (dai->capture.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_CAPTURE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + return ret;
> +}
> +
> +struct snd_soc_platform jz4740_soc_platform = {
> + .name = "jz4740-pcm",
> + .pcm_ops = &jz4740_pcm_ops,
> + .pcm_new = jz4740_pcm_new,
> + .pcm_free = jz4740_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_soc_platform);
> +
> +static int __init jz4740_soc_platform_init(void)
> +{
> + return snd_soc_register_platform(&jz4740_soc_platform);
> +}
> +module_init(jz4740_soc_platform_init);
> +
> +static void __exit jz4740_soc_platform_exit(void)
> +{
> + snd_soc_unregister_platform(&jz4740_soc_platform);
> +}
> +module_exit(jz4740_soc_platform_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
> new file mode 100644
> index 0000000..e3f221e
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.h
> @@ -0,0 +1,22 @@
> +/*
> + *
> + * 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 _JZ4740_PCM_H
> +#define _JZ4740_PCM_H
> +
> +#include <linux/dma-mapping.h>
> +#include <asm/mach-jz4740/dma.h>
> +
> +/* platform data */
> +extern struct snd_soc_platform jz4740_soc_platform;
> +
> +struct jz4740_pcm_config {
> + struct jz4740_dma_config dma_config;
> + phys_addr_t fifo_addr;
> +};
> +
> +#endif
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [alsa-devel] [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
@ 2010-06-03 5:45 ` Wan ZongShun
2010-06-03 12:03 ` Mark Brown
2010-06-03 12:32 ` Liam Girdwood
2010-06-03 17:49 ` Mark Brown
2 siblings, 1 reply; 22+ messages in thread
From: Wan ZongShun @ 2010-06-03 5:45 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Ralf Baechle, linux-mips, alsa-devel, Mark Brown, linux-kernel,
Liam Girdwood
Hi Lars-Peter Clausen,
Your all the patches have two kinds of 'WARNING:',as following:
(1)'WARNING: line over 80 characters' and
Please make a line less than 80 characters.
(2)'WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable'
I can illustrate by a example,bellow:
struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
.probe = jz4740_codec_dev_probe,
.remove = jz4740_codec_dev_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
I think the ';' should be removed according to 'checkpatch.pl', but Andrew Morton just said the 'warning'
reason from the checkpatch script's own BUG, so far, I could not get any comments about this.
So, Mark & Liam, any comments regarding this 'checkpatch script's own BUG'?
Lars-Peter Clausen:
> This patch adds support for the JZ4740 internal codec.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: alsa-devel@alsa-project.org
> ---
> sound/soc/codecs/Kconfig | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> sound/soc/codecs/jz4740-codec.h | 20 ++
> 4 files changed, 528 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/codecs/jz4740-codec.c
> create mode 100644 sound/soc/codecs/jz4740-codec.h
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 31ac553..b8008df 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
> select SND_SOC_AK4671 if I2C
> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
> select SND_SOC_CS4270 if I2C
> + select SND_SOC_JZ4740 if SOC_JZ4740
> select SND_SOC_MAX9877 if I2C
> select SND_SOC_DA7210 if I2C
> select SND_SOC_PCM3008
> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
> config SND_SOC_WM9713
> tristate
>
> +config SND_SOC_JZ4740_CODEC
> + tristate
> +
> # Amp
> config SND_SOC_MAX9877
> tristate
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 91429ea..4c7ee31 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>
> # Amp
> snd-soc-max9877-objs := max9877.o
> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>
> # Amp
> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
> new file mode 100644
> index 0000000..6e4b741
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.c
> @@ -0,0 +1,502 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/delay.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/soc.h>
> +
> +#define JZ4740_REG_CODEC_1 0x0
> +#define JZ4740_REG_CODEC_2 0x1
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
> +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
> +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
> +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
> +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
> +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
> +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
> +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
> +#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
> +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
> +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
> +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
> +#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
> +#define JZ4740_CODEC_1_SUSPEND BIT(1)
> +#define JZ4740_CODEC_1_RESET BIT(0)
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
> +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
> +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
> +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
> +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
> +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
> +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
> +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
> +
> +struct jz4740_codec {
> + void __iomem *base;
> + struct resource *mem;
> +
> + uint32_t reg_cache[2];
> + struct snd_soc_codec codec;
> +};
> +
> +static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
> +{
> + return container_of(codec, struct jz4740_codec, codec);
> +}
> +
> +static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + return readl(jz4740_codec->base + (reg << 2));
> +}
> +
> +static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
> + unsigned int val)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + jz4740_codec->reg_cache[reg] = val;
> +
> + writel(val, jz4740_codec->base + (reg << 2));
> + return 0;
> +}
> +
> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
> +
> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
> + jz4740_codec_output_controls,
> + ARRAY_SIZE(jz4740_codec_output_controls)),
> +
> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
> + jz4740_codec_input_controls,
> + ARRAY_SIZE(jz4740_codec_input_controls)),
> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
> +
> + SND_SOC_DAPM_OUTPUT("LOUT"),
> + SND_SOC_DAPM_OUTPUT("ROUT"),
> +
> + SND_SOC_DAPM_INPUT("MIC"),
> + SND_SOC_DAPM_INPUT("LIN"),
> + SND_SOC_DAPM_INPUT("RIN"),
> +};
> +
> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
> +
> + {"Line Input", NULL, "LIN"},
> + {"Line Input", NULL, "RIN"},
> +
> + {"Input Mixer", "Line Capture Switch", "Line Input"},
> + {"Input Mixer", "Mic Capture Switch", "MIC"},
> +
> + {"ADC", NULL, "Input Mixer"},
> +
> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
> + {"Output Mixer", "DAC Switch", "DAC"},
> +
> + {"LOUT", NULL, "Output Mixer"},
> + {"ROUT", NULL, "Output Mixer"},
> +};
> +
> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + uint32_t val;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_device *socdev = rtd->socdev;
> + struct snd_soc_codec *codec = socdev->card->codec;
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + case SNDRV_PCM_FORMAT_S16_LE:
> + case SNDRV_PCM_FORMAT_S18_3LE:
> + break;
> + default:
> + return -EINVAL;
> + break;
> + }
> +
> + switch (params_rate(params)) {
> + case 8000:
> + val = 0;
> + break;
> + case 11025:
> + val = 1;
> + break;
> + case 12000:
> + val = 2;
> + break;
> + case 16000:
> + val = 3;
> + break;
> + case 22050:
> + val = 4;
> + break;
> + case 24000:
> + val = 5;
> + break;
> + case 32000:
> + val = 6;
> + break;
> + case 44100:
> + val = 7;
> + break;
> + case 48000:
> + val = 8;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
> + .hw_params = jz4740_codec_hw_params,
> + .set_fmt = jz4740_codec_set_fmt,
> +};
> +
> +struct snd_soc_dai jz4740_codec_dai = {
> + .name = "jz-codec",
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .ops = &jz4740_codec_dai_ops,
> + .symmetric_rates = 1,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_codec_dai);
> +
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
> + }
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
> + break;
> + case SND_SOC_BIAS_PREPARE:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT);
> + break;
> + case SND_SOC_BIAS_STANDBY:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
> + break;
> + case SND_SOC_BIAS_OFF:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
> + break;
> + }
> + codec->bias_level = level;
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_codec *jz4740_codec_codec;
> +
> +static int jz4740_codec_dev_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + struct snd_soc_codec *codec = jz4740_codec_codec;
> +
> + BUG_ON(!codec);
> +
> + socdev->card->codec = codec;
> +
> + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
> + return ret;
> + }
> +
> + snd_soc_add_controls(codec, jz4740_codec_controls,
> + ARRAY_SIZE(jz4740_codec_controls));
> +
> + snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
> + ARRAY_SIZE(jz4740_codec_dapm_widgets));
> +
> + snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
> + ARRAY_SIZE(jz4740_codec_dapm_routes));
> +
> + snd_soc_dapm_new_widgets(codec);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_dev_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + snd_soc_free_pcms(socdev);
> + snd_soc_dapm_free(socdev);
> +
> + return 0;
> +}
> +
> +struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
> + .probe = jz4740_codec_dev_probe,
> + .remove = jz4740_codec_dev_remove,
> +};
> +EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
> +
> +static int __devinit jz4740_codec_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_codec *jz4740_codec;
> + struct snd_soc_codec *codec;
> +
> + jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
> +
> + if (!jz4740_codec)
> + return -ENOMEM;
> +
> + jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
> + ret = -ENOENT;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
> + resource_size(jz4740_codec->mem), pdev->name);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + ret = -EBUSY;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + if (!jz4740_codec->base) {
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + jz4740_codec_dai.dev = &pdev->dev;
> +
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
> + codec->owner = THIS_MODULE;
> +
> + codec->read = jz4740_codec_read;
> + codec->write = jz4740_codec_write;
> + codec->set_bias_level = jz4740_codec_set_bias_level;
> + codec->bias_level = SND_SOC_BIAS_OFF;
> +
> + codec->dai = &jz4740_codec_dai;
> + codec->num_dai = 1;
> +
> + codec->reg_cache = jz4740_codec->reg_cache;
> + codec->reg_cache_size = 2;
> +
> + mutex_init(&codec->mutex);
> + INIT_LIST_HEAD(&codec->dapm_widgets);
> + INIT_LIST_HEAD(&codec->dapm_paths);
> +
> + jz4740_codec_codec = codec;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
> +
> +
> + platform_set_drvdata(pdev, jz4740_codec);
> + ret = snd_soc_register_codec(codec);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec\n");
> + goto err_iounmap;
> + }
> +
> + ret = snd_soc_register_dai(&jz4740_codec_dai);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec dai\n");
> + goto err_unregister_codec;
> + }
> +
> + jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> + return 0;
> +err_unregister_codec:
> + snd_soc_unregister_codec(codec);
> +err_iounmap:
> + iounmap(jz4740_codec->base);
> +err_release_mem_region:
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +err_free_codec:
> + kfree(jz4740_codec);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_codec_remove(struct platform_device *pdev)
> +{
> + struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_codec_dai);
> + snd_soc_unregister_codec(&jz4740_codec->codec);
> +
> + iounmap(jz4740_codec->base);
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(jz4740_codec);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_codec_driver = {
> + .probe = jz4740_codec_probe,
> + .remove = __devexit_p(jz4740_codec_remove),
> + .driver = {
> + .name = "jz4740-codec",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_codec_init(void)
> +{
> + return platform_driver_register(&jz4740_codec_driver);
> +}
> +module_init(jz4740_codec_init);
> +
> +static void __exit jz4740_codec_exit(void)
> +{
> + platform_driver_unregister(&jz4740_codec_driver);
> +}
> +module_exit(jz4740_codec_exit);
> +
> +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:jz-codec");
> diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
> new file mode 100644
> index 0000000..b5a0691
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
> +#define __SND_SOC_CODECS_JZ4740_CODEC_H__
> +
> +extern struct snd_soc_dai jz4740_codec_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
> +
> +#endif
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-03 5:45 ` [alsa-devel] " Wan ZongShun
@ 2010-06-03 12:03 ` Mark Brown
0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2010-06-03 12:03 UTC (permalink / raw)
To: Wan ZongShun
Cc: linux-mips, alsa-devel, Lars-Peter Clausen, linux-kernel,
Ralf Baechle, Liam Girdwood
On Thu, Jun 03, 2010 at 01:45:21PM +0800, Wan ZongShun wrote:
> Your all the patches have two kinds of 'WARNING:',as following:
> (1)'WARNING: line over 80 characters' and
> Please make a line less than 80 characters.
You need to apply a certain degree of taste when looking at checkpatch
warnings, particularly things like line length. Sometimes fixing the
warning for the sake of fixing the warning makes the code uglier than it
would be with whatever the style issue that's been identified is.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
2010-06-03 5:45 ` [alsa-devel] " Wan ZongShun
@ 2010-06-03 12:32 ` Liam Girdwood
2010-06-03 12:50 ` Liam Girdwood
2010-06-03 16:58 ` Lars-Peter Clausen
2010-06-03 17:49 ` Mark Brown
2 siblings, 2 replies; 22+ messages in thread
From: Liam Girdwood @ 2010-06-03 12:32 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> This patch adds support for the JZ4740 internal codec.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: alsa-devel@alsa-project.org
> ---
> sound/soc/codecs/Kconfig | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> sound/soc/codecs/jz4740-codec.h | 20 ++
> 4 files changed, 528 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/codecs/jz4740-codec.c
> create mode 100644 sound/soc/codecs/jz4740-codec.h
no need for code in file name here.
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 31ac553..b8008df 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
> select SND_SOC_AK4671 if I2C
> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
> select SND_SOC_CS4270 if I2C
> + select SND_SOC_JZ4740 if SOC_JZ4740
> select SND_SOC_MAX9877 if I2C
> select SND_SOC_DA7210 if I2C
> select SND_SOC_PCM3008
> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
> config SND_SOC_WM9713
> tristate
>
> +config SND_SOC_JZ4740_CODEC
> + tristate
> +
> # Amp
> config SND_SOC_MAX9877
> tristate
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 91429ea..4c7ee31 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>
Please use the same format here
> # Amp
> snd-soc-max9877-objs := max9877.o
> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>
ditto.
> # Amp
> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
> new file mode 100644
> index 0000000..6e4b741
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.c
> @@ -0,0 +1,502 @@
> +/*
> + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/delay.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/soc.h>
> +
> +#define JZ4740_REG_CODEC_1 0x0
> +#define JZ4740_REG_CODEC_2 0x1
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
> +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
> +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
> +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
> +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
> +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
> +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
> +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
> +#define JZ4740_CODEC_1_VREF_PULL_DOWN BIT(18)
> +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
> +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
> +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
> +#define JZ4740_CODEC_1_HEADPHONE_CHANGE BIT(12)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_M BIT(11)
> +#define JZ4740_CODEC_1_HEADPHONE_PULL_DOWN_R BIT(10)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M BIT(9)
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN BIT(8)
> +#define JZ4740_CODEC_1_SUSPEND BIT(1)
> +#define JZ4740_CODEC_1_RESET BIT(0)
> +
> +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
> +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
> +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
> +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
> +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
> +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
> +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
> +#define JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET 8
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
> +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
> +
> +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
> +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
> +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
> +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
> +
> +struct jz4740_codec {
> + void __iomem *base;
> + struct resource *mem;
> +
> + uint32_t reg_cache[2];
> + struct snd_soc_codec codec;
> +};
> +
> +static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
> +{
> + return container_of(codec, struct jz4740_codec, codec);
> +}
> +
> +static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, unsigned int reg)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + return readl(jz4740_codec->base + (reg << 2));
> +}
> +
> +static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
> + unsigned int val)
> +{
> + struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
> + jz4740_codec->reg_cache[reg] = val;
> +
> + writel(val, jz4740_codec->base + (reg << 2));
> + return 0;
> +}
> +
> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
Is this the master capture volume ?
> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
> +
> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
> + jz4740_codec_output_controls,
> + ARRAY_SIZE(jz4740_codec_output_controls)),
> +
> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
> + jz4740_codec_input_controls,
> + ARRAY_SIZE(jz4740_codec_input_controls)),
> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
> +
> + SND_SOC_DAPM_OUTPUT("LOUT"),
> + SND_SOC_DAPM_OUTPUT("ROUT"),
> +
> + SND_SOC_DAPM_INPUT("MIC"),
> + SND_SOC_DAPM_INPUT("LIN"),
> + SND_SOC_DAPM_INPUT("RIN"),
> +};
> +
> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
> +
> + {"Line Input", NULL, "LIN"},
> + {"Line Input", NULL, "RIN"},
> +
> + {"Input Mixer", "Line Capture Switch", "Line Input"},
> + {"Input Mixer", "Mic Capture Switch", "MIC"},
> +
> + {"ADC", NULL, "Input Mixer"},
> +
> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
> + {"Output Mixer", "DAC Switch", "DAC"},
> +
> + {"LOUT", NULL, "Output Mixer"},
> + {"ROUT", NULL, "Output Mixer"},
> +};
> +
> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + uint32_t val;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_device *socdev = rtd->socdev;
> + struct snd_soc_codec *codec = socdev->card->codec;
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + case SNDRV_PCM_FORMAT_S16_LE:
> + case SNDRV_PCM_FORMAT_S18_3LE:
> + break;
> + default:
> + return -EINVAL;
> + break;
> + }
The PCM format check is not required here as core checks this.
> +
> + switch (params_rate(params)) {
> + case 8000:
> + val = 0;
> + break;
> + case 11025:
> + val = 1;
> + break;
> + case 12000:
> + val = 2;
> + break;
> + case 16000:
> + val = 3;
> + break;
> + case 22050:
> + val = 4;
> + break;
> + case 24000:
> + val = 5;
> + break;
> + case 32000:
> + val = 6;
> + break;
> + case 44100:
> + val = 7;
> + break;
> + case 48000:
> + val = 8;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
> + JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
> + .hw_params = jz4740_codec_hw_params,
> + .set_fmt = jz4740_codec_set_fmt,
> +};
> +
> +struct snd_soc_dai jz4740_codec_dai = {
> + .name = "jz-codec",
best to use jz4740 here
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> + },
> + .ops = &jz4740_codec_dai_ops,
> + .symmetric_rates = 1,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_codec_dai);
> +
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
> + }
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
> + break;
> + case SND_SOC_BIAS_PREPARE:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT);
> + break;
> + case SND_SOC_BIAS_STANDBY:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE,
> + JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE);
> + break;
> + case SND_SOC_BIAS_OFF:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SUSPEND, JZ4740_CODEC_1_SUSPEND);
> + break;
> + }
> + codec->bias_level = level;
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_codec *jz4740_codec_codec;
> +
> +static int jz4740_codec_dev_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + struct snd_soc_codec *codec = jz4740_codec_codec;
> +
> + BUG_ON(!codec);
> +
> + socdev->card->codec = codec;
> +
> + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
> + return ret;
> + }
> +
> + snd_soc_add_controls(codec, jz4740_codec_controls,
> + ARRAY_SIZE(jz4740_codec_controls));
> +
> + snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
> + ARRAY_SIZE(jz4740_codec_dapm_widgets));
> +
> + snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
> + ARRAY_SIZE(jz4740_codec_dapm_routes));
> +
> + snd_soc_dapm_new_widgets(codec);
> +
> + return 0;
> +}
> +
> +static int jz4740_codec_dev_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + snd_soc_free_pcms(socdev);
> + snd_soc_dapm_free(socdev);
> +
> + return 0;
> +}
> +
> +struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
> + .probe = jz4740_codec_dev_probe,
> + .remove = jz4740_codec_dev_remove,
> +};
> +EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
> +
> +static int __devinit jz4740_codec_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct jz4740_codec *jz4740_codec;
> + struct snd_soc_codec *codec;
> +
> + jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
> +
> + if (!jz4740_codec)
> + return -ENOMEM;
> +
> + jz4740_codec->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
> + ret = -ENOENT;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->mem = request_mem_region(jz4740_codec->mem->start,
> + resource_size(jz4740_codec->mem), pdev->name);
> +
> + if (!jz4740_codec->mem) {
> + dev_err(&pdev->dev, "Failed to request mmio memory region\n");
> + ret = -EBUSY;
> + goto err_free_codec;
> + }
> +
> + jz4740_codec->base = ioremap(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + if (!jz4740_codec->base) {
> + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + jz4740_codec_dai.dev = &pdev->dev;
> +
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
> + codec->owner = THIS_MODULE;
> +
> + codec->read = jz4740_codec_read;
> + codec->write = jz4740_codec_write;
> + codec->set_bias_level = jz4740_codec_set_bias_level;
> + codec->bias_level = SND_SOC_BIAS_OFF;
> +
> + codec->dai = &jz4740_codec_dai;
> + codec->num_dai = 1;
> +
> + codec->reg_cache = jz4740_codec->reg_cache;
> + codec->reg_cache_size = 2;
> +
> + mutex_init(&codec->mutex);
> + INIT_LIST_HEAD(&codec->dapm_widgets);
> + INIT_LIST_HEAD(&codec->dapm_paths);
> +
> + jz4740_codec_codec = codec;
> +
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
> +
> +
> + platform_set_drvdata(pdev, jz4740_codec);
> + ret = snd_soc_register_codec(codec);
> +
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec\n");
> + goto err_iounmap;
> + }
> +
> + ret = snd_soc_register_dai(&jz4740_codec_dai);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register codec dai\n");
> + goto err_unregister_codec;
> + }
> +
> + jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> + return 0;
> +err_unregister_codec:
> + snd_soc_unregister_codec(codec);
> +err_iounmap:
> + iounmap(jz4740_codec->base);
> +err_release_mem_region:
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +err_free_codec:
> + kfree(jz4740_codec);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_codec_remove(struct platform_device *pdev)
> +{
> + struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_codec_dai);
> + snd_soc_unregister_codec(&jz4740_codec->codec);
> +
> + iounmap(jz4740_codec->base);
> + release_mem_region(jz4740_codec->mem->start, resource_size(jz4740_codec->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(jz4740_codec);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_codec_driver = {
> + .probe = jz4740_codec_probe,
> + .remove = __devexit_p(jz4740_codec_remove),
> + .driver = {
> + .name = "jz4740-codec",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_codec_init(void)
> +{
> + return platform_driver_register(&jz4740_codec_driver);
> +}
> +module_init(jz4740_codec_init);
> +
> +static void __exit jz4740_codec_exit(void)
> +{
> + platform_driver_unregister(&jz4740_codec_driver);
> +}
> +module_exit(jz4740_codec_exit);
> +
> +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:jz-codec");
> diff --git a/sound/soc/codecs/jz4740-codec.h b/sound/soc/codecs/jz4740-codec.h
> new file mode 100644
> index 0000000..b5a0691
> --- /dev/null
> +++ b/sound/soc/codecs/jz4740-codec.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
> +#define __SND_SOC_CODECS_JZ4740_CODEC_H__
> +
> +extern struct snd_soc_dai jz4740_codec_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
> +
> +#endif
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
2010-06-03 3:36 ` Wan ZongShun
@ 2010-06-03 12:48 ` Liam Girdwood
2010-06-03 16:50 ` Lars-Peter Clausen
2010-06-03 17:55 ` Mark Brown
2 siblings, 1 reply; 22+ messages in thread
From: Liam Girdwood @ 2010-06-03 12:48 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> This patch adds ASoC support for JZ4740 SoCs I2S module.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: alsa-devel@alsa-project.org
> ---
> sound/soc/Kconfig | 1 +
> sound/soc/Makefile | 1 +
> sound/soc/jz4740/Kconfig | 13 +
> sound/soc/jz4740/Makefile | 9 +
> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-i2s.h | 18 ++
> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
> sound/soc/jz4740/jz4740-pcm.h | 22 ++
> 8 files changed, 982 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/jz4740/Kconfig
> create mode 100644 sound/soc/jz4740/Makefile
> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>
> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
> index b1749bc..5a7a724 100644
> --- a/sound/soc/Kconfig
> +++ b/sound/soc/Kconfig
> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
> source "sound/soc/s6000/Kconfig"
> source "sound/soc/sh/Kconfig"
> source "sound/soc/txx9/Kconfig"
> +source "sound/soc/jz4740/Kconfig"
>
> # Supported codecs
> source "sound/soc/codecs/Kconfig"
> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
> index 1470141..fdbe74d 100644
> --- a/sound/soc/Makefile
> +++ b/sound/soc/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
> obj-$(CONFIG_SND_SOC) += s6000/
> obj-$(CONFIG_SND_SOC) += sh/
> obj-$(CONFIG_SND_SOC) += txx9/
> +obj-$(CONFIG_SND_SOC) += jz4740/
> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
> new file mode 100644
> index 0000000..39df949
> --- /dev/null
> +++ b/sound/soc/jz4740/Kconfig
> @@ -0,0 +1,13 @@
> +config SND_JZ4740_SOC
> + tristate "SoC Audio for Ingenic JZ4740 SoC"
> + depends on SOC_JZ4740 && SND_SOC
> + help
> + Say Y or M if you want to add support for codecs attached to
> + the Jz4740 AC97, I2S or SSP interface. You will also need
Do you have an AC97 or SSP interface ?
> + to select the audio interfaces to support below.
> +
> +config SND_JZ4740_SOC_I2S
> + depends on SND_JZ4740_SOC
> + tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
> + help
> + Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
> diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
> new file mode 100644
> index 0000000..1be8d19
> --- /dev/null
> +++ b/sound/soc/jz4740/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Jz4740 Platform Support
> +#
> +snd-soc-jz4740-objs := jz4740-pcm.o
> +snd-soc-jz4740-i2s-objs := jz4740-i2s.o
> +
> +obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
> +obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
> +
> diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
> new file mode 100644
> index 0000000..2b139fd
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
Formatting looks broken here.
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/initval.h>
> +
> +#include "jz4740-i2s.h"
> +#include "jz4740-pcm.h"
> +
> +#define JZ_REG_AIC_CONF 0x00
> +#define JZ_REG_AIC_CTRL 0x04
> +#define JZ_REG_AIC_I2S_FMT 0x10
> +#define JZ_REG_AIC_FIFO_STATUS 0x14
> +#define JZ_REG_AIC_I2S_STATUS 0x1c
> +#define JZ_REG_AIC_CLK_DIV 0x30
> +#define JZ_REG_AIC_FIFO 0x34
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
> +#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
> +#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
> +#define JZ_AIC_CONF_I2S BIT(4)
> +#define JZ_AIC_CONF_RESET BIT(3)
> +#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
> +#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
> +#define JZ_AIC_CONF_ENABLE BIT(0)
> +
> +#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
> +#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
> +#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
> +#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
> +#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
> +#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
> +#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
> +#define JZ_AIC_CTRL_FLUSH BIT(8)
> +#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
> +#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
> +#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
> +#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
> +#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
> +#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
> +#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
> +
> +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
> +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
> +
> +#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
> +#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
> +#define JZ_AIC_I2S_FMT_MSB BIT(0)
> +
> +#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
> +
> +#define JZ_AIC_CLK_DIV_MASK 0xf
> +
> +struct jz4740_i2s {
> + struct resource *mem;
> + void __iomem *base;
> + dma_addr_t phys_base;
> +
> + struct clk *clk_aic;
> + struct clk *clk_i2s;
> +
> + struct jz4740_pcm_config pcm_config_playback;
> + struct jz4740_pcm_config pcm_config_capture;
> +};
> +
> +static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
> + unsigned int reg)
> +{
> + return readl(i2s->base + reg);
> +}
> +
> +static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
> + unsigned int reg, uint32_t value)
> +{
> + writel(value, i2s->base + reg);
> +}
> +
> +static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
> +{
> + return dai->private_data;
> +}
> +
> +static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf, ctrl;
> +
> + if (dai->active)
> + return 0;
> +
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> + ctrl |= JZ_AIC_CTRL_FLUSH;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (!dai->active)
> + return;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> +}
> +
> +
> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> +
> + uint32_t ctrl;
> + uint32_t mask;
> +
> + if (playback)
It's best to use (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) here.
> + mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
> + else
> + mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ctrl |= mask;
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ctrl &= ~mask;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + return 0;
> +}
> +
> +
> +static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
No new line is required here.
> + uint32_t format = 0;
> + uint32_t conf;
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> +
> + conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBS_CFS:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
> + format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFS:
> + conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBS_CFM:
> + conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
> + break;
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_MSB:
> + format |= JZ_AIC_I2S_FMT_MSB;
> + break;
> + case SND_SOC_DAIFMT_I2S:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_NB_NF:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
> + enum jz4740_dma_width dma_width;
> + struct jz4740_pcm_config *pcm_config;
> + unsigned int sample_size;
> + uint32_t ctrl;
> +
> + ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S8:
> + sample_size = 0;
> + dma_width = JZ4740_DMA_WIDTH_8BIT;
> + break;
> + case SNDRV_PCM_FORMAT_S16:
> + sample_size = 1;
> + dma_width = JZ4740_DMA_WIDTH_16BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (playback) {
Same here re substream->stream
> + ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
> + } else {
> + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
> + ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
> + }
> +
> + switch (params_channels(params)) {
> + case 2:
> + break;
> + case 1:
> + if (playback) {
> + ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
> + break;
> + }
> + default: /* Falltrough */
> + return -EINVAL;
> + }
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
> +
> + if (playback) {
ditto
> + pcm_config = &i2s->pcm_config_playback;
> + pcm_config->dma_config.dst_width = dma_width;
> + } else {
> + pcm_config = &i2s->pcm_config_capture;
> + pcm_config->dma_config.src_width = dma_width;
> + }
> +
> +
> + snd_soc_dai_set_dma_data(dai, substream, pcm_config);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + int ret = 0;
> + struct clk *parent;
> +
> + switch (clk_id) {
> + case JZ4740_I2S_CLKSRC_EXT:
> + parent = clk_get(NULL, "ext");
> + clk_set_parent(i2s->clk_i2s, parent);
> + break;
> + case JZ4740_I2S_CLKSRC_PLL:
> + parent = clk_get(NULL, "pll half");
> + clk_set_parent(i2s->clk_i2s, parent);
> + ret = clk_set_rate(i2s->clk_i2s, freq);
> + break;
> + default:
> + return -EINVAL;
> + }
> + clk_put(parent);
> +
> + return ret;
> +}
> +
> +static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_resume(struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + clk_enable(i2s->clk_aic);
> +
> + if (dai->active) {
> + clk_enable(i2s->clk_i2s);
> +
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf |= JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> + uint32_t conf;
> +
> + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
> + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
> + JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
> + JZ_AIC_CONF_I2S |
> + JZ_AIC_CONF_INTERNAL_CODEC;
> +
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + return 0;
> +}
> +
> +
> +static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
> + .startup = jz4740_i2s_startup,
> + .shutdown = jz4740_i2s_shutdown,
> + .trigger = jz4740_i2s_trigger,
> + .hw_params = jz4740_i2s_hw_params,
> + .set_fmt = jz4740_i2s_set_fmt,
> + .set_clkdiv = jz4740_i2s_set_clkdiv,
> + .set_sysclk = jz4740_i2s_set_sysclk,
> +};
> +
> +#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
> + SNDRV_PCM_FMTBIT_S16_LE)
> +
> +struct snd_soc_dai jz4740_i2s_dai = {
> + .name = "jz4740-i2s",
> + .probe = jz4740_i2s_probe,
> + .playback = {
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .capture = {
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = JZ4740_I2S_FMTS,
> + },
> + .symmetric_rates = 1,
> + .ops = &jz4740_i2s_dai_ops,
> + .suspend = jz4740_i2s_suspend,
> + .resume = jz4740_i2s_resume,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
> +
> +static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
> +{
> + struct jz4740_dma_config *dma_config;
> +
> + /* Playback */
> + dma_config = &i2s->pcm_config_playback.dma_config;
> + dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
> + dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +
> + /* Capture */
> + dma_config = &i2s->pcm_config_capture.dma_config;
> + dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
> + dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
> + dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
> + dma_config->flags = JZ4740_DMA_DST_AUTOINC;
> + dma_config->mode = JZ4740_DMA_MODE_SINGLE;
> + i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
> +}
> +
> +static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s;
> + int ret;
> +
> + i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
> +
> + if (!i2s)
> + return -ENOMEM;
> +
> + i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
Why the new lines here
> + if (!i2s->mem) {
> + ret = -ENOENT;
> + goto err_free;
> + }
> +
> + i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
> + pdev->name);
> +
and here
> + if (!i2s->mem) {
> + ret = -EBUSY;
> + goto err_free;
> + }
> +
> + i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
> +
ditto
> + if (!i2s->base) {
> + ret = -EBUSY;
> + goto err_release_mem_region;
> + }
> +
> + i2s->phys_base = i2s->mem->start;
> +
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
> +
> + clk_enable(i2s->clk_aic);
> +
> + jz4740_i2c_init_pcm_config(i2s);
> +
> + jz4740_i2s_dai.private_data = i2s;
> + ret = snd_soc_register_dai(&jz4740_i2s_dai);
> +
> + platform_set_drvdata(pdev, i2s);
> +
> + return 0;
> +
> +err_iounmap:
> + iounmap(i2s->base);
> +err_release_mem_region:
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +err_free:
> + kfree(i2s);
> +
> + return ret;
> +}
> +
> +static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
> +{
> + struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
> +
> + snd_soc_unregister_dai(&jz4740_i2s_dai);
> +
> + clk_disable(i2s->clk_aic);
> + clk_put(i2s->clk_i2s);
> + clk_put(i2s->clk_aic);
> +
> + iounmap(i2s->base);
> + release_mem_region(i2s->mem->start, resource_size(i2s->mem));
> +
> + platform_set_drvdata(pdev, NULL);
> + kfree(i2s);
> +
> + return 0;
> +}
> +
> +static struct platform_driver jz4740_i2s_driver = {
> + .probe = jz4740_i2s_dev_probe,
> + .remove = __devexit_p(jz4740_i2s_dev_remove),
> + .driver = {
> + .name = "jz4740-i2s",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init jz4740_i2s_init(void)
> +{
> + return platform_driver_register(&jz4740_i2s_driver);
> +}
> +module_init(jz4740_i2s_init);
> +
> +static void __exit jz4740_i2s_exit(void)
> +{
> + platform_driver_unregister(&jz4740_i2s_driver);
> +}
> +module_exit(jz4740_i2s_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:jz4740-i2s");
> diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
> new file mode 100644
> index 0000000..da22ed8
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-i2s.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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 _JZ4740_I2S_H
> +#define _JZ4740_I2S_H
> +
> +/* I2S clock source */
> +#define JZ4740_I2S_CLKSRC_EXT 0
> +#define JZ4740_I2S_CLKSRC_PLL 1
> +
> +#define JZ4740_I2S_BIT_CLK 0
> +
> +extern struct snd_soc_dai jz4740_i2s_dai;
> +
> +#endif
> diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
> new file mode 100644
> index 0000000..fd1c203
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.c
> @@ -0,0 +1,350 @@
> +/*
> + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/dma-mapping.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/mach-jz4740/dma.h>
> +#include "jz4740-pcm.h"
> +
> +struct jz4740_runtime_data {
> + unsigned int dma_period;
> + dma_addr_t dma_start;
> + dma_addr_t dma_pos;
> + dma_addr_t dma_end;
> +
> + struct jz4740_dma_chan *dma;
> +
> + dma_addr_t fifo_addr;
> +};
> +
> +/* identify hardware playback capabilities */
> +static const struct snd_pcm_hardware jz4740_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_MMAP_VALID |
> + SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BLOCK_TRANSFER,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
> +
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .channels_min = 1,
> + .channels_max = 2,
> + .period_bytes_min = 16,
> + .period_bytes_max = 2 * PAGE_SIZE,
> + .periods_min = 2,
> + .periods_max = 128,
> + .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
> + .fifo_size = 32,
> +};
> +
> +static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
> +{
> + unsigned int count;
> +
> + if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
> + count = prtd->dma_end - prtd->dma_pos;
> + else
> + count = prtd->dma_period;
> +
> + jz4740_dma_disable(prtd->dma);
> +
> + if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
> + } else {
> + jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
> + jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
> + }
> +
> + jz4740_dma_set_transfer_count(prtd->dma, count);
> +
> + jz4740_dma_enable(prtd->dma);
> +
> + prtd->dma_pos += prtd->dma_period;
> + if (prtd->dma_pos >= prtd->dma_end)
> + prtd->dma_pos = prtd->dma_start;
> +}
> +
> +static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
> + void *dev_id)
> +{
> + struct snd_pcm_substream *substream = dev_id;
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + snd_pcm_period_elapsed(substream);
> +
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> +}
> +
> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct jz4740_pcm_config *config;
> +
> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
> + if (!prtd->dma) {
> + const char *dma_channel_name;
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + dma_channel_name = "PCM Playback";
> + else
> + dma_channel_name = "PCM Capture";
> +
> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
dma_channel_name variable is not required here. Just use the const char
* directly.
> + }
> +
> + if (!prtd->dma)
> + return -EBUSY;
> +
> + jz4740_dma_configure(prtd->dma, &config->dma_config);
> + prtd->fifo_addr = config->fifo_addr;
> +
> + jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
> +
> + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> + runtime->dma_bytes = params_buffer_bytes(params);
> +
> + prtd->dma_period = params_period_bytes(params);
> + prtd->dma_start = runtime->dma_addr;
> + prtd->dma_pos = prtd->dma_start;
> + prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> +
> + snd_pcm_set_runtime_buffer(substream, NULL);
> + if (prtd->dma) {
> + jz4740_dma_free(prtd->dma);
> + prtd->dma = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> + struct jz4740_runtime_data *prtd = substream->runtime->private_data;
> + int ret = 0;
> +
> + if (!prtd->dma)
> + return 0;
> +
> + prtd->dma_pos = prtd->dma_start;
> +
> + return ret;
> +}
> +
> +static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
No new line.
> + int ret = 0;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + jz4740_pcm_start_transfer(prtd, substream->stream);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + jz4740_dma_disable(prtd->dma);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> + unsigned long count, pos;
> + snd_pcm_uframes_t offset;
> + struct jz4740_dma_chan *dma = prtd->dma;
> +
> + count = jz4740_dma_get_residue(dma);
> + if (prtd->dma_pos == prtd->dma_start)
> + pos = prtd->dma_end - prtd->dma_start - count;
> + else
> + pos = prtd->dma_pos - prtd->dma_start - count;
> +
> + offset = bytes_to_frames(runtime, pos);
> + if (offset >= runtime->buffer_size)
> + offset = 0;
> +
Could you comment your calculation a little more.
> + return offset;
> +}
> +
> +static int jz4740_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd;
> +
> + snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
> + prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
> +
> + if (prtd == NULL)
> + return -ENOMEM;
> +
> + runtime->private_data = prtd;
> + return 0;
> +}
> +
> +static int jz4740_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct jz4740_runtime_data *prtd = runtime->private_data;
> +
> + kfree(prtd);
> +
> + return 0;
> +}
> +
> +static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
> + struct vm_area_struct *vma)
> +{
> + return remap_pfn_range(vma, vma->vm_start,
> + substream->dma_buffer.addr >> PAGE_SHIFT,
> + vma->vm_end - vma->vm_start, vma->vm_page_prot);
> +}
> +
> +static struct snd_pcm_ops jz4740_pcm_ops = {
> + .open = jz4740_pcm_open,
> + .close = jz4740_pcm_close,
> + .ioctl = snd_pcm_lib_ioctl,
> + .hw_params = jz4740_pcm_hw_params,
> + .hw_free = jz4740_pcm_hw_free,
> + .prepare = jz4740_pcm_prepare,
> + .trigger = jz4740_pcm_trigger,
> + .pointer = jz4740_pcm_pointer,
> + .mmap = jz4740_pcm_mmap,
> +};
> +
> +static int jz4740_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 = jz4740_pcm_hardware.buffer_bytes_max;
> +
> + buf->dev.type = SNDRV_DMA_TYPE_DEV;
> + buf->dev.dev = pcm->card->dev;
> + buf->private_data = NULL;
> +
> + buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
> + &buf->addr, GFP_KERNEL);
> + if (!buf->area)
> + return -ENOMEM;
> +
> + buf->bytes = size;
> +
> + return 0;
> +}
> +
> +static void jz4740_pcm_free(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_noncoherent(pcm->card->dev, buf->bytes,
> + buf->area, buf->addr);
> + buf->area = NULL;
> + }
> +}
> +
> +static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
> +
> +int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> + struct snd_pcm *pcm)
> +{
> + int ret = 0;
> +
> + if (!card->dev->dma_mask)
> + card->dev->dma_mask = &jz4740_pcm_dmamask;
> +
> + if (!card->dev->coherent_dma_mask)
> + card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + if (dai->playback.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_PLAYBACK);
> + if (ret)
> + goto err;
> + }
> +
> + if (dai->capture.channels_min) {
> + ret = jz4740_pcm_preallocate_dma_buffer(pcm,
> + SNDRV_PCM_STREAM_CAPTURE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + return ret;
> +}
> +
> +struct snd_soc_platform jz4740_soc_platform = {
> + .name = "jz4740-pcm",
> + .pcm_ops = &jz4740_pcm_ops,
> + .pcm_new = jz4740_pcm_new,
> + .pcm_free = jz4740_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(jz4740_soc_platform);
> +
> +static int __init jz4740_soc_platform_init(void)
> +{
> + return snd_soc_register_platform(&jz4740_soc_platform);
> +}
> +module_init(jz4740_soc_platform_init);
> +
> +static void __exit jz4740_soc_platform_exit(void)
> +{
> + snd_soc_unregister_platform(&jz4740_soc_platform);
> +}
> +module_exit(jz4740_soc_platform_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
> new file mode 100644
> index 0000000..e3f221e
> --- /dev/null
> +++ b/sound/soc/jz4740/jz4740-pcm.h
> @@ -0,0 +1,22 @@
> +/*
> + *
> + * 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 _JZ4740_PCM_H
> +#define _JZ4740_PCM_H
> +
> +#include <linux/dma-mapping.h>
> +#include <asm/mach-jz4740/dma.h>
> +
> +/* platform data */
> +extern struct snd_soc_platform jz4740_soc_platform;
> +
> +struct jz4740_pcm_config {
> + struct jz4740_dma_config dma_config;
> + phys_addr_t fifo_addr;
> +};
> +
> +#endif
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-03 12:32 ` Liam Girdwood
@ 2010-06-03 12:50 ` Liam Girdwood
2010-06-03 16:58 ` Lars-Peter Clausen
1 sibling, 0 replies; 22+ messages in thread
From: Liam Girdwood @ 2010-06-03 12:50 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
On Thu, 2010-06-03 at 13:32 +0100, Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
> > This patch adds support for the JZ4740 internal codec.
> >
> > Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> > Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> > Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> > Cc: alsa-devel@alsa-project.org
> > ---
> > sound/soc/codecs/Kconfig | 4 +
> > sound/soc/codecs/Makefile | 2 +
> > sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
> > sound/soc/codecs/jz4740-codec.h | 20 ++
> > 4 files changed, 528 insertions(+), 0 deletions(-)
> > create mode 100644 sound/soc/codecs/jz4740-codec.c
> > create mode 100644 sound/soc/codecs/jz4740-codec.h
>
> no need for code in file name here.
My bad typing - I mean codec.
Thanks
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 12:48 ` Liam Girdwood
@ 2010-06-03 16:50 ` Lars-Peter Clausen
2010-06-03 17:03 ` Liam Girdwood
0 siblings, 1 reply; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 16:50 UTC (permalink / raw)
To: Liam Girdwood
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
>
>> This patch adds ASoC support for JZ4740 SoCs I2S module.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
>> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
>> Cc: alsa-devel@alsa-project.org
>> ---
>> sound/soc/Kconfig | 1 +
>> sound/soc/Makefile | 1 +
>> sound/soc/jz4740/Kconfig | 13 +
>> sound/soc/jz4740/Makefile | 9 +
>> sound/soc/jz4740/jz4740-i2s.c | 568 +++++++++++++++++++++++++++++++++++++++++
>> sound/soc/jz4740/jz4740-i2s.h | 18 ++
>> sound/soc/jz4740/jz4740-pcm.c | 350 +++++++++++++++++++++++++
>> sound/soc/jz4740/jz4740-pcm.h | 22 ++
>> 8 files changed, 982 insertions(+), 0 deletions(-)
>> create mode 100644 sound/soc/jz4740/Kconfig
>> create mode 100644 sound/soc/jz4740/Makefile
>> create mode 100644 sound/soc/jz4740/jz4740-i2s.c
>> create mode 100644 sound/soc/jz4740/jz4740-i2s.h
>> create mode 100644 sound/soc/jz4740/jz4740-pcm.c
>> create mode 100644 sound/soc/jz4740/jz4740-pcm.h
>>
>> diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
>> index b1749bc..5a7a724 100644
>> --- a/sound/soc/Kconfig
>> +++ b/sound/soc/Kconfig
>> @@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
>> source "sound/soc/s6000/Kconfig"
>> source "sound/soc/sh/Kconfig"
>> source "sound/soc/txx9/Kconfig"
>> +source "sound/soc/jz4740/Kconfig"
>>
>> # Supported codecs
>> source "sound/soc/codecs/Kconfig"
>> diff --git a/sound/soc/Makefile b/sound/soc/Makefile
>> index 1470141..fdbe74d 100644
>> --- a/sound/soc/Makefile
>> +++ b/sound/soc/Makefile
>> @@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
>> obj-$(CONFIG_SND_SOC) += s6000/
>> obj-$(CONFIG_SND_SOC) += sh/
>> obj-$(CONFIG_SND_SOC) += txx9/
>> +obj-$(CONFIG_SND_SOC) += jz4740/
>> diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
>> new file mode 100644
>> index 0000000..39df949
>> --- /dev/null
>> +++ b/sound/soc/jz4740/Kconfig
>> @@ -0,0 +1,13 @@
>> +config SND_JZ4740_SOC
>> + tristate "SoC Audio for Ingenic JZ4740 SoC"
>> + depends on SOC_JZ4740 && SND_SOC
>> + help
>> + Say Y or M if you want to add support for codecs attached to
>> + the Jz4740 AC97, I2S or SSP interface. You will also need
>>
>
> Do you have an AC97 or SSP interface ?
>
>
Whoops. Copy-paste leftover...
>> + [....]
>> +
>> +
>> +static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
>> + struct snd_soc_dai *dai)
>> +{
>> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
>> + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
>> +
>> + uint32_t ctrl;
>> + uint32_t mask;
>> +
>> + if (playback)
>>
>
> It's best to use (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) here.
>
>
hm, ok
>> + [...]
>> --- /dev/null
>> +++ b/sound/soc/jz4740/jz4740-pcm.c
>> @@ -0,0 +1,350 @@
>> + [...]
>> +static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
>> + struct snd_pcm_hw_params *params)
>> +{
>> + struct snd_pcm_runtime *runtime = substream->runtime;
>> + struct jz4740_runtime_data *prtd = runtime->private_data;
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct jz4740_pcm_config *config;
>> +
>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
>> + if (!prtd->dma) {
>> + const char *dma_channel_name;
>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> + dma_channel_name = "PCM Playback";
>> + else
>> + dma_channel_name = "PCM Capture";
>> +
>> + prtd->dma = jz4740_dma_request(substream, dma_channel_name);
>>
>
> dma_channel_name variable is not required here. Just use the const char
> * directly.
>
>
I actually had it like that before, but I think it is much more readable
in its current form. Considering that stream value will either be 0 or 1
it might work to put the channel names into a static array.
>> + [...]
>> +
>> +static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
>> +{
>> + struct snd_pcm_runtime *runtime = substream->runtime;
>> + struct jz4740_runtime_data *prtd = runtime->private_data;
>> + unsigned long count, pos;
>> + snd_pcm_uframes_t offset;
>> + struct jz4740_dma_chan *dma = prtd->dma;
>> +
>> + count = jz4740_dma_get_residue(dma);
>> + if (prtd->dma_pos == prtd->dma_start)
>> + pos = prtd->dma_end - prtd->dma_start - count;
>> + else
>> + pos = prtd->dma_pos - prtd->dma_start - count;
>> +
>> + offset = bytes_to_frames(runtime, pos);
>> + if (offset >= runtime->buffer_size)
>> + offset = 0;
>> +
>>
>
> Could you comment your calculation a little more.
>
Will do.
>
> Thanks
>
> Liam
>
Thanks for the review
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-03 12:32 ` Liam Girdwood
2010-06-03 12:50 ` Liam Girdwood
@ 2010-06-03 16:58 ` Lars-Peter Clausen
1 sibling, 0 replies; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 16:58 UTC (permalink / raw)
To: Liam Girdwood
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
Liam Girdwood wrote:
> On Wed, 2010-06-02 at 21:12 +0200, Lars-Peter Clausen wrote:
>
>> This patch adds support for the JZ4740 internal codec.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
>> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
>> Cc: alsa-devel@alsa-project.org
>> ---
>> sound/soc/codecs/Kconfig | 4 +
>> sound/soc/codecs/Makefile | 2 +
>> sound/soc/codecs/jz4740-codec.c | 502 +++++++++++++++++++++++++++++++++++++++
>> sound/soc/codecs/jz4740-codec.h | 20 ++
>> 4 files changed, 528 insertions(+), 0 deletions(-)
>> create mode 100644 sound/soc/codecs/jz4740-codec.c
>> create mode 100644 sound/soc/codecs/jz4740-codec.h
>>
>
> no need for code in file name here.
>
>
>> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
>> index 31ac553..b8008df 100644
>> --- a/sound/soc/codecs/Kconfig
>> +++ b/sound/soc/codecs/Kconfig
>> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
>> select SND_SOC_AK4671 if I2C
>> select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
>> select SND_SOC_CS4270 if I2C
>> + select SND_SOC_JZ4740 if SOC_JZ4740
>> select SND_SOC_MAX9877 if I2C
>> select SND_SOC_DA7210 if I2C
>> select SND_SOC_PCM3008
>> @@ -269,6 +270,9 @@ config SND_SOC_WM9712
>> config SND_SOC_WM9713
>> tristate
>>
>> +config SND_SOC_JZ4740_CODEC
>> + tristate
>> +
>> # Amp
>> config SND_SOC_MAX9877
>> tristate
>> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
>> index 91429ea..4c7ee31 100644
>> --- a/sound/soc/codecs/Makefile
>> +++ b/sound/soc/codecs/Makefile
>> @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
>> snd-soc-wm9712-objs := wm9712.o
>> snd-soc-wm9713-objs := wm9713.o
>> snd-soc-wm-hubs-objs := wm_hubs.o
>> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>>
>>
>
> Please use the same format here
>
>
>> # Amp
>> snd-soc-max9877-objs := max9877.o
>> @@ -121,6 +122,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
>> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
>> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
>> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
>> +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
>>
>>
>
> ditto.
>
Ok, I agree, but the Kconfig symbol should keep the "CODEC" in it,
otherwise it would clash with the JZ4740 ASoC platform support.
>
>> # Amp
>> obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
>> diff --git a/sound/soc/codecs/jz4740-codec.c b/sound/soc/codecs/jz4740-codec.c
>> new file mode 100644
>> index 0000000..6e4b741
>> --- /dev/null
>> +++ b/sound/soc/codecs/jz4740-codec.c
>> @@ -0,0 +1,502 @@
>> + [...]
>> +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
>> + SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
>> + SOC_SINGLE("Capture Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
>>
>
> Is this the master capture volume ?
>
Hm, yes.
>
>> + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
>> + SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
>> + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
>> +};
>> +
>> +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
>> + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
>> + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
>> +};
>> +
>> +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
>> + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
>> + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
>> + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
>> + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
>> +
>> + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_OFFSET, 1,
>> + jz4740_codec_output_controls,
>> + ARRAY_SIZE(jz4740_codec_output_controls)),
>> +
>> + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
>> + jz4740_codec_input_controls,
>> + ARRAY_SIZE(jz4740_codec_input_controls)),
>> + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
>> +
>> + SND_SOC_DAPM_OUTPUT("LOUT"),
>> + SND_SOC_DAPM_OUTPUT("ROUT"),
>> +
>> + SND_SOC_DAPM_INPUT("MIC"),
>> + SND_SOC_DAPM_INPUT("LIN"),
>> + SND_SOC_DAPM_INPUT("RIN"),
>> +};
>> +
>> +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
>> +
>> + {"Line Input", NULL, "LIN"},
>> + {"Line Input", NULL, "RIN"},
>> +
>> + {"Input Mixer", "Line Capture Switch", "Line Input"},
>> + {"Input Mixer", "Mic Capture Switch", "MIC"},
>> +
>> + {"ADC", NULL, "Input Mixer"},
>> +
>> + {"Output Mixer", "Bypass Switch", "Input Mixer"},
>> + {"Output Mixer", "DAC Switch", "DAC"},
>> +
>> + {"LOUT", NULL, "Output Mixer"},
>> + {"ROUT", NULL, "Output Mixer"},
>> +};
>> +
>> +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
>> + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
>> +{
>> + uint32_t val;
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct snd_soc_device *socdev = rtd->socdev;
>> + struct snd_soc_codec *codec = socdev->card->codec;
>> +
>> + switch (params_format(params)) {
>> + case SNDRV_PCM_FORMAT_S8:
>> + case SNDRV_PCM_FORMAT_S16_LE:
>> + case SNDRV_PCM_FORMAT_S18_3LE:
>> + break;
>> + default:
>> + return -EINVAL;
>> + break;
>> + }
>>
>
> The PCM format check is not required here as core checks this.
>
Ok.
>> + [...]
>
> Thanks
>
> Liam
>
Thanks for reviewing
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 16:50 ` Lars-Peter Clausen
@ 2010-06-03 17:03 ` Liam Girdwood
2010-06-03 17:16 ` Lars-Peter Clausen
0 siblings, 1 reply; 22+ messages in thread
From: Liam Girdwood @ 2010-06-03 17:03 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Ralf Baechle, linux-mips, linux-kernel, Mark Brown, alsa-devel
On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
> >> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
> substream);
> >> + if (!prtd->dma) {
> >> + const char *dma_channel_name;
> >> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> >> + dma_channel_name = "PCM Playback";
> >> + else
> >> + dma_channel_name = "PCM Capture";
> >> +
> >> + prtd->dma = jz4740_dma_request(substream,
> dma_channel_name);
> >>
> >
> > dma_channel_name variable is not required here. Just use the const
> char
> > * directly.
> >
> >
> I actually had it like that before, but I think it is much more readable
> in its current form.
I disagree, having the char pointer here just adds an extra level of
indirection and costs an extra two lines of code.
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 17:03 ` Liam Girdwood
@ 2010-06-03 17:16 ` Lars-Peter Clausen
2010-06-03 17:25 ` Liam Girdwood
0 siblings, 1 reply; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 17:16 UTC (permalink / raw)
To: Liam Girdwood
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>
>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>
>> substream);
>>
>>>> + if (!prtd->dma) {
>>>> + const char *dma_channel_name;
>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>> + dma_channel_name = "PCM Playback";
>>>> + else
>>>> + dma_channel_name = "PCM Capture";
>>>> +
>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>
>> dma_channel_name);
>>
>>>>
>>>>
>>> dma_channel_name variable is not required here. Just use the const
>>>
>> char
>>
>>> * directly.
>>>
>>>
>>>
>> I actually had it like that before, but I think it is much more readable
>> in its current form.
>>
>
> I disagree, having the char pointer here just adds an extra level of
> indirection and costs an extra two lines of code.
>
> Liam
>
Hi
Could you give an concrete example of how you would code it?
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 17:16 ` Lars-Peter Clausen
@ 2010-06-03 17:25 ` Liam Girdwood
2010-06-03 17:37 ` Lars-Peter Clausen
2010-06-03 18:14 ` Troy Kisky
0 siblings, 2 replies; 22+ messages in thread
From: Liam Girdwood @ 2010-06-03 17:25 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
> Liam Girdwood wrote:
> > On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
> >
> >>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
> >>>>
> >> substream);
> >>
> >>>> + if (!prtd->dma) {
> >>>> + const char *dma_channel_name;
> >>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> >>>> + dma_channel_name = "PCM Playback";
> >>>> + else
> >>>> + dma_channel_name = "PCM Capture";
> >>>> +
> >>>> + prtd->dma = jz4740_dma_request(substream,
> >>>>
> >> dma_channel_name);
> >>
> >>>>
> >>>>
> >>> dma_channel_name variable is not required here. Just use the const
> >>>
> >> char
> >>
> >>> * directly.
> >>>
> >>>
> >>>
> >> I actually had it like that before, but I think it is much more readable
> >> in its current form.
> >>
> >
> > I disagree, having the char pointer here just adds an extra level of
> > indirection and costs an extra two lines of code.
> >
> > Liam
> >
> Hi
>
> Could you give an concrete example of how you would code it?
>
Sure,
if (!prtd->dma) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
prtd->dma = jz4740_dma_request(substream, "PCM Playback");
else
prtd->dma = jz4740_dma_request(substream, "PCM Capture");
}
Liam
--
Freelance Developer, SlimLogic Ltd
ASoC and Voltage Regulator Maintainer.
http://www.slimlogic.co.uk
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 17:25 ` Liam Girdwood
@ 2010-06-03 17:37 ` Lars-Peter Clausen
2010-06-03 18:14 ` Troy Kisky
1 sibling, 0 replies; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 17:37 UTC (permalink / raw)
To: Liam Girdwood
Cc: linux-mips, alsa-devel, Mark Brown, linux-kernel, Ralf Baechle
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
>
>> Liam Girdwood wrote:
>>
>>> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>>>
>>>
>>>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>>>
>>>>>>
>>>> substream);
>>>>
>>>>
>>>>>> + if (!prtd->dma) {
>>>>>> + const char *dma_channel_name;
>>>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>>>> + dma_channel_name = "PCM Playback";
>>>>>> + else
>>>>>> + dma_channel_name = "PCM Capture";
>>>>>> +
>>>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>>>
>>>>>>
>>>> dma_channel_name);
>>>>
>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>> dma_channel_name variable is not required here. Just use the const
>>>>>
>>>>>
>>>> char
>>>>
>>>>
>>>>> * directly.
>>>>>
>>>>>
>>>>>
>>>>>
>>>> I actually had it like that before, but I think it is much more readable
>>>> in its current form.
>>>>
>>>>
>>> I disagree, having the char pointer here just adds an extra level of
>>> indirection and costs an extra two lines of code.
>>>
>>> Liam
>>>
>>>
>> Hi
>>
>> Could you give an concrete example of how you would code it?
>>
>>
>
> Sure,
>
> if (!prtd->dma) {
> if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> prtd->dma = jz4740_dma_request(substream, "PCM Playback");
> else
> prtd->dma = jz4740_dma_request(substream, "PCM Capture");
> }
>
> Liam
>
>
And now you have the same statement in two different lines. When it
needs to be changed you have to change both lines.
And furthermore in my opinion it distracts from the reason for the if
statement: We want a different channel name.
But ok, if you insist on it, I can live with changing it.
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
2010-06-03 5:45 ` [alsa-devel] " Wan ZongShun
2010-06-03 12:32 ` Liam Girdwood
@ 2010-06-03 17:49 ` Mark Brown
2010-06-03 23:57 ` Lars-Peter Clausen
2 siblings, 1 reply; 22+ messages in thread
From: Mark Brown @ 2010-06-03 17:49 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, linux-kernel, Ralf Baechle, Liam Girdwood
On Wed, Jun 02, 2010 at 09:12:26PM +0200, Lars-Peter Clausen wrote:
> This patch adds support for the JZ4740 internal codec.
This looks very good, there are some issues but nothing too major. I
may be repeating some things others have said but hopefully not too
much.
> snd-soc-wm9712-objs := wm9712.o
> snd-soc-wm9713-objs := wm9713.o
> snd-soc-wm-hubs-objs := wm_hubs.o
> +snd-soc-jz4740-codec-objs := jz4740-codec.o
Keep the devices sorted in both Makefile and Kconfig.
> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + break;
> + default:
> + return -EINVAL;
> + }
This does nothing except validate some parameters. Is there actually an
externally visible DAI for this CODEC? If it's just integrated into the
SoC and there's nothing to configure then just omit the DAI
configuration since it's not even useful to document the signal format.
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_48000,
> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
You listed an 18 bit format in hw_params - one or other of this and
hw_params is presumably out of date.
> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
> + enum snd_soc_bias_level level)
> +{
> +
> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
> + udelay(2);
I'd expect to see this as part of the _OFF in the main switch
statement.
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
> + JZ4740_CODEC_1_VREF_DISABLE |
> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
> + 0);
This looks suspiciously like you should be using DAPM for the headphone
at least, though if there's only headphone out that's possibly not worth
it. Also, are you sure that you want both low and high current VREF
configuring here? I'm not clear what these settings do but the way
they're being managed both here and in _PREPARE seems odd.
> + codec = &jz4740_codec->codec;
> +
> + codec->dev = &pdev->dev;
> + codec->name = "jz-codec";
Seems a bit odd to use the part number in some places and not others.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
2010-06-03 3:36 ` Wan ZongShun
2010-06-03 12:48 ` Liam Girdwood
@ 2010-06-03 17:55 ` Mark Brown
2010-06-03 19:27 ` Lars-Peter Clausen
2 siblings, 1 reply; 22+ messages in thread
From: Mark Brown @ 2010-06-03 17:55 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, linux-kernel, Ralf Baechle, Liam Girdwood
On Wed, Jun 02, 2010 at 09:12:27PM +0200, Lars-Peter Clausen wrote:
Again, overall very good.
> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
> +
> + switch (div_id) {
> + case JZ4740_I2S_BIT_CLK:
> + if (div & 1 || div > 16)
> + return -EINVAL;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
You can probably figure out the bit clock automatically by default...
> + if (dai->active) {
> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
> + conf &= ~JZ_AIC_CONF_ENABLE;
> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
> +
> + clk_disable(i2s->clk_i2s);
> + }
> +
> + clk_disable(i2s->clk_aic);
Might make sense to manage this clock dynamically at runtime too for a
little extra power saving?
> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
> + if (IS_ERR(i2s->clk_aic)) {
> + ret = PTR_ERR(i2s->clk_aic);
> + goto err_iounmap;
> + }
> +
> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
> + if (IS_ERR(i2s->clk_i2s)) {
> + ret = PTR_ERR(i2s->clk_i2s);
> + goto err_iounmap;
> + }
Ideally you'd free the AIC clock when unwinding (and later stop it after
it was enabled). Though since you don't do any error checking after
this point it's kind of academic :)
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver
2010-06-02 19:15 ` [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver Lars-Peter Clausen
@ 2010-06-03 17:57 ` Mark Brown
0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2010-06-03 17:57 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, linux-kernel, Ralf Baechle, Liam Girdwood
On Wed, Jun 02, 2010 at 09:15:32PM +0200, Lars-Peter Clausen wrote:
> + ret = gpio_request(QI_LB60_SND_GPIO, "SND");
> + if (ret) {
> + pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
> + QI_LB60_SND_GPIO, ret);
> + goto err_device_put;
> + }
> +
> + ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
> + if (ret) {
> + pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
> + QI_LB60_AMP_GPIO, ret);
> + goto err_gpio_free_snd;
> + }
> +
> + gpio_direction_output(JZ_GPIO_PORTB(29), 0);
> + gpio_direction_output(JZ_GPIO_PORTD(4), 0);
You're referring to the GPIOs by multiple different names - it'd be more
robust to pick one way of naming them and use it consistently (probably
the #define).
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 17:25 ` Liam Girdwood
2010-06-03 17:37 ` Lars-Peter Clausen
@ 2010-06-03 18:14 ` Troy Kisky
1 sibling, 0 replies; 22+ messages in thread
From: Troy Kisky @ 2010-06-03 18:14 UTC (permalink / raw)
To: Liam Girdwood
Cc: linux-mips, alsa-devel, Lars-Peter Clausen, Mark Brown,
linux-kernel, Ralf Baechle
Liam Girdwood wrote:
> On Thu, 2010-06-03 at 19:16 +0200, Lars-Peter Clausen wrote:
>> Liam Girdwood wrote:
>>> On Thu, 2010-06-03 at 18:50 +0200, Lars-Peter Clausen wrote:
>>>
>>>>>> + config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai,
>>>>>>
>>>> substream);
>>>>
>>>>>> + if (!prtd->dma) {
>>>>>> + const char *dma_channel_name;
>>>>>> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>>>>> + dma_channel_name = "PCM Playback";
>>>>>> + else
>>>>>> + dma_channel_name = "PCM Capture";
>>>>>> +
>>>>>> + prtd->dma = jz4740_dma_request(substream,
>>>>>>
>>>> dma_channel_name);
>>>>
>>>>>>
>>>>>>
>>>>> dma_channel_name variable is not required here. Just use the const
>>>>>
>>>> char
>>>>
>>>>> * directly.
>>>>>
>>>>>
>>>>>
>>>> I actually had it like that before, but I think it is much more readable
>>>> in its current form.
>>>>
>>> I disagree, having the char pointer here just adds an extra level of
>>> indirection and costs an extra two lines of code.
>>>
>>> Liam
>>>
>> Hi
>>
>> Could you give an concrete example of how you would code it?
>>
>
> Sure,
>
> if (!prtd->dma) {
> if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> prtd->dma = jz4740_dma_request(substream, "PCM Playback");
> else
> prtd->dma = jz4740_dma_request(substream, "PCM Capture");
> }
>
> Liam
>
or,
if (!prtd->dma)
prtd->dma = jz4740_dma_request(substream, (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
"PCM Playback" : "PCM Capture");
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support
2010-06-03 17:55 ` Mark Brown
@ 2010-06-03 19:27 ` Lars-Peter Clausen
0 siblings, 0 replies; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 19:27 UTC (permalink / raw)
To: Mark Brown
Cc: Ralf Baechle, linux-mips, linux-kernel, Liam Girdwood, alsa-devel
Mark Brown wrote:
> On Wed, Jun 02, 2010 at 09:12:27PM +0200, Lars-Peter Clausen wrote:
>
> Again, overall very good.
>
>
>> +static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
>> +{
>> + struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
>> +
>> + switch (div_id) {
>> + case JZ4740_I2S_BIT_CLK:
>> + if (div & 1 || div > 16)
>> + return -EINVAL;
>> + jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>>
>
> You can probably figure out the bit clock automatically by default...
>
Hm, yes, you are right.
>
>> + if (dai->active) {
>> + conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
>> + conf &= ~JZ_AIC_CONF_ENABLE;
>> + jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
>> +
>> + clk_disable(i2s->clk_i2s);
>> + }
>> +
>> + clk_disable(i2s->clk_aic);
>>
>
> Might make sense to manage this clock dynamically at runtime too for a
> little extra power saving?
>
I think I tried it once and the power savings were marginal. On the
other hand each callback would have to make sure the clock is enabled
before accessing registers and disabling it again if it was disabled.
>
>> + i2s->clk_aic = clk_get(&pdev->dev, "aic");
>> + if (IS_ERR(i2s->clk_aic)) {
>> + ret = PTR_ERR(i2s->clk_aic);
>> + goto err_iounmap;
>> + }
>> +
>> + i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
>> + if (IS_ERR(i2s->clk_i2s)) {
>> + ret = PTR_ERR(i2s->clk_i2s);
>> + goto err_iounmap;
>> + }
>>
>
> Ideally you'd free the AIC clock when unwinding (and later stop it after
> it was enabled). Though since you don't do any error checking after
> this point it's kind of academic :)
>
>
It should at least freed if the i2s clock is not found.
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-03 17:49 ` Mark Brown
@ 2010-06-03 23:57 ` Lars-Peter Clausen
2010-06-03 23:59 ` Mark Brown
0 siblings, 1 reply; 22+ messages in thread
From: Lars-Peter Clausen @ 2010-06-03 23:57 UTC (permalink / raw)
To: Mark Brown
Cc: linux-mips, alsa-devel, linux-kernel, Ralf Baechle, Liam Girdwood
Hi
Mark Brown wrote:
> On Wed, Jun 02, 2010 at 09:12:26PM +0200, Lars-Peter Clausen wrote:
>
>
>> This patch adds support for the JZ4740 internal codec.
>>
>
> This looks very good, there are some issues but nothing too major. I
> may be repeating some things others have said but hopefully not too
> much.
>
>
>> snd-soc-wm9712-objs := wm9712.o
>> snd-soc-wm9713-objs := wm9713.o
>> snd-soc-wm-hubs-objs := wm_hubs.o
>> +snd-soc-jz4740-codec-objs := jz4740-codec.o
>>
>
> Keep the devices sorted in both Makefile and Kconfig.
>
>
>> +static int jz4740_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
>> +{
>> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
>> + case SND_SOC_DAIFMT_CBM_CFM:
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>>
>
> This does nothing except validate some parameters. Is there actually an
> externally visible DAI for this CODEC? If it's just integrated into the
> SoC and there's nothing to configure then just omit the DAI
> configuration since it's not even useful to document the signal format.
>
Nope, there is no externally visible DAI for the codec, but internally
it is connected through the i2s controller of the JZ4740 which supports
different operating modes. And if you'll do
"snd_soc_dai_set_fmt(codec_dai, BOARD_DAIFMT);
snd_soc_dai_set_fmt(cpu_dai, BOARD_DAIFMT);" with a wrong dai format it
will be noticed.
>> + .capture = {
>> + .stream_name = "Capture",
>> + .channels_min = 2,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_48000,
>> + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
>>
>
> You listed an 18 bit format in hw_params - one or other of this and
> hw_params is presumably out of date.
>
In theory the codec supports 18 bit for playback, but the i2s controller
requires it to be 32 bit aligned, while alsa appears to have only
support for a 24 bit aligned 18 bit format(Correct me if I'm wrong). So
I dropped it, I'll remove the whole format check in hw_params as Liam
suggested.
>> +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
>> + enum snd_soc_bias_level level)
>> +{
>> +
>> + if (codec->bias_level == SND_SOC_BIAS_OFF && level != SND_SOC_BIAS_OFF) {
>> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
>> + udelay(2);
>>
>
> I'd expect to see this as part of the _OFF in the main switch
> statement.
>
Uhm, actually this code path is taken when switching from _OFF to
another state. If it is guaranteed that _OFF is always followed by a
certain other state I could put the reset code in its part of the switch
statement.
>
>> + switch (level) {
>> + case SND_SOC_BIAS_ON:
>> + snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
>> + JZ4740_CODEC_1_VREF_DISABLE |
>> + JZ4740_CODEC_1_VREF_AMP_DISABLE |
>> + JZ4740_CODEC_1_HEADPHONE_POWER_DOWN_M |
>> + JZ4740_CODEC_1_VREF_LOW_CURRENT |
>> + JZ4740_CODEC_1_VREF_HIGH_CURRENT,
>> + 0);
>>
>
> This looks suspiciously like you should be using DAPM for the headphone
> at least, though if there's only headphone out that's possibly not worth
> it. Also, are you sure that you want both low and high current VREF
> configuring here? I'm not clear what these settings do but the way
> they're being managed both here and in _PREPARE seems odd.
>
Hm, I'll take a look.
>
>> + codec = &jz4740_codec->codec;
>> +
>> + codec->dev = &pdev->dev;
>> + codec->name = "jz-codec";
>>
>
> Seems a bit odd to use the part number in some places and not others.
>
I renamed the driver from jz-codec to jz4740-codec shortly before
submitting it after I realized that the codec component on other jz47xx
chips is completely different. Seems like I missed the codec name.
Thanks for reviewing the patch
- Lars
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver
2010-06-03 23:57 ` Lars-Peter Clausen
@ 2010-06-03 23:59 ` Mark Brown
0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2010-06-03 23:59 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: linux-mips, alsa-devel, linux-kernel, Ralf Baechle, Liam Girdwood
On 4 Jun 2010, at 00:57, Lars-Peter Clausen wrote:
> Mark Brown wrote:
> Uhm, actually this code path is taken when switching from _OFF to
> another state. If it is guaranteed that _OFF is always followed by a
> certain other state I could put the reset code in its part of the switch
> statement.
The only possible transitions are OFF <-> STANDBY <-> PREPARE <-> ON
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2010-06-03 23:59 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1275505397-16758-1-git-send-email-lars@metafoo.de>
2010-06-02 19:12 ` [RFC][PATCH 20/26] alsa: ASoC: Add JZ4740 codec driver Lars-Peter Clausen
2010-06-03 5:45 ` [alsa-devel] " Wan ZongShun
2010-06-03 12:03 ` Mark Brown
2010-06-03 12:32 ` Liam Girdwood
2010-06-03 12:50 ` Liam Girdwood
2010-06-03 16:58 ` Lars-Peter Clausen
2010-06-03 17:49 ` Mark Brown
2010-06-03 23:57 ` Lars-Peter Clausen
2010-06-03 23:59 ` Mark Brown
2010-06-02 19:12 ` [RFC][PATCH 21/26] alsa: ASoC: Add JZ4740 ASoC support Lars-Peter Clausen
2010-06-03 3:36 ` Wan ZongShun
2010-06-03 12:48 ` Liam Girdwood
2010-06-03 16:50 ` Lars-Peter Clausen
2010-06-03 17:03 ` Liam Girdwood
2010-06-03 17:16 ` Lars-Peter Clausen
2010-06-03 17:25 ` Liam Girdwood
2010-06-03 17:37 ` Lars-Peter Clausen
2010-06-03 18:14 ` Troy Kisky
2010-06-03 17:55 ` Mark Brown
2010-06-03 19:27 ` Lars-Peter Clausen
2010-06-02 19:15 ` [RFC][PATCH 26/26] alsa: ASoC: JZ4740: Add qi_lb60 board driver Lars-Peter Clausen
2010-06-03 17:57 ` Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).