All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
@ 2010-01-22 23:15 Candelaria Villareal, Jorge
  2010-01-23  1:44 ` [alsa-devel] " Liam Girdwood
  0 siblings, 1 reply; 7+ messages in thread
From: Candelaria Villareal, Jorge @ 2010-01-22 23:15 UTC (permalink / raw)
  To: alsa-devel, linux-omap; +Cc: broonie

McPDM is the interface between Phoenix audio codec
and the OMAP4430 processor. It enables data to be transfered
to/from Phoenix at sample rates of 88.4 or 96 KHz.

Signed-off-by: Jorge Eduardo Candelaria <x0107209@ti.com>
Signed-off-by: Margarita Olaya <x0080101@ti.com>
---
 sound/soc/omap/Kconfig  |    3 +
 sound/soc/omap/Makefile |    2 +
 sound/soc/omap/mcpdm.c  |  474 +++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/omap/mcpdm.h  |  152 +++++++++++++++
 4 files changed, 631 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/omap/mcpdm.c
 create mode 100644 sound/soc/omap/mcpdm.h

diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 61952aa..b96e9d8 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP
        tristate
        select OMAP_MCBSP

+config SND_OMAP_SOC_MCPDM
+       tristate
+
 config SND_OMAP_SOC_N810
        tristate "SoC Audio support for Nokia N810"
        depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 3db8a6c..bf8b388 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -1,9 +1,11 @@
 # OMAP Platform Support
 snd-soc-omap-objs := omap-pcm.o
 snd-soc-omap-mcbsp-objs := omap-mcbsp.o
+snd-soc-omap-mcpdm-objs := mcpdm.o

 obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
 obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
+obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o

 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
new file mode 100644
index 0000000..2bc5acd
--- /dev/null
+++ b/sound/soc/omap/mcpdm.c
@@ -0,0 +1,474 @@
+/*
+ * mcpdm.c  --  McPDM interface driver
+ *
+ * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
+ * Copyright (C) 2009 - Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include "mcpdm.h"
+
+static struct omap_mcpdm *mcpdm;
+
+static inline void omap_mcpdm_write(u16 reg, u32 val)
+{
+       __raw_writel(val, mcpdm->io_base + reg);
+}
+
+static inline int omap_mcpdm_read(u16 reg)
+{
+       return __raw_readl(mcpdm->io_base + reg);
+}
+
+void omap_mcpdm_reg_dump(void)
+{
+       dev_dbg(mcpdm->dev, "***********************\n");
+       dev_dbg(mcpdm->dev, "IRQSTATUS_RAW:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
+       dev_dbg(mcpdm->dev, "IRQSTATUS:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQSTATUS));
+       dev_dbg(mcpdm->dev, "IRQENABLE_SET:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQENABLE_SET));
+       dev_dbg(mcpdm->dev, "IRQENABLE_CLR:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
+       dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQWAKE_EN));
+       dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAENABLE_SET));
+       dev_dbg(mcpdm->dev, "DMAENABLE_CLR:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
+       dev_dbg(mcpdm->dev, "DMAWAKEEN:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAWAKEEN));
+       dev_dbg(mcpdm->dev, "CTRL:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_CTRL));
+       dev_dbg(mcpdm->dev, "DN_DATA:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DN_DATA));
+       dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_UP_DATA));
+       dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
+       dev_dbg(mcpdm->dev, "FIFO_CTRL_UP:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
+       dev_dbg(mcpdm->dev, "DN_OFFSET:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DN_OFFSET));
+       dev_dbg(mcpdm->dev, "***********************\n");
+}
+
+/*
+ * Takes the McPDM module in and out of reset state.
+ * Uplink and downlink can be reset individually.
+ */
+static void omap_mcpdm_reset(int links, int reset)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (links & MCPDM_UPLINK) {
+               if (reset)
+                       ctrl |= SW_UP_RST;
+               else
+                       ctrl &= ~SW_UP_RST;
+       }
+
+       if (links & MCPDM_DOWNLINK) {
+               if (reset)
+                       ctrl |= SW_DN_RST;
+               else
+                       ctrl &= ~SW_DN_RST;
+       }
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Enables the transfer through the PDM interface to/from the Phoenix
+ * codec by enabling the corresponding UP or DN channels.
+ */
+void omap_mcpdm_start(int stream)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (stream)
+               ctrl |= mcpdm->up_channels;
+       else
+               ctrl |= mcpdm->dn_channels;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Disables the transfer through the PDM interface to/from the Phoenix
+ * codec by disabling the corresponding UP or DN channels.
+ */
+void omap_mcpdm_stop(int stream)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (stream)
+               ctrl &= ~mcpdm->up_channels;
+       else
+               ctrl &= ~mcpdm->dn_channels;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Configures McPDM uplink for audio recording.
+ * This function should be called before omap_mcpdm_start.
+ */
+int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink)
+{
+       int irq_mask = 0;
+       int ctrl;
+
+       if (!uplink)
+               return -EINVAL;
+
+       mcpdm->uplink = uplink;
+
+       /* Enable irq request generation */
+       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
+
+       /* Configure uplink threshold */
+       if (uplink->threshold > UP_THRES_MAX)
+               uplink->threshold = UP_THRES_MAX;
+
+       omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
+
+       /* Configure DMA controller */
+       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
+
+       /* Set pdm out format */
+       ctrl = omap_mcpdm_read(MCPDM_CTRL);
+       ctrl &= ~PDMOUTFORMAT;
+       ctrl |= uplink->format & PDMOUTFORMAT;
+
+       /* Uplink channels */
+       mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+
+       return 0;
+}
+
+/*
+ * Configures McPDM downlink for audio playback.
+ * This function should be called before omap_mcpdm_start.
+ */
+int omap_mcpdm_set_downlink(struct omap_mcpdm_link *downlink)
+{
+       int irq_mask = 0;
+       int ctrl;
+
+       if (!downlink)
+               return -EINVAL;
+
+       mcpdm->downlink = downlink;
+
+       /* Enable irq request generation */
+       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
+
+       /* Configure uplink threshold */
+       if (downlink->threshold > DN_THRES_MAX)
+               downlink->threshold = DN_THRES_MAX;
+
+       omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
+
+       /* Enable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
+
+       /* Set pdm out format */
+       ctrl = omap_mcpdm_read(MCPDM_CTRL);
+       ctrl &= ~PDMOUTFORMAT;
+       ctrl |= downlink->format & PDMOUTFORMAT;
+
+       /* Downlink channels */
+       mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+
+       return 0;
+}
+
+/*
+ * Cleans McPDM uplink configuration.
+ * This function should be called when the stream is closed.
+ */
+int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink)
+{
+       int irq_mask = 0;
+
+       if (!uplink)
+               return -EINVAL;
+
+       /* Disable irq request generation */
+       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
+
+       /* Disable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
+
+       /* Clear Downlink channels */
+       mcpdm->up_channels = 0;
+
+       mcpdm->uplink = NULL;
+
+       return 0;
+}
+
+/*
+ * Cleans McPDM downlink configuration.
+ * This function should be called when the stream is closed.
+ */
+int omap_mcpdm_clr_downlink(struct omap_mcpdm_link *downlink)
+{
+       int irq_mask = 0;
+
+       if (!downlink)
+               return -EINVAL;
+
+       /* Disable irq request generation */
+       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
+
+       /* Disable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
+
+       /* clear Downlink channels */
+       mcpdm->dn_channels = 0;
+
+       mcpdm->downlink = NULL;
+
+       return 0;
+}
+
+static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
+{
+       struct omap_mcpdm *mcpdm_irq = dev_id;
+       int irq_status;
+
+       irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
+
+       /* Acknowledge irq event */
+       omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
+
+       if (irq & MCPDM_DN_IRQ_FULL) {
+               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
+               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
+               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
+               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
+       }
+
+       if (irq & MCPDM_DN_IRQ_EMPTY) {
+               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
+               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
+               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
+               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
+       }
+
+       if (irq & MCPDM_DN_IRQ) {
+               dev_dbg(mcpdm_irq->dev, "DN write request\n");
+       }
+
+       if (irq & MCPDM_UP_IRQ_FULL) {
+               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
+               omap_mcpdm_reset(MCPDM_UPLINK, 1);
+               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
+               omap_mcpdm_reset(MCPDM_UPLINK, 0);
+       }
+
+       if (irq & MCPDM_UP_IRQ_EMPTY) {
+               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
+               omap_mcpdm_reset(MCPDM_UPLINK, 1);
+               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
+               omap_mcpdm_reset(MCPDM_UPLINK, 0);
+       }
+
+       if (irq & MCPDM_UP_IRQ) {
+               dev_dbg(mcpdm_irq->dev, "UP write request\n");
+       }
+
+       return IRQ_HANDLED;
+}
+
+int omap_mcpdm_request(void)
+{
+       int ret;
+
+       clk_enable(mcpdm->clk);
+
+       spin_lock(&mcpdm->lock);
+
+       if (!mcpdm->free) {
+               dev_err(mcpdm->dev, "McPDM interface is in use\n");
+               spin_unlock(&mcpdm->lock);
+               return -EBUSY;
+       }
+       mcpdm->free = 0;
+
+       spin_unlock(&mcpdm->lock);
+
+       /* Disable lines while request is ongoing */
+       omap_mcpdm_write(MCPDM_CTRL, 0x00);
+
+       ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
+                               0, "McPDM", (void *)mcpdm);
+       if (ret) {
+               dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void omap_mcpdm_free(void)
+{
+       clk_disable(mcpdm->clk);
+
+       spin_lock(&mcpdm->lock);
+       if (mcpdm->free) {
+               dev_err(mcpdm->dev, "McPDM interface is already free\n");
+               spin_unlock(&mcpdm->lock);
+               return;
+       }
+       mcpdm->free = 1;
+       spin_unlock(&mcpdm->lock);
+
+       free_irq(mcpdm->irq, (void *)mcpdm);
+}
+
+int omap_mcpdm_set_offset(int offset1, int offset2)
+{
+       int offset;
+
+       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
+               return -EINVAL;
+
+       offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
+
+       /* Enable/disable offset cancellation for downlink channel 1 */
+       if (offset1)
+               offset |= DN_OFST_RX1_EN;
+       else
+               offset &= ~DN_OFST_RX1_EN;
+
+       /* Enable/disable offset cancellation for downlink channel 2 */
+       if (offset2)
+               offset |= DN_OFST_RX2_EN;
+       else
+               offset &= ~DN_OFST_RX2_EN;
+
+       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
+
+       return 0;
+}
+
+static int __devinit omap_mcpdm_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret = 0;
+
+       mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
+       if (!mcpdm) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no resource\n");
+               goto exit;
+       }
+
+       spin_lock_init(&mcpdm->lock);
+       mcpdm->free = 1;
+       mcpdm->io_base = ioremap(res->start, resource_size(res));
+       if (!mcpdm->io_base) {
+               ret = -ENOMEM;
+               goto err_ioremap;
+       }
+
+       mcpdm->irq = platform_get_irq(pdev, 0);
+       mcpdm->dev = &pdev->dev;
+       platform_set_drvdata(pdev, mcpdm);
+
+       return 0;
+
+err_clk:
+       iounmap(mcpdm->io_base);
+err_ioremap:
+       kfree(mcpdm);
+exit:
+       return ret;
+}
+
+static int __devexit omap_mcpdm_remove(struct platform_device *pdev)
+{
+       struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       clk_disable(mcpdm_ptr->clk);
+       clk_put(mcpdm_ptr->clk);
+
+       iounmap(mcpdm_ptr->io_base);
+
+       mcpdm_ptr->clk = NULL;
+       mcpdm_ptr->free = 0;
+       mcpdm_ptr->dev = NULL;
+
+       kfree(mcpdm_ptr);
+
+       return 0;
+}
+
+static struct platform_driver omap_mcpdm_driver = {
+       .probe = omap_mcpdm_probe,
+       .remove = __devexit_p(omap_mcpdm_remove),
+       .driver = {
+               .name = "omap-mcpdm",
+       },
+};
+
+static struct platform_device *omap_mcpdm_device;
+
+static int __init omap_mcpdm_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&omap_mcpdm_driver);
+       if (ret)
+               goto error;
+       return 0;
+
+error:
+       return ret;
+}
+arch_initcall(omap_mcpdm_init);
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
new file mode 100644
index 0000000..e70f51c
--- /dev/null
+++ b/sound/soc/omap/mcpdm.h
@@ -0,0 +1,152 @@
+/*
+ * mcpdm.h -- Defines for McPDM driver
+ *
+ * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
+ *
+ * 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 program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/* McPDM registers */
+
+#define MCPDM_REVISION         0x00
+#define MCPDM_SYSCONFIG                0x10
+#define MCPDM_IRQSTATUS_RAW    0x24
+#define MCPDM_IRQSTATUS                0x28
+#define MCPDM_IRQENABLE_SET    0x2C
+#define MCPDM_IRQENABLE_CLR    0x30
+#define MCPDM_IRQWAKE_EN       0x34
+#define MCPDM_DMAENABLE_SET    0x38
+#define MCPDM_DMAENABLE_CLR    0x3C
+#define MCPDM_DMAWAKEEN                0x40
+#define MCPDM_CTRL             0x44
+#define MCPDM_DN_DATA          0x48
+#define MCPDM_UP_DATA          0x4C
+#define MCPDM_FIFO_CTRL_DN     0x50
+#define MCPDM_FIFO_CTRL_UP     0x54
+#define MCPDM_DN_OFFSET                0x58
+
+/*
+ * MCPDM_IRQ bit fields
+ * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
+ */
+
+#define MCPDM_DN_IRQ                   (1 << 0)
+#define MCPDM_DN_IRQ_EMPTY             (1 << 1)
+#define MCPDM_DN_IRQ_ALMST_EMPTY       (1 << 2)
+#define MCPDM_DN_IRQ_FULL              (1 << 3)
+
+#define MCPDM_UP_IRQ                   (1 << 8)
+#define MCPDM_UP_IRQ_EMPTY             (1 << 9)
+#define MCPDM_UP_IRQ_ALMST_FULL                (1 << 10)
+#define MCPDM_UP_IRQ_FULL              (1 << 11)
+
+#define MCPDM_DOWNLINK_IRQ_MASK                0x00F
+#define MCPDM_UPLINK_IRQ_MASK          0xF00
+
+/*
+ * MCPDM_DMAENABLE bit fields
+ */
+
+#define DMA_DN_ENABLE          0x1
+#define DMA_UP_ENABLE          0x2
+
+/*
+ * MCPDM_CTRL bit fields
+ */
+
+#define PDM_UP1_EN             0x0001
+#define PDM_UP2_EN             0x0002
+#define PDM_UP3_EN             0x0004
+#define PDM_DN1_EN             0x0008
+#define PDM_DN2_EN             0x0010
+#define PDM_DN3_EN             0x0020
+#define PDM_DN4_EN             0x0040
+#define PDM_DN5_EN             0x0080
+#define PDMOUTFORMAT           0x0100
+#define CMD_INT                        0x0200
+#define STATUS_INT             0x0400
+#define SW_UP_RST              0x0800
+#define SW_DN_RST              0x1000
+#define PDM_UP_MASK            0x007
+#define PDM_DN_MASK            0x0F8
+#define PDM_CMD_MASK           0x200
+#define PDM_STATUS_MASK                0x400
+
+
+#define PDMOUTFORMAT_LJUST     (0 << 8)
+#define PDMOUTFORMAT_RJUST     (1 << 8)
+
+/*
+ * MCPDM_FIFO_CTRL bit fields
+ */
+
+#define UP_THRES_MAX           0xF
+#define DN_THRES_MAX           0xF
+
+/*
+ * MCPDM_DN_OFFSET bit fields
+ */
+
+#define DN_OFST_RX1_EN         0x0001
+#define DN_OFST_RX2_EN         0x0100
+
+#define DN_OFST_RX1            1
+#define DN_OFST_RX2            9
+#define DN_OFST_MAX            0x1F
+
+#define MCPDM_UPLINK           1
+#define MCPDM_DOWNLINK         2
+
+struct omap_mcpdm_link {
+       int irq_mask;
+       int threshold;
+       int format;
+       int channels;
+};
+
+struct omap_mcpdm_platform_data {
+       unsigned long phys_base;
+       u16 irq;
+};
+
+struct omap_mcpdm {
+       struct device *dev;
+       unsigned long phys_base;
+       void __iomem *io_base;
+       u8 free;
+       int irq;
+
+       spinlock_t lock;
+       struct omap_mcpdm_platform_data *pdata;
+       struct clk *clk;
+       struct omap_mcpdm_link *downlink;
+       struct omap_mcpdm_link *uplink;
+       struct completion irq_completion;
+
+       int dn_channels;
+       int up_channels;
+};
+
+extern void omap_mcpdm_reg_dump(void);
+extern void omap_mcpdm_start(int stream);
+extern void omap_mcpdm_stop(int stream);
+extern int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink);
+extern int omap_mcpdm_set_downlink(struct omap_mcpdm_link *downlink);
+extern int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink);
+extern int omap_mcpdm_clr_downlink(struct omap_mcpdm_link *downlink);
+extern int omap_mcpdm_request(void);
+extern void omap_mcpdm_free(void);
+extern int omap_mcpdm_set_offset(int offset1, int offset2);
--
1.6.0.4

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

* Re: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-22 23:15 [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM Candelaria Villareal, Jorge
@ 2010-01-23  1:44 ` Liam Girdwood
  2010-01-25 21:06   ` Candelaria Villareal, Jorge
  0 siblings, 1 reply; 7+ messages in thread
From: Liam Girdwood @ 2010-01-23  1:44 UTC (permalink / raw)
  To: Candelaria Villareal, Jorge; +Cc: alsa-devel, linux-omap, broonie

On Fri, 2010-01-22 at 17:15 -0600, Candelaria Villareal, Jorge wrote:
> McPDM is the interface between Phoenix audio codec
> and the OMAP4430 processor. It enables data to be transfered
> to/from Phoenix at sample rates of 88.4 or 96 KHz.
> 
> Signed-off-by: Jorge Eduardo Candelaria <x0107209@ti.com>
> Signed-off-by: Margarita Olaya <x0080101@ti.com>

It initially looks like a some of this can be called directly as DAI ops
rather than by the machine driver.

> ---
>  sound/soc/omap/Kconfig  |    3 +
>  sound/soc/omap/Makefile |    2 +
>  sound/soc/omap/mcpdm.c  |  474 +++++++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/omap/mcpdm.h  |  152 +++++++++++++++
>  4 files changed, 631 insertions(+), 0 deletions(-)
>  create mode 100644 sound/soc/omap/mcpdm.c
>  create mode 100644 sound/soc/omap/mcpdm.h
> 
> diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
> index 61952aa..b96e9d8 100644
> --- a/sound/soc/omap/Kconfig
> +++ b/sound/soc/omap/Kconfig
> @@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP
>         tristate
>         select OMAP_MCBSP
> 
> +config SND_OMAP_SOC_MCPDM
> +       tristate
> +
>  config SND_OMAP_SOC_N810
>         tristate "SoC Audio support for Nokia N810"
>         depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
> diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
> index 3db8a6c..bf8b388 100644
> --- a/sound/soc/omap/Makefile
> +++ b/sound/soc/omap/Makefile
> @@ -1,9 +1,11 @@
>  # OMAP Platform Support
>  snd-soc-omap-objs := omap-pcm.o
>  snd-soc-omap-mcbsp-objs := omap-mcbsp.o
> +snd-soc-omap-mcpdm-objs := mcpdm.o
> 
>  obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
>  obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
> +obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
> 
>  # OMAP Machine Support
>  snd-soc-n810-objs := n810.o
> diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
> new file mode 100644
> index 0000000..2bc5acd
> --- /dev/null
> +++ b/sound/soc/omap/mcpdm.c
> @@ -0,0 +1,474 @@
> +/*
> + * mcpdm.c  --  McPDM interface driver
> + *
> + * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
> + * Copyright (C) 2009 - Texas Instruments, Inc.
> + *
> + * 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 program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * 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., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/wait.h>
> +#include <linux/interrupt.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +
> +#include "mcpdm.h"
> +
> +static struct omap_mcpdm *mcpdm;
> +
> +static inline void omap_mcpdm_write(u16 reg, u32 val)
> +{
> +       __raw_writel(val, mcpdm->io_base + reg);
> +}
> +
> +static inline int omap_mcpdm_read(u16 reg)
> +{
> +       return __raw_readl(mcpdm->io_base + reg);
> +}
> +
> +void omap_mcpdm_reg_dump(void)
> +{
> +       dev_dbg(mcpdm->dev, "***********************\n");
> +       dev_dbg(mcpdm->dev, "IRQSTATUS_RAW:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
> +       dev_dbg(mcpdm->dev, "IRQSTATUS:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_IRQSTATUS));
> +       dev_dbg(mcpdm->dev, "IRQENABLE_SET:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_IRQENABLE_SET));
> +       dev_dbg(mcpdm->dev, "IRQENABLE_CLR:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
> +       dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_IRQWAKE_EN));
> +       dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_DMAENABLE_SET));
> +       dev_dbg(mcpdm->dev, "DMAENABLE_CLR:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
> +       dev_dbg(mcpdm->dev, "DMAWAKEEN:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_DMAWAKEEN));
> +       dev_dbg(mcpdm->dev, "CTRL:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_CTRL));
> +       dev_dbg(mcpdm->dev, "DN_DATA:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_DN_DATA));
> +       dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_UP_DATA));
> +       dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
> +       dev_dbg(mcpdm->dev, "FIFO_CTRL_UP:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
> +       dev_dbg(mcpdm->dev, "DN_OFFSET:  0x%04x\n",
> +                       omap_mcpdm_read(MCPDM_DN_OFFSET));
> +       dev_dbg(mcpdm->dev, "***********************\n");
> +}
> +

static ?

> +/*
> + * Takes the McPDM module in and out of reset state.
> + * Uplink and downlink can be reset individually.
> + */
> +static void omap_mcpdm_reset(int links, int reset)
> +{
> +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> +
> +       if (links & MCPDM_UPLINK) {
> +               if (reset)
> +                       ctrl |= SW_UP_RST;
> +               else
> +                       ctrl &= ~SW_UP_RST;
> +       }
> +
> +       if (links & MCPDM_DOWNLINK) {
> +               if (reset)
> +                       ctrl |= SW_DN_RST;
> +               else
> +                       ctrl &= ~SW_DN_RST;
> +       }
> +
> +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> +}
> +

Would it not be better to rename uplink/downlink as playback and capture
for ALSA ? If so, you could have an inline playback and capture version
of this function.

> +/*
> + * Enables the transfer through the PDM interface to/from the Phoenix
> + * codec by enabling the corresponding UP or DN channels.
> + */
> +void omap_mcpdm_start(int stream)
> +{
> +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> +
> +       if (stream)
> +               ctrl |= mcpdm->up_channels;
> +       else
> +               ctrl |= mcpdm->dn_channels;
> +
> +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> +}
> +
> +/*
> + * Disables the transfer through the PDM interface to/from the Phoenix
> + * codec by disabling the corresponding UP or DN channels.
> + */
> +void omap_mcpdm_stop(int stream)
> +{
> +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> +
> +       if (stream)
> +               ctrl &= ~mcpdm->up_channels;
> +       else
> +               ctrl &= ~mcpdm->dn_channels;
> +
> +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> +}
> +
> +/*
> + * Configures McPDM uplink for audio recording.
> + * This function should be called before omap_mcpdm_start.
> + */
> +int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink)
> +{
> +       int irq_mask = 0;
> +       int ctrl;
> +
> +       if (!uplink)
> +               return -EINVAL;
> +
> +       mcpdm->uplink = uplink;
> +
> +       /* Enable irq request generation */
> +       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
> +       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
> +
> +       /* Configure uplink threshold */
> +       if (uplink->threshold > UP_THRES_MAX)
> +               uplink->threshold = UP_THRES_MAX;
> +
> +       omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
> +
> +       /* Configure DMA controller */
> +       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
> +
> +       /* Set pdm out format */
> +       ctrl = omap_mcpdm_read(MCPDM_CTRL);
> +       ctrl &= ~PDMOUTFORMAT;
> +       ctrl |= uplink->format & PDMOUTFORMAT;
> +
> +       /* Uplink channels */
> +       mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
> +
> +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> +
> +       return 0;
> +}
> +
> +/*
> + * Configures McPDM downlink for audio playback.
> + * This function should be called before omap_mcpdm_start.
> + */
> +int omap_mcpdm_set_downlink(struct omap_mcpdm_link *downlink)
> +{
> +       int irq_mask = 0;
> +       int ctrl;
> +
> +       if (!downlink)
> +               return -EINVAL;
> +
> +       mcpdm->downlink = downlink;
> +
> +       /* Enable irq request generation */
> +       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
> +       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
> +
> +       /* Configure uplink threshold */
> +       if (downlink->threshold > DN_THRES_MAX)
> +               downlink->threshold = DN_THRES_MAX;
> +
> +       omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
> +
> +       /* Enable DMA request generation */
> +       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
> +
> +       /* Set pdm out format */
> +       ctrl = omap_mcpdm_read(MCPDM_CTRL);
> +       ctrl &= ~PDMOUTFORMAT;
> +       ctrl |= downlink->format & PDMOUTFORMAT;
> +
> +       /* Downlink channels */
> +       mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
> +
> +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> +
> +       return 0;
> +}
> +]

It would be better if the naming here was more aligned with ALSA pcm
convention.

> +/*
> + * Cleans McPDM uplink configuration.
> + * This function should be called when the stream is closed.
> + */
> +int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink)
> +{
> +       int irq_mask = 0;
> +
> +       if (!uplink)
> +               return -EINVAL;
> +
> +       /* Disable irq request generation */
> +       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
> +       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
> +
> +       /* Disable DMA request generation */
> +       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
> +
> +       /* Clear Downlink channels */
> +       mcpdm->up_channels = 0;
> +
> +       mcpdm->uplink = NULL;
> +
> +       return 0;
> +}
> +
> +/*
> + * Cleans McPDM downlink configuration.
> + * This function should be called when the stream is closed.
> + */
> +int omap_mcpdm_clr_downlink(struct omap_mcpdm_link *downlink)
> +{
> +       int irq_mask = 0;
> +
> +       if (!downlink)
> +               return -EINVAL;
> +
> +       /* Disable irq request generation */
> +       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
> +       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
> +
> +       /* Disable DMA request generation */
> +       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
> +
> +       /* clear Downlink channels */
> +       mcpdm->dn_channels = 0;
> +
> +       mcpdm->downlink = NULL;
> +
> +       return 0;
> +}
> +
> +static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
> +{
> +       struct omap_mcpdm *mcpdm_irq = dev_id;
> +       int irq_status;
> +
> +       irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
> +
> +       /* Acknowledge irq event */
> +       omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
> +
> +       if (irq & MCPDM_DN_IRQ_FULL) {
> +               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
> +               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
> +               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
> +               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
> +       }
> +
> +       if (irq & MCPDM_DN_IRQ_EMPTY) {
> +               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
> +               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
> +               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
> +               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
> +       }
> +
> +       if (irq & MCPDM_DN_IRQ) {
> +               dev_dbg(mcpdm_irq->dev, "DN write request\n");
> +       }
> +
> +       if (irq & MCPDM_UP_IRQ_FULL) {
> +               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
> +               omap_mcpdm_reset(MCPDM_UPLINK, 1);
> +               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
> +               omap_mcpdm_reset(MCPDM_UPLINK, 0);
> +       }
> +
> +       if (irq & MCPDM_UP_IRQ_EMPTY) {
> +               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
> +               omap_mcpdm_reset(MCPDM_UPLINK, 1);
> +               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
> +               omap_mcpdm_reset(MCPDM_UPLINK, 0);
> +       }
> +
> +       if (irq & MCPDM_UP_IRQ) {
> +               dev_dbg(mcpdm_irq->dev, "UP write request\n");
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +int omap_mcpdm_request(void)
> +{
> +       int ret;
> +
> +       clk_enable(mcpdm->clk);
> +
> +       spin_lock(&mcpdm->lock);
> +
> +       if (!mcpdm->free) {
> +               dev_err(mcpdm->dev, "McPDM interface is in use\n");
> +               spin_unlock(&mcpdm->lock);
> +               return -EBUSY;
> +       }
> +       mcpdm->free = 0;
> +
> +       spin_unlock(&mcpdm->lock);
> +
> +       /* Disable lines while request is ongoing */
> +       omap_mcpdm_write(MCPDM_CTRL, 0x00);
> +
> +       ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
> +                               0, "McPDM", (void *)mcpdm);
> +       if (ret) {
> +               dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +void omap_mcpdm_free(void)
> +{
> +       clk_disable(mcpdm->clk);
> +
> +       spin_lock(&mcpdm->lock);
> +       if (mcpdm->free) {
> +               dev_err(mcpdm->dev, "McPDM interface is already free\n");
> +               spin_unlock(&mcpdm->lock);
> +               return;
> +       }
> +       mcpdm->free = 1;
> +       spin_unlock(&mcpdm->lock);
> +
> +       free_irq(mcpdm->irq, (void *)mcpdm);
> +}
> +
> +int omap_mcpdm_set_offset(int offset1, int offset2)
> +{
> +       int offset;
> +
> +       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
> +               return -EINVAL;
> +
> +       offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
> +
> +       /* Enable/disable offset cancellation for downlink channel 1 */
> +       if (offset1)
> +               offset |= DN_OFST_RX1_EN;
> +       else
> +               offset &= ~DN_OFST_RX1_EN;
> +
> +       /* Enable/disable offset cancellation for downlink channel 2 */
> +       if (offset2)
> +               offset |= DN_OFST_RX2_EN;
> +       else
> +               offset &= ~DN_OFST_RX2_EN;
> +
> +       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
> +
> +       return 0;
> +}

What does this do and why is it not static ?

> +
> +static int __devinit omap_mcpdm_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       int ret = 0;
> +
> +       mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
> +       if (!mcpdm) {
> +               ret = -ENOMEM;
> +               goto exit;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res == NULL) {
> +               dev_err(&pdev->dev, "no resource\n");
> +               goto exit;
> +       }
> +
> +       spin_lock_init(&mcpdm->lock);
> +       mcpdm->free = 1;
> +       mcpdm->io_base = ioremap(res->start, resource_size(res));
> +       if (!mcpdm->io_base) {
> +               ret = -ENOMEM;
> +               goto err_ioremap;
> +       }
> +
> +       mcpdm->irq = platform_get_irq(pdev, 0);
> +       mcpdm->dev = &pdev->dev;
> +       platform_set_drvdata(pdev, mcpdm);
> +
> +       return 0;
> +
> +err_clk:
> +       iounmap(mcpdm->io_base);
> +err_ioremap:
> +       kfree(mcpdm);
> +exit:
> +       return ret;
> +}
> +
> +static int __devexit omap_mcpdm_remove(struct platform_device *pdev)
> +{
> +       struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
> +
> +       platform_set_drvdata(pdev, NULL);
> +
> +       clk_disable(mcpdm_ptr->clk);
> +       clk_put(mcpdm_ptr->clk);
> +
> +       iounmap(mcpdm_ptr->io_base);
> +
> +       mcpdm_ptr->clk = NULL;
> +       mcpdm_ptr->free = 0;
> +       mcpdm_ptr->dev = NULL;
> +
> +       kfree(mcpdm_ptr);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver omap_mcpdm_driver = {
> +       .probe = omap_mcpdm_probe,
> +       .remove = __devexit_p(omap_mcpdm_remove),
> +       .driver = {
> +               .name = "omap-mcpdm",
> +       },
> +};
> +
> +static struct platform_device *omap_mcpdm_device;
> +
> +static int __init omap_mcpdm_init(void)
> +{
> +       int ret;
> +
> +       ret = platform_driver_register(&omap_mcpdm_driver);
> +       if (ret)
> +               goto error;
> +       return 0;
> +
> +error:
> +       return ret;
> +}
> +arch_initcall(omap_mcpdm_init);
> diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
> new file mode 100644
> index 0000000..e70f51c
> --- /dev/null
> +++ b/sound/soc/omap/mcpdm.h
> @@ -0,0 +1,152 @@
> +/*
> + * mcpdm.h -- Defines for McPDM driver
> + *
> + * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
> + *
> + * 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 program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * 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., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +/* McPDM registers */
> +
> +#define MCPDM_REVISION         0x00
> +#define MCPDM_SYSCONFIG                0x10
> +#define MCPDM_IRQSTATUS_RAW    0x24
> +#define MCPDM_IRQSTATUS                0x28
> +#define MCPDM_IRQENABLE_SET    0x2C
> +#define MCPDM_IRQENABLE_CLR    0x30
> +#define MCPDM_IRQWAKE_EN       0x34
> +#define MCPDM_DMAENABLE_SET    0x38
> +#define MCPDM_DMAENABLE_CLR    0x3C
> +#define MCPDM_DMAWAKEEN                0x40
> +#define MCPDM_CTRL             0x44
> +#define MCPDM_DN_DATA          0x48
> +#define MCPDM_UP_DATA          0x4C
> +#define MCPDM_FIFO_CTRL_DN     0x50
> +#define MCPDM_FIFO_CTRL_UP     0x54
> +#define MCPDM_DN_OFFSET                0x58
> +
> +/*
> + * MCPDM_IRQ bit fields
> + * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
> + */
> +
> +#define MCPDM_DN_IRQ                   (1 << 0)
> +#define MCPDM_DN_IRQ_EMPTY             (1 << 1)
> +#define MCPDM_DN_IRQ_ALMST_EMPTY       (1 << 2)
> +#define MCPDM_DN_IRQ_FULL              (1 << 3)
> +
> +#define MCPDM_UP_IRQ                   (1 << 8)
> +#define MCPDM_UP_IRQ_EMPTY             (1 << 9)
> +#define MCPDM_UP_IRQ_ALMST_FULL                (1 << 10)
> +#define MCPDM_UP_IRQ_FULL              (1 << 11)
> +
> +#define MCPDM_DOWNLINK_IRQ_MASK                0x00F
> +#define MCPDM_UPLINK_IRQ_MASK          0xF00
> +
> +/*
> + * MCPDM_DMAENABLE bit fields
> + */
> +
> +#define DMA_DN_ENABLE          0x1
> +#define DMA_UP_ENABLE          0x2
> +
> +/*
> + * MCPDM_CTRL bit fields
> + */
> +
> +#define PDM_UP1_EN             0x0001
> +#define PDM_UP2_EN             0x0002
> +#define PDM_UP3_EN             0x0004
> +#define PDM_DN1_EN             0x0008
> +#define PDM_DN2_EN             0x0010
> +#define PDM_DN3_EN             0x0020
> +#define PDM_DN4_EN             0x0040
> +#define PDM_DN5_EN             0x0080
> +#define PDMOUTFORMAT           0x0100
> +#define CMD_INT                        0x0200
> +#define STATUS_INT             0x0400
> +#define SW_UP_RST              0x0800
> +#define SW_DN_RST              0x1000
> +#define PDM_UP_MASK            0x007
> +#define PDM_DN_MASK            0x0F8
> +#define PDM_CMD_MASK           0x200
> +#define PDM_STATUS_MASK                0x400
> +
> +
> +#define PDMOUTFORMAT_LJUST     (0 << 8)
> +#define PDMOUTFORMAT_RJUST     (1 << 8)
> +
> +/*
> + * MCPDM_FIFO_CTRL bit fields
> + */
> +
> +#define UP_THRES_MAX           0xF
> +#define DN_THRES_MAX           0xF
> +
> +/*
> + * MCPDM_DN_OFFSET bit fields
> + */
> +
> +#define DN_OFST_RX1_EN         0x0001
> +#define DN_OFST_RX2_EN         0x0100
> +
> +#define DN_OFST_RX1            1
> +#define DN_OFST_RX2            9
> +#define DN_OFST_MAX            0x1F
> +
> +#define MCPDM_UPLINK           1
> +#define MCPDM_DOWNLINK         2
> +
> +struct omap_mcpdm_link {
> +       int irq_mask;
> +       int threshold;
> +       int format;
> +       int channels;
> +};
> +
> +struct omap_mcpdm_platform_data {
> +       unsigned long phys_base;
> +       u16 irq;
> +};
> +
> +struct omap_mcpdm {
> +       struct device *dev;
> +       unsigned long phys_base;
> +       void __iomem *io_base;
> +       u8 free;
> +       int irq;
> +
> +       spinlock_t lock;
> +       struct omap_mcpdm_platform_data *pdata;
> +       struct clk *clk;
> +       struct omap_mcpdm_link *downlink;
> +       struct omap_mcpdm_link *uplink;
> +       struct completion irq_completion;
> +
> +       int dn_channels;
> +       int up_channels;
> +};
> +
> +extern void omap_mcpdm_reg_dump(void);
> +extern void omap_mcpdm_start(int stream);
> +extern void omap_mcpdm_stop(int stream);
> +extern int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink);
> +extern int omap_mcpdm_set_downlink(struct omap_mcpdm_link *downlink);
> +extern int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink);
> +extern int omap_mcpdm_clr_downlink(struct omap_mcpdm_link *downlink);
> +extern int omap_mcpdm_request(void);
> +extern void omap_mcpdm_free(void);
> +extern int omap_mcpdm_set_offset(int offset1, int offset2);
> --
> 1.6.0.4
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel



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

* RE: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-23  1:44 ` [alsa-devel] " Liam Girdwood
@ 2010-01-25 21:06   ` Candelaria Villareal, Jorge
  2010-01-26  9:57     ` Mark Brown
  2010-01-26 10:25     ` Liam Girdwood
  0 siblings, 2 replies; 7+ messages in thread
From: Candelaria Villareal, Jorge @ 2010-01-25 21:06 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, linux-omap, broonie

Liam Girdwood wrote:
>
> On Fri, 2010-01-22 at 17:15 -0600, Candelaria Villareal, Jorge wrote:
> > McPDM is the interface between Phoenix audio codec
> > and the OMAP4430 processor. It enables data to be transfered
> > to/from Phoenix at sample rates of 88.4 or 96 KHz.
> >
> > Signed-off-by: Jorge Eduardo Candelaria <x0107209@ti.com>
> > Signed-off-by: Margarita Olaya <x0080101@ti.com>
>
> It initially looks like a some of this can be called directly
> as DAI ops
> rather than by the machine driver.

Could you explain a little further?

>
> > ---
> >  sound/soc/omap/Kconfig  |    3 +
> >  sound/soc/omap/Makefile |    2 +
> >  sound/soc/omap/mcpdm.c  |  474
> +++++++++++++++++++++++++++++++++++++++++++++++
> >  sound/soc/omap/mcpdm.h  |  152 +++++++++++++++
> >  4 files changed, 631 insertions(+), 0 deletions(-)
> >  create mode 100644 sound/soc/omap/mcpdm.c
> >  create mode 100644 sound/soc/omap/mcpdm.h
> >
> > diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
> > index 61952aa..b96e9d8 100644
> > --- a/sound/soc/omap/Kconfig
> > +++ b/sound/soc/omap/Kconfig
> > @@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP
> >         tristate
> >         select OMAP_MCBSP
> >
> > +config SND_OMAP_SOC_MCPDM
> > +       tristate
> > +
> >  config SND_OMAP_SOC_N810
> >         tristate "SoC Audio support for Nokia N810"
> >         depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
> > diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
> > index 3db8a6c..bf8b388 100644
> > --- a/sound/soc/omap/Makefile
> > +++ b/sound/soc/omap/Makefile
> > @@ -1,9 +1,11 @@
> >  # OMAP Platform Support
> >  snd-soc-omap-objs := omap-pcm.o
> >  snd-soc-omap-mcbsp-objs := omap-mcbsp.o
> > +snd-soc-omap-mcpdm-objs := mcpdm.o
> >
> >  obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
> >  obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
> > +obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
> >
> >  # OMAP Machine Support
> >  snd-soc-n810-objs := n810.o
> > diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
> > new file mode 100644
> > index 0000000..2bc5acd
> > --- /dev/null
> > +++ b/sound/soc/omap/mcpdm.c
> > @@ -0,0 +1,474 @@
> > +/*
> > + * mcpdm.c  --  McPDM interface driver
> > + *
> > + * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
> > + * Copyright (C) 2009 - Texas Instruments, Inc.
> > + *
> > + * 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 program is distributed in the hope that it will be
> useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> See the GNU
> > + * General Public License for more details.
> > + *
> > + * 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., 51 Franklin St, Fifth Floor, Boston, MA
> > + * 02110-1301 USA
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/wait.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/err.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/irq.h>
> > +
> > +#include "mcpdm.h"
> > +
> > +static struct omap_mcpdm *mcpdm;
> > +
> > +static inline void omap_mcpdm_write(u16 reg, u32 val)
> > +{
> > +       __raw_writel(val, mcpdm->io_base + reg);
> > +}
> > +
> > +static inline int omap_mcpdm_read(u16 reg)
> > +{
> > +       return __raw_readl(mcpdm->io_base + reg);
> > +}
> > +
> > +void omap_mcpdm_reg_dump(void)
> > +{
> > +       dev_dbg(mcpdm->dev, "***********************\n");
> > +       dev_dbg(mcpdm->dev, "IRQSTATUS_RAW:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
> > +       dev_dbg(mcpdm->dev, "IRQSTATUS:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_IRQSTATUS));
> > +       dev_dbg(mcpdm->dev, "IRQENABLE_SET:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_IRQENABLE_SET));
> > +       dev_dbg(mcpdm->dev, "IRQENABLE_CLR:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
> > +       dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_IRQWAKE_EN));
> > +       dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_DMAENABLE_SET));
> > +       dev_dbg(mcpdm->dev, "DMAENABLE_CLR:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
> > +       dev_dbg(mcpdm->dev, "DMAWAKEEN:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_DMAWAKEEN));
> > +       dev_dbg(mcpdm->dev, "CTRL:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_CTRL));
> > +       dev_dbg(mcpdm->dev, "DN_DATA:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_DN_DATA));
> > +       dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_UP_DATA));
> > +       dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
> > +       dev_dbg(mcpdm->dev, "FIFO_CTRL_UP:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
> > +       dev_dbg(mcpdm->dev, "DN_OFFSET:  0x%04x\n",
> > +                       omap_mcpdm_read(MCPDM_DN_OFFSET));
> > +       dev_dbg(mcpdm->dev, "***********************\n");
> > +}
> > +
>
> static ?

Ok, will make this static.

>
> > +/*
> > + * Takes the McPDM module in and out of reset state.
> > + * Uplink and downlink can be reset individually.
> > + */
> > +static void omap_mcpdm_reset(int links, int reset)
> > +{
> > +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > +
> > +       if (links & MCPDM_UPLINK) {
> > +               if (reset)
> > +                       ctrl |= SW_UP_RST;
> > +               else
> > +                       ctrl &= ~SW_UP_RST;
> > +       }
> > +
> > +       if (links & MCPDM_DOWNLINK) {
> > +               if (reset)
> > +                       ctrl |= SW_DN_RST;
> > +               else
> > +                       ctrl &= ~SW_DN_RST;
> > +       }
> > +
> > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > +}
> > +
>
> Would it not be better to rename uplink/downlink as playback
> and capture
> for ALSA ? If so, you could have an inline playback and
> capture version
> of this function.

Data paths in McPDM module are named uplink/downlink, so these
names were chosen to be consistent. Is renaming it according
to ALSA the best approach?

>
> > +/*
> > + * Enables the transfer through the PDM interface to/from
> the Phoenix
> > + * codec by enabling the corresponding UP or DN channels.
> > + */
> > +void omap_mcpdm_start(int stream)
> > +{
> > +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > +
> > +       if (stream)
> > +               ctrl |= mcpdm->up_channels;
> > +       else
> > +               ctrl |= mcpdm->dn_channels;
> > +
> > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > +}
> > +
> > +/*
> > + * Disables the transfer through the PDM interface to/from
> the Phoenix
> > + * codec by disabling the corresponding UP or DN channels.
> > + */
> > +void omap_mcpdm_stop(int stream)
> > +{
> > +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > +
> > +       if (stream)
> > +               ctrl &= ~mcpdm->up_channels;
> > +       else
> > +               ctrl &= ~mcpdm->dn_channels;
> > +
> > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > +}
> > +
> > +/*
> > + * Configures McPDM uplink for audio recording.
> > + * This function should be called before omap_mcpdm_start.
> > + */
> > +int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink)
> > +{
> > +       int irq_mask = 0;
> > +       int ctrl;
> > +
> > +       if (!uplink)
> > +               return -EINVAL;
> > +
> > +       mcpdm->uplink = uplink;
> > +
> > +       /* Enable irq request generation */
> > +       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
> > +       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
> > +
> > +       /* Configure uplink threshold */
> > +       if (uplink->threshold > UP_THRES_MAX)
> > +               uplink->threshold = UP_THRES_MAX;
> > +
> > +       omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
> > +
> > +       /* Configure DMA controller */
> > +       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
> > +
> > +       /* Set pdm out format */
> > +       ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > +       ctrl &= ~PDMOUTFORMAT;
> > +       ctrl |= uplink->format & PDMOUTFORMAT;
> > +
> > +       /* Uplink channels */
> > +       mcpdm->up_channels = uplink->channels &
> (PDM_UP_MASK | PDM_STATUS_MASK);
> > +
> > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > +
> > +       return 0;
> > +}
> > +
> > +/*
> > + * Configures McPDM downlink for audio playback.
> > + * This function should be called before omap_mcpdm_start.
> > + */
> > +int omap_mcpdm_set_downlink(struct omap_mcpdm_link *downlink)
> > +{
> > +       int irq_mask = 0;
> > +       int ctrl;
> > +
> > +       if (!downlink)
> > +               return -EINVAL;
> > +
> > +       mcpdm->downlink = downlink;
> > +
> > +       /* Enable irq request generation */
> > +       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
> > +       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
> > +
> > +       /* Configure uplink threshold */
> > +       if (downlink->threshold > DN_THRES_MAX)
> > +               downlink->threshold = DN_THRES_MAX;
> > +
> > +       omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
> > +
> > +       /* Enable DMA request generation */
> > +       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
> > +
> > +       /* Set pdm out format */
> > +       ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > +       ctrl &= ~PDMOUTFORMAT;
> > +       ctrl |= downlink->format & PDMOUTFORMAT;
> > +
> > +       /* Downlink channels */
> > +       mcpdm->dn_channels = downlink->channels &
> (PDM_DN_MASK | PDM_CMD_MASK);
> > +
> > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > +
> > +       return 0;
> > +}
> > +]
>
> It would be better if the naming here was more aligned with ALSA pcm
> convention.

ditto

>
> > +/*
> > + * Cleans McPDM uplink configuration.
> > + * This function should be called when the stream is closed.
> > + */
> > +int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink)
> > +{
> > +       int irq_mask = 0;
> > +
> > +       if (!uplink)
> > +               return -EINVAL;
> > +
> > +       /* Disable irq request generation */
> > +       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
> > +       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
> > +
> > +       /* Disable DMA request generation */
> > +       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
> > +
> > +       /* Clear Downlink channels */
> > +       mcpdm->up_channels = 0;
> > +
> > +       mcpdm->uplink = NULL;
> > +
> > +       return 0;
> > +}
> > +
> > +/*
> > + * Cleans McPDM downlink configuration.
> > + * This function should be called when the stream is closed.
> > + */
> > +int omap_mcpdm_clr_downlink(struct omap_mcpdm_link *downlink)
> > +{
> > +       int irq_mask = 0;
> > +
> > +       if (!downlink)
> > +               return -EINVAL;
> > +
> > +       /* Disable irq request generation */
> > +       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
> > +       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
> > +
> > +       /* Disable DMA request generation */
> > +       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
> > +
> > +       /* clear Downlink channels */
> > +       mcpdm->dn_channels = 0;
> > +
> > +       mcpdm->downlink = NULL;
> > +
> > +       return 0;
> > +}
> > +
> > +static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
> > +{
> > +       struct omap_mcpdm *mcpdm_irq = dev_id;
> > +       int irq_status;
> > +
> > +       irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
> > +
> > +       /* Acknowledge irq event */
> > +       omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
> > +
> > +       if (irq & MCPDM_DN_IRQ_FULL) {
> > +               dev_err(mcpdm_irq->dev, "DN FIFO error
> %x\n", irq_status);
> > +               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
> > +               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
> > +               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
> > +       }
> > +
> > +       if (irq & MCPDM_DN_IRQ_EMPTY) {
> > +               dev_err(mcpdm_irq->dev, "DN FIFO error
> %x\n", irq_status);
> > +               omap_mcpdm_reset(MCPDM_DOWNLINK, 1);
> > +               omap_mcpdm_set_downlink(mcpdm_irq->downlink);
> > +               omap_mcpdm_reset(MCPDM_DOWNLINK, 0);
> > +       }
> > +
> > +       if (irq & MCPDM_DN_IRQ) {
> > +               dev_dbg(mcpdm_irq->dev, "DN write request\n");
> > +       }
> > +
> > +       if (irq & MCPDM_UP_IRQ_FULL) {
> > +               dev_err(mcpdm_irq->dev, "UP FIFO error
> %x\n", irq_status);
> > +               omap_mcpdm_reset(MCPDM_UPLINK, 1);
> > +               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
> > +               omap_mcpdm_reset(MCPDM_UPLINK, 0);
> > +       }
> > +
> > +       if (irq & MCPDM_UP_IRQ_EMPTY) {
> > +               dev_err(mcpdm_irq->dev, "UP FIFO error
> %x\n", irq_status);
> > +               omap_mcpdm_reset(MCPDM_UPLINK, 1);
> > +               omap_mcpdm_set_uplink(mcpdm_irq->uplink);
> > +               omap_mcpdm_reset(MCPDM_UPLINK, 0);
> > +       }
> > +
> > +       if (irq & MCPDM_UP_IRQ) {
> > +               dev_dbg(mcpdm_irq->dev, "UP write request\n");
> > +       }
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +int omap_mcpdm_request(void)
> > +{
> > +       int ret;
> > +
> > +       clk_enable(mcpdm->clk);
> > +
> > +       spin_lock(&mcpdm->lock);
> > +
> > +       if (!mcpdm->free) {
> > +               dev_err(mcpdm->dev, "McPDM interface is in use\n");
> > +               spin_unlock(&mcpdm->lock);
> > +               return -EBUSY;
> > +       }
> > +       mcpdm->free = 0;
> > +
> > +       spin_unlock(&mcpdm->lock);
> > +
> > +       /* Disable lines while request is ongoing */
> > +       omap_mcpdm_write(MCPDM_CTRL, 0x00);
> > +
> > +       ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
> > +                               0, "McPDM", (void *)mcpdm);
> > +       if (ret) {
> > +               dev_err(mcpdm->dev, "Request for McPDM IRQ
> failed\n");
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +void omap_mcpdm_free(void)
> > +{
> > +       clk_disable(mcpdm->clk);
> > +
> > +       spin_lock(&mcpdm->lock);
> > +       if (mcpdm->free) {
> > +               dev_err(mcpdm->dev, "McPDM interface is
> already free\n");
> > +               spin_unlock(&mcpdm->lock);
> > +               return;
> > +       }
> > +       mcpdm->free = 1;
> > +       spin_unlock(&mcpdm->lock);
> > +
> > +       free_irq(mcpdm->irq, (void *)mcpdm);
> > +}
> > +
> > +int omap_mcpdm_set_offset(int offset1, int offset2)
> > +{
> > +       int offset;
> > +
> > +       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
> > +               return -EINVAL;
> > +
> > +       offset = (offset1 << DN_OFST_RX1) | (offset2 <<
> DN_OFST_RX2);
> > +
> > +       /* Enable/disable offset cancellation for downlink
> channel 1 */
> > +       if (offset1)
> > +               offset |= DN_OFST_RX1_EN;
> > +       else
> > +               offset &= ~DN_OFST_RX1_EN;
> > +
> > +       /* Enable/disable offset cancellation for downlink
> channel 2 */
> > +       if (offset2)
> > +               offset |= DN_OFST_RX2_EN;
> > +       else
> > +               offset &= ~DN_OFST_RX2_EN;
> > +
> > +       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
> > +
> > +       return 0;
> > +}
>
> What does this do and why is it not static ?

It enables and configures offset cancelation for the analog headset path. It is supposed to be called by the codec driver, so it should'nt be static. But, offset cancelation is probably not going to be used at first.

>
> > +
> > +static int __devinit omap_mcpdm_probe(struct platform_device *pdev)
> > +{
> > +       struct resource *res;
> > +       int ret = 0;
> > +
> > +       mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
> > +       if (!mcpdm) {
> > +               ret = -ENOMEM;
> > +               goto exit;
> > +       }
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       if (res == NULL) {
> > +               dev_err(&pdev->dev, "no resource\n");
> > +               goto exit;
> > +       }
> > +
> > +       spin_lock_init(&mcpdm->lock);
> > +       mcpdm->free = 1;
> > +       mcpdm->io_base = ioremap(res->start, resource_size(res));
> > +       if (!mcpdm->io_base) {
> > +               ret = -ENOMEM;
> > +               goto err_ioremap;
> > +       }
> > +
> > +       mcpdm->irq = platform_get_irq(pdev, 0);
> > +       mcpdm->dev = &pdev->dev;
> > +       platform_set_drvdata(pdev, mcpdm);
> > +
> > +       return 0;
> > +
> > +err_clk:
> > +       iounmap(mcpdm->io_base);
> > +err_ioremap:
> > +       kfree(mcpdm);
> > +exit:
> > +       return ret;
> > +}
> > +
> > +static int __devexit omap_mcpdm_remove(struct
> platform_device *pdev)
> > +{
> > +       struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
> > +
> > +       platform_set_drvdata(pdev, NULL);
> > +
> > +       clk_disable(mcpdm_ptr->clk);
> > +       clk_put(mcpdm_ptr->clk);
> > +
> > +       iounmap(mcpdm_ptr->io_base);
> > +
> > +       mcpdm_ptr->clk = NULL;
> > +       mcpdm_ptr->free = 0;
> > +       mcpdm_ptr->dev = NULL;
> > +
> > +       kfree(mcpdm_ptr);
> > +
> > +       return 0;
> > +}
> > +
> > +static struct platform_driver omap_mcpdm_driver = {
> > +       .probe = omap_mcpdm_probe,
> > +       .remove = __devexit_p(omap_mcpdm_remove),
> > +       .driver = {
> > +               .name = "omap-mcpdm",
> > +       },
> > +};
> > +
> > +static struct platform_device *omap_mcpdm_device;
> > +
> > +static int __init omap_mcpdm_init(void)
> > +{
> > +       int ret;
> > +
> > +       ret = platform_driver_register(&omap_mcpdm_driver);
> > +       if (ret)
> > +               goto error;
> > +       return 0;
> > +
> > +error:
> > +       return ret;
> > +}
> > +arch_initcall(omap_mcpdm_init);
> > diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
> > new file mode 100644
> > index 0000000..e70f51c
> > --- /dev/null
> > +++ b/sound/soc/omap/mcpdm.h
> > @@ -0,0 +1,152 @@
> > +/*
> > + * mcpdm.h -- Defines for McPDM driver
> > + *
> > + * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
> > + *
> > + * 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 program is distributed in the hope that it will be
> useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> See the GNU
> > + * General Public License for more details.
> > + *
> > + * 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., 51 Franklin St, Fifth Floor, Boston, MA
> > + * 02110-1301 USA
> > + *
> > + */
> > +
> > +/* McPDM registers */
> > +
> > +#define MCPDM_REVISION         0x00
> > +#define MCPDM_SYSCONFIG                0x10
> > +#define MCPDM_IRQSTATUS_RAW    0x24
> > +#define MCPDM_IRQSTATUS                0x28
> > +#define MCPDM_IRQENABLE_SET    0x2C
> > +#define MCPDM_IRQENABLE_CLR    0x30
> > +#define MCPDM_IRQWAKE_EN       0x34
> > +#define MCPDM_DMAENABLE_SET    0x38
> > +#define MCPDM_DMAENABLE_CLR    0x3C
> > +#define MCPDM_DMAWAKEEN                0x40
> > +#define MCPDM_CTRL             0x44
> > +#define MCPDM_DN_DATA          0x48
> > +#define MCPDM_UP_DATA          0x4C
> > +#define MCPDM_FIFO_CTRL_DN     0x50
> > +#define MCPDM_FIFO_CTRL_UP     0x54
> > +#define MCPDM_DN_OFFSET                0x58
> > +
> > +/*
> > + * MCPDM_IRQ bit fields
> > + * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
> > + */
> > +
> > +#define MCPDM_DN_IRQ                   (1 << 0)
> > +#define MCPDM_DN_IRQ_EMPTY             (1 << 1)
> > +#define MCPDM_DN_IRQ_ALMST_EMPTY       (1 << 2)
> > +#define MCPDM_DN_IRQ_FULL              (1 << 3)
> > +
> > +#define MCPDM_UP_IRQ                   (1 << 8)
> > +#define MCPDM_UP_IRQ_EMPTY             (1 << 9)
> > +#define MCPDM_UP_IRQ_ALMST_FULL                (1 << 10)
> > +#define MCPDM_UP_IRQ_FULL              (1 << 11)
> > +
> > +#define MCPDM_DOWNLINK_IRQ_MASK                0x00F
> > +#define MCPDM_UPLINK_IRQ_MASK          0xF00
> > +
> > +/*
> > + * MCPDM_DMAENABLE bit fields
> > + */
> > +
> > +#define DMA_DN_ENABLE          0x1
> > +#define DMA_UP_ENABLE          0x2
> > +
> > +/*
> > + * MCPDM_CTRL bit fields
> > + */
> > +
> > +#define PDM_UP1_EN             0x0001
> > +#define PDM_UP2_EN             0x0002
> > +#define PDM_UP3_EN             0x0004
> > +#define PDM_DN1_EN             0x0008
> > +#define PDM_DN2_EN             0x0010
> > +#define PDM_DN3_EN             0x0020
> > +#define PDM_DN4_EN             0x0040
> > +#define PDM_DN5_EN             0x0080
> > +#define PDMOUTFORMAT           0x0100
> > +#define CMD_INT                        0x0200
> > +#define STATUS_INT             0x0400
> > +#define SW_UP_RST              0x0800
> > +#define SW_DN_RST              0x1000
> > +#define PDM_UP_MASK            0x007
> > +#define PDM_DN_MASK            0x0F8
> > +#define PDM_CMD_MASK           0x200
> > +#define PDM_STATUS_MASK                0x400
> > +
> > +
> > +#define PDMOUTFORMAT_LJUST     (0 << 8)
> > +#define PDMOUTFORMAT_RJUST     (1 << 8)
> > +
> > +/*
> > + * MCPDM_FIFO_CTRL bit fields
> > + */
> > +
> > +#define UP_THRES_MAX           0xF
> > +#define DN_THRES_MAX           0xF
> > +
> > +/*
> > + * MCPDM_DN_OFFSET bit fields
> > + */
> > +
> > +#define DN_OFST_RX1_EN         0x0001
> > +#define DN_OFST_RX2_EN         0x0100
> > +
> > +#define DN_OFST_RX1            1
> > +#define DN_OFST_RX2            9
> > +#define DN_OFST_MAX            0x1F
> > +
> > +#define MCPDM_UPLINK           1
> > +#define MCPDM_DOWNLINK         2
> > +
> > +struct omap_mcpdm_link {
> > +       int irq_mask;
> > +       int threshold;
> > +       int format;
> > +       int channels;
> > +};
> > +
> > +struct omap_mcpdm_platform_data {
> > +       unsigned long phys_base;
> > +       u16 irq;
> > +};
> > +
> > +struct omap_mcpdm {
> > +       struct device *dev;
> > +       unsigned long phys_base;
> > +       void __iomem *io_base;
> > +       u8 free;
> > +       int irq;
> > +
> > +       spinlock_t lock;
> > +       struct omap_mcpdm_platform_data *pdata;
> > +       struct clk *clk;
> > +       struct omap_mcpdm_link *downlink;
> > +       struct omap_mcpdm_link *uplink;
> > +       struct completion irq_completion;
> > +
> > +       int dn_channels;
> > +       int up_channels;
> > +};
> > +
> > +extern void omap_mcpdm_reg_dump(void);
> > +extern void omap_mcpdm_start(int stream);
> > +extern void omap_mcpdm_stop(int stream);
> > +extern int omap_mcpdm_set_uplink(struct omap_mcpdm_link *uplink);
> > +extern int omap_mcpdm_set_downlink(struct omap_mcpdm_link
> *downlink);
> > +extern int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink);
> > +extern int omap_mcpdm_clr_downlink(struct omap_mcpdm_link
> *downlink);
> > +extern int omap_mcpdm_request(void);
> > +extern void omap_mcpdm_free(void);
> > +extern int omap_mcpdm_set_offset(int offset1, int offset2);
> > --
> > 1.6.0.4
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel@alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
>
>

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

* Re: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-25 21:06   ` Candelaria Villareal, Jorge
@ 2010-01-26  9:57     ` Mark Brown
  2010-01-26 17:55       ` Candelaria Villareal, Jorge
  2010-01-26 10:25     ` Liam Girdwood
  1 sibling, 1 reply; 7+ messages in thread
From: Mark Brown @ 2010-01-26  9:57 UTC (permalink / raw)
  To: Candelaria Villareal, Jorge; +Cc: Liam Girdwood, alsa-devel, linux-omap

On Mon, Jan 25, 2010 at 03:06:27PM -0600, Candelaria Villareal, Jorge wrote:
> Liam Girdwood wrote:

> > What does this do and why is it not static ?

> It enables and configures offset cancelation for the analog headset path. It is supposed to be called by the codec driver, so it should'nt be static. But, offset cancelation is probably not going to be used at first.

So this is a DC offset in the output?

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

* RE: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-25 21:06   ` Candelaria Villareal, Jorge
  2010-01-26  9:57     ` Mark Brown
@ 2010-01-26 10:25     ` Liam Girdwood
  2010-01-26 18:53       ` Candelaria Villareal, Jorge
  1 sibling, 1 reply; 7+ messages in thread
From: Liam Girdwood @ 2010-01-26 10:25 UTC (permalink / raw)
  To: Candelaria Villareal, Jorge; +Cc: alsa-devel, linux-omap, broonie

On Mon, 2010-01-25 at 15:06 -0600, Candelaria Villareal, Jorge wrote:
> Liam Girdwood wrote:
> >
> > On Fri, 2010-01-22 at 17:15 -0600, Candelaria Villareal, Jorge wrote:
> > > McPDM is the interface between Phoenix audio codec
> > > and the OMAP4430 processor. It enables data to be transfered
> > > to/from Phoenix at sample rates of 88.4 or 96 KHz.
> > >
> > > Signed-off-by: Jorge Eduardo Candelaria <x0107209@ti.com>
> > > Signed-off-by: Margarita Olaya <x0080101@ti.com>
> >
> > It initially looks like a some of this can be called directly
> > as DAI ops
> > rather than by the machine driver.
> 
> Could you explain a little further?

I was meaning here that some of the functions below are only called by
higher level Digital Audio Interface (DAI) operations. e.g. the
following is called by capture stream close :-


> > > + * Cleans McPDM uplink configuration.
> > > + * This function should be called when the stream is closed.
> > > + */
> > > +int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink)

Would probably be more meaningful wrt ALSA as
omap_mcpdm_capture_close() 


> > > +/*
> > > + * Takes the McPDM module in and out of reset state.
> > > + * Uplink and downlink can be reset individually.
> > > + */
> > > +static void omap_mcpdm_reset(int links, int reset)
> > > +{
> > > +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > > +
> > > +       if (links & MCPDM_UPLINK) {
> > > +               if (reset)
> > > +                       ctrl |= SW_UP_RST;
> > > +               else
> > > +                       ctrl &= ~SW_UP_RST;
> > > +       }
> > > +
> > > +       if (links & MCPDM_DOWNLINK) {
> > > +               if (reset)
> > > +                       ctrl |= SW_DN_RST;
> > > +               else
> > > +                       ctrl &= ~SW_DN_RST;
> > > +       }
> > > +
> > > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > > +}
> > > +
> >
> > Would it not be better to rename uplink/downlink as playback
> > and capture
> > for ALSA ? If so, you could have an inline playback and
> > capture version
> > of this function.
> 
> Data paths in McPDM module are named uplink/downlink, so these
> names were chosen to be consistent. Is renaming it according
> to ALSA the best approach?

Generally yes, as long as we can see the audio direction easily (a
comment would do here).

Fwiw, I would have written the above as :-

static inline void omap_mcpdm_reset_playback(int reset)
{
	int ctrl = omap_mcpdm_read(MCPDM_CTRL);

	if (reset)
		ctrl |= SW_UP_RST;
	else
		ctrl &= ~SW_UP_RST;
	
	omap_mcpdm_write(MCPDM_CTRL, ctrl);
}

and one for capture reset too.


> > > +int omap_mcpdm_set_offset(int offset1, int offset2)
> > > +{
> > > +       int offset;
> > > +
> > > +       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
> > > +               return -EINVAL;
> > > +
> > > +       offset = (offset1 << DN_OFST_RX1) | (offset2 <<
> > DN_OFST_RX2);
> > > +
> > > +       /* Enable/disable offset cancellation for downlink
> > channel 1 */
> > > +       if (offset1)
> > > +               offset |= DN_OFST_RX1_EN;
> > > +       else
> > > +               offset &= ~DN_OFST_RX1_EN;
> > > +
> > > +       /* Enable/disable offset cancellation for downlink
> > channel 2 */
> > > +       if (offset2)
> > > +               offset |= DN_OFST_RX2_EN;
> > > +       else
> > > +               offset &= ~DN_OFST_RX2_EN;
> > > +
> > > +       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
> > > +
> > > +       return 0;
> > > +}
> >
> > What does this do and why is it not static ?
> 
> It enables and configures offset cancelation for the analog headset path. It is supposed to be called by the codec driver, so it should'nt be static. But, offset cancelation is probably not going to be used at first.
> 

Ok, can we a comment to here to describe this.

Thanks

Liam


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

* RE: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-26  9:57     ` Mark Brown
@ 2010-01-26 17:55       ` Candelaria Villareal, Jorge
  0 siblings, 0 replies; 7+ messages in thread
From: Candelaria Villareal, Jorge @ 2010-01-26 17:55 UTC (permalink / raw)
  To: Mark Brown; +Cc: Liam Girdwood, alsa-devel, linux-omap

Mark Brown wrote:
> 
> On Mon, Jan 25, 2010 at 03:06:27PM -0600, Candelaria 
> Villareal, Jorge wrote:
> > Liam Girdwood wrote:
> 
> > > What does this do and why is it not static ?
> 
> > It enables and configures offset cancelation for the analog 
> headset path. It is supposed to be called by the codec 
> driver, so it should'nt be static. But, offset cancelation is 
> probably not going to be used at first.
> 
> So this is a DC offset in the output?
> 

Yes, its purpose is to get rid of the DC offset in the output

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

* RE: [alsa-devel] [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM
  2010-01-26 10:25     ` Liam Girdwood
