Merge branch 'upstream/wm8711' into for-2.6.33
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 21 Sep 2009 11:54:21 +0000 (04:54 -0700)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 21 Sep 2009 11:54:21 +0000 (04:54 -0700)
89 files changed:
arch/arm/mach-omap2/mcbsp.c
arch/arm/plat-omap/dma.c
arch/arm/plat-omap/include/mach/mcbsp.h
arch/arm/plat-omap/mcbsp.c
arch/arm/plat-s3c/include/plat/audio-simtec.h [new file with mode: 0644]
arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
include/sound/sh_fsi.h [new file with mode: 0644]
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
sound/arm/pxa2xx-ac97.c
sound/arm/pxa2xx-pcm-lib.c
sound/soc/atmel/playpaq_wm8510.c
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc.h
sound/soc/blackfin/Kconfig
sound/soc/blackfin/Makefile
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ac97.h
sound/soc/blackfin/bf5xx-ad1836.c [new file with mode: 0644]
sound/soc/blackfin/bf5xx-ad1938.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/blackfin/bf5xx-i2s.h
sound/soc/blackfin/bf5xx-sport.c
sound/soc/blackfin/bf5xx-tdm-pcm.c
sound/soc/blackfin/bf5xx-tdm.c
sound/soc/blackfin/bf5xx-tdm.h
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad1836.c
sound/soc/codecs/ad1938.c
sound/soc/codecs/ak4642.c [new file with mode: 0644]
sound/soc/codecs/ak4642.h [new file with mode: 0644]
sound/soc/codecs/ak4671.c [new file with mode: 0644]
sound/soc/codecs/ak4671.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_hubs.c
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/fsl/mpc5200_dma.c
sound/soc/imx/mx27vis_wm8974.c
sound/soc/omap/n810.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-mcbsp.h
sound/soc/omap/omap-pcm.c
sound/soc/omap/omap-pcm.h
sound/soc/omap/sdp3430.c
sound/soc/pxa/magician.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/zylonite.c
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/neo1973_gta02_wm8753.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c-i2s-v2.c
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-i2s.c
sound/soc/s3c24xx/s3c24xx-pcm.c
sound/soc/s3c24xx/s3c24xx_simtec.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c24xx_simtec.h [new file with mode: 0644]
sound/soc/s3c24xx/s3c24xx_simtec_hermes.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.h
sound/soc/s3c24xx/smdk64xx_wm8580.c [new file with mode: 0644]
sound/soc/s6000/s6105-ipcam.c
sound/soc/sh/Kconfig
sound/soc/sh/Makefile
sound/soc/sh/fsi-ak4642.c [new file with mode: 0644]
sound/soc/sh/fsi.c [new file with mode: 0644]
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index a5c0f04..7d22caf 100644 (file)
@@ -129,6 +129,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
                .rx_irq         = INT_24XX_MCBSP1_IRQ_RX,
                .tx_irq         = INT_24XX_MCBSP1_IRQ_TX,
                .ops            = &omap2_mcbsp_ops,
+               .buffer_size    = 0x6F,
        },
        {
                .phys_base      = OMAP34XX_MCBSP2_BASE,
@@ -137,6 +138,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
                .rx_irq         = INT_24XX_MCBSP2_IRQ_RX,
                .tx_irq         = INT_24XX_MCBSP2_IRQ_TX,
                .ops            = &omap2_mcbsp_ops,
+               .buffer_size    = 0x3FF,
        },
        {
                .phys_base      = OMAP34XX_MCBSP3_BASE,
@@ -145,6 +147,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
                .rx_irq         = INT_24XX_MCBSP3_IRQ_RX,
                .tx_irq         = INT_24XX_MCBSP3_IRQ_TX,
                .ops            = &omap2_mcbsp_ops,
+               .buffer_size    = 0x6F,
        },
        {
                .phys_base      = OMAP34XX_MCBSP4_BASE,
@@ -153,6 +156,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
                .rx_irq         = INT_24XX_MCBSP4_IRQ_RX,
                .tx_irq         = INT_24XX_MCBSP4_IRQ_TX,
                .ops            = &omap2_mcbsp_ops,
+               .buffer_size    = 0x6F,
        },
        {
                .phys_base      = OMAP34XX_MCBSP5_BASE,
@@ -161,6 +165,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
                .rx_irq         = INT_24XX_MCBSP5_IRQ_RX,
                .tx_irq         = INT_24XX_MCBSP5_IRQ_TX,
                .ops            = &omap2_mcbsp_ops,
+               .buffer_size    = 0x6F,
        },
 };
 #define OMAP34XX_MCBSP_PDATA_SZ                ARRAY_SIZE(omap34xx_mcbsp_pdata)
index def14ec..da4cc52 100644 (file)
@@ -1125,6 +1125,11 @@ int omap_dma_running(void)
 void omap_dma_link_lch(int lch_head, int lch_queue)
 {
        if (omap_dma_in_1510_mode()) {
+               if (lch_head == lch_queue) {
+                       dma_write(dma_read(CCR(lch_head)) | (3 << 8),
+                                                               CCR(lch_head));
+                       return;
+               }
                printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
                BUG();
                return;
@@ -1147,6 +1152,11 @@ EXPORT_SYMBOL(omap_dma_link_lch);
 void omap_dma_unlink_lch(int lch_head, int lch_queue)
 {
        if (omap_dma_in_1510_mode()) {
+               if (lch_head == lch_queue) {
+                       dma_write(dma_read(CCR(lch_head)) & ~(3 << 8),
+                                                               CCR(lch_head));
+                       return;
+               }
                printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
                BUG();
                return;
index 57249bb..63a3f25 100644 (file)
 #define OMAP_MCBSP_REG_XCERG   0x74
 #define OMAP_MCBSP_REG_XCERH   0x78
 #define OMAP_MCBSP_REG_SYSCON  0x8C
+#define OMAP_MCBSP_REG_THRSH2  0x90
+#define OMAP_MCBSP_REG_THRSH1  0x94
+#define OMAP_MCBSP_REG_IRQST   0xA0
+#define OMAP_MCBSP_REG_IRQEN   0xA4
+#define OMAP_MCBSP_REG_WAKEUPEN        0xA8
 #define OMAP_MCBSP_REG_XCCR    0xAC
 #define OMAP_MCBSP_REG_RCCR    0xB0
 
 #define RDISABLE               0x0001
 
 /********************** McBSP SYSCONFIG bit definitions ********************/
+#define CLOCKACTIVITY(value)   ((value)<<8)
+#define SIDLEMODE(value)       ((value)<<3)
+#define ENAWAKEUP              0x0004
 #define SOFTRST                        0x0002
 
+/********************** McBSP DMA operating modes **************************/
+#define MCBSP_DMA_MODE_ELEMENT         0
+#define MCBSP_DMA_MODE_THRESHOLD       1
+#define MCBSP_DMA_MODE_FRAME           2
+
+/********************** McBSP WAKEUPEN bit definitions *********************/
+#define XEMPTYEOFEN            0x4000
+#define XRDYEN                 0x0400
+#define XEOFEN                 0x0200
+#define XFSXEN                 0x0100
+#define XSYNCERREN             0x0080
+#define RRDYEN                 0x0008
+#define REOFEN                 0x0004
+#define RFSREN                 0x0002
+#define RSYNCERREN             0x0001
+
 /* we don't do multichannel for now */
 struct omap_mcbsp_reg_cfg {
        u16 spcr2;
@@ -344,6 +368,9 @@ struct omap_mcbsp_platform_data {
        u8 dma_rx_sync, dma_tx_sync;
        u16 rx_irq, tx_irq;
        struct omap_mcbsp_ops *ops;
+#ifdef CONFIG_ARCH_OMAP34XX
+       u16 buffer_size;
+#endif
 };
 
 struct omap_mcbsp {
@@ -377,6 +404,11 @@ struct omap_mcbsp {
        struct omap_mcbsp_platform_data *pdata;
        struct clk *iclk;
        struct clk *fclk;
+#ifdef CONFIG_ARCH_OMAP34XX
+       int dma_op_mode;
+       u16 max_tx_thres;
+       u16 max_rx_thres;
+#endif
 };
 extern struct omap_mcbsp **mcbsp_ptr;
 extern int omap_mcbsp_count;
@@ -385,6 +417,21 @@ int omap_mcbsp_init(void);
 void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
                                        int size);
 void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
+#ifdef CONFIG_ARCH_OMAP34XX
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
+int omap_mcbsp_get_dma_op_mode(unsigned int id);
+#else
+static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
+static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
+static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
+#endif
 int omap_mcbsp_request(unsigned int id);
 void omap_mcbsp_free(unsigned int id);
 void omap_mcbsp_start(unsigned int id, int tx, int rx);
index a3d2313..8dc7927 100644 (file)
@@ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
 }
 EXPORT_SYMBOL(omap_mcbsp_config);
 
+#ifdef CONFIG_ARCH_OMAP34XX
+/*
+ * omap_mcbsp_set_tx_threshold configures how to deal
+ * with transmit threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{
+       struct omap_mcbsp *mcbsp;
+       void __iomem *io_base;
+
+       if (!cpu_is_omap34xx())
+               return;
+
+       if (!omap_mcbsp_check_valid_id(id)) {
+               printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+               return;
+       }
+       mcbsp = id_to_mcbsp_ptr(id);
+       io_base = mcbsp->io_base;
+
+       OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
+
+/*
+ * omap_mcbsp_set_rx_threshold configures how to deal
+ * with receive threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{
+       struct omap_mcbsp *mcbsp;
+       void __iomem *io_base;
+
+       if (!cpu_is_omap34xx())
+               return;
+
+       if (!omap_mcbsp_check_valid_id(id)) {
+               printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+               return;
+       }
+       mcbsp = id_to_mcbsp_ptr(id);
+       io_base = mcbsp->io_base;
+
+       OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
+
+/*
+ * omap_mcbsp_get_max_tx_thres just return the current configured
+ * maximum threshold for transmission
+ */
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
+{
+       struct omap_mcbsp *mcbsp;
+
+       if (!omap_mcbsp_check_valid_id(id)) {
+               printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+               return -ENODEV;
+       }
+       mcbsp = id_to_mcbsp_ptr(id);
+
+       return mcbsp->max_tx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
+
+/*
+ * omap_mcbsp_get_max_rx_thres just return the current configured
+ * maximum threshold for reception
+ */
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
+{
+       struct omap_mcbsp *mcbsp;
+
+       if (!omap_mcbsp_check_valid_id(id)) {
+               printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+               return -ENODEV;
+       }
+       mcbsp = id_to_mcbsp_ptr(id);
+
+       return mcbsp->max_rx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
+
+/*
+ * omap_mcbsp_get_dma_op_mode just return the current configured
+ * operating mode for the mcbsp channel
+ */
+int omap_mcbsp_get_dma_op_mode(unsigned int id)
+{
+       struct omap_mcbsp *mcbsp;
+       int dma_op_mode;
+
+       if (!omap_mcbsp_check_valid_id(id)) {
+               printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
+               return -ENODEV;
+       }
+       mcbsp = id_to_mcbsp_ptr(id);
+
+       spin_lock_irq(&mcbsp->lock);
+       dma_op_mode = mcbsp->dma_op_mode;
+       spin_unlock_irq(&mcbsp->lock);
+
+       return dma_op_mode;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
+
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
+{
+       /*
+        * Enable wakup behavior, smart idle and all wakeups
+        * REVISIT: some wakeups may be unnecessary
+        */
+       if (cpu_is_omap34xx()) {
+               u16 syscon;
+
+               syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+               syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+
+               spin_lock_irq(&mcbsp->lock);
+               if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
+                       syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
+                                       CLOCKACTIVITY(0x02));
+                       OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
+                                       XRDYEN | RRDYEN);
+               } else {
+                       syscon |= SIDLEMODE(0x01);
+               }
+               spin_unlock_irq(&mcbsp->lock);
+
+               OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+       }
+}
+
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
+{
+       /*
+        * Disable wakup behavior, smart idle and all wakeups
+        */
+       if (cpu_is_omap34xx()) {
+               u16 syscon;
+
+               syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+               syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+               /*
+                * HW bug workaround - If no_idle mode is taken, we need to
+                * go to smart_idle before going to always_idle, or the
+                * device will not hit retention anymore.
+                */
+               syscon |= SIDLEMODE(0x02);
+               OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+               syscon &= ~(SIDLEMODE(0x03));
+               OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+               OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
+       }
+}
+#else
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
+#endif
+
 /*
  * We can choose between IRQ based or polled IO.
  * This needs to be called before omap_mcbsp_request().
@@ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id)
        clk_enable(mcbsp->iclk);
        clk_enable(mcbsp->fclk);
 
+       /* Do procedure specific to omap34xx arch, if applicable */
+       omap34xx_mcbsp_request(mcbsp);
+
        /*
         * Make sure that transmitter, receiver and sample-rate generator are
         * not running before activating IRQs.
@@ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id)
        if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
                mcbsp->pdata->ops->free(id);
 
+       /* Do procedure specific to omap34xx arch, if applicable */
+       omap34xx_mcbsp_free(mcbsp);
+
        clk_disable(mcbsp->fclk);
        clk_disable(mcbsp->iclk);
 
@@ -359,13 +529,21 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
        }
 
        /* Enable transmitter and receiver */
+       tx &= 1;
        w = OMAP_MCBSP_READ(io_base, SPCR2);
-       OMAP_MCBSP_WRITE(io_base, SPCR2, w | (tx & 1));
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx);
 
+       rx &= 1;
        w = OMAP_MCBSP_READ(io_base, SPCR1);
-       OMAP_MCBSP_WRITE(io_base, SPCR1, w | (rx & 1));
+       OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx);
 
-       udelay(100);
+       /*
+        * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
+        * REVISIT: 100us may give enough time for two CLKSRG, however
+        * due to some unknown PM related, clock gating etc. reason it
+        * is now at 500us.
+        */
+       udelay(500);
 
        if (idle) {
                /* Start frame sync */
@@ -373,6 +551,16 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
                OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
        }
 
+       if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+               /* Release the transmitter and receiver */
+               w = OMAP_MCBSP_READ(io_base, XCCR);
+               w &= ~(tx ? XDISABLE : 0);
+               OMAP_MCBSP_WRITE(io_base, XCCR, w);
+               w = OMAP_MCBSP_READ(io_base, RCCR);
+               w &= ~(rx ? RDISABLE : 0);
+               OMAP_MCBSP_WRITE(io_base, RCCR, w);
+       }
+
        /* Dump McBSP Regs */
        omap_mcbsp_dump_reg(id);
 }
@@ -394,12 +582,24 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
        io_base = mcbsp->io_base;
 
        /* Reset transmitter */
+       tx &= 1;
+       if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+               w = OMAP_MCBSP_READ(io_base, XCCR);
+               w |= (tx ? XDISABLE : 0);
+               OMAP_MCBSP_WRITE(io_base, XCCR, w);
+       }
        w = OMAP_MCBSP_READ(io_base, SPCR2);
-       OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(tx & 1));
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx);
 
        /* Reset receiver */
+       rx &= 1;
+       if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+               w = OMAP_MCBSP_READ(io_base, RCCR);
+               w |= (tx ? RDISABLE : 0);
+               OMAP_MCBSP_WRITE(io_base, RCCR, w);
+       }
        w = OMAP_MCBSP_READ(io_base, SPCR1);
-       OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(rx & 1));
+       OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx);
 
        idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
                  OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
@@ -897,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id,
 }
 EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
 
+#ifdef CONFIG_ARCH_OMAP34XX
+#define max_thres(m)                   (mcbsp->pdata->buffer_size)
+#define valid_threshold(m, val)                ((val) <= max_thres(m))
+#define THRESHOLD_PROP_BUILDER(prop)                                   \
+static ssize_t prop##_show(struct device *dev,                         \
+                       struct device_attribute *attr, char *buf)       \
+{                                                                      \
+       struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);                \
+                                                                       \
+       return sprintf(buf, "%u\n", mcbsp->prop);                       \
+}                                                                      \
+                                                                       \
+static ssize_t prop##_store(struct device *dev,                                \
+                               struct device_attribute *attr,          \
+                               const char *buf, size_t size)           \
+{                                                                      \
+       struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);                \
+       unsigned long val;                                              \
+       int status;                                                     \
+                                                                       \
+       status = strict_strtoul(buf, 0, &val);                          \
+       if (status)                                                     \
+               return status;                                          \
+                                                                       \
+       if (!valid_threshold(mcbsp, val))                               \
+               return -EDOM;                                           \
+                                                                       \
+       mcbsp->prop = val;                                              \
+       return size;                                                    \
+}                                                                      \
+                                                                       \
+static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
+
+THRESHOLD_PROP_BUILDER(max_tx_thres);
+THRESHOLD_PROP_BUILDER(max_rx_thres);
+
+static const char *dma_op_modes[] = {
+       "element", "threshold", "frame",
+};
+
+static ssize_t dma_op_mode_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+       int dma_op_mode, i = 0;
+       ssize_t len = 0;
+       const char * const *s;
+
+       spin_lock_irq(&mcbsp->lock);
+       dma_op_mode = mcbsp->dma_op_mode;
+       spin_unlock_irq(&mcbsp->lock);
+
+       for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
+               if (dma_op_mode == i)
+                       len += sprintf(buf + len, "[%s] ", *s);
+               else
+                       len += sprintf(buf + len, "%s ", *s);
+       }
+       len += sprintf(buf + len, "\n");
+
+       return len;
+}
+
+static ssize_t dma_op_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+       const char * const *s;
+       int i = 0;
+
+       for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
+               if (sysfs_streq(buf, *s))
+                       break;
+
+       if (i == ARRAY_SIZE(dma_op_modes))
+               return -EINVAL;
+
+       spin_lock_irq(&mcbsp->lock);
+       if (!mcbsp->free) {
+               size = -EBUSY;
+               goto unlock;
+       }
+       mcbsp->dma_op_mode = i;
+
+unlock:
+       spin_unlock_irq(&mcbsp->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
+
+static const struct attribute *additional_attrs[] = {
+       &dev_attr_max_tx_thres.attr,
+       &dev_attr_max_rx_thres.attr,
+       &dev_attr_dma_op_mode.attr,
+       NULL,
+};
+
+static const struct attribute_group additional_attr_group = {
+       .attrs = (struct attribute **)additional_attrs,
+};
+
+static inline int __devinit omap_additional_add(struct device *dev)
+{
+       return sysfs_create_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devexit omap_additional_remove(struct device *dev)
+{
+       sysfs_remove_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
+{
+       mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
+       if (cpu_is_omap34xx()) {
+               mcbsp->max_tx_thres = max_thres(mcbsp);
+               mcbsp->max_rx_thres = max_thres(mcbsp);
+               /*
+                * REVISIT: Set dmap_op_mode to THRESHOLD as default
+                * for mcbsp2 instances.
+                */
+               if (omap_additional_add(mcbsp->dev))
+                       dev_warn(mcbsp->dev,
+                               "Unable to create additional controls\n");
+       } else {
+               mcbsp->max_tx_thres = -EINVAL;
+               mcbsp->max_rx_thres = -EINVAL;
+       }
+}
+
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
+{
+       if (cpu_is_omap34xx())
+               omap_additional_remove(mcbsp->dev);
+}
+#else
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
+#endif /* CONFIG_ARCH_OMAP34XX */
+
 /*
  * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
  * 730 has only 2 McBSP, and both of them are MPU peripherals.
@@ -967,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
        mcbsp->dev = &pdev->dev;
        mcbsp_ptr[id] = mcbsp;
        platform_set_drvdata(pdev, mcbsp);
+
+       /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
+       omap34xx_device_init(mcbsp);
+
        return 0;
 
 err_fclk:
@@ -990,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
                                mcbsp->pdata->ops->free)
                        mcbsp->pdata->ops->free(mcbsp->id);
 
+               omap34xx_device_exit(mcbsp);
+
                clk_disable(mcbsp->fclk);
                clk_disable(mcbsp->iclk);
                clk_put(mcbsp->fclk);
diff --git a/arch/arm/plat-s3c/include/plat/audio-simtec.h b/arch/arm/plat-s3c/include/plat/audio-simtec.h
new file mode 100644 (file)
index 0000000..0f440b9
--- /dev/null
@@ -0,0 +1,37 @@
+/* arch/arm/plat-s3c/include/plat/audio-simtec.h
+ *
+ * Copyright 2008 Simtec Electronics
+ *     http://armlinux.simtec.co.uk/
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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.
+ *
+ * Simtec Audio support.
+*/
+
+/**
+ * struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
+ * @use_mpllin: Select codec clock from MPLLin
+ * @output_cdclk: Need to output CDCLK to the codec
+ * @have_mic: Set if we have a MIC socket
+ * @have_lout: Set if we have a LineOut socket
+ * @amp_gpio: GPIO pin to enable the AMP
+ * @amp_gain: Option GPIO to control AMP gain
+ */
+struct s3c24xx_audio_simtec_pdata {
+       unsigned int    use_mpllin:1;
+       unsigned int    output_cdclk:1;
+
+       unsigned int    have_mic:1;
+       unsigned int    have_lout:1;
+
+       int             amp_gpio;
+       int             amp_gain[2];
+
+       void    (*startup)(void);
+};
+
+extern int simtec_audio_add(const char *codec_name,
+                           struct s3c24xx_audio_simtec_pdata *pdata);
index 0fad757..abf2fbc 100644 (file)
 #define S3C2412_IISCON_RXDMA_ACTIVE    (1 << 1)
 #define S3C2412_IISCON_IIS_ACTIVE      (1 << 0)
 
+#define S3C64XX_IISMOD_BLC_16BIT       (0 << 13)
+#define S3C64XX_IISMOD_BLC_8BIT                (1 << 13)
+#define S3C64XX_IISMOD_BLC_24BIT       (2 << 13)
+#define S3C64XX_IISMOD_BLC_MASK                (3 << 13)
+
 #define S3C64XX_IISMOD_IMS_PCLK                (0 << 10)
 #define S3C64XX_IISMOD_IMS_SYSMUX      (1 << 10)
 
@@ -62,6 +67,8 @@
 #define S3C2412_IISMOD_BCLK_MASK       (3 << 1)
 #define S3C2412_IISMOD_8BIT            (1 << 0)
 
+#define S3C64XX_IISMOD_CDCLKCON                (1 << 12)
+
 #define S3C2412_IISPSR_PSREN           (1 << 15)
 
 #define S3C2412_IISFIC_TXFLUSH         (1 << 15)
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h
new file mode 100644 (file)
index 0000000..c022736
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __SOUND_FSI_H
+#define __SOUND_FSI_H
+
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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.
+ */
+
+/* flags format
+
+ * 0xABCDEEFF
+ *
+ * A:  channel size for TDM (input)
+ * B:  channel size for TDM (ooutput)
+ * C:  inversion
+ * D:  mode
+ * E:  input format
+ * F:  output format
+ */
+
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+/* TDM channel */
+#define SH_FSI_SET_CH_I(x)     ((x & 0xF) << 28)
+#define SH_FSI_SET_CH_O(x)     ((x & 0xF) << 24)
+
+#define SH_FSI_CH_IMASK                0xF0000000
+#define SH_FSI_CH_OMASK                0x0F000000
+#define SH_FSI_GET_CH_I(x)     ((x & SH_FSI_CH_IMASK) >> 28)
+#define SH_FSI_GET_CH_O(x)     ((x & SH_FSI_CH_OMASK) >> 24)
+
+/* clock inversion */
+#define SH_FSI_INVERSION_MASK  0x00F00000
+#define SH_FSI_LRM_INV         (1 << 20)
+#define SH_FSI_BRM_INV         (1 << 21)
+#define SH_FSI_LRS_INV         (1 << 22)
+#define SH_FSI_BRS_INV         (1 << 23)
+
+/* mode */
+#define SH_FSI_MODE_MASK       0x000F0000
+#define SH_FSI_IN_SLAVE_MODE   (1 << 16)  /* default master mode */
+#define SH_FSI_OUT_SLAVE_MODE  (1 << 17)  /* default master mode */
+
+/* DI format */
+#define SH_FSI_FMT_MASK                0x000000FF
+#define SH_FSI_IFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
+#define SH_FSI_OFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
+#define SH_FSI_GET_IFMT(x)     ((x >> 8) & SH_FSI_FMT_MASK)
+#define SH_FSI_GET_OFMT(x)     ((x >> 0) & SH_FSI_FMT_MASK)
+
+#define SH_FSI_FMT_MONO                (1 << 0)
+#define SH_FSI_FMT_MONO_DELAY  (1 << 1)
+#define SH_FSI_FMT_PCM         (1 << 2)
+#define SH_FSI_FMT_I2S         (1 << 3)
+#define SH_FSI_FMT_TDM         (1 << 4)
+#define SH_FSI_FMT_TDM_DELAY   (1 << 5)
+
+#define SH_FSI_IFMT_TDM_CH(x) \
+       (SH_FSI_IFMT(TDM)       | SH_FSI_SET_CH_I(x))
+#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
+       (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
+
+#define SH_FSI_OFMT_TDM_CH(x) \
+       (SH_FSI_OFMT(TDM)       | SH_FSI_SET_CH_O(x))
+#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
+       (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
+
+struct sh_fsi_platform_info {
+       unsigned long porta_flags;
+       unsigned long portb_flags;
+};
+
+extern struct snd_soc_dai fsi_soc_dai[2];
+extern struct snd_soc_platform fsi_soc_platform;
+
+#endif /* __SOUND_FSI_H */
index 97ca9af..e0c7fa7 100644 (file)
@@ -106,7 +106,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div);
 
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out);
+       int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
 
 /* Digital Audio interface formatting */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +114,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+       unsigned int tx_num, unsigned int *tx_slot,
+       unsigned int rx_num, unsigned int *rx_slot);
+
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 /* Digital Audio Interface mute */
@@ -136,8 +140,8 @@ struct snd_soc_dai_ops {
         */
        int (*set_sysclk)(struct snd_soc_dai *dai,
                int clk_id, unsigned int freq, int dir);
-       int (*set_pll)(struct snd_soc_dai *dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out);
+       int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out);
        int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
        /*
@@ -148,6 +152,9 @@ struct snd_soc_dai_ops {
        int (*set_tdm_slot)(struct snd_soc_dai *dai,
                unsigned int tx_mask, unsigned int rx_mask,
                int slots, int slot_width);
+       int (*set_channel_map)(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot);
        int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
        /*
index 35814ce..67224db 100644 (file)
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
 
 /* stream domain */
+#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert }
+#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert }
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert}
@@ -283,6 +289,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
 
 /* dapm audio pin control and status */
 int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
@@ -312,6 +319,8 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_pre,                       /* machine specific pre widget - exec first */
        snd_soc_dapm_post,                      /* machine specific post widget - exec last */
        snd_soc_dapm_supply,            /* power/clock supply */
+       snd_soc_dapm_aif_in,            /* audio interface input */
+       snd_soc_dapm_aif_out,           /* audio interface output */
 };
 
 /*
@@ -324,6 +333,10 @@ struct snd_soc_dapm_route {
        const char *sink;
        const char *control;
        const char *source;
+
+       /* Note: currently only supported for links where source is a supply */
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink);
 };
 
 /* dapm audio path between two widgets */
