All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
@ 2009-05-18 14:10 Atsushi Nemoto
  2009-05-18 14:23   ` Mark Brown
  0 siblings, 1 reply; 9+ messages in thread
From: Atsushi Nemoto @ 2009-05-18 14:10 UTC (permalink / raw)
  To: linux-mips; +Cc: ralf, alsa-devel, Mark Brown

This patch adds support for the integrated ACLC of the TXx9 family.

Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
---
Changes since v1:
* kill empty PM stuff
* move irq/mem resource stuff to the DAI driver
* move dma resource stuff to the DMA driver

 sound/soc/Kconfig                 |    1 +
 sound/soc/Makefile                |    1 +
 sound/soc/txx9/Kconfig            |   29 +++
 sound/soc/txx9/Makefile           |   11 +
 sound/soc/txx9/txx9aclc-ac97.c    |  243 +++++++++++++++++++++
 sound/soc/txx9/txx9aclc-generic.c |   99 +++++++++
 sound/soc/txx9/txx9aclc.c         |  428 +++++++++++++++++++++++++++++++++++++
 sound/soc/txx9/txx9aclc.h         |   76 +++++++
 8 files changed, 888 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/txx9/Kconfig
 create mode 100644 sound/soc/txx9/Makefile
 create mode 100644 sound/soc/txx9/txx9aclc-ac97.c
 create mode 100644 sound/soc/txx9/txx9aclc-generic.c
 create mode 100644 sound/soc/txx9/txx9aclc.c
 create mode 100644 sound/soc/txx9/txx9aclc.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3d2bb6f..85e985a 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -33,6 +33,7 @@ source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0237879..6039b0d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/txx9/Kconfig b/sound/soc/txx9/Kconfig
new file mode 100644
index 0000000..ebc9327
--- /dev/null
+++ b/sound/soc/txx9/Kconfig
@@ -0,0 +1,29 @@
+##
+## TXx9 ACLC
+##
+config SND_SOC_TXX9ACLC
+	tristate "SoC Audio for TXx9"
+	depends on HAS_TXX9_ACLC && TXX9_DMAC
+	help
+	  This option enables support for the AC Link Controllers in TXx9 SoC.
+
+config HAS_TXX9_ACLC
+	bool
+
+config SND_SOC_TXX9ACLC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_TXX9ACLC_GENERIC
+	tristate "Generic TXx9 ACLC sound machine"
+	depends on SND_SOC_TXX9ACLC
+	select SND_SOC_TXX9ACLC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  This is a generic AC97 sound machine for use in TXx9 based systems.
diff --git a/sound/soc/txx9/Makefile b/sound/soc/txx9/Makefile
new file mode 100644
index 0000000..551f16c
--- /dev/null
+++ b/sound/soc/txx9/Makefile
@@ -0,0 +1,11 @@
+# Platform
+snd-soc-txx9aclc-objs := txx9aclc.o
+snd-soc-txx9aclc-ac97-objs := txx9aclc-ac97.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC) += snd-soc-txx9aclc.o
+obj-$(CONFIG_SND_SOC_TXX9ACLC_AC97) += snd-soc-txx9aclc-ac97.o
+
+# Machine
+snd-soc-txx9aclc-generic-objs := txx9aclc-generic.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC_GENERIC) += snd-soc-txx9aclc-generic.o
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
new file mode 100644
index 0000000..5e04409
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -0,0 +1,243 @@
+/*
+ * TXx9 ACLC AC97 driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+#define AC97_DIR	\
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_48000
+
+#ifdef __BIG_ENDIAN
+#define AC97_FMTS	SNDRV_PCM_FMTBIT_S16_BE
+#else
+#define AC97_FMTS	SNDRV_PCM_FMTBIT_S16_LE
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq);
+
+/* REVISIT: How to find txx9aclc_soc_device from snd_ac97? */
+static struct txx9aclc_soc_device *txx9aclc_soc_dev;
+
+static int txx9aclc_regready(struct txx9aclc_soc_device *dev)
+{
+	return __raw_readl(dev->base + ACINTSTS) & ACINT_REGACCRDY;
+}
+
+/* AC97 controller reads codec register */
+static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97,
+					 unsigned short reg)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+	u32 dat;
+
+	if (!(__raw_readl(dev->base + ACINTSTS) & ACINT_CODECRDY(ac97->num)))
+		return 0xffff;
+	reg |= ac97->num << 7;
+	dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ;
+	__raw_writel(dat, dev->base + ACREGACC);
+	__raw_writel(ACINT_REGACCRDY, dev->base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+		__raw_writel(ACINT_REGACCRDY, dev->base + ACINTDIS);
+		dev_err(dev->soc_dev.dev, "ac97 read timeout (reg %#x)\n", reg);
+		dat = 0xffff;
+		goto done;
+	}
+	dat = __raw_readl(dev->base + ACREGACC);
+	if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) {
+		dev_err(dev->soc_dev.dev, "reg mismatch %x with %x\n",
+			dat, reg);
+		dat = 0xffff;
+		goto done;
+	}
+	dat = (dat >> ACREGACC_DAT_SHIFT) & 0xffff;
+done:
+	__raw_writel(ACINT_REGACCRDY, dev->base + ACINTDIS);
+	return dat;
+}
+
+/* AC97 controller writes to codec register */
+static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short val)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+
+	__raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) |
+		     (val << ACREGACC_DAT_SHIFT),
+		     dev->base + ACREGACC);
+	__raw_writel(ACINT_REGACCRDY, dev->base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+		dev_err(dev->soc_dev.dev,
+			"ac97 write timeout (reg %#x)\n", reg);
+	}
+	__raw_writel(ACINT_REGACCRDY, dev->base + ACINTDIS);
+}
+
+static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+	u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY;
+
+	__raw_writel(ACCTL_ENLINK, dev->base + ACCTLDIS);
+	mmiowb();
+	udelay(1);
+	__raw_writel(ACCTL_ENLINK, dev->base + ACCTLEN);
+	/* wait for primary codec ready status */
+	__raw_writel(ready, dev->base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq,
+				(__raw_readl(dev->base + ACINTSTS) & ready)
+				== ready, HZ)) {
+		dev_err(&ac97->dev, "primary codec is not ready "
+			"(status %#x)\n",
+			__raw_readl(dev->base + ACINTSTS));
+	}
+	__raw_writel(ACINT_REGACCRDY, dev->base + ACINTSTS);
+	__raw_writel(ready, dev->base + ACINTDIS);
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= txx9aclc_ac97_read,
+	.write		= txx9aclc_ac97_write,
+	.reset		= txx9aclc_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
+{
+	struct txx9aclc_soc_device *dev = dev_id;
+
+	__raw_writel(__raw_readl(dev->base + ACINTMSTS), dev->base + ACINTDIS);
+	wake_up(&ac97_waitq);
+	return IRQ_HANDLED;
+}
+
+static void txx9aclc_ac97_cleanup(struct txx9aclc_soc_device *dev)
+{
+	struct resource *r;
+
+	if (dev->irq >= 0) {
+		free_irq(dev->irq, dev);
+		dev->irq = -1;
+	}
+	r = dev->mem_res;
+	if (r) {
+		if (dev->base) {
+			iounmap(dev->base);
+			dev->base = NULL;
+		}
+		release_mem_region(r->start, resource_size(r));
+		dev->mem_res = NULL;
+	}
+}
+
+static int txx9aclc_ac97_probe(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+	struct platform_device *aclc_pdev = dev->aclc_pdev;
+	struct resource *r;
+	int err;
+	int irq;
+
+	dev->irq = -1;
+	irq = platform_get_irq(aclc_pdev, 0);
+	if (irq < 0)
+		return irq;
+	r = platform_get_resource(aclc_pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -EBUSY;
+
+	if (!request_mem_region(r->start, resource_size(r),
+				dev_name(&aclc_pdev->dev))) {
+		err = -EBUSY;
+		goto err;
+	}
+	dev->mem_res = r;
+	dev->base = ioremap(r->start, resource_size(r));
+	if (!dev->base) {
+		err = -EBUSY;
+		goto err;
+	}
+	err = request_irq(irq, txx9aclc_ac97_irq,
+			  IRQF_DISABLED, dev_name(&aclc_pdev->dev), dev);
+	if (err < 0)
+		goto err;
+	dev->irq = irq;
+	txx9aclc_soc_dev = dev;
+	return 0;
+err:
+	txx9aclc_ac97_cleanup(dev);
+	return err;
+}
+
+static void txx9aclc_ac97_remove(struct platform_device *pdev,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+
+	if (dev->base)
+		/* disable AC-link */
+		__raw_writel(ACCTL_ENLINK, dev->base + ACCTLDIS);
+	txx9aclc_ac97_cleanup(dev);
+	txx9aclc_soc_dev = NULL;
+}
+
+struct snd_soc_dai txx9aclc_ac97_dai = {
+	.name			= "txx9aclc_ac97",
+	.ac97_control		= 1,
+	.probe			= txx9aclc_ac97_probe,
+	.remove			= txx9aclc_ac97_remove,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+};
+EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai);
+
+static int __init txx9aclc_ac97_init(void)
+{
+	return snd_soc_register_dai(&txx9aclc_ac97_dai);
+}
+
+static void __exit txx9aclc_ac97_exit(void)
+{
+	snd_soc_unregister_dai(&txx9aclc_ac97_dai);
+}
+
+module_init(txx9aclc_ac97_init);
+module_exit(txx9aclc_ac97_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c
new file mode 100644
index 0000000..d27aba6
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc-generic.c
@@ -0,0 +1,99 @@
+/*
+ * Generic TXx9 ACLC machine driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * 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.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at ACLC (e.g. RBTX49XX boards).
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "../codecs/ac97.h"
+#include "txx9aclc.h"
+
+static struct snd_soc_dai_link txx9aclc_generic_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &txx9aclc_ac97_dai,
+	.codec_dai = &ac97_dai,
+};
+
+static struct snd_soc_card txx9aclc_generic_card = {
+	.name		= "Generic TXx9 ACLC Audio",
+	.platform	= &txx9aclc_soc_platform,
+	.dai_link	= &txx9aclc_generic_dai,
+	.num_links	= 1,
+};
+
+static struct txx9aclc_soc_device txx9aclc_generic_soc_device = {
+	.soc_dev = {
+		.card		= &txx9aclc_generic_card,
+		.codec_dev	= &soc_codec_dev_ac97,
+	},
+};
+
+static int __init txx9aclc_generic_probe(struct platform_device *pdev)
+{
+	struct txx9aclc_soc_device *dev = &txx9aclc_generic_soc_device;
+	struct platform_device *soc_pdev;
+	int ret;
+
+	soc_pdev = platform_device_alloc("soc-audio", -1);
+	if (!soc_pdev)
+		return -ENOMEM;
+	platform_set_drvdata(soc_pdev, &dev->soc_dev);
+	dev->soc_dev.dev = &soc_pdev->dev;
+	dev->aclc_pdev = pdev;
+	ret = platform_device_add(soc_pdev);
+	if (ret) {
+		platform_device_put(soc_pdev);
+		return ret;
+	}
+	platform_set_drvdata(pdev, soc_pdev);
+	return 0;
+}
+
+static int __exit txx9aclc_generic_remove(struct platform_device *pdev)
+{
+	struct platform_device *soc_pdev = platform_get_drvdata(pdev);
+
+	platform_device_unregister(soc_pdev);
+	return 0;
+}
+
+static struct platform_driver txx9aclc_generic_driver = {
+	.remove = txx9aclc_generic_remove,
+	.driver = {
+		.name = "txx9aclc-generic",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init txx9aclc_generic_init(void)
+{
+	return platform_driver_probe(&txx9aclc_generic_driver,
+				     txx9aclc_generic_probe);
+}
+
+static void __exit txx9aclc_generic_exit(void)
+{
+	platform_driver_unregister(&txx9aclc_generic_driver);
+}
+
+module_init(txx9aclc_generic_init);
+module_exit(txx9aclc_generic_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Generic TXx9 ACLC ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
new file mode 100644
index 0000000..9e7d26e
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc.c
@@ -0,0 +1,428 @@
+/*
+ * Generic TXx9 ACLC platform driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+static const struct snd_pcm_hardware txx9aclc_pcm_hardware = {
+	/*
+	 * REVISIT: SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+	 * needs more works for noncoherent MIPS.
+	 */
+	.info		  = SNDRV_PCM_INFO_INTERLEAVED |
+			    SNDRV_PCM_INFO_BATCH |
+			    SNDRV_PCM_INFO_PAUSE,
+#ifdef __BIG_ENDIAN
+	.formats	  = SNDRV_PCM_FMTBIT_S16_BE,
+#else
+	.formats	  = SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+	.period_bytes_min = 1024,
+	.period_bytes_max = 8 * 1024,
+	.periods_min	  = 2,
+	.periods_max	  = 4096,
+	.buffer_bytes_max = 32 * 1024,
+};
+
+static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct txx9aclc_dmadata *dmadata = runtime->private_data;
+	int ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(socdev->dev,
+		"runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd "
+		"runtime->min_align %ld\n",
+		(unsigned long)runtime->dma_area,
+		(unsigned long)runtime->dma_addr, runtime->dma_bytes,
+		runtime->min_align);
+	dev_dbg(socdev->dev,
+		"periods %d period_bytes %d stream %d\n",
+		params_periods(params), params_period_bytes(params),
+		substream->stream);
+
+	dmadata->substream = substream;
+	dmadata->pos = 0;
+	return 0;
+}
+
+static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct txx9aclc_dmadata *dmadata = runtime->private_data;
+
+	dmadata->dma_addr = runtime->dma_addr;
+	dmadata->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+	dmadata->period_bytes = snd_pcm_lib_period_bytes(substream);
+
+	if (dmadata->buffer_bytes == dmadata->period_bytes) {
+		dmadata->frag_bytes = dmadata->period_bytes >> 1;
+		dmadata->frags = 2;
+	} else {
+		dmadata->frag_bytes = dmadata->period_bytes;
+		dmadata->frags = dmadata->buffer_bytes / dmadata->period_bytes;
+	}
+	dmadata->frag_count = 0;
+	dmadata->pos = 0;
+	return 0;
+}
+
+static void txx9aclc_dma_complete(void *arg)
+{
+	struct txx9aclc_dmadata *dmadata = arg;
+	unsigned long flags;
+
+	/* dma completion handler cannot submit new operations */
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	if (dmadata->frag_count >= 0) {
+		dmadata->dmacount--;
+		BUG_ON(dmadata->dmacount < 0);
+		tasklet_schedule(&dmadata->tasklet);
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static struct dma_async_tx_descriptor *
+txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr)
+{
+	struct dma_chan *chan = dmadata->dma_chan;
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist sg;
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf_dma_addr)),
+		    dmadata->frag_bytes, buf_dma_addr & (PAGE_SIZE - 1));
+	sg_dma_address(&sg) = buf_dma_addr;
+	desc = chan->device->device_prep_slave_sg(chan, &sg, 1,
+		dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE,
+		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(&chan->dev->device, "cannot prepare slave dma\n");
+		return NULL;
+	}
+	desc->callback = txx9aclc_dma_complete;
+	desc->callback_param = dmadata;
+	desc->tx_submit(desc);
+	return desc;
+}
+
+#define NR_DMA_CHAIN		2
+
+static void txx9aclc_dma_tasklet(unsigned long data)
+{
+	struct txx9aclc_dmadata *dmadata = (struct txx9aclc_dmadata *)data;
+	struct dma_chan *chan = dmadata->dma_chan;
+	struct dma_async_tx_descriptor *desc;
+	struct snd_pcm_substream *substream = dmadata->substream;
+	u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		ACCTL_AUDODMA : ACCTL_AUDIDMA;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	if (dmadata->frag_count < 0) {
+		struct txx9aclc_soc_device *dev =
+			container_of(dmadata, struct txx9aclc_soc_device,
+				     dmadata[substream->stream]);
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		chan->device->device_terminate_all(chan);
+		/* first time */
+		for (i = 0; i < NR_DMA_CHAIN; i++) {
+			desc = txx9aclc_dma_submit(dmadata,
+				dmadata->dma_addr + i * dmadata->frag_bytes);
+			if (!desc)
+				return;
+		}
+		dmadata->dmacount = NR_DMA_CHAIN;
+		chan->device->device_issue_pending(chan);
+		spin_lock_irqsave(&dmadata->dma_lock, flags);
+		__raw_writel(ctlbit, dev->base + ACCTLEN);
+		dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags;
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		return;
+	}
+	BUG_ON(dmadata->dmacount >= NR_DMA_CHAIN);
+	while (dmadata->dmacount < NR_DMA_CHAIN) {
+		dmadata->dmacount++;
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		desc = txx9aclc_dma_submit(dmadata,
+			dmadata->dma_addr +
+			dmadata->frag_count * dmadata->frag_bytes);
+		if (!desc)
+			return;
+		chan->device->device_issue_pending(chan);
+
+		spin_lock_irqsave(&dmadata->dma_lock, flags);
+		dmadata->frag_count++;
+		dmadata->frag_count %= dmadata->frags;
+		dmadata->pos += dmadata->frag_bytes;
+		dmadata->pos %= dmadata->buffer_bytes;
+		if ((dmadata->frag_count * dmadata->frag_bytes) %
+		    dmadata->period_bytes == 0)
+			snd_pcm_period_elapsed(substream);
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct txx9aclc_soc_device *dev =
+		container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+	unsigned long flags;
+	int ret = 0;
+	u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		ACCTL_AUDODMA : ACCTL_AUDIDMA;
+
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dmadata->frag_count = -1;
+		tasklet_schedule(&dmadata->tasklet);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		__raw_writel(ctlbit, dev->base + ACCTLDIS);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		__raw_writel(ctlbit, dev->base + ACCTLEN);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+	return ret;
+}
+
+static snd_pcm_uframes_t
+txx9aclc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, dmadata->pos);
+}
+
+static int txx9aclc_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct txx9aclc_soc_device *dev =
+		container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+	struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream];
+	int ret;
+
+	ret = snd_soc_set_runtime_hwparams(substream, &txx9aclc_pcm_hardware);
+	if (ret)
+		return ret;
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+	substream->runtime->private_data = dmadata;
+	return 0;
+}
+
+static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+	struct dma_chan *chan = dmadata->dma_chan;
+
+	dmadata->frag_count = -1;
+	chan->device->device_terminate_all(chan);
+	return 0;
+}
+
+static struct snd_pcm_ops txx9aclc_pcm_ops = {
+	.open		= txx9aclc_pcm_open,
+	.close		= txx9aclc_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= txx9aclc_pcm_hw_params,
+	.hw_free	= txx9aclc_pcm_hw_free,
+	.prepare	= txx9aclc_pcm_prepare,
+	.trigger	= txx9aclc_pcm_trigger,
+	.pointer	= txx9aclc_pcm_pointer,
+};
+
+static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+			    struct snd_pcm *pcm)
+{
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+		card->dev, 64 * 1024, 4 * 1024 * 1024);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct txx9aclc_dmadata *dmadata = param;
+	char devname[BUS_ID_SIZE + 2];
+
+	sprintf(devname, "%s.%d", dmadata->dma_res->name,
+		(int)dmadata->dma_res->start);
+	if (strcmp(dev_name(chan->device->dev), devname) == 0) {
+		chan->private = &dmadata->dma_slave;
+		return true;
+	}
+	return false;
+}
+
+static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
+			     struct txx9aclc_dmadata *dmadata)
+{
+	struct txx9dmac_slave *ds = &dmadata->dma_slave;
+	dma_cap_mask_t mask;
+
+	spin_lock_init(&dmadata->dma_lock);
+
+	ds->reg_width = sizeof(u32);
+	if (dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ds->tx_reg = dev->physbase + ACAUDODAT;
+		ds->rx_reg = 0;
+	} else {
+		ds->tx_reg = 0;
+		ds->rx_reg = dev->physbase + ACAUDIDAT;
+	}
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dmadata->dma_chan = dma_request_channel(mask, filter, dmadata);
+	if (!dmadata->dma_chan) {
+		dev_err(dev->soc_dev.dev,
+			"DMA channel for %s is not available\n",
+			dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			"playback" : "capture");
+		return -EBUSY;
+	}
+	tasklet_init(&dmadata->tasklet, txx9aclc_dma_tasklet,
+		     (unsigned long)dmadata);
+	return 0;
+}
+
+static int txx9aclc_pcm_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+	struct resource *r;
+	int i;
+	int ret;
+
+	r = dev->mem_res;
+	dev->physbase = r->start;
+	if (sizeof(dev->physbase) > sizeof(r->start) &&
+	    r->start >= TXX9_DIRECTMAP_BASE &&
+	    r->start < TXX9_DIRECTMAP_BASE + 0x400000)
+		dev->physbase |= 0xf00000000ull;
+	dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK;
+	dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE;
+	for (i = 0; i < 2; i++) {
+		r = platform_get_resource(dev->aclc_pdev, IORESOURCE_DMA, i);
+		if (!r) {
+			ret = -EBUSY;
+			goto exit;
+		}
+		dev->dmadata[i].dma_res = r;
+		ret = txx9aclc_dma_init(dev, &dev->dmadata[i]);
+		if (ret)
+			goto exit;
+	}
+	return 0;
+
+exit:
+	for (i = 0; i < 2; i++) {
+		if (dev->dmadata[i].dma_chan)
+			dma_release_channel(dev->dmadata[i].dma_chan);
+		dev->dmadata[i].dma_chan = NULL;
+	}
+	return ret;
+}
+
+static int txx9aclc_pcm_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+	int i;
+
+	/* disable all FIFO DMAs */
+	__raw_writel(ACCTL_AUDODMA | ACCTL_AUDIDMA, dev->base + ACCTLDIS);
+	/* dummy R/W to clear pending DMAREQ if any */
+	__raw_writel(__raw_readl(dev->base + ACAUDIDAT),
+		     dev->base + ACAUDODAT);
+
+	for (i = 0; i < 2; i++) {
+		struct txx9aclc_dmadata *dmadata = &dev->dmadata[i];
+		struct dma_chan *chan = dmadata->dma_chan;
+		if (chan) {
+			dmadata->frag_count = -1;
+			chan->device->device_terminate_all(chan);
+			dma_release_channel(chan);
+		}
+		dev->dmadata[i].dma_chan = NULL;
+	}
+	return 0;
+}
+
+struct snd_soc_platform txx9aclc_soc_platform = {
+	.name		= "txx9aclc-audio",
+	.probe		= txx9aclc_pcm_probe,
+	.remove		= txx9aclc_pcm_remove,
+	.pcm_ops 	= &txx9aclc_pcm_ops,
+	.pcm_new	= txx9aclc_pcm_new,
+	.pcm_free	= txx9aclc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(txx9aclc_soc_platform);
+
+static int __init txx9aclc_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&txx9aclc_soc_platform);
+}
+
+static void __exit txx9aclc_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&txx9aclc_soc_platform);
+}
+
+module_init(txx9aclc_soc_platform_init);
+module_exit(txx9aclc_soc_platform_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC Audio DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h
new file mode 100644
index 0000000..2396fe5
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc.h
@@ -0,0 +1,76 @@
+/*
+ * TXx9 SoC AC Link Controller
+ *
+ * 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 __TXX9ACLC_H
+#define __TXX9ACLC_H
+
+#include <linux/interrupt.h>
+#include <asm/txx9/dmac.h>
+
+#define ACCTLEN			0x00	/* control enable */
+#define ACCTLDIS		0x04	/* control disable */
+#define   ACCTL_ENLINK		0x00000001	/* enable/disable AC-link */
+#define   ACCTL_AUDODMA		0x00000100	/* AUDODMA enable/disable */
+#define   ACCTL_AUDIDMA		0x00001000	/* AUDIDMA enable/disable */
+#define   ACCTL_AUDOEHLT	0x00010000	/* AUDO error halt
+						   enable/disable */
+#define   ACCTL_AUDIEHLT	0x00100000	/* AUDI error halt
+						   enable/disable */
+#define ACREGACC		0x08	/* codec register access */
+#define   ACREGACC_DAT_SHIFT	0	/* data field */
+#define   ACREGACC_REG_SHIFT	16	/* address field */
+#define   ACREGACC_CODECID_SHIFT	24	/* CODEC ID field */
+#define   ACREGACC_READ		0x80000000	/* CODEC read */
+#define   ACREGACC_WRITE	0x00000000	/* CODEC write */
+#define ACINTSTS		0x10	/* interrupt status */
+#define ACINTMSTS		0x14	/* interrupt masked status */
+#define ACINTEN			0x18	/* interrupt enable */
+#define ACINTDIS		0x1c	/* interrupt disable */
+#define   ACINT_CODECRDY(n)	(0x00000001 << (n))	/* CODECn ready */
+#define   ACINT_REGACCRDY	0x00000010	/* ACREGACC ready */
+#define   ACINT_AUDOERR		0x00000100	/* AUDO underrun error */
+#define   ACINT_AUDIERR		0x00001000	/* AUDI overrun error */
+#define ACDMASTS		0x80	/* DMA request status */
+#define   ACDMA_AUDO		0x00000001	/* AUDODMA pending */
+#define   ACDMA_AUDI		0x00000010	/* AUDIDMA pending */
+#define ACAUDODAT		0xa0	/* audio out data */
+#define ACAUDIDAT		0xb0	/* audio in data */
+#define ACREVID			0xfc	/* revision ID */
+
+struct txx9aclc_dmadata {
+	struct resource *dma_res;
+	struct txx9dmac_slave dma_slave;
+	struct dma_chan *dma_chan;
+	struct tasklet_struct tasklet;
+	spinlock_t dma_lock;
+	int stream; /* SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE */
+	struct snd_pcm_substream *substream;
+	unsigned long pos;
+	dma_addr_t dma_addr;
+	unsigned long buffer_bytes;
+	unsigned long period_bytes;
+	unsigned long frag_bytes;
+	int frags;
+	int frag_count;
+	int dmacount;
+};
+
+struct txx9aclc_soc_device {
+	struct snd_soc_device soc_dev;
+	struct platform_device *aclc_pdev;	/* for ioresources */
+	struct resource *mem_res;
+	void __iomem *base;
+	u64 physbase;
+	int irq;
+	struct txx9aclc_dmadata dmadata[2];
+};
+
+extern struct snd_soc_platform txx9aclc_soc_platform;
+extern struct snd_soc_dai txx9aclc_ac97_dai;
+
+#endif /* __TXX9ACLC_H */
-- 
1.5.6.5

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

* Re: [alsa-devel] [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
  2009-05-18 14:10 [PATCH] ASoC: Add TXx9 AC link controller driver (v2) Atsushi Nemoto
@ 2009-05-18 14:23   ` Mark Brown
  0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2009-05-18 14:23 UTC (permalink / raw)
  To: Atsushi Nemoto; +Cc: linux-mips, alsa-devel, ralf