@ 2010-01-26 18:53       ` Candelaria Villareal, Jorge
  0 siblings, 0 replies; 7+ messages in thread
From: Candelaria Villareal, Jorge @ 2010-01-26 18:53 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, linux-omap, broonie

Liam Girdwood wrote:
> 
> On Mon, 2010-01-25 at 15:06 -0600, Candelaria Villareal, Jorge wrote:
> > Liam Girdwood wrote:
> > >
> > > On Fri, 2010-01-22 at 17:15 -0600, Candelaria Villareal, 
> Jorge wrote:
> > > > McPDM is the interface between Phoenix audio codec
> > > > and the OMAP4430 processor. It enables data to be transfered
> > > > to/from Phoenix at sample rates of 88.4 or 96 KHz.
> > > >
> > > > Signed-off-by: Jorge Eduardo Candelaria <x0107209@ti.com>
> > > > Signed-off-by: Margarita Olaya <x0080101@ti.com>
> > >
> > > It initially looks like a some of this can be called directly
> > > as DAI ops
> > > rather than by the machine driver.
> > 
> > Could you explain a little further?
> 
> I was meaning here that some of the functions below are only called by
> higher level Digital Audio Interface (DAI) operations. e.g. the
> following is called by capture stream close :-
> 
> 
> > > > + * Cleans McPDM uplink configuration.
> > > > + * This function should be called when the stream is closed.
> > > > + */
> > > > +int omap_mcpdm_clr_uplink(struct omap_mcpdm_link *uplink)
> 
> Would probably be more meaningful wrt ALSA as
> omap_mcpdm_capture_close() 

Ok, I think I see your point. Since McPDM is only going to be used
for ALSA, it makes sense... But I still would think McPDM driver
should be independent of ALSA.

> 
> 
> > > > +/*
> > > > + * Takes the McPDM module in and out of reset state.
> > > > + * Uplink and downlink can be reset individually.
> > > > + */
> > > > +static void omap_mcpdm_reset(int links, int reset)
> > > > +{
> > > > +       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> > > > +
> > > > +       if (links & MCPDM_UPLINK) {
> > > > +               if (reset)
> > > > +                       ctrl |= SW_UP_RST;
> > > > +               else
> > > > +                       ctrl &= ~SW_UP_RST;
> > > > +       }
> > > > +
> > > > +       if (links & MCPDM_DOWNLINK) {
> > > > +               if (reset)
> > > > +                       ctrl |= SW_DN_RST;
> > > > +               else
> > > > +                       ctrl &= ~SW_DN_RST;
> > > > +       }
> > > > +
> > > > +       omap_mcpdm_write(MCPDM_CTRL, ctrl);
> > > > +}
> > > > +
> > >
> > > Would it not be better to rename uplink/downlink as playback
> > > and capture
> > > for ALSA ? If so, you could have an inline playback and
> > > capture version
> > > of this function.
> > 
> > Data paths in McPDM module are named uplink/downlink, so these
> > names were chosen to be consistent. Is renaming it according
> > to ALSA the best approach?
> 
> Generally yes, as long as we can see the audio direction easily (a
> comment would do here).
> 
> Fwiw, I would have written the above as :-
> 
> static inline void omap_mcpdm_reset_playback(int reset)
> {
> 	int ctrl = omap_mcpdm_read(MCPDM_CTRL);
> 
> 	if (reset)
> 		ctrl |= SW_UP_RST;
> 	else
> 		ctrl &= ~SW_UP_RST;
> 	
> 	omap_mcpdm_write(MCPDM_CTRL, ctrl);
> }
> 
> and one for capture reset too.

Ok, so at this point I see two approachs:

1. To leave as uplink/downlink in McPDM driver ops and add a comment on
how it relates to ALSA playback/capture data paths.

2. To change driver ops to playback/capture and leave a comment regarding
McPDM downlink/uplink data paths.

The only reason I see to use the first approach is to maintain McPDM
independent from ALSA side, but since it is a driver specifically for
audio, that shouldn't be a problem. 

The idea would still be to show the relationship between McPDM and ALSA
data path names.

> 
> 
> > > > +int omap_mcpdm_set_offset(int offset1, int offset2)
> > > > +{
> > > > +       int offset;
> > > > +
> > > > +       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
> > > > +               return -EINVAL;
> > > > +
> > > > +       offset = (offset1 << DN_OFST_RX1) | (offset2 <<
> > > DN_OFST_RX2);
> > > > +
> > > > +       /* Enable/disable offset cancellation for downlink
> > > channel 1 */
> > > > +       if (offset1)
> > > > +               offset |= DN_OFST_RX1_EN;
> > > > +       else
> > > > +               offset &= ~DN_OFST_RX1_EN;
> > > > +
> > > > +       /* Enable/disable offset cancellation for downlink
> > > channel 2 */
> > > > +       if (offset2)
> > > > +               offset |= DN_OFST_RX2_EN;
> > > > +       else
> > > > +               offset &= ~DN_OFST_RX2_EN;
> > > > +
> > > > +       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
> > > > +
> > > > +       return 0;
> > > > +}
> > >
> > > What does this do and why is it not static ?
> > 
> > It enables and configures offset cancelation for the analog 
> headset path. It is supposed to be called by the codec 
> driver, so it should'nt be static. But, offset cancelation is 
> probably not going to be used at first.
> > 
> 
> Ok, can we a comment to here to describe this.

Yes, I should've set a comment here from the beginning.

> 
> Thanks
> 
> Liam
> 
> 

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

end of thread, other threads:[~2010-01-26 18:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-22 23:15 [PATCHv2 5/6] ASoC: OMAP4: Add support for McPDM Candelaria Villareal, Jorge
2010-01-23  1:44 ` [alsa-devel] " Liam Girdwood
2010-01-25 21:06   ` Candelaria Villareal, Jorge
2010-01-26  9:57     ` Mark Brown
2010-01-26 17:55       ` Candelaria Villareal, Jorge
2010-01-26 10:25     ` Liam Girdwood
2010-01-26 18:53       ` Candelaria Villareal, Jorge

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.