@@ -340,6 +353,9 @@ struct snd_soc_dapm_path {
        u32 connect:1;  /* source and sink widgets are connected */
        u32 walked:1;   /* path has been walked */
 
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink);
+
        struct list_head list_source;
        struct list_head list_sink;
        struct list_head list;
index dbb1702..475cb7e 100644 (file)
@@ -205,7 +205,6 @@ struct snd_soc_jack_gpio;
 #endif
 
 typedef int (*hw_write_t)(void *,const char* ,int);
-typedef int (*hw_read_t)(void *,char* ,int);
 
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
@@ -416,6 +415,7 @@ struct snd_soc_codec {
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_reg;
        struct dentry *debugfs_pop_time;
+       struct dentry *debugfs_dapm;
 #endif
 };
 
index 6c00ea4..4e34d19 100644 (file)
@@ -207,8 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
        snprintf(card->longname, sizeof(card->longname),
                 "%s (%s)", dev->dev.driver->name, card->mixername);
 
-       if (pdata && pdata->codec_data)
-               snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata);
+       if (pdata && pdata->codec_pdata[0])
+               snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
        snd_card_set_dev(card, &dev->dev);
        ret = snd_card_register(card);
        if (ret == 0) {
index 6205f37..743ac6a 100644 (file)
@@ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
 {
        struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
 
+       if (!prtd || !prtd->params)
+               return 0;
+
        DCSR(prtd->dma_ch) &= ~DCSR_RUN;
        DCSR(prtd->dma_ch) = 0;
        DCMD(prtd->dma_ch) = 0;
index 9eb610c..9df4c68 100644 (file)
@@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
 
 
-       ret = snd_soc_dai_set_pll(codec_dai, 0,
+       ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
                                         clk_get_rate(CODEC_CLK), pll_out);
        if (ret < 0) {
                pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
index 479d7bd..a521aa9 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
- * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *     Manuel Lauss <mano@roarinelk.homelinux.net>
+ * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
+ *     Manuel Lauss <manuel.lauss@gmail.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
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/delay.h>
+#include <linux/mutex.h>
 #include <linux/suspend.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -29,6 +30,9 @@
 
 #include "psc.h"
 
+/* how often to retry failed codec register reads/writes */
+#define AC97_RW_RETRIES        5
+
 #define AC97_DIR       \
        (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
 
@@ -45,6 +49,9 @@
 #define AC97PCR_CLRFIFO(stype) \
        ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
 
+#define AC97STAT_BUSY(stype)   \
+       ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
+
 /* instance data. There can be only one, MacLeod!!!! */
 static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
 
@@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-       unsigned short data, tmo;
+       unsigned short data, retry, tmo;
 
-       au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+       au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
        au_sync();
 
-       tmo = 1000;
-       while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
-               udelay(2);
+       retry = AC97_RW_RETRIES;
+       do {
+               mutex_lock(&pscdata->lock);
+
+               au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
+                         AC97_CDC(pscdata));
+               au_sync();
+
+               tmo = 2000;
+               while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+                       && --tmo)
+                       udelay(2);
 
-       if (!tmo)
-               data = 0xffff;
-       else
                data = au_readl(AC97_CDC(pscdata)) & 0xffff;
 
-       au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
-       au_sync();
+               au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+               au_sync();
+
+               mutex_unlock(&pscdata->lock);
+       } while (--retry && !tmo);
 
-       return data;
+       return retry ? data : 0xffff;
 }
 
 /* AC97 controller writes to codec register */
@@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-       unsigned int tmo;
+       unsigned int tmo, retry;
 
-       au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+       au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
        au_sync();
-       tmo = 1000;
-       while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+
+       retry = AC97_RW_RETRIES;
+       do {
+               mutex_lock(&pscdata->lock);
+
+               au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
+                         AC97_CDC(pscdata));
                au_sync();
 
-       au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
-       au_sync();
+               tmo = 2000;
+               while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+                      && --tmo)
+                       udelay(2);
+
+               au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+               au_sync();
+
+               mutex_unlock(&pscdata->lock);
+       } while (--retry && !tmo);
 }
 
 /* AC97 controller asserts a warm reset */
@@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
        au_sync();
 
        /* wait for PSC to indicate it's ready */
-       i = 100000;
+       i = 1000;
        while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
-               au_sync();
+               msleep(1);
 
        if (i == 0) {
                printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
@@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
        au_sync();
 
        /* wait for AC97 core to become ready */
-       i = 100000;
+       i = 1000;
        while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
-               au_sync();
+               msleep(1);
        if (i == 0)
                printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
 }
@@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-       unsigned long r, stat;
+       unsigned long r, ro, stat;
        int chans, stype = SUBSTREAM_TYPE(substream);
 
        chans = params_channels(params);
 
-       r = au_readl(AC97_CFG(pscdata));
+       r = ro = au_readl(AC97_CFG(pscdata));
        stat = au_readl(AC97_STAT(pscdata));
 
        /* already active? */
@@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
                    (pscdata->rate != params_rate(params)))
                        return -EINVAL;
        } else {
-               /* disable AC97 device controller first */
-               au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
-               au_sync();
 
                /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
                r &= ~PSC_AC97CFG_LEN_MASK;
@@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
                        r |= PSC_AC97CFG_RXSLOT_ENA(4);
                }
 
-               /* finally enable the AC97 controller again */
+               /* do we need to poke the hardware? */
+               if (!(r ^ ro))
+                       goto out;
+
+               /* ac97 engine is about to be disabled */
+               mutex_lock(&pscdata->lock);
+
+               /* disable AC97 device controller first... */
+               au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+               au_sync();
+
+               /* ...wait for it... */
+               while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
+                       asm volatile ("nop");
+
+               /* ...write config... */
+               au_writel(r, AC97_CFG(pscdata));
+               au_sync();
+
+               /* ...enable the AC97 controller again... */
                au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
                au_sync();
 
+               /* ...and wait for ready bit */
+               while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
+                       asm volatile ("nop");
+
+               mutex_unlock(&pscdata->lock);
+
                pscdata->cfg = r;
                pscdata->rate = params_rate(params);
        }
 
+out:
        return 0;
 }
 
@@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
+               au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+               au_sync();
                au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
                au_sync();
                break;
@@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_SUSPEND:
                au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
                au_sync();
+
+               while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
+                       asm volatile ("nop");
+
+               au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+               au_sync();
+
                break;
        default:
                ret = -EINVAL;
@@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
        if (!au1xpsc_ac97_workdata)
                return -ENOMEM;
 
+       mutex_init(&au1xpsc_ac97_workdata->lock);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!r) {
                ret = -ENODEV;
@@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
                goto out1;
 
        /* configuration: max dma trigger threshold, enable ac97 */
-        au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
-                                     PSC_AC97CFG_TT_FIFO8 |
-                                     PSC_AC97CFG_DE_ENABLE;
+       au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+                                    PSC_AC97CFG_TT_FIFO8 |
+                                    PSC_AC97CFG_DE_ENABLE;
 
        /* preserve PSC clock source set up by platform (dev.platform_data
         * is already occupied by soc layer)
@@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
index 8fdb1a0..3f474e8 100644 (file)
@@ -29,6 +29,7 @@ struct au1xpsc_audio_data {
 
        unsigned long pm[2];
        struct resource *ioarea;
+       struct mutex lock;
 };
 
 #define PCM_TX 0
index 8a4de4d..ac927ff 100644 (file)
@@ -88,6 +88,14 @@ config SND_BF5XX_SOC_AC97
        select SND_SOC_AC97_BUS
        select SND_BF5XX_SOC_SPORT
 
+config SND_BF5XX_SOC_AD1836
+       tristate "SoC AD1836 Audio support for BF5xx"
+       depends on SND_BF5XX_TDM
+       select SND_BF5XX_SOC_TDM
+       select SND_SOC_AD1836
+       help
+         Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+
 config SND_BF5XX_SOC_AD1980
        tristate "SoC AD1980/1 Audio support for BF5xx"
        depends on SND_BF5XX_AC97
index f4d7607..87e3042 100644 (file)
@@ -16,11 +16,13 @@ obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
 obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
 
 # Blackfin Machine Support
+snd-ad1836-objs := bf5xx-ad1836.o
 snd-ad1980-objs := bf5xx-ad1980.o
 snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad1938-objs := bf5xx-ad1938.o
 
+obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
 obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
index 2758b90..e693229 100644 (file)
@@ -277,7 +277,11 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
        if (!dai->active)
                return 0;
 
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+       ret = sport_set_multichannel(sport, 16, 0x3FF, 1);
+#else
        ret = sport_set_multichannel(sport, 16, 0x1F, 1);
+#endif
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
@@ -334,7 +338,11 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
                goto sport_err;
        }
        /*SPORT works in TDM mode to simulate AC97 transfers*/
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+       ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
+#else
        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+#endif
        if (ret) {
                pr_err("SPORT is busy!\n");
                ret = -EBUSY;
index 3f2a911..a1f97dd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * linux/sound/arm/bf5xx-ac97.h
+ * sound/soc/blackfin/bf5xx-ac97.h
  *
  * 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
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
new file mode 100644 (file)
index 0000000..0f45a3f
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-ad1836.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Aug 4 2009
+ * Description:  Board driver for ad1836 sound chip
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1836.h"
+#include "bf5xx-sport.h"
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+
+static struct snd_soc_card bf5xx_ad1836;
+
+static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+       cpu_dai->private_data = sport_handle;
+       return 0;
+}
+
+static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
+       int ret = 0;
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+               SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+               SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI channel mapping */
+       ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+               channel_map, ARRAY_SIZE(channel_map), channel_map);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops bf5xx_ad1836_ops = {
+       .startup = bf5xx_ad1836_startup,
+       .hw_params = bf5xx_ad1836_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad1836_dai = {
+       .name = "ad1836",
+       .stream_name = "AD1836",
+       .cpu_dai = &bf5xx_tdm_dai,
+       .codec_dai = &ad1836_dai,
+       .ops = &bf5xx_ad1836_ops,
+};
+
+static struct snd_soc_card bf5xx_ad1836 = {
+       .name = "bf5xx_ad1836",
+       .platform = &bf5xx_tdm_soc_platform,
+       .dai_link = &bf5xx_ad1836_dai,
+       .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad1836_snd_devdata = {
+       .card = &bf5xx_ad1836,
+       .codec_dev = &soc_codec_dev_ad1836,
+};
+
+static struct platform_device *bfxx_ad1836_snd_device;
+
+static int __init bf5xx_ad1836_init(void)
+{
+       int ret;
+
+       bfxx_ad1836_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!bfxx_ad1836_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836_snd_devdata);
+       bf5xx_ad1836_snd_devdata.dev = &bfxx_ad1836_snd_device->dev;
+       ret = platform_device_add(bfxx_ad1836_snd_device);
+
+       if (ret)
+               platform_device_put(bfxx_ad1836_snd_device);
+
+       return ret;
+}
+
+static void __exit bf5xx_ad1836_exit(void)
+{
+       platform_device_unregister(bfxx_ad1836_snd_device);
+}
+
+module_init(bf5xx_ad1836_init);
+module_exit(bf5xx_ad1836_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ALSA SoC AD1836 board driver");
+MODULE_LICENSE("GPL");
+
index 08269e9..2ef1e50 100644 (file)
@@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
        int ret = 0;
        /* set cpu DAI configuration */
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* set codec DAI slots, 8 channels, all channels are enabled */
-       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI channel mapping */
+       ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+               channel_map, ARRAY_SIZE(channel_map), channel_map);
        if (ret < 0)
                return ret;
 
index 876abad..1e9d161 100644 (file)
@@ -227,7 +227,8 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
        return 0;
 }
 
-static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
+static void bf5xx_i2s_remove(struct platform_device *pdev,
+                       struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
        peripheral_free_list(&sport_req[sport_num][0]);
@@ -236,36 +237,31 @@ static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
 #ifdef CONFIG_PM
 static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
 {
-       struct sport_device *sport =
-               (struct sport_device *)dai->private_data;
 
        pr_debug("%s : sport %d\n", __func__, dai->id);
-       if (!dai->active)
-               return 0;
+
        if (dai->capture.active)
-               sport_rx_stop(sport);
+               sport_rx_stop(sport_handle);
        if (dai->playback.active)
-               sport_tx_stop(sport);
+               sport_tx_stop(sport_handle);
        return 0;
 }
 
 static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
 {
        int ret;
-       struct sport_device *sport =
-               (struct sport_device *)dai->private_data;
 
        pr_debug("%s : sport %d\n", __func__, dai->id);
-       if (!dai->active)
-               return 0;
 
-       ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+       ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
+                                     bf5xx_i2s.rcr2, 0, 0);
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+       ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
+                                     bf5xx_i2s.tcr2, 0, 0);
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
index 7107d1a..264ecdc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * linux/sound/arm/bf5xx-i2s.h
+ * sound/soc/blackfin/bf5xx-i2s.h
  *
  * 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
index 469ce7f..99051ff 100644 (file)
@@ -326,7 +326,7 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport)
 
 int sport_tx_start(struct sport_device *sport)
 {
-       unsigned flags;
+       unsigned long flags;
        pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__,
                        sport->tx_run, sport->rx_run);
        if (sport->tx_run)
index ccb5e82..a8c73cb 100644 (file)
@@ -43,7 +43,7 @@
 #include "bf5xx-tdm.h"
 #include "bf5xx-sport.h"
 
-#define PCM_BUFFER_MAX  0x10000
+#define PCM_BUFFER_MAX  0x8000
 #define FRAGMENT_SIZE_MIN  (4*1024)
 #define FRAGMENTS_MIN  2
 #define FRAGMENTS_MAX  32
@@ -177,6 +177,9 @@ out:
 static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
        snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sport_device *sport = runtime->private_data;
+       struct bf5xx_tdm_port *tdm_port = sport->private_data;
        unsigned int *src;
        unsigned int *dst;
        int i;
@@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
                dst += pos * 8;
                while (count--) {
                        for (i = 0; i < substream->runtime->channels; i++)
-                               *(dst + i) = *src++;
+                               *(dst + tdm_port->tx_map[i]) = *src++;
                        dst += 8;
                }
        } else {
@@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
                src += pos * 8;
                while (count--) {
                        for (i = 0; i < substream->runtime->channels; i++)
-                               *dst++ = *(src+i);
+                               *dst++ = *(src + tdm_port->rx_map[i]);
                        src += 8;
                }
        }
index 3096bad..600987d 100644 (file)
 #include "bf5xx-sport.h"
 #include "bf5xx-tdm.h"
 
-struct bf5xx_tdm_port {
-       u16 tcr1;
-       u16 rcr1;
-       u16 tcr2;
-       u16 rcr2;
-       int configured;
-};
-
 static struct bf5xx_tdm_port bf5xx_tdm;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
@@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
                bf5xx_tdm.configured = 0;
 }
 
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot)
+{
+       int i;
+       unsigned int slot;
+       unsigned int tx_mapped = 0, rx_mapped = 0;
+
+       if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+                       (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+               return -EINVAL;
+
+       for (i = 0; i < tx_num; i++) {
+               slot = tx_slot[i];
+               if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+                               (!(tx_mapped & (1 << slot)))) {
+                       bf5xx_tdm.tx_map[i] = slot;
+                       tx_mapped |= 1 << slot;
+               } else
+                       return -EINVAL;
+       }
+       for (i = 0; i < rx_num; i++) {
+               slot = rx_slot[i];
+               if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+                               (!(rx_mapped & (1 << slot)))) {
+                       bf5xx_tdm.rx_map[i] = slot;
+                       rx_mapped |= 1 << slot;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_PM
 static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
 {
@@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
        .hw_params      = bf5xx_tdm_hw_params,
        .set_fmt        = bf5xx_tdm_set_dai_fmt,
        .shutdown       = bf5xx_tdm_shutdown,
+       .set_channel_map   = bf5xx_tdm_set_channel_map,
 };
 
 struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
                pr_err("Failed to register DAI: %d\n", ret);
                goto sport_config_err;
        }
+
+       sport_handle->private_data = &bf5xx_tdm;
        return 0;
 
 sport_config_err:
index 618ec3d..04189a1 100644 (file)
@@ -9,6 +9,17 @@
 #ifndef _BF5XX_TDM_H
 #define _BF5XX_TDM_H
 
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+       u16 tcr1;
+       u16 rcr1;
+       u16 tcr2;
+       u16 rcr2;
+       unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+       unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+       int configured;
+};
+
 extern struct snd_soc_dai bf5xx_tdm_dai;
 
 #endif
index 20ebf74..3c46f34 100644 (file)
@@ -18,6 +18,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AD73311 if I2C
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
+       select SND_SOC_AK4642 if I2C
+       select SND_SOC_AK4671 if I2C
        select SND_SOC_CS4270 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
@@ -93,6 +95,12 @@ config SND_SOC_AK4104
 config SND_SOC_AK4535
        tristate
 
+config SND_SOC_AK4642
+       tristate
+
+config SND_SOC_AK4671
+       tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
        tristate