On Mon, May 18, 2009 at 11:10:12PM +0900, Atsushi Nemoto wrote:

> +static int txx9aclc_ac97_probe(struct platform_device *pdev,
> +			       struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct txx9aclc_soc_device *dev =
> +		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
> +	struct platform_device *aclc_pdev = dev->aclc_pdev;
> +	struct resource *r;
> +	int err;
> +	int irq;
> +
> +	dev->irq = -1;
> +	irq = platform_get_irq(aclc_pdev, 0);

This isn't what I meant by moving the resources to the DAI and DMA
drivers.  You have moved the calls to read the resources to these
drivers (which is good) but the resources are still being obtained from
the main ASoC device rather than by themselves from the device code.

There aren't too many platforms using this approach yet but take a look
at the way the pxa2xx-ac97 driver deals with registering the DAI (it
doesn't do anything with resources ATM) - you want to be registering
your DAI and grabbing the resources from a platform driver probe like it
does.

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

* Re: [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
@ 2009-05-18 14:23   ` Mark Brown
  0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2009-05-18 14:23 UTC (permalink / raw)
  To: Atsushi Nemoto; +Cc: linux-mips, alsa-devel, ralf

On Mon, May 18, 2009 at 11:10:12PM +0900, Atsushi Nemoto wrote:

> +static int txx9aclc_ac97_probe(struct platform_device *pdev,
> +			       struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct txx9aclc_soc_device *dev =
> +		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
> +	struct platform_device *aclc_pdev = dev->aclc_pdev;
> +	struct resource *r;
> +	int err;
> +	int irq;
> +
> +	dev->irq = -1;
> +	irq = platform_get_irq(aclc_pdev, 0);

This isn't what I meant by moving the resources to the DAI and DMA
drivers.  You have moved the calls to read the resources to these
drivers (which is good) but the resources are still being obtained from
the main ASoC device rather than by themselves from the device code.

There aren't too many platforms using this approach yet but take a look
at the way the pxa2xx-ac97 driver deals with registering the DAI (it
doesn't do anything with resources ATM) - you want to be registering
your DAI and grabbing the resources from a platform driver probe like it
does.

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

* Re: [alsa-devel] [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
  2009-05-18 14:23   ` Mark Brown
@ 2009-05-18 15:25     ` Atsushi Nemoto
  -1 siblings, 0 replies; 9+ messages in thread
From: Atsushi Nemoto @ 2009-05-18 15:25 UTC (permalink / raw)
  To: broonie; +Cc: linux-mips, alsa-devel, ralf

On Mon, 18 May 2009 15:23:05 +0100, Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
> > +static int txx9aclc_ac97_probe(struct platform_device *pdev,
> > +			       struct snd_soc_dai *dai)
...
> > +	irq = platform_get_irq(aclc_pdev, 0);
> 
> This isn't what I meant by moving the resources to the DAI and DMA
> drivers.  You have moved the calls to read the resources to these
> drivers (which is good) but the resources are still being obtained from
> the main ASoC device rather than by themselves from the device code.
> 
> There aren't too many platforms using this approach yet but take a look
> at the way the pxa2xx-ac97 driver deals with registering the DAI (it
> doesn't do anything with resources ATM) - you want to be registering
> your DAI and grabbing the resources from a platform driver probe like it
> does.

Thank you for elaboration.  Then, this untested patch (on top of the
last patch) is what you mean?  If yes, I will split this into arch
part and driver part, and then update both patches.


 arch/mips/txx9/generic/setup.c    |    6 +++---
 arch/mips/txx9/rbtx4927/setup.c   |    1 +
 arch/mips/txx9/rbtx4938/setup.c   |    1 +
 arch/mips/txx9/rbtx4939/setup.c   |    1 +
 sound/soc/txx9/txx9aclc-ac97.c    |   28 +++++++++++++++++++++++++---
 sound/soc/txx9/txx9aclc-generic.c |    1 -
 6 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c
index bc358ab..7f91012 100644
--- a/arch/mips/txx9/generic/setup.c
+++ b/arch/mips/txx9/generic/setup.c
@@ -882,8 +882,8 @@ void __init txx9_aclc_init(unsigned long baseaddr, int irq,
 			   unsigned int dma_chan_out,
 			   unsigned int dma_chan_in)
 {
-#if defined(CONFIG_SND_SOC_TXX9ACLC_GENERIC) || \
-	defined(CONFIG_SND_SOC_TXX9ACLC_GENERIC_MODULE)
+#if defined(CONFIG_SND_SOC_TXX9ACLC) || \
+	defined(CONFIG_SND_SOC_TXX9ACLC_MODULE)
 	unsigned int dma_base = dmac_id * TXX9_DMA_MAX_NR_CHANNELS;
 	struct resource res[] = {
 		{
@@ -904,7 +904,7 @@ void __init txx9_aclc_init(unsigned long baseaddr, int irq,
 		}
 	};
 	struct platform_device *pdev =
-		platform_device_alloc("txx9aclc-generic", -1);
+		platform_device_alloc("txx9aclc-ac97", -1);
 
 	if (!pdev ||
 	    platform_device_add_resources(pdev, res, ARRAY_SIZE(res)) ||
diff --git a/arch/mips/txx9/rbtx4927/setup.c b/arch/mips/txx9/rbtx4927/setup.c
index ece43db..ee468ea 100644
--- a/arch/mips/txx9/rbtx4927/setup.c
+++ b/arch/mips/txx9/rbtx4927/setup.c
@@ -344,6 +344,7 @@ static void __init rbtx4927_device_init(void)
 		tx4938_dmac_init(0, 2);
 		tx4938_aclc_init();
 	}
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 	txx9_iocled_init(RBTX4927_LED_ADDR - IO_BASE, -1, 3, 1, "green", NULL);
 	rbtx4927_gpioled_init();
 }
diff --git a/arch/mips/txx9/rbtx4938/setup.c b/arch/mips/txx9/rbtx4938/setup.c
index c0aa889..8da66e9 100644
--- a/arch/mips/txx9/rbtx4938/setup.c
+++ b/arch/mips/txx9/rbtx4938/setup.c
@@ -357,6 +357,7 @@ static void __init rbtx4938_device_init(void)
 	tx4938_ata_init(RBTX4938_IRQ_IOC_ATA, 0, 1);
 	tx4938_dmac_init(0, 2);
 	tx4938_aclc_init();
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 	txx9_iocled_init(RBTX4938_LED_ADDR - IO_BASE, -1, 8, 1, "green", NULL);
 }
 
diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c
index 1d01126..d5ad5ab 100644
--- a/arch/mips/txx9/rbtx4939/setup.c
+++ b/arch/mips/txx9/rbtx4939/setup.c
@@ -500,6 +500,7 @@ static void __init rbtx4939_device_init(void)
 	tx4939_rtc_init();
 	tx4939_dmac_init(0, 2);
 	tx4939_aclc_init();
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 }
 
 static void __init rbtx4939_setup(void)
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 5e04409..f198104 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -155,11 +155,12 @@ static int txx9aclc_ac97_probe(struct platform_device *pdev,
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct txx9aclc_soc_device *dev =
 		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
-	struct platform_device *aclc_pdev = dev->aclc_pdev;
+	struct platform_device *aclc_pdev = to_platform_device(dai->dev);
 	struct resource *r;
 	int err;
 	int irq;
 
+	dev->aclc_pdev = aclc_pdev;
 	dev->irq = -1;
 	irq = platform_get_irq(aclc_pdev, 0);
 	if (irq < 0)
@@ -225,14 +226,35 @@ struct snd_soc_dai txx9aclc_ac97_dai = {
 };
 EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai);
 
-static int __init txx9aclc_ac97_init(void)
+static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
 {
+	txx9aclc_ac97_dai.dev = &pdev->dev;
 	return snd_soc_register_dai(&txx9aclc_ac97_dai);
 }
 
-static void __exit txx9aclc_ac97_exit(void)
+static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_dai(&txx9aclc_ac97_dai);
+	return 0;
+}
+
+static struct platform_driver txx9aclc_ac97_driver = {
+	.probe		= txx9aclc_ac97_dev_probe,
+	.remove		= __devexit_p(txx9aclc_ac97_dev_remove),
+	.driver		= {
+		.name	= "txx9aclc-ac97",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init txx9aclc_ac97_init(void)
+{
+	return platform_driver_register(&txx9aclc_ac97_driver);
+}
+
+static void __exit txx9aclc_ac97_exit(void)
+{
+	platform_driver_unregister(&txx9aclc_ac97_driver);
 }
 
 module_init(txx9aclc_ac97_init);
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c
index d27aba6..3175de9 100644
--- a/sound/soc/txx9/txx9aclc-generic.c
+++ b/sound/soc/txx9/txx9aclc-generic.c
@@ -54,7 +54,6 @@ static int __init txx9aclc_generic_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(soc_pdev, &dev->soc_dev);
 	dev->soc_dev.dev = &soc_pdev->dev;
-	dev->aclc_pdev = pdev;
 	ret = platform_device_add(soc_pdev);
 	if (ret) {
 		platform_device_put(soc_pdev);

---
Atsushi Nemoto

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

* Re: [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
@ 2009-05-18 15:25     ` Atsushi Nemoto
  0 siblings, 0 replies; 9+ messages in thread
From: Atsushi Nemoto @ 2009-05-18 15:25 UTC (permalink / raw)
  To: broonie; +Cc: linux-mips, alsa-devel, ralf

On Mon, 18 May 2009 15:23:05 +0100, Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
> > +static int txx9aclc_ac97_probe(struct platform_device *pdev,
> > +			       struct snd_soc_dai *dai)
...
> > +	irq = platform_get_irq(aclc_pdev, 0);
> 
> This isn't what I meant by moving the resources to the DAI and DMA
> drivers.  You have moved the calls to read the resources to these
> drivers (which is good) but the resources are still being obtained from
> the main ASoC device rather than by themselves from the device code.
> 
> There aren't too many platforms using this approach yet but take a look
> at the way the pxa2xx-ac97 driver deals with registering the DAI (it
> doesn't do anything with resources ATM) - you want to be registering
> your DAI and grabbing the resources from a platform driver probe like it
> does.

Thank you for elaboration.  Then, this untested patch (on top of the
last patch) is what you mean?  If yes, I will split this into arch
part and driver part, and then update both patches.


 arch/mips/txx9/generic/setup.c    |    6 +++---
 arch/mips/txx9/rbtx4927/setup.c   |    1 +
 arch/mips/txx9/rbtx4938/setup.c   |    1 +
 arch/mips/txx9/rbtx4939/setup.c   |    1 +
 sound/soc/txx9/txx9aclc-ac97.c    |   28 +++++++++++++++++++++++++---
 sound/soc/txx9/txx9aclc-generic.c |    1 -
 6 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c
index bc358ab..7f91012 100644
--- a/arch/mips/txx9/generic/setup.c
+++ b/arch/mips/txx9/generic/setup.c
@@ -882,8 +882,8 @@ void __init txx9_aclc_init(unsigned long baseaddr, int irq,
 			   unsigned int dma_chan_out,
 			   unsigned int dma_chan_in)
 {
-#if defined(CONFIG_SND_SOC_TXX9ACLC_GENERIC) || \
-	defined(CONFIG_SND_SOC_TXX9ACLC_GENERIC_MODULE)
+#if defined(CONFIG_SND_SOC_TXX9ACLC) || \
+	defined(CONFIG_SND_SOC_TXX9ACLC_MODULE)
 	unsigned int dma_base = dmac_id * TXX9_DMA_MAX_NR_CHANNELS;
 	struct resource res[] = {
 		{
@@ -904,7 +904,7 @@ void __init txx9_aclc_init(unsigned long baseaddr, int irq,
 		}
 	};
 	struct platform_device *pdev =
-		platform_device_alloc("txx9aclc-generic", -1);
+		platform_device_alloc("txx9aclc-ac97", -1);
 
 	if (!pdev ||
 	    platform_device_add_resources(pdev, res, ARRAY_SIZE(res)) ||
diff --git a/arch/mips/txx9/rbtx4927/setup.c b/arch/mips/txx9/rbtx4927/setup.c
index ece43db..ee468ea 100644
--- a/arch/mips/txx9/rbtx4927/setup.c
+++ b/arch/mips/txx9/rbtx4927/setup.c
@@ -344,6 +344,7 @@ static void __init rbtx4927_device_init(void)
 		tx4938_dmac_init(0, 2);
 		tx4938_aclc_init();
 	}
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 	txx9_iocled_init(RBTX4927_LED_ADDR - IO_BASE, -1, 3, 1, "green", NULL);
 	rbtx4927_gpioled_init();
 }
diff --git a/arch/mips/txx9/rbtx4938/setup.c b/arch/mips/txx9/rbtx4938/setup.c
index c0aa889..8da66e9 100644
--- a/arch/mips/txx9/rbtx4938/setup.c
+++ b/arch/mips/txx9/rbtx4938/setup.c
@@ -357,6 +357,7 @@ static void __init rbtx4938_device_init(void)
 	tx4938_ata_init(RBTX4938_IRQ_IOC_ATA, 0, 1);
 	tx4938_dmac_init(0, 2);
 	tx4938_aclc_init();
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 	txx9_iocled_init(RBTX4938_LED_ADDR - IO_BASE, -1, 8, 1, "green", NULL);
 }
 
diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c
index 1d01126..d5ad5ab 100644
--- a/arch/mips/txx9/rbtx4939/setup.c
+++ b/arch/mips/txx9/rbtx4939/setup.c
@@ -500,6 +500,7 @@ static void __init rbtx4939_device_init(void)
 	tx4939_rtc_init();
 	tx4939_dmac_init(0, 2);
 	tx4939_aclc_init();
+	platform_device_register_simple("txx9aclc-generic", -1, NULL, 0);
 }
 
 static void __init rbtx4939_setup(void)
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 5e04409..f198104 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -155,11 +155,12 @@ static int txx9aclc_ac97_probe(struct platform_device *pdev,
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct txx9aclc_soc_device *dev =
 		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
-	struct platform_device *aclc_pdev = dev->aclc_pdev;
+	struct platform_device *aclc_pdev = to_platform_device(dai->dev);
 	struct resource *r;
 	int err;
 	int irq;
 
+	dev->aclc_pdev = aclc_pdev;
 	dev->irq = -1;
 	irq = platform_get_irq(aclc_pdev, 0);
 	if (irq < 0)
@@ -225,14 +226,35 @@ struct snd_soc_dai txx9aclc_ac97_dai = {
 };
 EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai);
 
-static int __init txx9aclc_ac97_init(void)
+static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
 {
+	txx9aclc_ac97_dai.dev = &pdev->dev;
 	return snd_soc_register_dai(&txx9aclc_ac97_dai);
 }
 
-static void __exit txx9aclc_ac97_exit(void)
+static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_dai(&txx9aclc_ac97_dai);
+	return 0;
+}
+
+static struct platform_driver txx9aclc_ac97_driver = {
+	.probe		= txx9aclc_ac97_dev_probe,
+	.remove		= __devexit_p(txx9aclc_ac97_dev_remove),
+	.driver		= {
+		.name	= "txx9aclc-ac97",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init txx9aclc_ac97_init(void)
+{
+	return platform_driver_register(&txx9aclc_ac97_driver);
+}
+
+static void __exit txx9aclc_ac97_exit(void)
+{
+	platform_driver_unregister(&txx9aclc_ac97_driver);
 }
 
 module_init(txx9aclc_ac97_init);
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c
index d27aba6..3175de9 100644
--- a/sound/soc/txx9/txx9aclc-generic.c
+++ b/sound/soc/txx9/txx9aclc-generic.c
@@ -54,7 +54,6 @@ static int __init txx9aclc_generic_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(soc_pdev, &dev->soc_dev);
 	dev->soc_dev.dev = &soc_pdev->dev;
-	dev->aclc_pdev = pdev;
 	ret = platform_device_add(soc_pdev);
 	if (ret) {
 		platform_device_put(soc_pdev);

---
Atsushi Nemoto

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

* Re: [alsa-devel] [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
  2009-05-18 15:25     ` Atsushi Nemoto
@ 2009-05-18 15:33       ` Mark Brown
  -1 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2009-05-18 15:33 UTC (permalink / raw)
  To: Atsushi Nemoto; +Cc: linux-mips, alsa-devel, ralf

On Tue, May 19, 2009 at 12:25:42AM +0900, Atsushi Nemoto wrote:

> Thank you for elaboration.  Then, this untested patch (on top of the
> last patch) is what you mean?  If yes, I will split this into arch
> part and driver part, and then update both patches.

Partly - I'd also expect to see the resources moved around in the
architecture code so that they are on the platform devices that are
being used to probe the DAIs (unless that's what the architecture side
of your patch was doing, patch wasn't quite giving me enough context to
be 100% clear).

You could also roll the resource requesting and unrequesting into the
probe functions for the DAI & DMA platform devices, though that is not
essential.

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

* Re: [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
@ 2009-05-18 15:33       ` Mark Brown
  0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2009-05-18 15:33 UTC (permalink / raw)
  To: Atsushi Nemoto; +Cc: linux-mips, alsa-devel, ralf

On Tue, May 19, 2009 at 12:25:42AM +0900, Atsushi Nemoto wrote:

> Thank you for elaboration.  Then, this untested patch (on top of the
> last patch) is what you mean?  If yes, I will split this into arch
> part and driver part, and then update both patches.

Partly - I'd also expect to see the resources moved around in the
architecture code so that they are on the platform devices that are
being used to probe the DAIs (unless that's what the architecture side
of your patch was doing, patch wasn't quite giving me enough context to
be 100% clear).

You could also roll the resource requesting and unrequesting into the
probe functions for the DAI & DMA platform devices, though that is not
essential.

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

* Re: [alsa-devel] [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
  2009-05-18 15:33       ` Mark Brown
@ 2009-05-18 16:10         ` Atsushi Nemoto
  -1 siblings, 0 replies; 9+ messages in thread
From: Atsushi Nemoto @ 2009-05-18 16:10 UTC (permalink / raw)
  To: broonie; +Cc: linux-mips, alsa-devel, ralf

On Mon, 18 May 2009 16:33:18 +0100, Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, May 19, 2009 at 12:25:42AM +0900, Atsushi Nemoto wrote:
> 
> > Thank you for elaboration.  Then, this untested patch (on top of the
> > last patch) is what you mean?  If yes, I will split this into arch
> > part and driver part, and then update both patches.
> 
> Partly - I'd also expect to see the resources moved around in the
> architecture code so that they are on the platform devices that are
> being used to probe the DAIs (unless that's what the architecture side
> of your patch was doing, patch wasn't quite giving me enough context to
> be 100% clear).

Now the resources are provided with "txx9aclc-ac97" platform device
which used to probe DAIs.  I will send whole patchset again after some
testing.

> You could also roll the resource requesting and unrequesting into the
> probe functions for the DAI & DMA platform devices, though that is not
> essential.

OK, I will move them into txx9aclc_ac97_dev_probe().  Then I can use
devres functions to make the resource management much simpler.

---
Atsushi Nemoto

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

* Re: [PATCH] ASoC: Add TXx9 AC link controller driver (v2)
@ 2009-05-18 16:10         ` Atsushi Nemoto
  0 siblings, 0 replies; 9+ messages in thread
From: Atsushi Nemoto @ 2009-05-18 16:10 UTC (permalink / raw)
  To: broonie; +Cc: linux-mips, alsa-devel, ralf

On Mon, 18 May 2009 16:33:18 +0100, Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, May 19, 2009 at 12:25:42AM +0900, Atsushi Nemoto wrote:
> 
> > Thank you for elaboration.  Then, this untested patch (on top of the
> > last patch) is what you mean?  If yes, I will split this into arch
> > part and driver part, and then update both patches.
> 
> Partly - I'd also expect to see the resources moved around in the
> architecture code so that they are on the platform devices that are
> being used to probe the DAIs (unless that's what the architecture side
> of your patch was doing, patch wasn't quite giving me enough context to
> be 100% clear).

Now the resources are provided with "txx9aclc-ac97" platform device
which used to probe DAIs.  I will send whole patchset again after some
testing.

> You could also roll the resource requesting and unrequesting into the
> probe functions for the DAI & DMA platform devices, though that is not
> essential.

OK, I will move them into txx9aclc_ac97_dev_probe().  Then I can use
devres functions to make the resource management much simpler.

---
Atsushi Nemoto

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

end of thread, other threads:[~2009-05-18 16:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-18 14:10 [PATCH] ASoC: Add TXx9 AC link controller driver (v2) Atsushi Nemoto
2009-05-18 14:23 ` [alsa-devel] " Mark Brown
2009-05-18 14:23   ` Mark Brown
2009-05-18 15:25   ` [alsa-devel] " Atsushi Nemoto
2009-05-18 15:25     ` Atsushi Nemoto
2009-05-18 15:33     ` [alsa-devel] " Mark Brown
2009-05-18 15:33       ` Mark Brown
2009-05-18 16:10       ` [alsa-devel] " Atsushi Nemoto
2009-05-18 16:10         ` Atsushi Nemoto

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