index 711d8f5..fc1c458 100644 (file)
@@ -5,6 +5,8 @@ snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-l3-objs := l3.o
@@ -55,6 +57,8 @@ obj-$(CONFIG_SND_SOC_AD1980)  += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
+obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
index 3612bb9..01343dc 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <sound/core.h>
index e62b277..9a049a1 100644 (file)
@@ -28,7 +28,6 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <sound/core.h>
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
new file mode 100644 (file)
index 0000000..e057c7b
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * ak4642.c  --  AK4642/AK4643 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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.
+ */
+
+/* ** CAUTION **
+ *
+ * This is very simple driver.
+ * It can use headphone output / stereo input only
+ *
+ * AK4642 is not tested.
+ * AK4643 is tested.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak4642.h"
+
+#define AK4642_VERSION "0.0.1"
+
+#define PW_MGMT1       0x00
+#define PW_MGMT2       0x01
+#define SG_SL1         0x02
+#define SG_SL2         0x03
+#define MD_CTL1                0x04
+#define MD_CTL2                0x05
+#define TIMER          0x06
+#define ALC_CTL1       0x07
+#define ALC_CTL2       0x08
+#define L_IVC          0x09
+#define L_DVC          0x0a
+#define ALC_CTL3       0x0b
+#define R_IVC          0x0c
+#define R_DVC          0x0d
+#define MD_CTL3                0x0e
+#define MD_CTL4                0x0f
+#define PW_MGMT3       0x10
+#define DF_S           0x11
+#define FIL3_0         0x12
+#define FIL3_1         0x13
+#define FIL3_2         0x14
+#define FIL3_3         0x15
+#define EQ_0           0x16
+#define EQ_1           0x17
+#define EQ_2           0x18
+#define EQ_3           0x19
+#define EQ_4           0x1a
+#define EQ_5           0x1b
+#define FIL1_0         0x1c
+#define FIL1_1         0x1d
+#define FIL1_2         0x1e
+#define FIL1_3         0x1f
+#define PW_MGMT4       0x20
+#define MD_CTL5                0x21
+#define LO_MS          0x22
+#define HP_MS          0x23
+#define SPK_MS         0x24
+
+#define AK4642_CACHEREGNUM     0x25
+
+struct snd_soc_codec_device soc_codec_dev_ak4642;
+
+/* codec private data */
+struct ak4642_priv {
+       struct snd_soc_codec codec;
+       unsigned int sysclk;
+};
+
+static struct snd_soc_codec *ak4642_codec;
+
+/*
+ * ak4642 register cache
+ */
+static const u16 ak4642_reg[AK4642_CACHEREGNUM] = {
+       0x0000, 0x0000, 0x0001, 0x0000,
+       0x0002, 0x0000, 0x0000, 0x0000,
+       0x00e1, 0x00e1, 0x0018, 0x0000,
+       0x00e1, 0x0018, 0x0011, 0x0008,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000,
+};
+
+/*
+ * read ak4642 register cache
+ */
+static inline unsigned int ak4642_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg >= AK4642_CACHEREGNUM)
+               return -1;
+       return cache[reg];
+}
+
+/*
+ * write ak4642 register cache
+ */
+static inline void ak4642_write_reg_cache(struct snd_soc_codec *codec,
+       u16 reg, unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg >= AK4642_CACHEREGNUM)
+               return;
+
+       cache[reg] = value;
+}
+
+/*
+ * write to the AK4642 register space
+ */
+static int ak4642_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       u8 data[2];
+
+       /* data is
+        *   D15..D8 AK4642 register offset
+        *   D7...D0 register data
+        */
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2) {
+               ak4642_write_reg_cache(codec, reg, value);
+               return 0;
+       } else
+               return -EIO;
+}
+
+static int ak4642_sync(struct snd_soc_codec *codec)
+{
+       u16 *cache = codec->reg_cache;
+       int i, r = 0;
+
+       for (i = 0; i < AK4642_CACHEREGNUM; i++)
+               r |= ak4642_write(codec, i, cache[i]);
+
+       return r;
+};
+
+static int ak4642_dai_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (is_play) {
+               /*
+                * start headphone output
+                *
+                * PLL, Master Mode
+                * Audio I/F Format :MSB justified (ADC & DAC)
+                * Sampling Frequency: 44.1kHz
+                * Digital Volume: −8dB
+                * Bass Boost Level : Middle
+                *
+                * This operation came from example code of
+                * "ASAHI KASEI AK4642" (japanese) manual p97.
+                *
+                * Example code use 0x39, 0x79 value for 0x01 address,
+                * But we need MCKO (0x02) bit now
+                */
+               ak4642_write(codec, 0x05, 0x27);
+               ak4642_write(codec, 0x0f, 0x09);
+               ak4642_write(codec, 0x0e, 0x19);
+               ak4642_write(codec, 0x09, 0x91);
+               ak4642_write(codec, 0x0c, 0x91);
+               ak4642_write(codec, 0x0a, 0x28);
+               ak4642_write(codec, 0x0d, 0x28);
+               ak4642_write(codec, 0x00, 0x64);
+               ak4642_write(codec, 0x01, 0x3b); /* + MCKO bit */
+               ak4642_write(codec, 0x01, 0x7b); /* + MCKO bit */
+       } else {
+               /*
+                * start stereo input
+                *
+                * PLL Master Mode
+                * Audio I/F Format:MSB justified (ADC & DAC)
+                * Sampling Frequency:44.1kHz
+                * Pre MIC AMP:+20dB
+                * MIC Power On
+                * ALC setting:Refer to Table 35
+                * ALC bit=“1”
+                *
+                * This operation came from example code of
+                * "ASAHI KASEI AK4642" (japanese) manual p94.
+                */
+               ak4642_write(codec, 0x05, 0x27);
+               ak4642_write(codec, 0x02, 0x05);
+               ak4642_write(codec, 0x06, 0x3c);
+               ak4642_write(codec, 0x08, 0xe1);
+               ak4642_write(codec, 0x0b, 0x00);
+               ak4642_write(codec, 0x07, 0x21);
+               ak4642_write(codec, 0x00, 0x41);
+               ak4642_write(codec, 0x10, 0x01);
+       }
+
+       return 0;
+}
+
+static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (is_play) {
+               /* stop headphone output */
+               ak4642_write(codec, 0x01, 0x3b);
+               ak4642_write(codec, 0x01, 0x0b);
+               ak4642_write(codec, 0x00, 0x40);
+               ak4642_write(codec, 0x0e, 0x11);
+               ak4642_write(codec, 0x0f, 0x08);
+       } else {
+               /* stop stereo input */
+               ak4642_write(codec, 0x00, 0x40);
+               ak4642_write(codec, 0x10, 0x00);
+               ak4642_write(codec, 0x07, 0x01);
+       }
+}
+
+static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+       int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct ak4642_priv *ak4642 = codec->private_data;
+
+       ak4642->sysclk = freq;
+       return 0;
+}
+
+static struct snd_soc_dai_ops ak4642_dai_ops = {
+       .startup        = ak4642_dai_startup,
+       .shutdown       = ak4642_dai_shutdown,
+       .set_sysclk     = ak4642_dai_set_sysclk,
+};
+
+struct snd_soc_dai ak4642_dai = {
+       .name = "AK4642",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE },
+       .ops = &ak4642_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4642_dai);
+
+static int ak4642_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       ak4642_sync(codec);
+       return 0;
+}
+
+/*
+ * initialise the AK4642 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4642_init(struct ak4642_priv *ak4642)
+{
+       struct snd_soc_codec *codec = &ak4642->codec;
+       int ret = 0;
+
+       if (ak4642_codec) {
+               dev_err(codec->dev, "Another ak4642 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data     = ak4642;
+       codec->name             = "AK4642";
+       codec->owner            = THIS_MODULE;
+       codec->read             = ak4642_read_reg_cache;
+       codec->write            = ak4642_write;
+       codec->dai              = &ak4642_dai;
+       codec->num_dai          = 1;
+       codec->hw_write         = (hw_write_t)i2c_master_send;
+       codec->reg_cache_size   = ARRAY_SIZE(ak4642_reg);
+       codec->reg_cache        = kmemdup(ak4642_reg,
+                                         sizeof(ak4642_reg), GFP_KERNEL);
+
+       if (!codec->reg_cache)
+               return -ENOMEM;
+
+       ak4642_dai.dev = codec->dev;
+       ak4642_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto reg_cache_err;
+       }
+
+       ret = snd_soc_register_dai(&ak4642_dai);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               goto reg_cache_err;
+       }
+
+       /*
+        * clock setting
+        *
+        * Audio I/F Format: MSB justified (ADC & DAC)
+        * BICK frequency at Master Mode: 64fs
+        * Input Master Clock Select at PLL Mode: 11.2896MHz
+        * MCKO: Enable
+        * Sampling Frequency: 44.1kHz
+        *
+        * This operation came from example code of
+        * "ASAHI KASEI AK4642" (japanese) manual p89.
+        *
+        * please fix-me
+        */
+       ak4642_write(codec, 0x01, 0x08);
+       ak4642_write(codec, 0x04, 0x4a);
+       ak4642_write(codec, 0x05, 0x27);
+       ak4642_write(codec, 0x00, 0x40);
+       ak4642_write(codec, 0x01, 0x0b);
+
+       return ret;
+
+reg_cache_err:
+       kfree(codec->reg_cache);
+       codec->reg_cache = NULL;
+
+       return ret;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ak4642_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct ak4642_priv *ak4642;
+       struct snd_soc_codec *codec;
+       int ret;
+
+       ak4642 = kzalloc(sizeof(struct ak4642_priv), GFP_KERNEL);
+       if (!ak4642)
+               return -ENOMEM;
+
+       codec = &ak4642->codec;
+       codec->dev = &i2c->dev;
+
+       i2c_set_clientdata(i2c, ak4642);
+       codec->control_data = i2c;
+
+       ret = ak4642_init(ak4642);
+       if (ret < 0)
+               printk(KERN_ERR "failed to initialise AK4642\n");
+
+       return ret;
+}
+
+static int ak4642_i2c_remove(struct i2c_client *client)
+{
+       struct ak4642_priv *ak4642 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_dai(&ak4642_dai);
+       snd_soc_unregister_codec(&ak4642->codec);
+       kfree(ak4642->codec.reg_cache);
+       kfree(ak4642);
+       ak4642_codec = NULL;
+
+       return 0;
+}
+
+static const struct i2c_device_id ak4642_i2c_id[] = {
+       { "ak4642", 0 },
+       { "ak4643", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
+
+static struct i2c_driver ak4642_i2c_driver = {
+       .driver = {
+               .name = "AK4642 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe          = ak4642_i2c_probe,
+       .remove         = ak4642_i2c_remove,
+       .id_table       = ak4642_i2c_id,
+};
+
+#endif
+
+static int ak4642_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       int ret;
+
+       if (!ak4642_codec) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = ak4642_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "ak4642: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "ak4642: failed to register card\n");
+               goto card_err;
+       }
+
+       dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+
+}
+
+/* power down chip */
+static int ak4642_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4642 = {
+       .probe =        ak4642_probe,
+       .remove =       ak4642_remove,
+       .resume =       ak4642_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4642);
+
+static int __init ak4642_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&ak4642_i2c_driver);
+#endif
+       return ret;
+
+}
+module_init(ak4642_modinit);
+
+static void __exit ak4642_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&ak4642_i2c_driver);
+#endif
+
+}
+module_exit(ak4642_exit);
+
+MODULE_DESCRIPTION("Soc AK4642 driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4642.h b/sound/soc/codecs/ak4642.h
new file mode 100644 (file)
index 0000000..e476833
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * ak4642.h  --  AK4642 Soc Audio driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ak4535.c
+ *
+ * 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 _AK4642_H
+#define _AK4642_H
+
+extern struct snd_soc_dai ak4642_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4642;
+
+#endif
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644 (file)
index 0000000..b61214d
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * ak4671.c  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+       struct snd_soc_codec codec;
+       u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+       0x00,   /* AK4671_AD_DA_POWER_MANAGEMENT        (0x00)  */
+       0xf6,   /* AK4671_PLL_MODE_SELECT0              (0x01)  */
+       0x00,   /* AK4671_PLL_MODE_SELECT1              (0x02)  */
+       0x02,   /* AK4671_FORMAT_SELECT                 (0x03)  */
+       0x00,   /* AK4671_MIC_SIGNAL_SELECT             (0x04)  */
+       0x55,   /* AK4671_MIC_AMP_GAIN                  (0x05)  */
+       0x00,   /* AK4671_MIXING_POWER_MANAGEMENT0      (0x06)  */
+       0x00,   /* AK4671_MIXING_POWER_MANAGEMENT1      (0x07)  */
+       0xb5,   /* AK4671_OUTPUT_VOLUME_CONTROL         (0x08)  */
+       0x00,   /* AK4671_LOUT1_SIGNAL_SELECT           (0x09)  */
+       0x00,   /* AK4671_ROUT1_SIGNAL_SELECT           (0x0a)  */
+       0x00,   /* AK4671_LOUT2_SIGNAL_SELECT           (0x0b)  */
+       0x00,   /* AK4671_ROUT2_SIGNAL_SELECT           (0x0c)  */
+       0x00,   /* AK4671_LOUT3_SIGNAL_SELECT           (0x0d)  */
+       0x00,   /* AK4671_ROUT3_SIGNAL_SELECT           (0x0e)  */
+       0x00,   /* AK4671_LOUT1_POWER_MANAGERMENT       (0x0f)  */
+       0x00,   /* AK4671_LOUT2_POWER_MANAGERMENT       (0x10)  */
+       0x80,   /* AK4671_LOUT3_POWER_MANAGERMENT       (0x11)  */
+       0x91,   /* AK4671_LCH_INPUT_VOLUME_CONTROL      (0x12)  */
+       0x91,   /* AK4671_RCH_INPUT_VOLUME_CONTROL      (0x13)  */
+       0xe1,   /* AK4671_ALC_REFERENCE_SELECT          (0x14)  */
+       0x00,   /* AK4671_DIGITAL_MIXING_CONTROL        (0x15)  */
+       0x00,   /* AK4671_ALC_TIMER_SELECT              (0x16)  */
+       0x00,   /* AK4671_ALC_MODE_CONTROL              (0x17)  */
+       0x02,   /* AK4671_MODE_CONTROL1                 (0x18)  */
+       0x01,   /* AK4671_MODE_CONTROL2                 (0x19)  */
+       0x18,   /* AK4671_LCH_OUTPUT_VOLUME_CONTROL     (0x1a)  */
+       0x18,   /* AK4671_RCH_OUTPUT_VOLUME_CONTROL     (0x1b)  */
+       0x00,   /* AK4671_SIDETONE_A_CONTROL            (0x1c)  */
+       0x02,   /* AK4671_DIGITAL_FILTER_SELECT         (0x1d)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT0             (0x1e)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT1             (0x1f)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT2             (0x20)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT3             (0x21)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT0               (0x22)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT1               (0x23)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT2               (0x24)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT3               (0x25)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT4               (0x26)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT5               (0x27)  */
+       0xa9,   /* AK4671_FIL1_COEFFICIENT0             (0x28)  */
+       0x1f,   /* AK4671_FIL1_COEFFICIENT1             (0x29)  */
+       0xad,   /* AK4671_FIL1_COEFFICIENT2             (0x2a)  */
+       0x20,   /* AK4671_FIL1_COEFFICIENT3             (0x2b)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT0             (0x2c)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT1             (0x2d)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT2             (0x2e)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT3             (0x2f)  */
+       0x00,   /* AK4671_DIGITAL_FILTER_SELECT2        (0x30)  */
+       0x00,   /* this register not used                       */
+       0x00,   /* AK4671_E1_COEFFICIENT0               (0x32)  */
+       0x00,   /* AK4671_E1_COEFFICIENT1               (0x33)  */
+       0x00,   /* AK4671_E1_COEFFICIENT2               (0x34)  */
+       0x00,   /* AK4671_E1_COEFFICIENT3               (0x35)  */
+       0x00,   /* AK4671_E1_COEFFICIENT4               (0x36)  */
+       0x00,   /* AK4671_E1_COEFFICIENT5               (0x37)  */
+       0x00,   /* AK4671_E2_COEFFICIENT0               (0x38)  */
+       0x00,   /* AK4671_E2_COEFFICIENT1               (0x39)  */
+       0x00,   /* AK4671_E2_COEFFICIENT2               (0x3a)  */
+       0x00,   /* AK4671_E2_COEFFICIENT3               (0x3b)  */
+       0x00,   /* AK4671_E2_COEFFICIENT4               (0x3c)  */
+       0x00,   /* AK4671_E2_COEFFICIENT5               (0x3d)  */
+       0x00,   /* AK4671_E3_COEFFICIENT0               (0x3e)  */
+       0x00,   /* AK4671_E3_COEFFICIENT1               (0x3f)  */
+       0x00,   /* AK4671_E3_COEFFICIENT2               (0x40)  */
+       0x00,   /* AK4671_E3_COEFFICIENT3               (0x41)  */
+       0x00,   /* AK4671_E3_COEFFICIENT4               (0x42)  */
+       0x00,   /* AK4671_E3_COEFFICIENT5               (0x43)  */
+       0x00,   /* AK4671_E4_COEFFICIENT0               (0x44)  */
+       0x00,   /* AK4671_E4_COEFFICIENT1               (0x45)  */
+       0x00,   /* AK4671_E4_COEFFICIENT2               (0x46)  */
+       0x00,   /* AK4671_E4_COEFFICIENT3               (0x47)  */
+       0x00,   /* AK4671_E4_COEFFICIENT4               (0x48)  */
+       0x00,   /* AK4671_E4_COEFFICIENT5               (0x49)  */
+       0x00,   /* AK4671_E5_COEFFICIENT0               (0x4a)  */
+       0x00,   /* AK4671_E5_COEFFICIENT1               (0x4b)  */
+       0x00,   /* AK4671_E5_COEFFICIENT2               (0x4c)  */
+       0x00,   /* AK4671_E5_COEFFICIENT3               (0x4d)  */
+       0x00,   /* AK4671_E5_COEFFICIENT4               (0x4e)  */
+       0x00,   /* AK4671_E5_COEFFICIENT5               (0x4f)  */
+       0x88,   /* AK4671_EQ_CONTROL_250HZ_100HZ        (0x50)  */
+       0x88,   /* AK4671_EQ_CONTROL_3500HZ_1KHZ        (0x51)  */
+       0x08,   /* AK4671_EQ_CONTRO_10KHZ               (0x52)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL0               (0x53)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL1               (0x54)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL2               (0x55)  */
+       0x18,   /* AK4671_DIGITAL_VOLUME_B_CONTROL      (0x56)  */
+       0x18,   /* AK4671_DIGITAL_VOLUME_C_CONTROL      (0x57)  */
+       0x00,   /* AK4671_SIDETONE_VOLUME_CONTROL       (0x58)  */
+       0x00,   /* AK4671_DIGITAL_MIXING_CONTROL2       (0x59)  */
+       0x00,   /* AK4671_SAR_ADC_CONTROL               (0x5a)  */
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+       /* Common playback gain controls */
+       SOC_SINGLE_TLV("Line Output1 Playback Volume",
+                       AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+       SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+                       AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+       SOC_SINGLE_TLV("Line Output3 Playback Volume",
+                       AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+       /* Common capture gain controls */
+       SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+                       AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u8 reg;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+               reg |= AK4671_MUTEN;
+               snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+               reg &= ~AK4671_MUTEN;
+               snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+               break;
+       }
+
+       return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+               {"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+       SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+                       ARRAY_SIZE(ak4671_lin_mux_texts),
+                       ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+       SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+               {"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+       SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+                       ARRAY_SIZE(ak4671_rin_mux_texts),
+                       ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+       SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+       SND_SOC_DAPM_INPUT("LIN3"),
+       SND_SOC_DAPM_INPUT("RIN3"),
+       SND_SOC_DAPM_INPUT("LIN4"),
+       SND_SOC_DAPM_INPUT("RIN4"),
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+       /* DAC */
+       SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+       SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+       /* ADC */
+       SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+       SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+       /* PGA */
+       SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+                       AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+                       AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+       /* Output Mixers */
+       SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+                       &ak4671_lout1_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+       SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+                       &ak4671_rout1_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+       SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+                       0, 0, &ak4671_lout2_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout2_mixer_controls),
+                       ak4671_out2_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+                       1, 0, &ak4671_rout2_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout2_mixer_controls),
+                       ak4671_out2_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+                       &ak4671_lout3_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+       SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+                       &ak4671_rout3_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+       /* Input MUXs */
+       SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+                       &ak4671_lin_mux_control),
+       SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+                       &ak4671_rin_mux_control),
+
+       /* Mic Power */
+       SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+       /* Supply */
+       SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"DAC Left", "NULL", "PMPLL"},
+       {"DAC Right", "NULL", "PMPLL"},
+       {"ADC Left", "NULL", "PMPLL"},
+       {"ADC Right", "NULL", "PMPLL"},
+
+       /* Outputs */
+       {"LOUT1", "NULL", "LOUT1 Mixer"},
+       {"ROUT1", "NULL", "ROUT1 Mixer"},
+       {"LOUT2", "NULL", "LOUT2 Mix Amp"},
+       {"ROUT2", "NULL", "ROUT2 Mix Amp"},
+       {"LOUT3", "NULL", "LOUT3 Mixer"},
+       {"ROUT3", "NULL", "ROUT3 Mixer"},
+
+       {"LOUT1 Mixer", "DACL", "DAC Left"},
+       {"ROUT1 Mixer", "DACR", "DAC Right"},
+       {"LOUT2 Mixer", "DACHL", "DAC Left"},
+       {"ROUT2 Mixer", "DACHR", "DAC Right"},
+       {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+       {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+       {"LOUT3 Mixer", "DACSL", "DAC Left"},
+       {"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+       /* Inputs */
+       {"LIN MUX", "LIN1", "LIN1"},
+       {"LIN MUX", "LIN2", "LIN2"},
+       {"LIN MUX", "LIN3", "LIN3"},
+       {"LIN MUX", "LIN4", "LIN4"},
+
+       {"RIN MUX", "RIN1", "RIN1"},
+       {"RIN MUX", "RIN2", "RIN2"},
+       {"RIN MUX", "RIN3", "RIN3"},
+       {"RIN MUX", "RIN4", "RIN4"},
+
+       {"LIN1", NULL, "Mic Bias"},
+       {"RIN1", NULL, "Mic Bias"},
+       {"LIN2", NULL, "Mic Bias"},
+       {"RIN2", NULL, "Mic Bias"},
+
+       {"ADC Left", "NULL", "LIN MUX"},
+       {"ADC Right", "NULL", "RIN MUX"},
+
+       /* Analog Loops */
+       {"LIN1 Mixing Circuit", "NULL", "LIN1"},
+       {"RIN1 Mixing Circuit", "NULL", "RIN1"},
+       {"LIN2 Mixing Circuit", "NULL", "LIN2"},
+       {"RIN2 Mixing Circuit", "NULL", "RIN2"},
+       {"LIN3 Mixing Circuit", "NULL", "LIN3"},
+       {"RIN3 Mixing Circuit", "NULL", "RIN3"},
+       {"LIN4 Mixing Circuit", "NULL", "LIN4"},
+       {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+       {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+                                 ARRAY_SIZE(ak4671_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static int ak4671_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 fs;
+
+       fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+       fs &= ~AK4671_FS;
+
+       switch (params_rate(params)) {
+       case 8000:
+               fs |= AK4671_FS_8KHZ;
+               break;
+       case 12000:
+               fs |= AK4671_FS_12KHZ;
+               break;
+       case 16000:
+               fs |= AK4671_FS_16KHZ;
+               break;
+       case 24000:
+               fs |= AK4671_FS_24KHZ;
+               break;
+       case 11025:
+               fs |= AK4671_FS_11_025KHZ;
+               break;
+       case 22050:
+               fs |= AK4671_FS_22_05KHZ;
+               break;
+       case 32000:
+               fs |= AK4671_FS_32KHZ;
+               break;
+       case 44100:
+               fs |= AK4671_FS_44_1KHZ;
+               break;
+       case 48000:
+               fs |= AK4671_FS_48KHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+       return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+               unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 pll;
+
+       pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+       pll &= ~AK4671_PLL;
+
+       switch (freq) {
+       case 11289600:
+               pll |= AK4671_PLL_11_2896MHZ;
+               break;
+       case 12000000:
+               pll |= AK4671_PLL_12MHZ;
+               break;
+       case 12288000:
+               pll |= AK4671_PLL_12_288MHZ;
+               break;
+       case 13000000:
+               pll |= AK4671_PLL_13MHZ;
+               break;
+       case 13500000:
+               pll |= AK4671_PLL_13_5MHZ;
+               break;
+       case 19200000:
+               pll |= AK4671_PLL_19_2MHZ;
+               break;
+       case 24000000:
+               pll |= AK4671_PLL_24MHZ;
+               break;
+       case 26000000:
+               pll |= AK4671_PLL_26MHZ;
+               break;
+       case 27000000:
+               pll |= AK4671_PLL_27MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+       return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 mode;
+       u8 format;
+
+       /* set master/slave audio interface */
+       mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               mode |= AK4671_M_S;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               mode &= ~(AK4671_M_S);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+       format &= ~AK4671_DIF;
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               format |= AK4671_DIF_I2S_MODE;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               format |= AK4671_DIF_MSB_MODE;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               format |= AK4671_DIF_DSP_MODE;
+               format |= AK4671_BCKP;
+               format |= AK4671_MSBS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set mode and format */
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+       snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+       return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       u8 reg;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+       case SND_SOC_BIAS_STANDBY:
+               reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+               snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+                               reg | AK4671_PMVCM);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define AK4671_RATES           (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+                               SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                               SNDRV_PCM_RATE_48000)
+
+#define AK4671_FORMATS         SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+       .hw_params      = ak4671_hw_params,
+       .set_sysclk     = ak4671_set_dai_sysclk,
+       .set_fmt        = ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+       .name = "AK4671",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AK4671_RATES,
+               .formats = AK4671_FORMATS,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AK4671_RATES,
+               .formats = AK4671_FORMATS,},
+       .ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (ak4671_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = ak4671_codec;
+       codec = ak4671_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, ak4671_snd_controls,
+                            ARRAY_SIZE(ak4671_snd_controls));
+       ak4671_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int ak4671_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4671 = {
+       .probe = ak4671_probe,
+       .remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+               enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &ak4671->codec;
+
+       if (ak4671_codec) {
+               dev_err(codec->dev, "Another AK4671 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = ak4671;
+       codec->name = "AK4671";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = ak4671_set_bias_level;
+       codec->dai = &ak4671_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = AK4671_CACHEREGNUM;
+       codec->reg_cache = &ak4671->reg_cache;
+
+       memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       ak4671_dai.dev = codec->dev;
+       ak4671_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dai(&ak4671_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
+       }
+
+       return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(ak4671);
+       return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+       ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&ak4671_dai);
+       snd_soc_unregister_codec(&ak4671->codec);
+       kfree(ak4671);
+       ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct ak4671_priv *ak4671;
+       struct snd_soc_codec *codec;
+
+       ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+       if (ak4671 == NULL)
+               return -ENOMEM;
+
+       codec = &ak4671->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(client, ak4671);
+       codec->control_data = client;
+
+       codec->dev = &client->dev;
+
+       return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+       struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+       ak4671_unregister(ak4671);
+
+       return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+       { "ak4671", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+       .driver = {
+               .name = "ak4671",
+               .owner = THIS_MODULE,
+       },
+       .probe = ak4671_i2c_probe,
+       .remove = __devexit_p(ak4671_i2c_remove),
+       .id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+       return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+       i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644 (file)
index 0000000..e2fad96
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT          0x00
+#define AK4671_PLL_MODE_SELECT0                        0x01
+#define AK4671_PLL_MODE_SELECT1                        0x02
+#define AK4671_FORMAT_SELECT                   0x03
+#define AK4671_MIC_SIGNAL_SELECT               0x04
+#define AK4671_MIC_AMP_GAIN                    0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0                0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1                0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL           0x08
+#define AK4671_LOUT1_SIGNAL_SELECT             0x09
+#define AK4671_ROUT1_SIGNAL_SELECT             0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT             0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT             0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT             0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT             0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT         0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT         0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT         0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL                0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL                0x13
+#define AK4671_ALC_REFERENCE_SELECT            0x14
+#define AK4671_DIGITAL_MIXING_CONTROL          0x15
+#define AK4671_ALC_TIMER_SELECT                        0x16
+#define AK4671_ALC_MODE_CONTROL                        0x17
+#define AK4671_MODE_CONTROL1                   0x18
+#define AK4671_MODE_CONTROL2                   0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL       0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL       0x1b
+#define AK4671_SIDETONE_A_CONTROL              0x1c
+#define AK4671_DIGITAL_FILTER_SELECT           0x1d
+#define AK4671_FIL3_COEFFICIENT0               0x1e
+#define AK4671_FIL3_COEFFICIENT1               0x1f
+#define AK4671_FIL3_COEFFICIENT2               0x20
+#define AK4671_FIL3_COEFFICIENT3               0x21
+#define AK4671_EQ_COEFFICIENT0                 0x22
+#define AK4671_EQ_COEFFICIENT1                 0x23
+#define AK4671_EQ_COEFFICIENT2                 0x24
+#define AK4671_EQ_COEFFICIENT3                 0x25
+#define AK4671_EQ_COEFFICIENT4                 0x26
+#define AK4671_EQ_COEFFICIENT5                 0x27
+#define AK4671_FIL1_COEFFICIENT0               0x28
+#define AK4671_FIL1_COEFFICIENT1               0x29
+#define AK4671_FIL1_COEFFICIENT2               0x2a
+#define AK4671_FIL1_COEFFICIENT3               0x2b
+#define AK4671_FIL2_COEFFICIENT0               0x2c
+#define AK4671_FIL2_COEFFICIENT1               0x2d
+#define AK4671_FIL2_COEFFICIENT2               0x2e
+#define AK4671_FIL2_COEFFICIENT3               0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2          0x30
+#define AK4671_E1_COEFFICIENT0                 0x32
+#define AK4671_E1_COEFFICIENT1                 0x33
+#define AK4671_E1_COEFFICIENT2                 0x34
+#define AK4671_E1_COEFFICIENT3                 0x35
+#define AK4671_E1_COEFFICIENT4                 0x36
+#define AK4671_E1_COEFFICIENT5                 0x37
+#define AK4671_E2_COEFFICIENT0                 0x38
+#define AK4671_E2_COEFFICIENT1                 0x39
+#define AK4671_E2_COEFFICIENT2                 0x3a
+#define AK4671_E2_COEFFICIENT3                 0x3b
+#define AK4671_E2_COEFFICIENT4                 0x3c
+#define AK4671_E2_COEFFICIENT5                 0x3d
+#define AK4671_E3_COEFFICIENT0                 0x3e
+#define AK4671_E3_COEFFICIENT1                 0x3f
+#define AK4671_E3_COEFFICIENT2                 0x40
+#define AK4671_E3_COEFFICIENT3                 0x41
+#define AK4671_E3_COEFFICIENT4                 0x42
+#define AK4671_E3_COEFFICIENT5                 0x43
+#define AK4671_E4_COEFFICIENT0                 0x44
+#define AK4671_E4_COEFFICIENT1                 0x45
+#define AK4671_E4_COEFFICIENT2                 0x46
+#define AK4671_E4_COEFFICIENT3                 0x47
+#define AK4671_E4_COEFFICIENT4                 0x48
+#define AK4671_E4_COEFFICIENT5                 0x49
+#define AK4671_E5_COEFFICIENT0                 0x4a
+#define AK4671_E5_COEFFICIENT1                 0x4b
+#define AK4671_E5_COEFFICIENT2                 0x4c
+#define AK4671_E5_COEFFICIENT3                 0x4d
+#define AK4671_E5_COEFFICIENT4                 0x4e
+#define AK4671_E5_COEFFICIENT5                 0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ          0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ          0x51
+#define AK4671_EQ_CONTRO_10KHZ                 0x52
+#define AK4671_PCM_IF_CONTROL0                 0x53
+#define AK4671_PCM_IF_CONTROL1                 0x54
+#define AK4671_PCM_IF_CONTROL2                 0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL                0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL                0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL         0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2         0x59
+#define AK4671_SAR_ADC_CONTROL                 0x5a
+
+#define AK4671_CACHEREGNUM                     (AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM                           0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL                             0x0f
+#define AK4671_PLL_11_2896MHZ                  (4 << 0)
+#define AK4671_PLL_12_288MHZ                   (5 << 0)
+#define AK4671_PLL_12MHZ                       (6 << 0)
+#define AK4671_PLL_24MHZ                       (7 << 0)
+#define AK4671_PLL_19_2MHZ                     (8 << 0)
+#define AK4671_PLL_13_5MHZ                     (12 << 0)
+#define AK4671_PLL_27MHZ                       (13 << 0)
+#define AK4671_PLL_13MHZ                       (14 << 0)
+#define AK4671_PLL_26MHZ                       (15 << 0)
+#define AK4671_FS                              0xf0
+#define AK4671_FS_8KHZ                         (0 << 4)
+#define AK4671_FS_12KHZ                                (1 << 4)
+#define AK4671_FS_16KHZ                                (2 << 4)
+#define AK4671_FS_24KHZ                                (3 << 4)
+#define AK4671_FS_11_025KHZ                    (5 << 4)
+#define AK4671_FS_22_05KHZ                     (7 << 4)
+#define AK4671_FS_32KHZ                                (10 << 4)
+#define AK4671_FS_48KHZ                                (11 << 4)
+#define AK4671_FS_44_1KHZ                      (15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL                           0x01
+#define AK4671_M_S                             0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF                             0x03
+#define AK4671_DIF_DSP_MODE                    (0 << 0)
+#define AK4671_DIF_MSB_MODE                    (2 << 0)
+#define AK4671_DIF_I2S_MODE                    (3 << 0)
+#define AK4671_BCKP                            0x04
+#define AK4671_MSBS                            0x08
+#define AK4671_SDOD                            0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN                           0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
index 126b15b..3395cf9 100644 (file)
@@ -53,6 +53,7 @@
 
 /* codec private data */
 struct aic3x_priv {
+       struct snd_soc_codec codec;
        unsigned int sysclk;
        int master;
 };
@@ -1156,11 +1157,13 @@ static int aic3x_resume(struct platform_device *pdev)
  * initialise the AIC3X driver
  * register the mixer and dsp interfaces with the kernel
  */
-static int aic3x_init(struct snd_soc_device *socdev)
+static int aic3x_init(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec = socdev->card->codec;
-       struct aic3x_setup_data *setup = socdev->codec_data;
-       int reg, ret = 0;
+       int reg;
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
 
        codec->name = "tlv320aic3x";
        codec->owner = THIS_MODULE;
@@ -1177,13 +1180,6 @@ static int aic3x_init(struct snd_soc_device *socdev)
        aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
        aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
 
-       /* register pcms */
-       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-       if (ret < 0) {
-               printk(KERN_ERR "aic3x: failed to create pcms\n");
-               goto pcm_err;
-       }
-
        /* DAC default volume and mute */
        aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
        aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
@@ -1250,30 +1246,51 @@ static int aic3x_init(struct snd_soc_device *socdev)
        /* off, with power on */
        aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       /* setup GPIO functions */
-       aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
-       aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
+       return 0;
+}
 
-       snd_soc_add_controls(codec, aic3x_snd_controls,
-                               ARRAY_SIZE(aic3x_snd_controls));
-       aic3x_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
+static struct snd_soc_codec *aic3x_codec;
+
+static int aic3x_register(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = aic3x_init(codec);
        if (ret < 0) {
-               printk(KERN_ERR "aic3x: failed to register card\n");
-               goto card_err;
+               dev_err(codec->dev, "Failed to initialise device\n");
+               return ret;
        }
 
-       return ret;
+       aic3x_codec = codec;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-pcm_err:
-       kfree(codec->reg_cache);
-       return ret;
+       ret = snd_soc_register_codec(codec);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register codec\n");
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&aic3x_dai);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register dai\n");
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
 }
 
-static struct snd_soc_device *aic3x_socdev;
+static int aic3x_unregister(struct aic3x_priv *aic3x)
+{
+       aic3x_set_bias_level(&aic3x->codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_unregister_dai(&aic3x_dai);
+       snd_soc_unregister_codec(&aic3x->codec);
+
+       kfree(aic3x);
+       aic3x_codec = NULL;
+
+       return 0;
+}
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 /*
@@ -1288,28 +1305,36 @@ static struct snd_soc_device *aic3x_socdev;
 static int aic3x_i2c_probe(struct i2c_client *i2c,
                           const struct i2c_device_id *id)
 {
-       struct snd_soc_device *socdev = aic3x_socdev;
-       struct snd_soc_codec *codec = socdev->card->codec;
-       int ret;
+       struct snd_soc_codec *codec;
+       struct aic3x_priv *aic3x;
+
+       aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
+       if (aic3x == NULL) {
+               dev_err(&i2c->dev, "failed to create private data\n");
+               return -ENOMEM;
+       }
 
-       i2c_set_clientdata(i2c, codec);
+       codec = &aic3x->codec;
+       codec->dev = &i2c->dev;
+       codec->private_data = aic3x;
        codec->control_data = i2c;
+       codec->hw_write = (hw_write_t) i2c_master_send;
 
-       ret = aic3x_init(socdev);
-       if (ret < 0)
-               printk(KERN_ERR "aic3x: failed to initialise AIC3X\n");
-       return ret;
+       i2c_set_clientdata(i2c, aic3x);
+
+       return aic3x_register(codec);
 }
 
 static int aic3x_i2c_remove(struct i2c_client *client)
 {
-       struct snd_soc_codec *codec = i2c_get_clientdata(client);
-       kfree(codec->reg_cache);
-       return 0;
+       struct aic3x_priv *aic3x = i2c_get_clientdata(client);
+
+       return aic3x_unregister(aic3x);
 }
 
 static const struct i2c_device_id aic3x_i2c_id[] = {
        { "tlv320aic3x", 0 },
+       { "tlv320aic33", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
@@ -1320,50 +1345,28 @@ static struct i2c_driver aic3x_i2c_driver = {
                .name = "aic3x I2C Codec",
                .owner = THIS_MODULE,
        },
-       .probe = aic3x_i2c_probe,
+       .probe  = aic3x_i2c_probe,
        .remove = aic3x_i2c_remove,
        .id_table = aic3x_i2c_id,
 };
 
-static int aic3x_add_i2c_device(struct platform_device *pdev,
-                                const struct aic3x_setup_data *setup)
+static inline void aic3x_i2c_init(void)
 {
-       struct i2c_board_info info;
-       struct i2c_adapter *adapter;
-       struct i2c_client *client;
        int ret;
 
        ret = i2c_add_driver(&aic3x_i2c_driver);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "can't add i2c driver\n");
-               return ret;
-       }
-
-       memset(&info, 0, sizeof(struct i2c_board_info));
-       info.addr = setup->i2c_address;
-       strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE);
-
-       adapter = i2c_get_adapter(setup->i2c_bus);
-       if (!adapter) {
-               dev_err(&pdev->dev, "can't get i2c adapter %d\n",
-                       setup->i2c_bus);
-               goto err_driver;
-       }
-
-       client = i2c_new_device(adapter, &info);
-       i2c_put_adapter(adapter);
-       if (!client) {
-               dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
-                       (unsigned int)info.addr);
-               goto err_driver;
-       }
-
-       return 0;
+       if (ret)
+               printk(KERN_ERR "%s: error regsitering i2c driver, %d\n",
+                      __func__, ret);
+}
 
-err_driver:
+static inline void aic3x_i2c_exit(void)
+{
        i2c_del_driver(&aic3x_i2c_driver);
-       return -ENODEV;
 }
+#else
+static inline void aic3x_i2c_init(void) { }
+static inline void aic3x_i2c_exit(void) { }
 #endif
 
 static int aic3x_probe(struct platform_device *pdev)
@@ -1371,42 +1374,51 @@ static int aic3x_probe(struct platform_device *pdev)
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct aic3x_setup_data *setup;
        struct snd_soc_codec *codec;
-       struct aic3x_priv *aic3x;
        int ret = 0;
 
-       printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION);
+       codec = aic3x_codec;
+       if (!codec) {
+               dev_err(&pdev->dev, "Codec not registered\n");
+               return -ENODEV;
+       }
 
+       socdev->card->codec = codec;
        setup = socdev->codec_data;
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
-               return -ENOMEM;
 
-       aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
-       if (aic3x == NULL) {
-               kfree(codec);
-               return -ENOMEM;
+       if (setup) {
+               /* setup GPIO functions */
+               aic3x_write(codec, AIC3X_GPIO1_REG,
+                           (setup->gpio_func[0] & 0xf) << 4);
+               aic3x_write(codec, AIC3X_GPIO2_REG,
+                           (setup->gpio_func[1] & 0xf) << 4);
        }
 
-       codec->private_data = aic3x;
-       socdev->card->codec = codec;
-       mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
-
-       aic3x_socdev = socdev;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       if (setup->i2c_address) {
-               codec->hw_write = (hw_write_t) i2c_master_send;
-               ret = aic3x_add_i2c_device(pdev, setup);
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "aic3x: failed to create pcms\n");
+               goto pcm_err;
        }
-#else
-       /* Add other interfaces here */
-#endif
 
-       if (ret != 0) {
-               kfree(codec->private_data);
-               kfree(codec);
+       snd_soc_add_controls(codec, aic3x_snd_controls,
+                            ARRAY_SIZE(aic3x_snd_controls));
+
+       aic3x_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               printk(KERN_ERR "aic3x: failed to register card\n");
+               goto card_err;
        }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+pcm_err:
+       kfree(codec->reg_cache);
        return ret;
 }
 
@@ -1421,12 +1433,8 @@ static int aic3x_remove(struct platform_device *pdev)
 
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       i2c_unregister_device(codec->control_data);
-       i2c_del_driver(&aic3x_i2c_driver);
-#endif
-       kfree(codec->private_data);
-       kfree(codec);
+
+       kfree(codec->reg_cache);
 
        return 0;
 }
@@ -1441,13 +1449,15 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
 
 static int __init aic3x_modinit(void)
 {
-       return snd_soc_register_dai(&aic3x_dai);
+       aic3x_i2c_init();
+
+       return 0;
 }
 module_init(aic3x_modinit);
 
 static void __exit aic3x_exit(void)
 {
-       snd_soc_unregister_dai(&aic3x_dai);
+       aic3x_i2c_exit();
 }
 module_exit(aic3x_exit);
 
index ac827e5..9af1c88 100644 (file)
@@ -282,8 +282,6 @@ int aic3x_headset_detected(struct snd_soc_codec *codec);
 int aic3x_button_pressed(struct snd_soc_codec *codec);
 
 struct aic3x_setup_data {
-       int i2c_bus;
-       unsigned short i2c_address;
        unsigned int gpio_func[2];
 };
 
index 4ded0e3..3f7e8a8 100644 (file)
@@ -63,6 +63,8 @@ struct wm8350_data {
        struct wm8350_jack_data hpl;
        struct wm8350_jack_data hpr;
        struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+       int fll_freq_out;
+       int fll_freq_in;
 };
 
 static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
@@ -610,7 +612,7 @@ SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
 
 /* Out4 Capture Mux */
 static const struct snd_kcontrol_new wm8350_out4_capture_controls =
-SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+SOC_DAPM_ENUM("Route", wm8350_enum[7]);
 
 static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
 
@@ -1099,15 +1101,19 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
 }
 
 static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
-                         int pll_id, unsigned int freq_in,
+                         int pll_id, int source, unsigned int freq_in,
                          unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct wm8350 *wm8350 = codec->control_data;
+       struct wm8350_data *priv = codec->private_data;
        struct _fll_div fll_div;
        int ret = 0;
        u16 fll_1, fll_4;
 
+       if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out)
+               return 0;
+
        /* power down FLL - we need to do this for reconfiguration */
        wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
                          WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
@@ -1142,6 +1148,9 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
        wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
        wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
 
+       priv->fll_freq_out = freq_out;
+       priv->fll_freq_in = freq_in;
+
        return 0;
 }
 
index b9ef4d9..9cb8e50 100644 (file)
@@ -1011,7 +1011,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
 }
 
 static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
-                             unsigned int freq_in, unsigned int freq_out)
+                             int source, unsigned int freq_in,
+                             unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct wm8400_priv *wm8400 = codec->private_data;
index 060d5d0..5702435 100644 (file)
@@ -271,8 +271,8 @@ static void pll_factors(unsigned int target, unsigned int source)
        pll_div.k = K;
 }
 
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        u16 reg;
index d547347..3be5c0b 100644 (file)
@@ -407,8 +407,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
        return 0;
 }
 
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        int offset;
        struct snd_soc_codec *codec = codec_dai->codec;
@@ -458,12 +458,12 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
                return 0;
 
        snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
-       snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
+       snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff);
        snd_soc_write(codec, WM8580_PLLA3 + offset,
                     (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
 
        reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
-       reg &= ~0x3f;
+       reg &= ~0x1b;
        reg |= pll_div.prescale | pll_div.postscale << 1 |
                pll_div.freqmode << 3;
 
index d80d414..f60f3a0 100644 (file)
@@ -723,8 +723,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
        pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        u16 reg, enable;
        int offset;
index 5e9c855..882604e 100644 (file)
@@ -814,8 +814,8 @@ reenable:
        return 0;
 }
 
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
 }
index da97aae..914d788 100644 (file)
@@ -536,8 +536,8 @@ static void pll_factors(unsigned int target, unsigned int source)
 }
 
 /* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        u16 reg;
index f59703b..416fb3c 100644 (file)
@@ -540,8 +540,8 @@ static int pll_factors(unsigned int source, unsigned int target,
        return 0;
 }
 
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        u16 reg;
index d8a013a..93d66e3 100644 (file)
@@ -12,7 +12,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
@@ -329,8 +328,8 @@ static void pll_factors(unsigned int target, unsigned int source)
        pll_div.k = K;
 }
 
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        u16 reg;
index 2d702db..f657e9a 100644 (file)
@@ -972,8 +972,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
        pll_div->k = K;
 }
 
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        u16 reg;
        struct snd_soc_codec *codec = codec_dai->codec;
index 13befa3..6b32a28 100644 (file)
@@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
        return 0;
 }
 
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
                          unsigned int Fref, unsigned int Fout)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -846,18 +846,76 @@ SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
 SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
 };
 
+static const char *aif_text[] = {
+       "Left", "Right"
+};
+
+static const struct soc_enum aifoutl_enum =
+       SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 15, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+       SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static const struct soc_enum aifoutr_enum =
+       SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 14, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+       SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static const struct soc_enum aifinl_enum =
+       SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+       SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static const struct soc_enum aifinr_enum =
+       SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 14, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+       SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const char *sidetone_text[] = {
+       "None", "Left", "Right"
+};
+
+static const struct soc_enum sidetonel_enum =
+       SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new sidetonel_mux =
+       SOC_DAPM_ENUM("Left Sidetone", sidetonel_enum);
+
+static const struct soc_enum sidetoner_enum =
+       SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 0, 3, sidetone_text);
+
+static const struct snd_kcontrol_new sidetoner_mux =
+       SOC_DAPM_ENUM("Right Sidetone", sidetoner_enum);
+
 static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
                    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
 
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
 
-SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0),
-SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
 
-SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0),
-SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0),
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &sidetonel_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
 
 SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
 SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
@@ -875,10 +933,32 @@ static const struct snd_soc_dapm_route routes[] = {
        { "ADCR", NULL, "CLK_SYS" },
        { "ADCR", NULL, "CLK_DSP" },
 
+       { "AIFOUTL Mux", "Left", "ADCL" },
+       { "AIFOUTL Mux", "Right", "ADCR" },
+       { "AIFOUTR Mux", "Left", "ADCL" },
+       { "AIFOUTR Mux", "Right", "ADCR" },
+
+       { "AIFOUTL", NULL, "AIFOUTL Mux" },
+       { "AIFOUTR", NULL, "AIFOUTR Mux" },
+
+       { "DACL Mux", "Left", "AIFINL" },
+       { "DACL Mux", "Right", "AIFINR" },
+       { "DACR Mux", "Left", "AIFINL" },
+       { "DACR Mux", "Right", "AIFINR" },
+
+       { "DACL Sidetone", "Left", "ADCL" },
+       { "DACL Sidetone", "Right", "ADCR" },
+       { "DACR Sidetone", "Left", "ADCL" },
+       { "DACR Sidetone", "Right", "ADCR" },
+
        { "DACL", NULL, "CLK_SYS" },
        { "DACL", NULL, "CLK_DSP" },
+       { "DACL", NULL, "DACL Mux" },
+       { "DACL", NULL, "DACL Sidetone" },
        { "DACR", NULL, "CLK_SYS" },
        { "DACR", NULL, "CLK_DSP" },
+       { "DACR", NULL, "DACR Mux" },
+       { "DACR", NULL, "DACR Sidetone" },
 
        { "Left Output Mixer", "DAC Switch", "DACL" },
 
index fa88b46..e7d2840 100644 (file)
@@ -406,7 +406,7 @@ static int wm9705_soc_probe(struct platform_device *pdev)
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
                printk(KERN_ERR "wm9705: failed to register card\n");
-               goto pcm_err;
+               goto reset_err;
        }
 
        return 0;
index abed37a..ca3d449 100644 (file)
@@ -800,8 +800,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
index e8fc474..e542027 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -274,17 +273,12 @@ static int hp_event(struct snd_soc_dapm_widget *w,
 
                /* Start the DC servo */
                snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
-                                   WM8993_DCS_ENA_CHAN_0 |
-                                   WM8993_DCS_ENA_CHAN_1 |
-                                   WM8993_DCS_TRIG_STARTUP_1 |
-                                   WM8993_DCS_TRIG_STARTUP_0,
+                                   0xFFFF,
                                    WM8993_DCS_ENA_CHAN_0 |
                                    WM8993_DCS_ENA_CHAN_1 |
                                    WM8993_DCS_TRIG_STARTUP_1 |
                                    WM8993_DCS_TRIG_STARTUP_0);
                wait_for_dc_servo(codec);
-               snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
-                                   WM8993_DCS_TIMER_PERIOD_01_MASK, 0xa);
 
                reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
                        WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
@@ -299,11 +293,8 @@ static int hp_event(struct snd_soc_dapm_widget *w,
                         WM8993_HPOUT1R_DLY |
                         WM8993_HPOUT1R_OUTP);
 
-               snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
-                                   WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
                snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
-                                   WM8993_DCS_ENA_CHAN_0 |
-                                   WM8993_DCS_ENA_CHAN_1, 0);
+                                   0xffff, 0);
 
                snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
                snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
@@ -474,12 +465,6 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
 SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
                   mixinr, ARRAY_SIZE(mixinr)),
 
-SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0),
-SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0),
-
-SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0),
-SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0),
-
 SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
                   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
 SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
index 4dfd4ad..047ee39 100644 (file)
@@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP
        tristate
 
 config SND_DAVINCI_SOC_EVM
-       tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+       tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
        depends on SND_DAVINCI_SOC
-       depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+       depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM  || MACH_DAVINCI_DM365_EVM
        select SND_DAVINCI_SOC_I2S
        select SND_SOC_TLV320AIC3X
        help
index 46c1b0c..7ccbe66 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/i2c.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -44,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        unsigned sysclk;
 
        /* ASP1 on DM355 EVM is clocked by an external oscillator */
-       if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+       if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+           machine_is_davinci_dm365_evm())
                sysclk = 27000000;
 
        /* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -175,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = {
        .ops = &evm_ops,
 };
 
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
        .name = "DaVinci EVM",
        .platform = &davinci_soc_platform,
@@ -205,48 +207,33 @@ static struct snd_soc_card da850_snd_soc_card = {
        .num_links = 1,
 };
 
-/* evm audio private data */
-static struct aic3x_setup_data evm_aic3x_setup = {
-       .i2c_bus = 1,
-       .i2c_address = 0x1b,
-};
-
-/* dm6467 evm audio private data */
-static struct aic3x_setup_data dm6467_evm_aic3x_setup = {
-       .i2c_bus = 1,
-       .i2c_address = 0x18,
-};
-
-static struct aic3x_setup_data da8xx_evm_aic3x_setup = {
-       .i2c_bus = 1,
-       .i2c_address = 0x18,
-};
+static struct aic3x_setup_data aic3x_setup;
 
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
        .card = &snd_soc_card_evm,
        .codec_dev = &soc_codec_dev_aic3x,
-       .codec_data = &evm_aic3x_setup,
+       .codec_data = &aic3x_setup,
 };
 
 /* evm audio subsystem */
 static struct snd_soc_device dm6467_evm_snd_devdata = {
        .card = &dm6467_snd_soc_card_evm,
        .codec_dev = &soc_codec_dev_aic3x,
-       .codec_data = &dm6467_evm_aic3x_setup,
+       .codec_data = &aic3x_setup,
 };
 
 /* evm audio subsystem */
 static struct snd_soc_device da830_evm_snd_devdata = {
        .card = &da830_snd_soc_card,
        .codec_dev = &soc_codec_dev_aic3x,
-       .codec_data = &da8xx_evm_aic3x_setup,
+       .codec_data = &aic3x_setup,
 };
 
 static struct snd_soc_device da850_evm_snd_devdata = {
        .card           = &da850_snd_soc_card,
        .codec_dev      = &soc_codec_dev_aic3x,
-       .codec_data     = &da8xx_evm_aic3x_setup,
+       .codec_data     = &aic3x_setup,
 };
 
 static struct platform_device *evm_snd_device;
@@ -257,7 +244,7 @@ static int __init evm_init(void)
        int index;
        int ret;
 
-       if (machine_is_davinci_evm()) {
+       if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
                evm_snd_dev_data = &evm_snd_devdata;
                index = 0;
        } else if (machine_is_davinci_dm355_evm()) {
index eca22d7..7a06c0a 100644 (file)
@@ -512,34 +512,49 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev,
                                       int channel_size)
 {
        u32 fmt = 0;
+       u32 mask, rotate;
 
        switch (channel_size) {
        case DAVINCI_AUDIO_WORD_8:
                fmt = 0x03;
+               rotate = 6;
+               mask = 0x000000ff;
                break;
 
        case DAVINCI_AUDIO_WORD_12:
                fmt = 0x05;
+               rotate = 5;
+               mask = 0x00000fff;
                break;
 
        case DAVINCI_AUDIO_WORD_16:
                fmt = 0x07;
+               rotate = 4;
+               mask = 0x0000ffff;
                break;
 
        case DAVINCI_AUDIO_WORD_20:
                fmt = 0x09;
+               rotate = 3;
+               mask = 0x000fffff;
                break;
 
        case DAVINCI_AUDIO_WORD_24:
                fmt = 0x0B;
+               rotate = 2;
+               mask = 0x00ffffff;
                break;
 
        case DAVINCI_AUDIO_WORD_28:
                fmt = 0x0D;
+               rotate = 1;
+               mask = 0x0fffffff;
                break;
 
        case DAVINCI_AUDIO_WORD_32:
                fmt = 0x0F;
+               rotate = 0;
+               mask = 0xffffffff;
                break;
 
        default:
@@ -550,6 +565,13 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev,
                                        RXSSZ(fmt), RXSSZ(0x0F));
        mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
                                        TXSSZ(fmt), TXSSZ(0x0F));
+       mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXROT(rotate),
+                                                       TXROT(7));
+       mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXROT(rotate),
+                                                       RXROT(7));
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, mask);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, mask);
+
        return 0;
 }
 
@@ -638,7 +660,6 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
                        printk(KERN_ERR "playback tdm slot %d not supported\n",
                                dev->tdm_slots);
 
-               mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF);
                mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
        } else {
                /* bit stream is MSB first with no delay */
@@ -655,7 +676,6 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
                        printk(KERN_ERR "capture tdm slot %d not supported\n",
                                dev->tdm_slots);
 
-               mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF);
                mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
        }
 }
index 9ff62e3..6096d22 100644 (file)
@@ -447,6 +447,7 @@ int mpc5200_audio_dma_create(struct of_device *op)
        int size, irq, rc;
        const __be32 *prop;
        void __iomem *regs;
+       int ret;
 
        /* Fetch the registers and IRQ of the PSC */
        irq = irq_of_parse_and_map(op->node, 0);
@@ -463,14 +464,16 @@ int mpc5200_audio_dma_create(struct of_device *op)
        /* Allocate and initialize the driver private data */
        psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL);
        if (!psc_dma) {
-               iounmap(regs);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_unmap;
        }
 
        /* Get the PSC ID */
        prop = of_get_property(op->node, "cell-index", &size);
-       if (!prop || size < sizeof *prop)
-               return -ENODEV;
+       if (!prop || size < sizeof *prop) {
+               ret = -ENODEV;
+               goto out_free;
+       }
 
        spin_lock_init(&psc_dma->lock);
        mutex_init(&psc_dma->mutex);
@@ -493,9 +496,8 @@ int mpc5200_audio_dma_create(struct of_device *op)
        if (!psc_dma->capture.bcom_task ||
            !psc_dma->playback.bcom_task) {
                dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
-               iounmap(regs);
-               kfree(psc_dma);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto out_free;
        }
 
        /* Disable all interrupts and reset the PSC */
@@ -537,12 +539,8 @@ int mpc5200_audio_dma_create(struct of_device *op)
                          &psc_dma_bcom_irq_tx, IRQF_SHARED,
                          "psc-dma-playback", &psc_dma->playback);
        if (rc) {
-               free_irq(psc_dma->irq, psc_dma);
-               free_irq(psc_dma->capture.irq,
-                        &psc_dma->capture);
-               free_irq(psc_dma->playback.irq,
-                        &psc_dma->playback);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto out_irq;
        }
 
        /* Save what we've done so it can be found again later */
@@ -550,6 +548,15 @@ int mpc5200_audio_dma_create(struct of_device *op)
 
        /* Tell the ASoC OF helpers about it */
        return snd_soc_register_platform(&mpc5200_audio_dma_platform);
+out_irq:
+       free_irq(psc_dma->irq, psc_dma);
+       free_irq(psc_dma->capture.irq, &psc_dma->capture);
+       free_irq(psc_dma->playback.irq, &psc_dma->playback);
+out_free:
+       kfree(psc_dma);
+out_unmap:
+       iounmap(regs);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);
 
index e4dcb53..0267d2d 100644 (file)
@@ -157,7 +157,7 @@ static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
 
 
        /* codec PLL input is 25 MHz */
-       ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+       ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
                                        25000000, pll_out);
        if (ret < 0) {
                printk(KERN_ERR "Error when setting PLL input\n");
index b60b1df..0a50593 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -322,8 +323,6 @@ static struct snd_soc_card snd_soc_n810 = {
 
 /* Audio private data */
 static struct aic3x_setup_data n810_aic33_setup = {
-       .i2c_bus = 2,
-       .i2c_address = 0x18,
        .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
        .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
 };
@@ -337,6 +336,13 @@ static struct snd_soc_device n810_snd_devdata = {
 
 static struct platform_device *n810_snd_device;
 
+/* temporary i2c device creation until this can be moved into the machine
+ * support file.
+*/
+static struct i2c_board_info i2c_device[] = {
+       { I2C_BOARD_INFO("tlv320aic3x", 0x1b), }
+};
+
 static int __init n810_soc_init(void)
 {
        int err;
@@ -345,6 +351,8 @@ static int __init n810_soc_init(void)
        if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
                return -ENODEV;
 
+       i2c_register_board_info(1, i2c_device, ARRAY_SIZE(i2c_device));
+
        n810_snd_device = platform_device_alloc("soc-audio", -1);
        if (!n810_snd_device)
                return -ENOMEM;
index 6a837ff..3341f49 100644 (file)
@@ -139,27 +139,67 @@ static const unsigned long omap34xx_mcbsp_port[][2] = {
 static const unsigned long omap34xx_mcbsp_port[][2] = {};
 #endif
 
+static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
+       int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
+       int samples;
+
+       /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
+       if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
+               samples = snd_pcm_lib_period_bytes(substream) >> 1;
+       else
+               samples = 1;
+
+       /* Configure McBSP internal buffer usage */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1);
+       else
+               omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1);
+}
+
 static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
+       int bus_id = mcbsp_data->bus_id;
        int err = 0;
 
-       if (cpu_is_omap343x() && mcbsp_data->bus_id == 1) {
+       if (!cpu_dai->active)
+               err = omap_mcbsp_request(bus_id);
+
+       if (cpu_is_omap343x()) {
+               int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id);
+               int max_period;
+
                /*
                 * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer.
                 * Set constraint for minimum buffer size to the same than FIFO
                 * size in order to avoid underruns in playback startup because
                 * HW is keeping the DMA request active until FIFO is filled.
                 */
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                       SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4096, UINT_MAX);
-       }
+               if (bus_id == 1)
+                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                       SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                       4096, UINT_MAX);
 
-       if (!cpu_dai->active)
-               err = omap_mcbsp_request(mcbsp_data->bus_id);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       max_period = omap_mcbsp_get_max_tx_threshold(bus_id);
+               else
+                       max_period = omap_mcbsp_get_max_rx_threshold(bus_id);
+
+               max_period++;
+               max_period <<= 1;
+
+               if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
+                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                               SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                               32, max_period);
+       }
 
        return err;
 }
@@ -215,7 +255,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
-       int wlen, channels, wpf;
+       int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
        unsigned long port;
        unsigned int format;
 
@@ -231,6 +271,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        } else if (cpu_is_omap343x()) {
                dma = omap24xx_dma_reqs[bus_id][substream->stream];
                port = omap34xx_mcbsp_port[bus_id][substream->stream];
+               omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold =
+                                               omap_mcbsp_set_threshold;
+               /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
+               if (omap_mcbsp_get_dma_op_mode(bus_id) ==
+                                               MCBSP_DMA_MODE_THRESHOLD)
+                       sync_mode = OMAP_DMA_SYNC_FRAME;
        } else {
                return -ENODEV;
        }
@@ -238,6 +284,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                substream->stream ? "Audio Capture" : "Audio Playback";
        omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
        omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
+       omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
        cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
 
        if (mcbsp_data->configured) {
@@ -321,11 +368,14 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        /* Generic McBSP register settings */
        regs->spcr2     |= XINTM(3) | FREE;
        regs->spcr1     |= RINTM(3);
-       regs->rcr2      |= RFIG;
-       regs->xcr2      |= XFIG;
+       /* RFIG and XFIG are not defined in 34xx */
+       if (!cpu_is_omap34xx()) {
+               regs->rcr2      |= RFIG;
+               regs->xcr2      |= XFIG;
+       }
        if (cpu_is_omap2430() || cpu_is_omap34xx()) {
-               regs->xccr = DXENDLY(1) | XDMAEN;
-               regs->rccr = RFULL_CYCLE | RDMAEN;
+               regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
+               regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
        }
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -462,6 +512,40 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
        return 0;
 }
 
+static int omap_mcbsp_dai_set_rcvr_src(struct omap_mcbsp_data *mcbsp_data,
+                                      int clk_id)
+{
+       int sel_bit, set = 0;
+       u16 reg = OMAP2_CONTROL_DEVCONF0;
+
+       if (cpu_class_is_omap1())
+               return -EINVAL; /* TODO: Can this be implemented for OMAP1? */
+       if (mcbsp_data->bus_id != 0)
+               return -EINVAL;
+
+       switch (clk_id) {
+       case OMAP_MCBSP_CLKR_SRC_CLKX:
+               set = 1;
+       case OMAP_MCBSP_CLKR_SRC_CLKR:
+               sel_bit = 3;
+               break;
+       case OMAP_MCBSP_FSR_SRC_FSX:
+               set = 1;
+       case OMAP_MCBSP_FSR_SRC_FSR:
+               sel_bit = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (set)
+               omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
+       else
+               omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
+
+       return 0;
+}
+
 static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
                                         int clk_id, unsigned int freq,
                                         int dir)
@@ -484,6 +568,13 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
        case OMAP_MCBSP_SYSCLK_CLKR_EXT:
                regs->pcr0      |= SCLKME;
                break;
+
+       case OMAP_MCBSP_CLKR_SRC_CLKR:
+       case OMAP_MCBSP_CLKR_SRC_CLKX:
+       case OMAP_MCBSP_FSR_SRC_FSR:
+       case OMAP_MCBSP_FSR_SRC_FSX:
+               err = omap_mcbsp_dai_set_rcvr_src(mcbsp_data, clk_id);
+               break;
        default:
                err = -ENODEV;
        }
index c8147aa..647d2f9 100644 (file)
@@ -32,6 +32,10 @@ enum omap_mcbsp_clksrg_clk {
        OMAP_MCBSP_SYSCLK_CLK,          /* Internal ICLK */
        OMAP_MCBSP_SYSCLK_CLKX_EXT,     /* External CLKX pin */
        OMAP_MCBSP_SYSCLK_CLKR_EXT,     /* External CLKR pin */
+       OMAP_MCBSP_CLKR_SRC_CLKR,       /* CLKR from CLKR pin */
+       OMAP_MCBSP_CLKR_SRC_CLKX,       /* CLKR from CLKX pin */
+       OMAP_MCBSP_FSR_SRC_FSR,         /* FSR from FSR pin */
+       OMAP_MCBSP_FSR_SRC_FSX,         /* FSR from FSX pin */
 };
 
 /* McBSP dividers */
index 12e14c0..5735945 100644 (file)
@@ -162,7 +162,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
         */
        dma_params.data_type                    = OMAP_DMA_DATA_TYPE_S16;
        dma_params.trigger                      = dma_data->dma_req;
-       dma_params.sync_mode                    = OMAP_DMA_SYNC_ELEMENT;
+       dma_params.sync_mode                    = dma_data->sync_mode;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                dma_params.src_amode            = OMAP_DMA_AMODE_POST_INC;
                dma_params.dst_amode            = OMAP_DMA_AMODE_CONSTANT;
@@ -195,6 +195,9 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
        else
                omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
 
+       omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
+       omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
+
        return 0;
 }
 
@@ -202,6 +205,7 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct omap_runtime_data *prtd = runtime->private_data;
+       struct omap_pcm_dma_data *dma_data = prtd->dma_data;
        unsigned long flags;
        int ret = 0;
 
@@ -211,6 +215,10 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                prtd->period_index = 0;
+               /* Configure McBSP internal buffer usage */
+               if (dma_data->set_threshold)
+                       dma_data->set_threshold(substream);
+
                omap_start_dma(prtd->dma_ch);
                break;
 
@@ -307,7 +315,7 @@ static struct snd_pcm_ops omap_pcm_ops = {
        .mmap           = omap_pcm_mmap,
 };
 
-static u64 omap_pcm_dmamask = DMA_BIT_MASK(32);
+static u64 omap_pcm_dmamask = DMA_BIT_MASK(64);
 
 static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
        int stream)
@@ -357,7 +365,7 @@ static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
        if (!card->dev->dma_mask)
                card->dev->dma_mask = &omap_pcm_dmamask;
        if (!card->dev->coherent_dma_mask)
-               card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+               card->dev->coherent_dma_mask = DMA_BIT_MASK(64);
 
        if (dai->playback.channels_min) {
                ret = omap_pcm_preallocate_dma_buffer(pcm,
index 8d9d269..38a821d 100644 (file)
@@ -29,6 +29,8 @@ struct omap_pcm_dma_data {
        char            *name;          /* stream identifier */
        int             dma_req;        /* DMA request line */
        unsigned long   port_addr;      /* transmit/receive register */
+       int             sync_mode;      /* DMA sync mode */
+       void (*set_threshold)(struct snd_pcm_substream *substream);
 };
 
 extern struct snd_soc_platform omap_soc_platform;
index f7e5b74..4a3f62d 100644 (file)
 #include "omap-pcm.h"
 #include "../codecs/twl4030.h"
 
-#define TWL4030_INTBR_PMBR1    0x0D
-#define EXTMUTE(value)         (value << 2)
+/* TWL4030 PMBR1 Register */
+#define TWL4030_INTBR_PMBR1            0x0D
+/* TWL4030 PMBR1 Register GPIO6 mux bit */
+#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2)
 
 static struct snd_soc_card snd_soc_sdp3430;
 
@@ -299,6 +301,7 @@ static struct platform_device *sdp3430_snd_device;
 static int __init sdp3430_soc_init(void)
 {
        int ret;
+       u8 pin_mux;
 
        if (!machine_is_omap_3430sdp()) {
                pr_debug("Not SDP3430!\n");
@@ -318,8 +321,12 @@ static int __init sdp3430_soc_init(void)
        *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
 
        /* Set TWL4030 GPIO6 as EXTMUTE signal */
-       twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, EXTMUTE(0x02),
-                                                       TWL4030_MODULE_INTBR);
+       twl4030_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
+                                               TWL4030_INTBR_PMBR1);
+       pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
+       pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
+       twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
+                                               TWL4030_INTBR_PMBR1);
 
        ret = platform_device_add(sdp3430_snd_device);
        if (ret)
index 9f7c61e..4c8d99a 100644 (file)
@@ -213,7 +213,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* set SSP audio pll clock */
-       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
        if (ret < 0)
                return ret;
 
index 5b9ed64..57f201c 100644 (file)
@@ -305,8 +305,8 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
 /*
  * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
  */
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+       int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct ssp_priv *priv = cpu_dai->private_data;
        struct ssp_device *ssp = priv->dev.ssp;
index 7330e5c..e9ae7b3 100644 (file)
@@ -251,8 +251,8 @@ static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev)
 
        for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) {
                pxa_ac97_dai[i].dev = &pdev->dev;
-               if (pdata && pdata->codec_pdata)
-                       pxa_ac97_dai[i].ac97_pdata = pdata->codec_pdata;
+               if (pdata && pdata->codec_pdata[0])
+                       pxa_ac97_dai[i].ac97_pdata = pdata->codec_pdata[0];
        }
 
        /* Punt most of the init to the SoC probe; we may need the machine
index 9a386b4..dd678ae 100644 (file)
@@ -74,7 +74,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int zylonite_wm9713_init(struct snd_soc_codec *codec)
 {
        if (clk_pout)
-               snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+               snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+                                   clk_get_rate(pout), 0);
 
        snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
                                  ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
-       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
        if (ret < 0)
                return ret;
 
index 808de5c..d7912f1 100644 (file)
@@ -1,6 +1,7 @@
 config SND_S3C24XX_SOC
        tristate "SoC Audio for the Samsung S3CXXXX chips"
-       depends on ARCH_S3C2410
+       depends on ARCH_S3C2410 || ARCH_S3C64XX
+       select S3C64XX_DMA if ARCH_S3C64XX
        help
          Say Y or M if you want to add support for codecs attached to
          the S3C24XX AC97 or I2S interfaces. You will also need to
@@ -55,6 +56,15 @@ config SND_S3C24XX_SOC_JIVE_WM8750
        help
          Sat Y if you want to add support for SoC audio on the Jive.
 
+config SND_S3C64XX_SOC_WM8580
+       tristate "SoC I2S Audio support for WM8580 on SMDK64XX"
+       depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410)
+       depends on BROKEN
+       select SND_SOC_WM8580
+       select SND_S3C64XX_SOC_I2S
+       help
+         Sat Y if you want to add support for SoC audio on the SMDK64XX.
+
 config SND_S3C24XX_SOC_SMDK2443_WM9710
        tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
        depends on SND_S3C24XX_SOC && MACH_SMDK2443
@@ -79,3 +89,22 @@ config SND_S3C24XX_SOC_S3C24XX_UDA134X
                select SND_S3C24XX_SOC_I2S
        select SND_SOC_L3
                select SND_SOC_UDA134X
+
+config SND_S3C24XX_SOC_SIMTEC
+       tristate
+       help
+         Internal node for common S3C24XX/Simtec suppor
+
+config SND_S3C24XX_SOC_SIMTEC_TLV320AIC23
+       tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
+       depends on SND_S3C24XX_SOC && ARCH_S3C2410
+       select SND_S3C24XX_SOC_I2S
+       select SND_SOC_TLV320AIC23
+       select SND_S3C24XX_SOC_SIMTEC
+
+config SND_S3C24XX_SOC_SIMTEC_HERMES
+       tristate "SoC I2S Audio support for Simtec Hermes board"
+       depends on SND_S3C24XX_SOC && ARCH_S3C2410
+       select SND_S3C24XX_SOC_I2S
+       select SND_SOC_TLV320AIC3X
+       select SND_S3C24XX_SOC_SIMTEC
index eb219b0..7790406 100644 (file)
@@ -20,6 +20,10 @@ snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
 snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
+snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
+snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
+snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
+snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -27,3 +31,8 @@ obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm87
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
 obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
+obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
+
index 0c52e36..6ddd1b3 100644 (file)
@@ -119,7 +119,7 @@ static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* codec PLL input is PCLK/4 */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
                iis_clkrate / 4, pll_out);
        if (ret < 0)
                return ret;
index 906709e..16009eb 100644 (file)
@@ -137,7 +137,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* codec PLL input is PCLK/4 */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
                iis_clkrate / 4, pll_out);
        if (ret < 0)
                return ret;
index 1a28317..11c45a3 100644 (file)
@@ -36,6 +36,7 @@
 #include <mach/dma.h>
 
 #include "s3c-i2s-v2.h"
+#include "s3c24xx-pcm.h"
 
 #undef S3C_IIS_V2_SUPPORTED
 
@@ -229,6 +230,8 @@ static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
        pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 }
 
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
 /*
  * Wait for the LR signal to allow synchronisation to the L/R clock
  * from the codec. May only be needed for slave mode.
@@ -236,19 +239,21 @@ static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
 static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
 {
        u32 iiscon;
-       unsigned long timeout = jiffies + msecs_to_jiffies(5);
+       unsigned long loops = msecs_to_loops(5);
 
        pr_debug("Entered %s\n", __func__);
 
-       while (1) {
+       while (--loops) {
                iiscon = readl(i2s->regs + S3C2412_IISCON);
                if (iiscon & S3C2412_IISCON_LRINDEX)
                        break;
 
-               if (timeout < jiffies) {
-                       printk(KERN_ERR "%s: timeout\n", __func__);
-                       return -ETIMEDOUT;
-               }
+               cpu_relax();
+       }
+
+       if (!loops) {
+               printk(KERN_ERR "%s: timeout\n", __func__);
+               return -ETIMEDOUT;
        }
 
        return 0;
@@ -307,12 +312,15 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_RIGHT_J:
+               iismod |= S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_MSB;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
+               iismod |= S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_LSB;
                break;
        case SND_SOC_DAIFMT_I2S:
+               iismod &= ~S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_IIS;
                break;
        default:
@@ -357,19 +365,19 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
 #endif
 
 #ifdef CONFIG_PLAT_S3C64XX
-       iismod &= ~0x606;
+       iismod &= ~(S3C64XX_IISMOD_BLC_MASK | S3C2412_IISMOD_BCLK_MASK);
        /* Sample size */
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
                /* 8 bit sample, 16fs BCLK */
-               iismod |= 0x2004;
+               iismod |= (S3C64XX_IISMOD_BLC_8BIT | S3C2412_IISMOD_BCLK_16FS);
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
                /* 16 bit sample, 32fs BCLK */
                break;
        case SNDRV_PCM_FORMAT_S24_LE:
                /* 24 bit sample, 48fs BCLK */
-               iismod |= 0x4002;
+               iismod |= (S3C64XX_IISMOD_BLC_24BIT | S3C2412_IISMOD_BCLK_48FS);
                break;
        }
 #endif
@@ -387,6 +395,8 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
        int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
        unsigned long irqs;
        int ret = 0;
+       int channel = ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->channel;
 
        pr_debug("Entered %s\n", __func__);
 
@@ -416,6 +426,14 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
                        s3c2412_snd_txctrl(i2s, 1);
 
                local_irq_restore(irqs);
+
+               /*
+                * Load the next buffer to DMA to meet the reqirement
+                * of the auto reload mechanism of S3C24XX.
+                * This call won't bother S3C64XX.
+                */
+               s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
@@ -452,6 +470,31 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 
        switch (div_id) {
        case S3C_I2SV2_DIV_BCLK:
+               if (div > 3) {
+                       /* convert value to bit field */
+
+                       switch (div) {
+                       case 16:
+                               div = S3C2412_IISMOD_BCLK_16FS;
+                               break;
+
+                       case 32:
+                               div = S3C2412_IISMOD_BCLK_32FS;
+                               break;
+
+                       case 24:
+                               div = S3C2412_IISMOD_BCLK_24FS;
+                               break;
+
+                       case 48:
+                               div = S3C2412_IISMOD_BCLK_48FS;
+                               break;
+
+                       default:
+                               return -EINVAL;
+                       }
+               }
+
                reg = readl(i2s->regs + S3C2412_IISMOD);
                reg &= ~S3C2412_IISMOD_BCLK_MASK;
                writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -611,7 +654,7 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
        }
 
        i2s->iis_pclk = clk_get(dev, "iis");
-       if (i2s->iis_pclk == NULL) {
+       if (IS_ERR(i2s->iis_pclk)) {
                dev_err(dev, "failed to get iis_clock\n");
                iounmap(i2s->regs);
                return -ENOENT;
index bf16f20..fc1beb0 100644 (file)
@@ -290,6 +290,9 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
                                struct snd_soc_dai *dai)
 {
        u32 ac_glbctrl;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int channel = ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->channel;
 
        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
        switch (cmd) {
@@ -312,6 +315,8 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
        }
        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 
+       s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
        return 0;
 }
 
@@ -334,6 +339,9 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
                                    int cmd, struct snd_soc_dai *dai)
 {
        u32 ac_glbctrl;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int channel = ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->channel;
 
        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
        switch (cmd) {
@@ -349,6 +357,8 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
        }
        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 
+       s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
        return 0;
 }
 
index 556e35f..40e2c47 100644 (file)
@@ -279,6 +279,9 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
                               struct snd_soc_dai *dai)
 {
        int ret = 0;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int channel = ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->channel;
 
        pr_debug("Entered %s\n", __func__);
 
@@ -296,6 +299,8 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
                        s3c24xx_snd_rxctrl(1);
                else
                        s3c24xx_snd_txctrl(1);
+
+               s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
index eecfa5e..5cbbdc8 100644 (file)
@@ -255,7 +255,6 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                prtd->state |= ST_RUNNING;
                s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
-               s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STARTED);
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
@@ -318,6 +317,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
 
        pr_debug("Entered %s\n", __func__);
 
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
        snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
 
        prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/s3c24xx/s3c24xx_simtec.c
new file mode 100644 (file)
index 0000000..1966e0d
--- /dev/null
@@ -0,0 +1,394 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+static struct s3c24xx_audio_simtec_pdata *pdata;
+static struct clk *xtal_clk;
+
+static int spk_gain;
+static int spk_unmute;
+
+/**
+ * speaker_gain_get - read the speaker gain setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be updated.
+ *
+ * Read the value for the AMP gain control.
+ */
+static int speaker_gain_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = spk_gain;
+       return 0;
+}
+
+/**
+ * speaker_gain_set - set the value of the speaker amp gain
+ * @value: The value to write.
+ */
+static void speaker_gain_set(int value)
+{
+       gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
+       gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
+}
+
+/**
+ * speaker_gain_put - set the speaker gain setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be set.
+ *
+ * Set the value of the speaker gain from the specified
+ * @ucontrol setting.
+ *
+ * Note, if the speaker amp is muted, then we do not set a gain value
+ * as at-least one of the ICs that is fitted will try and power up even
+ * if the main control is set to off.
+ */
+static int speaker_gain_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       int value = ucontrol->value.integer.value[0];
+
+       spk_gain = value;
+
+       if (!spk_unmute)
+               speaker_gain_set(value);
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new amp_gain_controls[] = {
+       SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
+                      speaker_gain_get, speaker_gain_put),
+};
+
+/**
+ * spk_unmute_state - set the unmute state of the speaker
+ * @to: zero to unmute, non-zero to ununmute.
+ */
+static void spk_unmute_state(int to)
+{
+       pr_debug("%s: to=%d\n", __func__, to);
+
+       spk_unmute = to;
+       gpio_set_value(pdata->amp_gpio, to);
+
+       /* if we're umuting, also re-set the gain */
+       if (to && pdata->amp_gain[0] > 0)
+               speaker_gain_set(spk_gain);
+}
+
+/**
+ * speaker_unmute_get - read the speaker unmute setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be updated.
+ *
+ * Read the value for the AMP gain control.
+ */
+static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = spk_unmute;
+       return 0;
+}
+
+/**
+ * speaker_unmute_put - set the speaker unmute setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be set.
+ *
+ * Set the value of the speaker gain from the specified
+ * @ucontrol setting.
+ */
+static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       spk_unmute_state(ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+/* This is added as a manual control as the speaker amps create clicks
+ * when their power state is changed, which are far more noticeable than
+ * anything produced by the CODEC itself.
+ */
+static const struct snd_kcontrol_new amp_unmute_controls[] = {
+       SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
+                      speaker_unmute_get, speaker_unmute_put),
+};
+
+void simtec_audio_init(struct snd_soc_codec *codec)
+{
+       if (pdata->amp_gpio > 0) {
+               pr_debug("%s: adding amp routes\n", __func__);
+
+               snd_soc_add_controls(codec, amp_unmute_controls,
+                                    ARRAY_SIZE(amp_unmute_controls));
+       }
+
+       if (pdata->amp_gain[0] > 0) {
+               pr_debug("%s: adding amp controls\n", __func__);
+               snd_soc_add_controls(codec, amp_gain_controls,
+                                    ARRAY_SIZE(amp_gain_controls));
+       }
+}
+EXPORT_SYMBOL_GPL(simtec_audio_init);
+
+#define CODEC_CLOCK 12000000
+
+/**
+ * simtec_hw_params - update hardware parameters
+ * @substream: The audio substream instance.
+ * @params: The parameters requested.
+ *
+ * Update the codec data routing and configuration  settings
+ * from the supplied data.
+ */
+static int simtec_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set the CODEC as the bus clock master, I2S */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret) {
+               pr_err("%s: failed set cpu dai format\n", __func__);
+               return ret;
+       }
+
+       /* Set the CODEC as the bus clock master */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret) {
+               pr_err("%s: failed set codec dai format\n", __func__);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+                                    CODEC_CLOCK, SND_SOC_CLOCK_IN);
+       if (ret) {
+               pr_err( "%s: failed setting codec sysclk\n", __func__);
+               return ret;
+       }
+
+       if (pdata->use_mpllin) {
+               ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
+                                            0, SND_SOC_CLOCK_OUT);
+
+               if (ret) {
+                       pr_err("%s: failed to set MPLLin as clksrc\n",
+                              __func__);
+                       return ret;
+               }
+       }
+
+       if (pdata->output_cdclk) {
+               int cdclk_scale;
+
+               cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
+               cdclk_scale--;
+
+               ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+                                            cdclk_scale);
+       }
+
+       return 0;
+}
+
+static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
+{
+       /* call any board supplied startup code, this currently only
+        * covers the bast/vr1000 which have a CPLD in the way of the
+        * LRCLK */
+       if (pd->startup)
+               pd->startup();
+
+       return 0;
+}
+
+static struct snd_soc_ops simtec_snd_ops = {
+       .hw_params      = simtec_hw_params,
+};
+
+/**
+ * attach_gpio_amp - get and configure the necessary gpios
+ * @dev: The device we're probing.
+ * @pd: The platform data supplied by the board.
+ *
+ * If there is a GPIO based amplifier attached to the board, claim
+ * the necessary GPIO lines for it, and set default values.
+ */
+static int attach_gpio_amp(struct device *dev,
+                          struct s3c24xx_audio_simtec_pdata *pd)
+{
+       int ret;
+
+       /* attach gpio amp gain (if any) */
+       if (pdata->amp_gain[0] > 0) {
+               ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
+               if (ret) {
+                       dev_err(dev, "cannot get amp gpio gain0\n");
+                       return ret;
+               }
+
+               ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
+               if (ret) {
+                       dev_err(dev, "cannot get amp gpio gain1\n");
+                       gpio_free(pdata->amp_gain[0]);
+                       return ret;
+               }
+
+               gpio_direction_output(pd->amp_gain[0], 0);
+               gpio_direction_output(pd->amp_gain[1], 0);
+       }
+
+       /* note, curently we assume GPA0 isn't valid amp */
+       if (pdata->amp_gpio > 0) {
+               ret = gpio_request(pd->amp_gpio, "gpio-amp");
+               if (ret) {
+                       dev_err(dev, "cannot get amp gpio %d (%d)\n",
+                               pd->amp_gpio, ret);
+                       goto err_amp;
+               }
+
+               /* set the amp off at startup */
+               spk_unmute_state(0);
+       }
+
+       return 0;
+
+err_amp:
+       if (pd->amp_gain[0] > 0) {
+               gpio_free(pd->amp_gain[0]);
+               gpio_free(pd->amp_gain[1]);
+       }
+
+       return ret;
+}
+
+static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
+{
+       if (pd->amp_gain[0] > 0) {
+               gpio_free(pd->amp_gain[0]);
+               gpio_free(pd->amp_gain[1]);
+       }
+
+       if (pd->amp_gpio > 0)
+               gpio_free(pd->amp_gpio);
+}
+
+#ifdef CONFIG_PM
+int simtec_audio_resume(struct device *dev)
+{
+       simtec_call_startup(pdata);
+       return 0;
+}
+
+struct dev_pm_ops simtec_audio_pmops = {
+       .resume = simtec_audio_resume,
+};
+EXPORT_SYMBOL_GPL(simtec_audio_pmops);
+#endif
+
+int __devinit simtec_audio_core_probe(struct platform_device *pdev,
+                                     struct snd_soc_device *socdev)
+{
+       struct platform_device *snd_dev;
+       int ret;
+
+       socdev->card->dai_link->ops = &simtec_snd_ops;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data supplied\n");
+               return -EINVAL;
+       }
+
+       simtec_call_startup(pdata);
+
+       xtal_clk = clk_get(&pdev->dev, "xtal");
+       if (IS_ERR(xtal_clk)) {
+               dev_err(&pdev->dev, "could not get clkout0\n");
+               return -EINVAL;
+       }
+
+       dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
+
+       ret = attach_gpio_amp(&pdev->dev, pdata);
+       if (ret)
+               goto err_clk;
+
+       snd_dev = platform_device_alloc("soc-audio", -1);
+       if (!snd_dev) {
+               dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
+               ret = -ENOMEM;
+               goto err_gpio;
+       }
+
+       platform_set_drvdata(snd_dev, socdev);
+       socdev->dev = &snd_dev->dev;
+
+       ret = platform_device_add(snd_dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to add soc-audio dev\n");
+               goto err_pdev;
+       }
+
+       platform_set_drvdata(pdev, snd_dev);
+       return 0;
+
+err_pdev:
+       platform_device_put(snd_dev);
+
+err_gpio:
+       detach_gpio_amp(pdata);
+
+err_clk:
+       clk_put(xtal_clk);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
+
+int __devexit simtec_audio_remove(struct platform_device *pdev)
+{
+       struct platform_device *snd_dev = platform_get_drvdata(pdev);
+
+       platform_device_unregister(snd_dev);
+
+       detach_gpio_amp(pdata);
+       clk_put(xtal_clk);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(simtec_audio_remove);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.h b/sound/soc/s3c24xx/s3c24xx_simtec.h
new file mode 100644 (file)
index 0000000..2714203
--- /dev/null
@@ -0,0 +1,22 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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.
+*/
+
+extern void simtec_audio_init(struct snd_soc_codec *codec);
+
+extern int simtec_audio_core_probe(struct platform_device *pdev,
+                                  struct snd_soc_device *socdev);
+
+extern int simtec_audio_remove(struct platform_device *pdev);
+
+#ifdef CONFIG_PM
+extern struct dev_pm_ops simtec_audio_pmops;
+#define simtec_audio_pm &simtec_audio_pmops
+#else
+#define simtec_audio_pm NULL
+#endif
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
new file mode 100644 (file)
index 0000000..8346bd9
--- /dev/null
@@ -0,0 +1,153 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/clk.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+#include "../codecs/tlv320aic3x.h"
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("GSM Out", NULL),
+       SND_SOC_DAPM_LINE("GSM In", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+       SND_SOC_DAPM_LINE("Line Out", NULL),
+       SND_SOC_DAPM_LINE("ZV", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route base_map[] = {
+       /* Headphone connected to HP{L,R}OUT and HP{L,R}COM */
+
+       { "Headphone Jack", NULL, "HPLOUT" },
+       { "Headphone Jack", NULL, "HPLCOM" },
+       { "Headphone Jack", NULL, "HPROUT" },
+       { "Headphone Jack", NULL, "HPRCOM" },
+
+       /* ZV connected to Line1 */
+
+       { "LINE1L", NULL, "ZV" },
+       { "LINE1R", NULL, "ZV" },
+
+       /* Line In connected to Line2 */
+
+       { "LINE2L", NULL, "Line In" },
+       { "LINE2R", NULL, "Line In" },
+
+       /* Microphone connected to MIC3R and MIC_BIAS */
+
+       { "MIC3L", NULL, "Mic Jack" },
+
+       /* GSM connected to MONO_LOUT and MIC3L (in) */
+
+       { "GSM Out", NULL, "MONO_LOUT" },
+       { "MIC3L", NULL, "GSM In" },
+
+       /* Speaker is connected to LINEOUT{LN,LP,RN,RP}, however we are
+        * not using the DAPM to power it up and down as there it makes
+        * a click when powering up. */
+};
+
+/**
+ * simtec_hermes_init - initialise and add controls
+ * @codec; The codec instance to attach to.
+ *
+ * Attach our controls and configure the necessary codec
+ * mappings for our sound card instance.
+*/
+static int simtec_hermes_init(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, dapm_widgets,
+                                 ARRAY_SIZE(dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map));
+
+       snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       snd_soc_dapm_enable_pin(codec, "Line In");
+       snd_soc_dapm_enable_pin(codec, "Line Out");
+       snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+       simtec_audio_init(codec);
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct aic3x_setup_data codec_setup = {
+};
+
+static struct snd_soc_dai_link simtec_dai_aic33 = {
+       .name           = "tlv320aic33",
+       .stream_name    = "TLV320AIC33",
+       .cpu_dai        = &s3c24xx_i2s_dai,
+       .codec_dai      = &aic3x_dai,
+       .init           = simtec_hermes_init,
+};
+
+/* simtec audio machine driver */
+static struct snd_soc_card snd_soc_machine_simtec_aic33 = {
+       .name           = "Simtec-Hermes",
+       .platform       = &s3c24xx_soc_platform,
+       .dai_link       = &simtec_dai_aic33,
+       .num_links      = 1,
+};
+
+/* simtec audio subsystem */
+static struct snd_soc_device simtec_snd_devdata_aic33 = {
+       .card           = &snd_soc_machine_simtec_aic33,
+       .codec_dev      = &soc_codec_dev_aic3x,
+       .codec_data     = &codec_setup,
+};
+
+static int __devinit simtec_audio_hermes_probe(struct platform_device *pd)
+{
+       dev_info(&pd->dev, "probing....\n");
+       return simtec_audio_core_probe(pd, &simtec_snd_devdata_aic33);
+}
+
+static struct platform_driver simtec_audio_hermes_platdrv = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "s3c24xx-simtec-hermes-snd",
+               .pm     = simtec_audio_pm,
+       },
+       .probe  = simtec_audio_hermes_probe,
+       .remove = __devexit_p(simtec_audio_remove),
+};
+
+MODULE_ALIAS("platform:s3c24xx-simtec-hermes-snd");
+
+static int __init simtec_hermes_modinit(void)
+{
+       return platform_driver_register(&simtec_audio_hermes_platdrv);
+}
+
+static void __exit simtec_hermes_modexit(void)
+{
+       platform_driver_unregister(&simtec_audio_hermes_platdrv);
+}
+
+module_init(simtec_hermes_modinit);
+module_exit(simtec_hermes_modexit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
new file mode 100644 (file)
index 0000000..25797e0
--- /dev/null
@@ -0,0 +1,137 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/clk.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+#include "../codecs/tlv320aic23.h"
+
+/* supported machines:
+ *
+ * Machine     Connections             AMP
+ * -------     -----------             ---
+ * BAST                MIC, HPOUT, LOUT, LIN   TPA2001D1 (HPOUTL,R) (gain hardwired)
+ * VR1000      HPOUT, LIN              None
+ * VR2000      LIN, LOUT, MIC, HP      LM4871 (HPOUTL,R)
+ * DePicture   LIN, LOUT, MIC, HP      LM4871 (HPOUTL,R)
+ * Anubis      LIN, LOUT, MIC, HP      TPA2001D1 (HPOUTL,R)
+ */
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+       SND_SOC_DAPM_LINE("Line Out", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route base_map[] = {
+       { "Headphone Jack", NULL, "LHPOUT"},
+       { "Headphone Jack", NULL, "RHPOUT"},
+
+       { "Line Out", NULL, "LOUT" },
+       { "Line Out", NULL, "ROUT" },
+
+       { "LLINEIN", NULL, "Line In"},
+       { "RLINEIN", NULL, "Line In"},
+
+       { "MICIN", NULL, "Mic Jack"},
+};
+
+/**
+ * simtec_tlv320aic23_init - initialise and add controls
+ * @codec; The codec instance to attach to.
+ *
+ * Attach our controls and configure the necessary codec
+ * mappings for our sound card instance.
+*/
+static int simtec_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, dapm_widgets,
+                                 ARRAY_SIZE(dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map));
+
+       snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       snd_soc_dapm_enable_pin(codec, "Line In");
+       snd_soc_dapm_enable_pin(codec, "Line Out");
+       snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+       simtec_audio_init(codec);
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link simtec_dai_aic23 = {
+       .name           = "tlv320aic23",
+       .stream_name    = "TLV320AIC23",
+       .cpu_dai        = &s3c24xx_i2s_dai,
+       .codec_dai      = &tlv320aic23_dai,
+       .init           = simtec_tlv320aic23_init,
+};
+
+/* simtec audio machine driver */
+static struct snd_soc_card snd_soc_machine_simtec_aic23 = {
+       .name           = "Simtec",
+       .platform       = &s3c24xx_soc_platform,
+       .dai_link       = &simtec_dai_aic23,
+       .num_links      = 1,
+};
+
+/* simtec audio subsystem */
+static struct snd_soc_device simtec_snd_devdata_aic23 = {
+       .card           = &snd_soc_machine_simtec_aic23,
+       .codec_dev      = &soc_codec_dev_tlv320aic23,
+};
+
+static int __devinit simtec_audio_tlv320aic23_probe(struct platform_device *pd)
+{
+       return simtec_audio_core_probe(pd, &simtec_snd_devdata_aic23);
+}
+
+static struct platform_driver simtec_audio_tlv320aic23_platdrv = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "s3c24xx-simtec-tlv320aic23",
+               .pm     = simtec_audio_pm,
+       },
+       .probe  = simtec_audio_tlv320aic23_probe,
+       .remove = __devexit_p(simtec_audio_remove),
+};
+
+MODULE_ALIAS("platform:s3c24xx-simtec-tlv320aic23");
+
+static int __init simtec_tlv320aic23_modinit(void)
+{
+       return platform_driver_register(&simtec_audio_tlv320aic23_platdrv);
+}
+
+static void __exit simtec_tlv320aic23_modexit(void)
+{
+       platform_driver_unregister(&simtec_audio_tlv320aic23_platdrv);
+}
+
+module_init(simtec_tlv320aic23_modinit);
+module_exit(simtec_tlv320aic23_modexit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
+MODULE_LICENSE("GPL");
index 3c06c40..43fb253 100644 (file)
@@ -99,6 +99,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
                iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
                break;
 
+       case S3C64XX_CLKSRC_CDCLK:
+               switch (dir) {
+               case SND_SOC_CLOCK_IN:
+                       iismod |= S3C64XX_IISMOD_CDCLKCON;
+                       break;
+               case SND_SOC_CLOCK_OUT:
+                       iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
        default:
                return -EINVAL;
        }
@@ -111,8 +124,12 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
        struct s3c_i2sv2_info *i2s = to_info(dai);
+       u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
 
-       return i2s->iis_cclk;
+       if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+               return i2s->iis_cclk;
+       else
+               return i2s->iis_pclk;
 }
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
index 02148ce..abe7253 100644 (file)
@@ -25,6 +25,7 @@ struct clk;
 
 #define S3C64XX_CLKSRC_PCLK    (0)
 #define S3C64XX_CLKSRC_MUX     (1)
+#define S3C64XX_CLKSRC_CDCLK    (2)
 
 extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644 (file)
index 0000000..482aaf1
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *  smdk64xx_wm8580.c
+ *
+ *  Copyright (c) 2009 Samsung Electronics Co. Ltd
+ *  Author: Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8580.h"
+#include "s3c24xx-pcm.h"
+#include "s3c64xx-i2s.h"
+
+#define S3C64XX_I2S_V4 2
+
+/* SMDK64XX has a 12MHZ crystal attached to WM8580 */
+#define SMDK64XX_WM8580_FREQ 12000000
+
+static int smdk64xx_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int pll_out;
+       int bfs, rfs, ret;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_U8:
+       case SNDRV_PCM_FORMAT_S8:
+               bfs = 16;
+               break;
+       case SNDRV_PCM_FORMAT_U16_LE:
+       case SNDRV_PCM_FORMAT_S16_LE:
+               bfs = 32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
+        * This criterion can't be met if we request PLL output
+        * as {8000x256, 64000x256, 11025x256}Hz.
+        * As a wayout, we rather change rfs to a minimum value that
+        * results in (params_rate(params) * rfs), and itself, acceptable
+        * to both - the CODEC and the CPU.
+        */
+       switch (params_rate(params)) {
+       case 16000:
+       case 22050:
+       case 32000:
+       case 44100:
+       case 48000:
+       case 88200:
+       case 96000:
+               rfs = 256;
+               break;
+       case 64000:
+               rfs = 384;
+               break;
+       case 8000:
+       case 11025:
+               rfs = 512;
+               break;
+       default:
+               return -EINVAL;
+       }
+       pll_out = params_rate(params) * rfs;
+
+       /* Set the Codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+                                        | SND_SOC_DAIFMT_NB_NF
+                                        | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       /* Set the AP DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+                                        | SND_SOC_DAIFMT_NB_NF
+                                        | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK,
+                                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* We use PCLK for basic ops in SoC-Slave mode */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK,
+                                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* Set WM8580 to drive MCLK from it's PLLA */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+                                       WM8580_CLKSRC_PLLA);
+       if (ret < 0)
+               return ret;
+
+       /* Explicitly set WM8580-DAC to source from MCLK */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+                                       WM8580_CLKSRC_MCLK);
+       if (ret < 0)
+               return ret;
+
+       /* Assuming the CODEC driver evaluates it's rfs too from this call */
+       ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA,
+                                       SMDK64XX_WM8580_FREQ, pll_out);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * SMDK64XX WM8580 DAI operations.
+ */
+static struct snd_soc_ops smdk64xx_ops = {
+       .hw_params = smdk64xx_hw_params,
+};
+
+/* SMDK64xx Playback widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
+       SND_SOC_DAPM_HP("Front-L/R", NULL),
+       SND_SOC_DAPM_HP("Center/Sub", NULL),
+       SND_SOC_DAPM_HP("Rear-L/R", NULL),
+};
+
+/* SMDK64xx Capture widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
+       SND_SOC_DAPM_MIC("MicIn", NULL),
+       SND_SOC_DAPM_LINE("LineIn", NULL),
+};
+
+/* SMDK-PAIFTX connections */
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+       /* MicIn feeds AINL */
+       {"AINL", NULL, "MicIn"},
+
+       /* LineIn feeds AINL/R */
+       {"AINL", NULL, "LineIn"},
+       {"AINR", NULL, "LineIn"},
+};
+
+/* SMDK-PAIFRX connections */
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+       /* Front Left/Right are fed VOUT1L/R */
+       {"Front-L/R", NULL, "VOUT1L"},
+       {"Front-L/R", NULL, "VOUT1R"},
+
+       /* Center/Sub are fed VOUT2L/R */
+       {"Center/Sub", NULL, "VOUT2L"},
+       {"Center/Sub", NULL, "VOUT2R"},
+
+       /* Rear Left/Right are fed VOUT3L/R */
+       {"Rear-L/R", NULL, "VOUT3L"},
+       {"Rear-L/R", NULL, "VOUT3R"},
+};
+
+static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec)
+{
+       /* Add smdk64xx specific Capture widgets */
+       snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt,
+                                 ARRAY_SIZE(wm8580_dapm_widgets_cpt));
+
+       /* Set up PAIFTX audio path */
+       snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx));
+
+       /* All enabled by default */
+       snd_soc_dapm_enable_pin(codec, "MicIn");
+       snd_soc_dapm_enable_pin(codec, "LineIn");
+
+       /* signal a DAPM event */
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec)
+{
+       /* Add smdk64xx specific Playback widgets */
+       snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk,
+                                 ARRAY_SIZE(wm8580_dapm_widgets_pbk));
+
+       /* Set up PAIFRX audio path */
+       snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx));
+
+       /* All enabled by default */
+       snd_soc_dapm_enable_pin(codec, "Front-L/R");
+       snd_soc_dapm_enable_pin(codec, "Center/Sub");
+       snd_soc_dapm_enable_pin(codec, "Rear-L/R");
+
+       /* signal a DAPM event */
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link smdk64xx_dai[] = {
+{ /* Primary Playback i/f */
+       .name = "WM8580 PAIF RX",
+       .stream_name = "Playback",
+       .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+       .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX],
+       .init = smdk64xx_wm8580_init_paifrx,
+       .ops = &smdk64xx_ops,
+},
+{ /* Primary Capture i/f */
+       .name = "WM8580 PAIF TX",
+       .stream_name = "Capture",
+       .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+       .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX],
+       .init = smdk64xx_wm8580_init_paiftx,
+       .ops = &smdk64xx_ops,
+},
+};
+
+static struct snd_soc_card smdk64xx = {
+       .name = "smdk64xx",
+       .platform = &s3c24xx_soc_platform,
+       .dai_link = smdk64xx_dai,
+       .num_links = ARRAY_SIZE(smdk64xx_dai),
+};
+
+static struct snd_soc_device smdk64xx_snd_devdata = {
+       .card = &smdk64xx,
+       .codec_dev = &soc_codec_dev_wm8580,
+};
+
+static struct platform_device *smdk64xx_snd_device;
+
+static int __init smdk64xx_audio_init(void)
+{
+       int ret;
+
+       smdk64xx_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!smdk64xx_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata);
+       smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev;
+       ret = platform_device_add(smdk64xx_snd_device);
+
+       if (ret)
+               platform_device_put(smdk64xx_snd_device);
+
+       return ret;
+}
+module_init(smdk64xx_audio_init);
+
+MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580");
+MODULE_LICENSE("GPL");
index b5f95f9..c1b40ac 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/i2c.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -189,8 +190,6 @@ static struct snd_soc_card snd_soc_card_s6105 = {
 
 /* s6105 audio private data */
 static struct aic3x_setup_data s6105_aic3x_setup = {
-       .i2c_bus = 0,
-       .i2c_address = 0x18,
 };
 
 /* s6105 audio subsystem */
@@ -211,10 +210,19 @@ static struct s6000_snd_platform_data __initdata s6105_snd_data = {
 
 static struct platform_device *s6105_snd_device;
 
+/* temporary i2c device creation until this can be moved into the machine
+ * support file.
+*/
+static struct i2c_board_info i2c_device[] = {
+       { I2C_BOARD_INFO("tlv320aic33", 0x18), }
+};
+
 static int __init s6105_init(void)
 {
        int ret;
 
+       i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device));
+
        s6105_snd_device = platform_device_alloc("soc-audio", -1);
        if (!s6105_snd_device)
                return -ENOMEM;
index 54bd604..9154b43 100644 (file)
@@ -20,7 +20,12 @@ config SND_SOC_SH4_HAC
 config SND_SOC_SH4_SSI
        tristate
 
-
+config SND_SOC_SH4_FSI
+       tristate "SH4 FSI support"
+       depends on CPU_SUBTYPE_SH7724
+        select SH_DMA
+       help
+         This option enables FSI sound support
 
 ##
 ## Boards
@@ -35,4 +40,12 @@ config SND_SH7760_AC97
          This option enables generic sound support for the first
          AC97 unit of the SH7760.
 
+config SND_FSI_AK4642
+       bool "FSI-AK4642 sound support"
+       depends on SND_SOC_SH4_FSI
+       select SND_SOC_AK4642
+       help
+         This option enables generic sound support for the
+         FSI - AK4642 unit
+
 endmenu
index a8e8ab8..a699787 100644 (file)
@@ -5,10 +5,14 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760)      += snd-soc-dma-sh7760.o
 ## audio units found on some SH-4
 snd-soc-hac-objs       := hac.o
 snd-soc-ssi-objs       := ssi.o
+snd-soc-fsi-objs       := fsi.o
 obj-$(CONFIG_SND_SOC_SH4_HAC)  += snd-soc-hac.o
 obj-$(CONFIG_SND_SOC_SH4_SSI)  += snd-soc-ssi.o
+obj-$(CONFIG_SND_SOC_SH4_FSI)  += snd-soc-fsi.o
 
 ## boards
 snd-soc-sh7760-ac97-objs       := sh7760-ac97.o
+snd-soc-fsi-ak4642-objs                := fsi-ak4642.o
 
 obj-$(CONFIG_SND_SH7760_AC97)  += snd-soc-sh7760-ac97.o
+obj-$(CONFIG_SND_FSI_AK4642)   += snd-soc-fsi-ak4642.o
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
new file mode 100644 (file)
index 0000000..c7af097
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * FSI-AK464x sound support for ms7724se
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <sound/sh_fsi.h>
+#include <../sound/soc/codecs/ak4642.h>
+
+static struct snd_soc_dai_link fsi_dai_link = {
+       .name           = "AK4642",
+       .stream_name    = "AK4642",
+       .cpu_dai        = &fsi_soc_dai[0], /* fsi */
+       .codec_dai      = &ak4642_dai,
+       .ops            = NULL,
+};
+
+static struct snd_soc_card fsi_soc_card  = {
+       .name           = "FSI",
+       .platform       = &fsi_soc_platform,
+       .dai_link       = &fsi_dai_link,
+       .num_links      = 1,
+};
+
+static struct snd_soc_device fsi_snd_devdata = {
+       .card           = &fsi_soc_card,
+       .codec_dev      = &soc_codec_dev_ak4642,
+};
+
+#define AK4642_BUS 0
+#define AK4642_ADR 0x12
+static int ak4642_add_i2c_device(void)
+{
+       struct i2c_board_info info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       info.addr = AK4642_ADR;
+       strlcpy(info.type, "ak4642", I2C_NAME_SIZE);
+
+       adapter = i2c_get_adapter(AK4642_BUS);
+       if (!adapter) {
+               printk(KERN_DEBUG "can't get i2c adapter\n");
+               return -ENODEV;
+       }
+
+       client = i2c_new_device(adapter, &info);
+       i2c_put_adapter(adapter);
+       if (!client) {
+               printk(KERN_DEBUG "can't add i2c device\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct platform_device *fsi_snd_device;
+
+static int __init fsi_ak4642_init(void)
+{
+       int ret = -ENOMEM;
+
+       ak4642_add_i2c_device();
+
+       fsi_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!fsi_snd_device)
+               goto out;
+
+       platform_set_drvdata(fsi_snd_device,
+                            &fsi_snd_devdata);
+       fsi_snd_devdata.dev = &fsi_snd_device->dev;
+       ret = platform_device_add(fsi_snd_device);
+
+       if (ret)
+               platform_device_put(fsi_snd_device);
+
+out:
+       return ret;
+}
+
+static void __exit fsi_ak4642_exit(void)
+{
+       platform_device_unregister(fsi_snd_device);
+}
+
+module_init(fsi_ak4642_init);
+module_exit(fsi_ak4642_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
new file mode 100644 (file)
index 0000000..4412324
--- /dev/null
@@ -0,0 +1,1004 @@
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ssi.c
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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/platform_device.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/sh_fsi.h>
+#include <asm/atomic.h>
+#include <asm/dma.h>
+#include <asm/dma-sh.h>
+
+#define DO_FMT         0x0000
+#define DOFF_CTL       0x0004
+#define DOFF_ST                0x0008
+#define DI_FMT         0x000C
+#define DIFF_CTL       0x0010
+#define DIFF_ST                0x0014
+#define CKG1           0x0018
+#define CKG2           0x001C
+#define DIDT           0x0020
+#define DODT           0x0024
+#define MUTE_ST                0x0028
+#define REG_END                MUTE_ST
+
+#define INT_ST         0x0200
+#define IEMSK          0x0204
+#define IMSK           0x0208
+#define MUTE           0x020C
+#define CLK_RST                0x0210
+#define SOFT_RST       0x0214
+#define MREG_START     INT_ST
+#define MREG_END       SOFT_RST
+
+/* DO_FMT */
+/* DI_FMT */
+#define CR_FMT(param) ((param) << 4)
+# define CR_MONO       0x0
+# define CR_MONO_D     0x1
+# define CR_PCM                0x2
+# define CR_I2S                0x3
+# define CR_TDM                0x4
+# define CR_TDM_D      0x5
+
+/* DOFF_CTL */
+/* DIFF_CTL */
+#define IRQ_HALF       0x00100000
+#define FIFO_CLR       0x00000001
+
+/* DOFF_ST */
+#define ERR_OVER       0x00000010
+#define ERR_UNDER      0x00000001
+
+/* CLK_RST */
+#define B_CLK          0x00000010
+#define A_CLK          0x00000001
+
+/* INT_ST */
+#define INT_B_IN       (1 << 12)
+#define INT_B_OUT      (1 << 8)
+#define INT_A_IN       (1 << 4)
+#define INT_A_OUT      (1 << 0)
+
+#define FSI_RATES SNDRV_PCM_RATE_8000_96000
+
+#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/************************************************************************
+
+
+               struct
+
+
+************************************************************************/
+struct fsi_priv {
+       void __iomem *base;
+       struct snd_pcm_substream *substream;
+
+       int fifo_max;
+       int chan;
+       int dma_chan;
+
+       int byte_offset;
+       int period_len;
+       int buffer_len;
+       int periods;
+};
+
+struct fsi_master {
+       void __iomem *base;
+       int irq;
+       struct clk *clk;
+       struct fsi_priv fsia;
+       struct fsi_priv fsib;
+       struct sh_fsi_platform_info *info;
+};
+
+static struct fsi_master *master;
+
+/************************************************************************
+
+
+               basic read write function
+
+
+************************************************************************/
+static int __fsi_reg_write(u32 reg, u32 data)
+{
+       /* valid data area is 24bit */
+       data &= 0x00ffffff;
+
+       return ctrl_outl(data, reg);
+}
+
+static u32 __fsi_reg_read(u32 reg)
+{
+       return ctrl_inl(reg);
+}
+
+static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
+{
+       u32 val = __fsi_reg_read(reg);
+
+       val &= ~mask;
+       val |= data & mask;
+
+       return __fsi_reg_write(reg, val);
+}
+
+static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
+{
+       if (reg > REG_END)
+               return -1;
+
+       return __fsi_reg_write((u32)(fsi->base + reg), data);
+}
+
+static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
+{
+       if (reg > REG_END)
+               return 0;
+
+       return __fsi_reg_read((u32)(fsi->base + reg));
+}
+
+static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
+{
+       if (reg > REG_END)
+               return -1;
+
+       return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
+}
+
+static int fsi_master_write(u32 reg, u32 data)
+{
+       if ((reg < MREG_START) ||
+           (reg > MREG_END))
+               return -1;
+
+       return __fsi_reg_write((u32)(master->base + reg), data);
+}
+
+static u32 fsi_master_read(u32 reg)
+{
+       if ((reg < MREG_START) ||
+           (reg > MREG_END))
+               return 0;
+
+       return __fsi_reg_read((u32)(master->base + reg));
+}
+
+static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+{
+       if ((reg < MREG_START) ||
+           (reg > MREG_END))
+               return -1;
+
+       return __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+}
+
+/************************************************************************
+
+
+               basic function
+
+
+************************************************************************/
+static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd;
+       struct fsi_priv *fsi = NULL;
+
+       if (!substream || !master)
+               return NULL;
+
+       rtd = substream->private_data;
+       switch (rtd->dai->cpu_dai->id) {
+       case 0:
+               fsi = &master->fsia;
+               break;
+       case 1:
+               fsi = &master->fsib;
+               break;
+       }
+
+       return fsi;
+}
+
+static int fsi_is_port_a(struct fsi_priv *fsi)
+{
+       /* return
+        * 1 : port a
+        * 0 : port b
+        */
+
+       if (fsi == &master->fsia)
+               return 1;
+
+       return 0;
+}
+
+static u32 fsi_get_info_flags(struct fsi_priv *fsi)
+{
+       int is_porta = fsi_is_port_a(fsi);
+
+       return is_porta ? master->info->porta_flags :
+               master->info->portb_flags;
+}
+
+static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play)
+{
+       u32 mode;
+       u32 flags = fsi_get_info_flags(fsi);
+
+       mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE;
+
+       /* return
+        * 1 : master mode
+        * 0 : slave mode
+        */
+
+       return (mode & flags) != mode;
+}
+
+static u32 fsi_port_ab_io_bit(struct fsi_priv *fsi, int is_play)
+{
+       int is_porta = fsi_is_port_a(fsi);
+       u32 data;
+
+       if (is_porta)
+               data = is_play ? (1 << 0) : (1 << 4);
+       else
+               data = is_play ? (1 << 8) : (1 << 12);
+
+       return data;
+}
+
+static void fsi_stream_push(struct fsi_priv *fsi,
+                           struct snd_pcm_substream *substream,
+                           u32 buffer_len,
+                           u32 period_len)
+{
+       fsi->substream          = substream;
+       fsi->buffer_len         = buffer_len;
+       fsi->period_len         = period_len;
+       fsi->byte_offset        = 0;
+       fsi->periods            = 0;
+}
+
+static void fsi_stream_pop(struct fsi_priv *fsi)
+{
+       fsi->substream          = NULL;
+       fsi->buffer_len         = 0;
+       fsi->period_len         = 0;
+       fsi->byte_offset        = 0;
+       fsi->periods            = 0;
+}
+
+static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
+{
+       u32 status;
+       u32 reg = is_play ? DOFF_ST : DIFF_ST;
+       int residue;
+
+       status = fsi_reg_read(fsi, reg);
+       residue = 0x1ff & (status >> 8);
+       residue *= fsi->chan;
+
+       return residue;
+}
+
+static int fsi_get_residue(struct fsi_priv *fsi, int is_play)
+{
+       int residue;
+       int width;
+       struct snd_pcm_runtime *runtime;
+
+       runtime = fsi->substream->runtime;
+
+       /* get 1 channel data width */
+       width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+       if (2 == width)
+               residue = fsi_get_fifo_residue(fsi, is_play);
+       else
+               residue = get_dma_residue(fsi->dma_chan);
+
+       return residue;
+}
+
+/************************************************************************
+
+
+               basic dma function
+
+
+************************************************************************/
+#define PORTA_DMA 0
+#define PORTB_DMA 1
+
+static int fsi_get_dma_chan(void)
+{
+       if (0 != request_dma(PORTA_DMA, "fsia"))
+               return -EIO;
+
+       if (0 != request_dma(PORTB_DMA, "fsib")) {
+               free_dma(PORTA_DMA);
+               return -EIO;
+       }
+
+       master->fsia.dma_chan = PORTA_DMA;
+       master->fsib.dma_chan = PORTB_DMA;
+
+       return 0;
+}
+
+static void fsi_free_dma_chan(void)
+{
+       dma_wait_for_completion(PORTA_DMA);
+       dma_wait_for_completion(PORTB_DMA);
+       free_dma(PORTA_DMA);
+       free_dma(PORTB_DMA);
+
+       master->fsia.dma_chan = -1;
+       master->fsib.dma_chan = -1;
+}
+
+/************************************************************************
+
+
+               ctrl function
+
+
+************************************************************************/
+static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
+{
+       u32 data = fsi_port_ab_io_bit(fsi, is_play);
+
+       fsi_master_mask_set(IMSK,  data, data);
+       fsi_master_mask_set(IEMSK, data, data);
+}
+
+static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
+{
+       u32 data = fsi_port_ab_io_bit(fsi, is_play);
+
+       fsi_master_mask_set(IMSK,  data, 0);
+       fsi_master_mask_set(IEMSK, data, 0);
+}
+
+static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
+{
+       u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
+
+       if (enable)
+               fsi_master_mask_set(CLK_RST, val, val);
+       else
+               fsi_master_mask_set(CLK_RST, val, 0);
+}
+
+static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
+{
+       u32 data;
+       u32 ctrl;
+
+       data = fsi_port_ab_io_bit(fsi, is_play);
+       ctrl = is_play ? DOFF_CTL : DIFF_CTL;
+
+       /* set IMSK */
+       fsi_irq_disable(fsi, is_play);
+
+       /* set interrupt generation factor */
+       fsi_reg_write(fsi, ctrl, IRQ_HALF);
+
+       /* clear FIFO */
+       fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);
+
+       /* clear interrupt factor */
+       fsi_master_mask_set(INT_ST, data, 0);
+}
+
+static void fsi_soft_all_reset(void)
+{
+       u32 status = fsi_master_read(SOFT_RST);
+
+       /* port AB reset */
+       status &= 0x000000ff;
+       fsi_master_write(SOFT_RST, status);
+       mdelay(10);
+
+       /* soft reset */
+       status &= 0x000000f0;
+       fsi_master_write(SOFT_RST, status);
+       status |= 0x00000001;
+       fsi_master_write(SOFT_RST, status);
+       mdelay(10);
+}
+
+static void fsi_16data_push(struct fsi_priv *fsi,
+                          struct snd_pcm_runtime *runtime,
+                          int send)
+{
+       u16 *dma_start;
+       u32 snd;
+       int i;
+
+       /* get dma start position for FSI */
+       dma_start = (u16 *)runtime->dma_area;
+       dma_start += fsi->byte_offset / 2;
+
+       /*
+        * soft dma
+        * FSI can not use DMA when 16bpp
+        */
+       for (i = 0; i < send; i++) {
+               snd = (u32)dma_start[i];
+               fsi_reg_write(fsi, DODT, snd << 8);
+       }
+}
+
+static void fsi_32data_push(struct fsi_priv *fsi,
+                          struct snd_pcm_runtime *runtime,
+                          int send)
+{
+       u32 *dma_start;
+
+       /* get dma start position for FSI */
+       dma_start = (u32 *)runtime->dma_area;
+       dma_start += fsi->byte_offset / 4;
+
+       dma_wait_for_completion(fsi->dma_chan);
+       dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR));
+       dma_write(fsi->dma_chan, (u32)dma_start,
+                 (u32)(fsi->base + DODT), send * 4);
+}
+
+/* playback interrupt */
+static int fsi_data_push(struct fsi_priv *fsi)
+{
+       struct snd_pcm_runtime *runtime;
+       struct snd_pcm_substream *substream = NULL;
+       int send;
+       int fifo_free;
+       int width;
+
+       if (!fsi                        ||
+           !fsi->substream             ||
+           !fsi->substream->runtime)
+               return -EINVAL;
+
+       runtime = fsi->substream->runtime;
+
+       /* FSI FIFO has limit.
+        * So, this driver can not send periods data at a time
+        */
+       if (fsi->byte_offset >=
+           fsi->period_len * (fsi->periods + 1)) {
+
+               substream = fsi->substream;
+               fsi->periods = (fsi->periods + 1) % runtime->periods;
+
+               if (0 == fsi->periods)
+                       fsi->byte_offset = 0;
+       }
+
+       /* get 1 channel data width */
+       width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+       /* get send size for alsa */
+       send = (fsi->buffer_len - fsi->byte_offset) / width;
+
+       /*  get FIFO free size */
+       fifo_free = (fsi->fifo_max * fsi->chan) - fsi_get_fifo_residue(fsi, 1);
+
+       /* size check */
+       if (fifo_free < send)
+               send = fifo_free;
+
+       if (2 == width)
+               fsi_16data_push(fsi, runtime, send);
+       else if (4 == width)
+               fsi_32data_push(fsi, runtime, send);
+       else
+               return -EINVAL;
+
+       fsi->byte_offset += send * width;
+
+       fsi_irq_enable(fsi, 1);
+
+       if (substream)
+               snd_pcm_period_elapsed(substream);
+
+       return 0;
+}
+
+static irqreturn_t fsi_interrupt(int irq, void *data)
+{
+       u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
+       u32 int_st = fsi_master_read(INT_ST);
+
+       /* clear irq status */
+       fsi_master_write(SOFT_RST, status);
+       fsi_master_write(SOFT_RST, status | 0x00000010);
+
+       if (int_st & INT_A_OUT)
+               fsi_data_push(&master->fsia);
+       if (int_st & INT_B_OUT)
+               fsi_data_push(&master->fsib);
+
+       fsi_master_write(INT_ST, 0x0000000);
+
+       return IRQ_HANDLED;
+}
+
+/************************************************************************
+
+
+               dai ops
+
+
+************************************************************************/
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct fsi_priv *fsi = fsi_get(substream);
+       const char *msg;
+       u32 flags = fsi_get_info_flags(fsi);
+       u32 fmt;
+       u32 reg;
+       u32 data;
+       int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int is_master;
+       int ret = 0;
+
+       clk_enable(master->clk);
+
+       /* CKG1 */
+       data = is_play ? (1 << 0) : (1 << 4);
+       is_master = fsi_is_master_mode(fsi, is_play);
+       if (is_master)
+               fsi_reg_mask_set(fsi, CKG1, data, data);
+       else
+               fsi_reg_mask_set(fsi, CKG1, data, 0);
+
+       /* clock inversion (CKG2) */
+       data = 0;
+       switch (SH_FSI_INVERSION_MASK & flags) {
+       case SH_FSI_LRM_INV:
+               data = 1 << 12;
+               break;
+       case SH_FSI_BRM_INV:
+               data = 1 << 8;
+               break;
+       case SH_FSI_LRS_INV:
+               data = 1 << 4;
+               break;
+       case SH_FSI_BRS_INV:
+               data = 1 << 0;
+               break;
+       }
+       fsi_reg_write(fsi, CKG2, data);
+
+       /* do fmt, di fmt */
+       data = 0;
+       reg = is_play ? DO_FMT : DI_FMT;
+       fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
+       switch (fmt) {
+       case SH_FSI_FMT_MONO:
+               msg = "MONO";
+               data = CR_FMT(CR_MONO);
+               fsi->chan = 1;
+               break;
+       case SH_FSI_FMT_MONO_DELAY:
+               msg = "MONO Delay";
+               data = CR_FMT(CR_MONO_D);
+               fsi->chan = 1;
+               break;
+       case SH_FSI_FMT_PCM:
+               msg = "PCM";
+               data = CR_FMT(CR_PCM);
+               fsi->chan = 2;
+               break;
+       case SH_FSI_FMT_I2S:
+               msg = "I2S";
+               data = CR_FMT(CR_I2S);
+               fsi->chan = 2;
+               break;
+       case SH_FSI_FMT_TDM:
+               msg = "TDM";
+               data = CR_FMT(CR_TDM) | (fsi->chan - 1);
+               fsi->chan = is_play ?
+                       SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
+               break;
+       case SH_FSI_FMT_TDM_DELAY:
+               msg = "TDM Delay";
+               data = CR_FMT(CR_TDM_D) | (fsi->chan - 1);
+               fsi->chan = is_play ?
+                       SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
+               break;
+       default:
+               dev_err(dai->dev, "unknown format.\n");
+               return -EINVAL;
+       }
+
+       switch (fsi->chan) {
+       case 1:
+               fsi->fifo_max = 256;
+               break;
+       case 2:
+               fsi->fifo_max = 128;
+               break;
+       case 3:
+       case 4:
+               fsi->fifo_max = 64;
+               break;
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+               fsi->fifo_max = 32;
+               break;
+       default:
+               dev_err(dai->dev, "channel size error.\n");
+               return -EINVAL;
+       }
+
+       fsi_reg_write(fsi, reg, data);
+       dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n",
+               msg, fsi->chan, fsi->dma_chan);
+
+       /*
+        * clear clk reset if master mode
+        */
+       if (is_master)
+               fsi_clk_ctrl(fsi, 1);
+
+       /* irq setting */
+       fsi_irq_init(fsi, is_play);
+
+       return ret;
+}
+
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct fsi_priv *fsi = fsi_get(substream);
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+       fsi_irq_disable(fsi, is_play);
+       fsi_clk_ctrl(fsi, 0);
+
+       clk_disable(master->clk);
+}
+
+static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
+{
+       struct fsi_priv *fsi = fsi_get(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int ret = 0;
+
+       /* capture not supported */
+       if (!is_play)
+               return -ENODEV;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               fsi_stream_push(fsi, substream,
+                               frames_to_bytes(runtime, runtime->buffer_size),
+                               frames_to_bytes(runtime, runtime->period_size));
+               ret = fsi_data_push(fsi);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               fsi_irq_disable(fsi, is_play);
+               fsi_stream_pop(fsi);
+               break;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_dai_ops fsi_dai_ops = {
+       .startup        = fsi_dai_startup,
+       .shutdown       = fsi_dai_shutdown,
+       .trigger        = fsi_dai_trigger,
+};
+
+/************************************************************************
+
+
+               pcm ops
+
+
+************************************************************************/
+static struct snd_pcm_hardware fsi_pcm_hardware = {
+       .info =         SNDRV_PCM_INFO_INTERLEAVED      |
+                       SNDRV_PCM_INFO_MMAP             |
+                       SNDRV_PCM_INFO_MMAP_VALID       |
+                       SNDRV_PCM_INFO_PAUSE,
+       .formats                = FSI_FMTS,
+       .rates                  = FSI_RATES,
+       .rate_min               = 8000,
+       .rate_max               = 192000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 64 * 1024,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8192,
+       .periods_min            = 1,
+       .periods_max            = 32,
+       .fifo_size              = 256,
+};
+
+static int fsi_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret = 0;
+
+       snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware);
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+
+       return ret;
+}
+
+static int fsi_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int fsi_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsi_priv *fsi = fsi_get(substream);
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       long location;
+
+       location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play);
+       if (location < 0)
+               location = 0;
+
+       return bytes_to_frames(runtime, location);
+}
+
+static struct snd_pcm_ops fsi_pcm_ops = {
+       .open           = fsi_pcm_open,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = fsi_hw_params,
+       .hw_free        = fsi_hw_free,
+       .pointer        = fsi_pointer,
+};
+
+/************************************************************************
+
+
+               snd_soc_platform
+
+
+************************************************************************/
+#define PREALLOC_BUFFER                (32 * 1024)
+#define PREALLOC_BUFFER_MAX    (32 * 1024)
+
+static void fsi_pcm_free(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int fsi_pcm_new(struct snd_card *card,
+                      struct snd_soc_dai *dai,
+                      struct snd_pcm *pcm)
+{
+       /*
+        * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+        * in MMAP mode (i.e. aplay -M)
+        */
+       return snd_pcm_lib_preallocate_pages_for_all(
+               pcm,
+               SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL),
+               PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+/************************************************************************
+
+
+               alsa struct
+
+
+************************************************************************/
+struct snd_soc_dai fsi_soc_dai[] = {
+       {
+               .name                   = "FSIA",
+               .id                     = 0,
+               .playback = {
+                       .rates          = FSI_RATES,
+                       .formats        = FSI_FMTS,
+                       .channels_min   = 1,
+                       .channels_max   = 8,
+               },
+               /* capture not supported */
+               .ops = &fsi_dai_ops,
+       },
+       {
+               .name                   = "FSIB",
+               .id                     = 1,
+               .playback = {
+                       .rates          = FSI_RATES,
+                       .formats        = FSI_FMTS,
+                       .channels_min   = 1,
+                       .channels_max   = 8,
+               },
+               /* capture not supported */
+               .ops = &fsi_dai_ops,
+       },
+};
+EXPORT_SYMBOL_GPL(fsi_soc_dai);
+
+struct snd_soc_platform fsi_soc_platform = {
+       .name           = "fsi-pcm",
+       .pcm_ops        = &fsi_pcm_ops,
+       .pcm_new        = fsi_pcm_new,
+       .pcm_free       = fsi_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsi_soc_platform);
+
+/************************************************************************
+
+
+               platform function
+
+
+************************************************************************/
+static int fsi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       char clk_name[8];
+       unsigned int irq;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!res || !irq) {
+               dev_err(&pdev->dev, "Not enough FSI platform resources.\n");
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       master = kzalloc(sizeof(*master), GFP_KERNEL);
+       if (!master) {
+               dev_err(&pdev->dev, "Could not allocate master\n");
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       master->base = ioremap_nocache(res->start, resource_size(res));
+       if (!master->base) {
+               ret = -ENXIO;
+               dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
+               goto exit_kfree;
+       }
+
+       master->irq             = irq;
+       master->info            = pdev->dev.platform_data;
+       master->fsia.base       = master->base;
+       master->fsib.base       = master->base + 0x40;
+
+       master->fsia.dma_chan = -1;
+       master->fsib.dma_chan = -1;
+
+       ret = fsi_get_dma_chan();
+       if (ret < 0) {
+               dev_err(&pdev->dev, "cannot get dma api\n");
+               goto exit_iounmap;
+       }
+
+       /* FSI is based on SPU mstp */
+       snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id);
+       master->clk = clk_get(NULL, clk_name);
+       if (IS_ERR(master->clk)) {
+               dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name);
+               ret = -EIO;
+               goto exit_free_dma;
+       }
+
+       fsi_soc_dai[0].dev              = &pdev->dev;
+       fsi_soc_dai[1].dev              = &pdev->dev;
+
+       fsi_soft_all_reset();
+
+       ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
+       if (ret) {
+               dev_err(&pdev->dev, "irq request err\n");
+               goto exit_free_dma;
+       }
+
+       ret = snd_soc_register_platform(&fsi_soc_platform);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "cannot snd soc register\n");
+               goto exit_free_irq;
+       }
+
+       return snd_soc_register_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
+
+exit_free_irq:
+       free_irq(irq, master);
+exit_free_dma:
+       fsi_free_dma_chan();
+exit_iounmap:
+       iounmap(master->base);
+exit_kfree:
+       kfree(master);
+       master = NULL;
+exit:
+       return ret;
+}
+
+static int fsi_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
+       snd_soc_unregister_platform(&fsi_soc_platform);
+
+       clk_put(master->clk);
+
+       fsi_free_dma_chan();
+
+       free_irq(master->irq, master);
+
+       iounmap(master->base);
+       kfree(master);
+       master = NULL;
+       return 0;
+}
+
+static struct platform_driver fsi_driver = {
+       .driver         = {
+               .name   = "sh_fsi",
+       },
+       .probe          = fsi_probe,
+       .remove         = fsi_remove,
+};
+
+static int __init fsi_mobile_init(void)
+{
+       return platform_driver_register(&fsi_driver);
+}
+
+static void __exit fsi_mobile_exit(void)
+{
+       platform_driver_unregister(&fsi_driver);
+}
+module_init(fsi_mobile_init);
+module_exit(fsi_mobile_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip FSI audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
index c8ceddc..d2505e8 100644 (file)
@@ -77,6 +77,35 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data,
 #define snd_soc_7_9_spi_write NULL
 #endif
 
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 *cache = codec->reg_cache;
+       u8 data[2];
+
+       BUG_ON(codec->volatile_register);
+
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       if (reg < codec->reg_cache_size)
+               cache[reg] = value;
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+                                    unsigned int reg)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= codec->reg_cache_size)
+               return -1;
+       return cache[reg];
+}
+
 static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
                              unsigned int value)
 {
@@ -150,9 +179,20 @@ static struct {
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
 } io_types[] = {
-       { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
-       { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
-         snd_soc_8_16_read_i2c },
+       {
+               .addr_bits = 7, .data_bits = 9,
+               .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
+               .spi_write = snd_soc_7_9_spi_write 
+       },
+       {
+               .addr_bits = 8, .data_bits = 8,
+               .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
+       },
+       {
+               .addr_bits = 8, .data_bits = 16,
+               .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
+               .i2c_read = snd_soc_8_16_read_i2c,
+       },
 };
 
 /**
index e984a17..f5b356f 100644 (file)
@@ -1267,10 +1267,18 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
        if (!codec->debugfs_pop_time)
                printk(KERN_WARNING
                       "Failed to create pop time debugfs file\n");
+
+       codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
+       if (!codec->debugfs_dapm)
+               printk(KERN_WARNING
+                      "Failed to create DAPM debugfs directory\n");
+
+       snd_soc_dapm_debugfs_init(codec);
 }
 
 static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
 {
+       debugfs_remove_recursive(codec->debugfs_dapm);
        debugfs_remove(codec->debugfs_pop_time);
        debugfs_remove(codec->debugfs_reg);
 }
@@ -2189,16 +2197,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
  * snd_soc_dai_set_pll - configure DAI PLL.
  * @dai: DAI
  * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
  * @freq_in: PLL input clock frequency in Hz
  * @freq_out: requested PLL output clock frequency in Hz
  *
  * Configures and enables PLL to generate output clock based on input clock.
  */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+       unsigned int freq_in, unsigned int freq_out)
 {
        if (dai->ops && dai->ops->set_pll)
-               return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+               return dai->ops->set_pll(dai, pll_id, source,
+                                        freq_in, freq_out);
        else
                return -EINVAL;
 }
@@ -2242,6 +2252,30 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
+/**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ *           0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ *           0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+       unsigned int tx_num, unsigned int *tx_slot,
+       unsigned int rx_num, unsigned int *rx_slot)
+{
+       if (dai->ops && dai->ops->set_channel_map)
+               return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+                       rx_num, rx_slot);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
 /**
  * snd_soc_dai_set_tristate - configure DAI system or master clock.
  * @dai: DAI
index 8846131..9babda5 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
+#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -55,17 +56,19 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
        [snd_soc_dapm_supply] = 1,
        [snd_soc_dapm_micbias] = 2,
-       [snd_soc_dapm_mic] = 3,
-       [snd_soc_dapm_mux] = 4,
-       [snd_soc_dapm_value_mux] = 4,
-       [snd_soc_dapm_dac] = 5,
-       [snd_soc_dapm_mixer] = 6,
-       [snd_soc_dapm_mixer_named_ctl] = 6,
-       [snd_soc_dapm_pga] = 7,
-       [snd_soc_dapm_adc] = 8,
-       [snd_soc_dapm_hp] = 9,
-       [snd_soc_dapm_spk] = 9,
-       [snd_soc_dapm_post] = 10,
+       [snd_soc_dapm_aif_in] = 3,
+       [snd_soc_dapm_aif_out] = 3,
+       [snd_soc_dapm_mic] = 4,
+       [snd_soc_dapm_mux] = 5,
+       [snd_soc_dapm_value_mux] = 5,
+       [snd_soc_dapm_dac] = 6,
+       [snd_soc_dapm_mixer] = 7,
+       [snd_soc_dapm_mixer_named_ctl] = 7,
+       [snd_soc_dapm_pga] = 8,
+       [snd_soc_dapm_adc] = 9,
+       [snd_soc_dapm_hp] = 10,
+       [snd_soc_dapm_spk] = 10,
+       [snd_soc_dapm_post] = 11,
 };
 
 static int dapm_down_seq[] = {
@@ -81,8 +84,10 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_micbias] = 8,
        [snd_soc_dapm_mux] = 9,
        [snd_soc_dapm_value_mux] = 9,
-       [snd_soc_dapm_supply] = 10,
-       [snd_soc_dapm_post] = 11,
+       [snd_soc_dapm_aif_in] = 10,
+       [snd_soc_dapm_aif_out] = 10,
+       [snd_soc_dapm_supply] = 11,
+       [snd_soc_dapm_post] = 12,
 };
 
 static void pop_wait(u32 pop_time)
@@ -148,8 +153,12 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
 
        if (card->set_bias_level)
                ret = card->set_bias_level(card, level);
-       if (ret == 0 && codec->set_bias_level)
-               ret = codec->set_bias_level(codec, level);
+       if (ret == 0) {
+               if (codec->set_bias_level)
+                       ret = codec->set_bias_level(codec, level);
+               else
+                       codec->bias_level = level;
+       }
 
        return ret;
 }
@@ -224,6 +233,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
                p->connect = 1;
        break;
        /* does effect routing - dynamically connected */
@@ -497,8 +508,14 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        if (widget->id == snd_soc_dapm_supply)
                return 0;
 
-       if (widget->id == snd_soc_dapm_adc && widget->active)
-               return 1;
+       switch (widget->id) {
+       case snd_soc_dapm_adc:
+       case snd_soc_dapm_aif_out:
+               if (widget->active)
+                       return 1;
+       default:
+               break;
+       }
 
        if (widget->connected) {
                /* connected pin ? */
@@ -537,8 +554,14 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
                return 0;
 
        /* active stream ? */
-       if (widget->id == snd_soc_dapm_dac && widget->active)
-               return 1;
+       switch (widget->id) {
+       case snd_soc_dapm_dac:
+       case snd_soc_dapm_aif_in:
+               if (widget->active)
+                       return 1;
+       default:
+               break;
+       }
 
        if (widget->connected) {
                /* connected pin ? */
@@ -695,6 +718,10 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 
        /* Check if one of our outputs is connected */
        list_for_each_entry(path, &w->sinks, list_source) {
+               if (path->connected &&
+                   !path->connected(path->source, path->sink))
+                       continue;
+
                if (path->sink && path->sink->power_check &&
                    path->sink->power_check(path->sink)) {
                        power = 1;
@@ -1052,6 +1079,8 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                case snd_soc_dapm_supply:
+               case snd_soc_dapm_aif_in:
+               case snd_soc_dapm_aif_out:
                        if (w->name) {
                                in = is_connected_input_ep(w);
                                dapm_clear_walk(w->codec);
@@ -1077,6 +1106,99 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
 }
 #endif
 
+#ifdef CONFIG_DEBUG_FS
+static int dapm_widget_power_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t dapm_widget_power_read_file(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct snd_soc_dapm_widget *w = file->private_data;
+       char *buf;
+       int in, out;
+       ssize_t ret;
+       struct snd_soc_dapm_path *p = NULL;
+
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       in = is_connected_input_ep(w);
+       dapm_clear_walk(w->codec);
+       out = is_connected_output_ep(w);
+       dapm_clear_walk(w->codec);
+
+       ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d\n",
+                      w->name, w->power ? "On" : "Off", in, out);
+
+       if (w->sname)
+               ret += snprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",
+                               w->sname,
+                               w->active ? "active" : "inactive");
+
+       list_for_each_entry(p, &w->sources, list_sink) {
+               if (p->connected && !p->connected(w, p->sink))
+                       continue;
+
+               if (p->connect)
+                       ret += snprintf(buf + ret, PAGE_SIZE - ret,
+                                       " in  %s %s\n",
+                                       p->name ? p->name : "static",
+                                       p->source->name);
+       }
+       list_for_each_entry(p, &w->sinks, list_source) {
+               if (p->connected && !p->connected(w, p->sink))
+                       continue;
+
+               if (p->connect)
+                       ret += snprintf(buf + ret, PAGE_SIZE - ret,
+                                       " out %s %s\n",
+                                       p->name ? p->name : "static",
+                                       p->sink->name);
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations dapm_widget_power_fops = {
+       .open = dapm_widget_power_open_file,
+       .read = dapm_widget_power_read_file,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+{
+       struct snd_soc_dapm_widget *w;
+       struct dentry *d;
+
+       if (!codec->debugfs_dapm)
+               return;
+
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               if (!w->name)
+                       continue;
+
+               d = debugfs_create_file(w->name, 0444,
+                                       codec->debugfs_dapm, w,
+                                       &dapm_widget_power_fops);
+               if (!d)
+                       printk(KERN_WARNING
+                              "ASoC: Failed to create %s debugfs file\n",
+                              w->name);
+       }
+}
+#else
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 /* test and update the power status of a mux widget */
 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
                                 struct snd_kcontrol *kcontrol, int mask,
@@ -1274,10 +1396,13 @@ int snd_soc_dapm_sync(struct snd_soc_codec *codec)
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
 static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
-       const char *sink, const char *control, const char *source)
+                                 const struct snd_soc_dapm_route *route)
 {
        struct snd_soc_dapm_path *path;
        struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+       const char *sink = route->sink;
+       const char *control = route->control;
+       const char *source = route->source;
        int ret = 0;
 
        /* find src and dest widgets */
@@ -1301,6 +1426,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
 
        path->source = wsource;
        path->sink = wsink;
+       path->connected = route->connected;
        INIT_LIST_HEAD(&path->list);
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
@@ -1342,6 +1468,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_pre:
        case snd_soc_dapm_post:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
                list_add(&path->list, &codec->dapm_paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
@@ -1399,8 +1527,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
        int i, ret;
 
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_add_route(codec, route->sink,
-                                            route->control, route->source);
+               ret = snd_soc_dapm_add_route(codec, route);
                if (ret < 0) {
                        printk(KERN_ERR "Failed to add route %s->%s\n",
                               route->source,
@@ -1444,9 +1571,11 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                        dapm_new_mux(codec, w);
                        break;
                case snd_soc_dapm_adc:
+               case snd_soc_dapm_aif_out:
                        w->power_check = dapm_adc_check_power;
                        break;
                case snd_soc_dapm_dac:
+               case snd_soc_dapm_aif_in:
                        w->power_check = dapm_dac_check_power;
                        break;
                case snd_soc_dapm_pga: