]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'for-2.6.32' into for-2.6.33
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 23 Nov 2009 13:37:04 +0000 (13:37 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 23 Nov 2009 13:37:04 +0000 (13:37 +0000)
151 files changed:
arch/arm/mach-davinci/include/mach/asp.h
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/board-zoom2.c
arch/arm/mach-s3c6400/include/mach/map.h
arch/arm/plat-s3c/include/plat/audio.h
arch/arm/plat-s3c/include/plat/devs.h
arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
arch/arm/plat-s3c64xx/dev-audio.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/twl4030-codec.c [new file with mode: 0644]
drivers/mfd/twl4030-core.c
include/linux/i2c/twl4030.h
include/linux/mfd/twl4030-codec.h [new file with mode: 0644]
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/tlv320dac33-plat.h [new file with mode: 0644]
include/sound/tpa6130a2-plat.h [new file with mode: 0644]
sound/soc/Makefile
sound/soc/atmel/playpaq_wm8510.c
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/au1x/dbdma2.c
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc-i2s.c
sound/soc/au1x/psc.h
sound/soc/blackfin/bf5xx-ad1836.c
sound/soc/blackfin/bf5xx-ad1938.c
sound/soc/blackfin/bf5xx-i2s.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/ac97.c
sound/soc/codecs/ad1836.c
sound/soc/codecs/ad1938.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad73311.c
sound/soc/codecs/ads117x.c [new file with mode: 0644]
sound/soc/codecs/ads117x.h [new file with mode: 0644]
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/ak4671.c [new file with mode: 0644]
sound/soc/codecs/ak4671.h [new file with mode: 0644]
sound/soc/codecs/cs4270.c
sound/soc/codecs/cx20442.c
sound/soc/codecs/pcm3008.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/stac9766.c
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic26.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320dac33.c [new file with mode: 0644]
sound/soc/codecs/tlv320dac33.h [new file with mode: 0644]
sound/soc/codecs/tpa6130a2.c [new file with mode: 0644]
sound/soc/codecs/tpa6130a2.h [new file with mode: 0644]
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl4030.h
sound/soc/codecs/uda134x.c
sound/soc/codecs/uda1380.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8711.c [new file with mode: 0644]
sound/soc/codecs/wm8711.h [new file with mode: 0644]
sound/soc/codecs/wm8727.c [new file with mode: 0644]
sound/soc/codecs/wm8727.h [new file with mode: 0644]
sound/soc/codecs/wm8728.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8988.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_hubs.c
sound/soc/codecs/wm_hubs.h
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/fsl/mpc5200_dma.c
sound/soc/fsl/mpc5200_dma.h
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/imx/mx27vis_wm8974.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/am3517evm.c [new file with mode: 0644]
sound/soc/omap/ams-delta.c
sound/soc/omap/igep0020.c [new file with mode: 0644]
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap3evm.c
sound/soc/omap/omap3pandora.c
sound/soc/omap/overo.c
sound/soc/pxa/Kconfig
sound/soc/pxa/magician.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/zylonite.c
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/jive_wm8750.c
sound/soc/s3c24xx/ln2440sbc_alc650.c
sound/soc/s3c24xx/neo1973_gta02_wm8753.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c-dma.c [moved from sound/soc/s3c24xx/s3c24xx-pcm.c with 82% similarity]
sound/soc/s3c24xx/s3c-dma.h [moved from sound/soc/s3c24xx/s3c24xx-pcm.h with 87% similarity]
sound/soc/s3c24xx/s3c-i2s-v2.c
sound/soc/s3c24xx/s3c-i2s-v2.h
sound/soc/s3c24xx/s3c-pcm.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c-pcm.h [new file with mode: 0644]
sound/soc/s3c24xx/s3c2412-i2s.c
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-i2s.c
sound/soc/s3c24xx/s3c24xx_simtec.c
sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
sound/soc/s3c24xx/s3c24xx_uda134x.c
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.h
sound/soc/s3c24xx/smdk2443_wm9710.c
sound/soc/s3c24xx/smdk64xx_wm8580.c [new file with mode: 0644]
sound/soc/s6000/s6000-pcm.c
sound/soc/sh/Kconfig
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-utils.c [new file with mode: 0644]

index 18e4ce34ece61ade58e555a57ad5f91ea3db85a5..e07f70ed7c53d413b620daff67b4beee8a5eb723 100644 (file)
@@ -51,6 +51,14 @@ struct snd_platform_data {
        u32 rx_dma_offset;
        enum dma_event_q eventq_no;     /* event queue number */
        unsigned int codec_fmt;
+       /*
+        * Allowing this is more efficient and eliminates left and right swaps
+        * caused by underruns, but will swap the left and right channels
+        * when compared to previous behavior.
+        */
+       unsigned enable_channel_combine:1;
+       unsigned sram_size_playback;
+       unsigned sram_size_capture;
 
        /* McASP specific fields */
        int tdm_slots;
index 0acb5560229c349ba66fa2327e838a9d4409a8d0..08e535d92c0671657e1292c08f8204ceb21426d8 100644 (file)
@@ -410,6 +410,15 @@ static struct regulator_init_data sdp3430_vpll2 = {
        .consumer_supplies      = &sdp3430_vdvi_supply,
 };
 
+static struct twl4030_codec_audio_data sdp3430_audio = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data sdp3430_codec = {
+       .audio_mclk = 26000000,
+       .audio = &sdp3430_audio,
+};
+
 static struct twl4030_platform_data sdp3430_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
@@ -420,6 +429,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
        .madc           = &sdp3430_madc_data,
        .keypad         = &sdp3430_kp_data,
        .usb            = &sdp3430_usb_data,
+       .codec          = &sdp3430_codec,
 
        .vaux1          = &sdp3430_vaux1,
        .vaux2          = &sdp3430_vaux2,
index 70df6b4dbcd4c65c5d339c797a48c5263a876f79..8f0c106a449d09f2330c6867fbc8f5aca0b9594b 100644 (file)
@@ -254,6 +254,15 @@ static struct twl4030_usb_data beagle_usb_data = {
        .usb_mode       = T2_USB_MODE_ULPI,
 };
 
+static struct twl4030_codec_audio_data beagle_audio_data = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+       .audio_mclk = 26000000,
+       .audio = &beagle_audio_data,
+};
+
 static struct twl4030_platform_data beagle_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
@@ -261,6 +270,7 @@ static struct twl4030_platform_data beagle_twldata = {
        /* platform_data for children goes here */
        .usb            = &beagle_usb_data,
        .gpio           = &beagle_gpio_data,
+       .codec          = &beagle_codec_data,
        .vmmc1          = &beagle_vmmc1,
        .vsim           = &beagle_vsim,
        .vdac           = &beagle_vdac,
index 4c4d7f8dbd7236cf6d33afa2eee8748e9d78e357..25ca5f6a0d3d6ef33c0637d4806bcd006191606a 100644 (file)
@@ -194,6 +194,15 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = {
        .irq_line       = 1,
 };
 
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3evm_codec_data = {
+       .audio_mclk = 26000000,
+       .audio = &omap3evm_audio_data,
+};
+
 static struct twl4030_platform_data omap3evm_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
@@ -203,6 +212,7 @@ static struct twl4030_platform_data omap3evm_twldata = {
        .madc           = &omap3evm_madc_data,
        .usb            = &omap3evm_usb_data,
        .gpio           = &omap3evm_gpio_data,
+       .codec          = &omap3evm_codec_data,
 };
 
 static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
index 5326e0d61597725aab8da89f930383f6d9400a16..ed97b54e1fa5266482bd2ae6818edb9492fafddd 100644 (file)
@@ -281,11 +281,21 @@ static struct twl4030_usb_data omap3pandora_usb_data = {
        .usb_mode       = T2_USB_MODE_ULPI,
 };
 
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3pandora_codec_data = {
+       .audio_mclk = 26000000,
+       .audio = &omap3pandora_audio_data,
+};
+
 static struct twl4030_platform_data omap3pandora_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
        .gpio           = &omap3pandora_gpio_data,
        .usb            = &omap3pandora_usb_data,
+       .codec          = &omap3pandora_codec_data,
        .vmmc1          = &pandora_vmmc1,
        .vmmc2          = &pandora_vmmc2,
        .keypad         = &pandora_kp_data,
index 9917d2fddc2f6093025ffa9979e31d8aa1d4ac21..e1fb50451e192269262c1d87593420919f558ce0 100644 (file)
@@ -329,6 +329,15 @@ static struct regulator_init_data overo_vmmc1 = {
        .consumer_supplies      = &overo_vmmc1_supply,
 };
 
+static struct twl4030_codec_audio_data overo_audio_data = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data overo_codec_data = {
+       .audio_mclk = 26000000,
+       .audio = &overo_audio_data,
+};
+
 /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
 
 static struct twl4030_platform_data overo_twldata = {
@@ -336,6 +345,7 @@ static struct twl4030_platform_data overo_twldata = {
        .irq_end        = TWL4030_IRQ_END,
        .gpio           = &overo_gpio_data,
        .usb            = &overo_usb_data,
+       .codec          = &overo_codec_data,
        .vmmc1          = &overo_vmmc1,
 };
 
index ea00486a5e5314b5e5255a7c3aa9d4a9af306960..5fcb20f41a687350b8f631dd56fd8069cd775645 100644 (file)
@@ -231,6 +231,15 @@ static struct twl4030_madc_platform_data zoom2_madc_data = {
        .irq_line       = 1,
 };
 
+static struct twl4030_codec_audio_data zoom2_audio_data = {
+       .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data zoom2_codec_data = {
+       .audio_mclk = 26000000,
+       .audio = &zoom2_audio_data,
+};
+
 static struct twl4030_platform_data zoom2_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
@@ -241,6 +250,7 @@ static struct twl4030_platform_data zoom2_twldata = {
        .usb            = &zoom2_usb_data,
        .gpio           = &zoom2_gpio_data,
        .keypad         = &zoom2_kp_twl4030_data,
+       .codec          = &zoom2_codec_data,
        .vmmc1          = &zoom2_vmmc1,
        .vmmc2          = &zoom2_vmmc2,
        .vsim           = &zoom2_vsim,
index fc8b223bad4fb91aeaedcd36e2257abf38ae6ba4..866be31872a527d65393b468435804b25dd5b522 100644 (file)
@@ -48,6 +48,8 @@
 #define S3C64XX_PA_IIS1                (0x7F003000)
 #define S3C64XX_PA_TIMER       (0x7F006000)
 #define S3C64XX_PA_IIC0                (0x7F004000)
+#define S3C64XX_PA_PCM0                (0x7F009000)
+#define S3C64XX_PA_PCM1                (0x7F00A000)
 #define S3C64XX_PA_IISV4       (0x7F00D000)
 #define S3C64XX_PA_IIC1                (0x7F00F000)
 
index de0e8da48bc33e77f5ec49e2776255fb80c7ff0a..f22d23bb6271dd661d6a56973ddc81ea626936c4 100644 (file)
@@ -1,45 +1,17 @@
-/* arch/arm/mach-s3c2410/include/mach/audio.h
+/* arch/arm/plat-s3c/include/plat/audio.h
  *
- * Copyright (c) 2004-2005 Simtec Electronics
- *     http://www.simtec.co.uk/products/SWLINUX/
- *     Ben Dooks <ben@simtec.co.uk>
- *
- * S3C24XX - Audio platfrom_device info
+ * 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 version 2 as
  * published by the Free Software Foundation.
-*/
-
-#ifndef __ASM_ARCH_AUDIO_H
-#define __ASM_ARCH_AUDIO_H __FILE__
-
-/* struct s3c24xx_iis_ops
- *
- * called from the s3c24xx audio core to deal with the architecture
- * or the codec's setup and control.
- *
- * the pointer to itself is passed through in case the caller wants to
- * embed this in an larger structure for easy reference to it's context.
-*/
+ */
 
-struct s3c24xx_iis_ops {
-       struct module *owner;
-
-       int     (*startup)(struct s3c24xx_iis_ops *me);
-       void    (*shutdown)(struct s3c24xx_iis_ops *me);
-       int     (*suspend)(struct s3c24xx_iis_ops *me);
-       int     (*resume)(struct s3c24xx_iis_ops *me);
-
-       int     (*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-       int     (*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-       int     (*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
+/**
+ * struct s3c_audio_pdata - common platform data for audio device drivers
+ * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
+ */
+struct s3c_audio_pdata {
+       int (*cfg_gpio)(struct platform_device *);
 };
-
-struct s3c24xx_platdata_iis {
-       const char              *codec_clk;
-       struct s3c24xx_iis_ops  *ops;
-       int                     (*match_dev)(struct device *dev);
-};
-
-#endif /* __ASM_ARCH_AUDIO_H */
index 0f540ea1e99977d7925f0997d0381c0de136e519..932cbbbb4273a0c34daaa0875b4f7c86887af2e4 100644 (file)
@@ -28,6 +28,9 @@ extern struct platform_device s3c64xx_device_iis0;
 extern struct platform_device s3c64xx_device_iis1;
 extern struct platform_device s3c64xx_device_iisv4;
 
+extern struct platform_device s3c64xx_device_pcm0;
+extern struct platform_device s3c64xx_device_pcm1;
+
 extern struct platform_device s3c_device_fb;
 extern struct platform_device s3c_device_usb;
 extern struct platform_device s3c_device_lcd;
index 07659dad1748a95ebcba9ad72f3d76f1905ea113..abf2fbc2eb2f0d25c02daa82e896690aecd5263b 100644 (file)
@@ -67,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)
index 1322beb40dd7f3c58daba138edf0da6efa48b50f..9e07344913d9c2631b8ce90711128782759c6e0b 100644 (file)
 
 #include <mach/irqs.h>
 #include <mach/map.h>
+#include <mach/dma.h>
+#include <mach/gpio.h>
 
 #include <plat/devs.h>
-
+#include <plat/audio.h>
+#include <plat/gpio-bank-d.h>
+#include <plat/gpio-bank-e.h>
+#include <plat/gpio-cfg.h>
 
 static struct resource s3c64xx_iis0_resource[] = {
        [0] = {
@@ -66,3 +71,97 @@ struct platform_device s3c64xx_device_iisv4 = {
        .resource         = s3c64xx_iisv4_resource,
 };
 EXPORT_SYMBOL(s3c64xx_device_iisv4);
+
+
+/* PCM Controller platform_devices */
+
+static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
+{
+       switch (pdev->id) {
+       case 0:
+               s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_PCM0_SCLK);
+               s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_PCM0_EXTCLK);
+               s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_PCM0_FSYNC);
+               s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_PCM0_SIN);
+               s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_PCM0_SOUT);
+               break;
+       case 1:
+               s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_PCM1_SCLK);
+               s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_PCM1_EXTCLK);
+               s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_PCM1_FSYNC);
+               s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_PCM1_SIN);
+               s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT);
+               break;
+       default:
+               printk(KERN_DEBUG "Invalid PCM Controller number!");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct resource s3c64xx_pcm0_resource[] = {
+       [0] = {
+               .start = S3C64XX_PA_PCM0,
+               .end   = S3C64XX_PA_PCM0 + 0x100 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = DMACH_PCM0_TX,
+               .end   = DMACH_PCM0_TX,
+               .flags = IORESOURCE_DMA,
+       },
+       [2] = {
+               .start = DMACH_PCM0_RX,
+               .end   = DMACH_PCM0_RX,
+               .flags = IORESOURCE_DMA,
+       },
+};
+
+struct s3c_audio_pdata s3c_pcm0_pdata = {
+       .cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm0 = {
+       .name             = "samsung-pcm",
+       .id               = 0,
+       .num_resources    = ARRAY_SIZE(s3c64xx_pcm0_resource),
+       .resource         = s3c64xx_pcm0_resource,
+       .dev = {
+               .platform_data = &s3c_pcm0_pdata,
+       },
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm0);
+
+static struct resource s3c64xx_pcm1_resource[] = {
+       [0] = {
+               .start = S3C64XX_PA_PCM1,
+               .end   = S3C64XX_PA_PCM1 + 0x100 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = DMACH_PCM1_TX,
+               .end   = DMACH_PCM1_TX,
+               .flags = IORESOURCE_DMA,
+       },
+       [2] = {
+               .start = DMACH_PCM1_RX,
+               .end   = DMACH_PCM1_RX,
+               .flags = IORESOURCE_DMA,
+       },
+};
+
+struct s3c_audio_pdata s3c_pcm1_pdata = {
+       .cfg_gpio = s3c64xx_pcm_cfg_gpio,
+};
+
+struct platform_device s3c64xx_device_pcm1 = {
+       .name             = "samsung-pcm",
+       .id               = 1,
+       .num_resources    = ARRAY_SIZE(s3c64xx_pcm1_resource),
+       .resource         = s3c64xx_pcm1_resource,
+       .dev = {
+               .platform_data = &s3c_pcm1_pdata,
+       },
+};
+EXPORT_SYMBOL(s3c64xx_device_pcm1);
index 570be139f9df9b2d63f0a53a072b46acc0db4af1..08f2d07bf56aceba27761f707fa737884fb16ad9 100644 (file)
@@ -121,6 +121,12 @@ config TWL4030_POWER
          and load scripts controling which resources are switched off/on
          or reset when a sleep, wakeup or warm reset event occurs.
 
+config TWL4030_CODEC
+       bool
+       depends on TWL4030_CORE
+       select MFD_CORE
+       default n
+
 config MFD_TMIO
        bool
        default n
index f3b277b90d402e2be5b9ed124c4a7a8425afd2bb..af0fc903cec83c0e6d66d1a15950b5531170faf6 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS)                += menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)     += twl4030-core.o twl4030-irq.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC)    += twl4030-codec.o
 
 obj-$(CONFIG_MFD_MC13783)      += mc13783-core.o
 
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644 (file)
index 0000000..77b9149
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:     Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS    2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+       int request_count;
+       u8 reg;
+       u8 mask;
+};
+
+struct twl4030_codec {
+       unsigned int audio_mclk;
+       struct mutex mutex;
+       struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+       struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+       u8 val;
+
+       twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+                       codec->resource[id].reg);
+
+       if (enable)
+               val |= codec->resource[id].mask;
+       else
+               val &= ~codec->resource[id].mask;
+
+       twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+                                       val, codec->resource[id].reg);
+
+       return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+       u8 val;
+
+       twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+                       codec->resource[id].reg);
+
+       return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+       int val;
+
+       if (id >= TWL4030_CODEC_RES_MAX) {
+               dev_err(&twl4030_codec_dev->dev,
+                               "Invalid resource ID (%u)\n", id);
+               return -EINVAL;
+       }
+
+       mutex_lock(&codec->mutex);
+       if (!codec->resource[id].request_count)
+               /* Resource was disabled, enable it */
+               val = twl4030_codec_set_resource(id, 1);
+       else
+               val = twl4030_codec_get_resource(id);
+
+       codec->resource[id].request_count++;
+       mutex_unlock(&codec->mutex);
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+       int val;
+
+       if (id >= TWL4030_CODEC_RES_MAX) {
+               dev_err(&twl4030_codec_dev->dev,
+                               "Invalid resource ID (%u)\n", id);
+               return -EINVAL;
+       }
+
+       mutex_lock(&codec->mutex);
+       if (!codec->resource[id].request_count) {
+               dev_err(&twl4030_codec_dev->dev,
+                       "Resource has been disabled already (%u)\n", id);
+               mutex_unlock(&codec->mutex);
+               return -EPERM;
+       }
+       codec->resource[id].request_count--;
+
+       if (!codec->resource[id].request_count)
+               /* Resource can be disabled now */
+               val = twl4030_codec_set_resource(id, 0);
+       else
+               val = twl4030_codec_get_resource(id);
+
+       mutex_unlock(&codec->mutex);
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+unsigned int twl4030_codec_get_mclk(void)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+
+       return codec->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+       struct twl4030_codec *codec;
+       struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+       struct mfd_cell *cell = NULL;
+       int ret, childs = 0;
+       u8 val;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "Platform data is missing\n");
+               return -EINVAL;
+       }
+
+       /* Configure APLL_INFREQ and disable APLL if enabled */
+       val = 0;
+       switch (pdata->audio_mclk) {
+       case 19200000:
+               val |= TWL4030_APLL_INFREQ_19200KHZ;
+               break;
+       case 26000000:
+               val |= TWL4030_APLL_INFREQ_26000KHZ;
+               break;
+       case 38400000:
+               val |= TWL4030_APLL_INFREQ_38400KHZ;
+               break;
+       default:
+               dev_err(&pdev->dev, "Invalid audio_mclk\n");
+               return -EINVAL;
+       }
+       twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+                                       val, TWL4030_REG_APLL_CTL);
+
+       codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+       if (!codec)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, codec);
+
+       twl4030_codec_dev = pdev;
+       mutex_init(&codec->mutex);
+       codec->audio_mclk = pdata->audio_mclk;
+
+       /* Codec power */
+       codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+       codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+       /* PLL */
+       codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+       codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+       if (pdata->audio) {
+               cell = &codec->cells[childs];
+               cell->name = "twl4030_codec_audio";
+               cell->platform_data = pdata->audio;
+               cell->data_size = sizeof(*pdata->audio);
+               childs++;
+       }
+       if (pdata->vibra) {
+               cell = &codec->cells[childs];
+               cell->name = "twl4030_codec_vibra";
+               cell->platform_data = pdata->vibra;
+               cell->data_size = sizeof(*pdata->vibra);
+               childs++;
+       }
+
+       if (childs)
+               ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+                                     childs, NULL, 0);
+       else {
+               dev_err(&pdev->dev, "No platform data found for childs\n");
+               ret = -ENODEV;
+       }
+
+       if (!ret)
+               return 0;
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(codec);
+       twl4030_codec_dev = NULL;
+       return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+       struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+       mfd_remove_devices(&pdev->dev);
+       platform_set_drvdata(pdev, NULL);
+       kfree(codec);
+       twl4030_codec_dev = NULL;
+
+       return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+       .probe          = twl4030_codec_probe,
+       .remove         = __devexit_p(twl4030_codec_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "twl4030_codec",
+       },
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+       return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+       platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+
index a1c47ee95c0eef065e591cfeadde949eafb8e414..cc3f77bd76080d29dc69f4dc68a7639789017b2f 100644 (file)
 #define twl_has_watchdog()        false
 #endif
 
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec()        true
+#else
+#define twl_has_codec()        false
+#endif
+
 /* Triton Core internal information (BEGIN) */
 
 /* Last - for index max*/
@@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                        return PTR_ERR(child);
        }
 
+       if (twl_has_codec() && pdata->codec) {
+               child = add_child(1, "twl4030_codec",
+                               pdata->codec, sizeof(*pdata->codec),
+                               false, 0, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
        if (twl_has_regulator()) {
                /*
                child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
index 508824ee35e6e4bea836d0a42d40a83dc42a570b..5306a759cbdede6e70245440a5140f2df7821e03 100644 (file)
@@ -401,6 +401,24 @@ struct twl4030_power_data {
 
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 
+struct twl4030_codec_audio_data {
+       unsigned int    audio_mclk;
+       unsigned int ramp_delay_value;
+       unsigned int hs_extmute:1;
+       void (*set_hs_extmute)(int mute);
+};
+
+struct twl4030_codec_vibra_data {
+       unsigned int    audio_mclk;
+       unsigned int    coexist;
+};
+
+struct twl4030_codec_data {
+       unsigned int    audio_mclk;
+       struct twl4030_codec_audio_data         *audio;
+       struct twl4030_codec_vibra_data         *vibra;
+};
+
 struct twl4030_platform_data {
        unsigned                                irq_base, irq_end;
        struct twl4030_bci_platform_data        *bci;
@@ -409,6 +427,7 @@ struct twl4030_platform_data {
        struct twl4030_keypad_data              *keypad;
        struct twl4030_usb_data                 *usb;
        struct twl4030_power_data               *power;
+       struct twl4030_codec_data               *codec;
 
        /* LDO regulators */
        struct regulator_init_data              *vdac;
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h
new file mode 100644 (file)
index 0000000..2ec317c
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:     Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_CODEC_H__
+#define __TWL4030_CODEC_H__
+
+/* Codec registers */
+#define TWL4030_REG_CODEC_MODE         0x01
+#define TWL4030_REG_OPTION             0x02
+#define TWL4030_REG_UNKNOWN            0x03
+#define TWL4030_REG_MICBIAS_CTL                0x04
+#define TWL4030_REG_ANAMICL            0x05
+#define TWL4030_REG_ANAMICR            0x06
+#define TWL4030_REG_AVADC_CTL          0x07
+#define TWL4030_REG_ADCMICSEL          0x08
+#define TWL4030_REG_DIGMIXING          0x09
+#define TWL4030_REG_ATXL1PGA           0x0A
+#define TWL4030_REG_ATXR1PGA           0x0B
+#define TWL4030_REG_AVTXL2PGA          0x0C
+#define TWL4030_REG_AVTXR2PGA          0x0D
+#define TWL4030_REG_AUDIO_IF           0x0E
+#define TWL4030_REG_VOICE_IF           0x0F
+#define TWL4030_REG_ARXR1PGA           0x10
+#define TWL4030_REG_ARXL1PGA           0x11
+#define TWL4030_REG_ARXR2PGA           0x12
+#define TWL4030_REG_ARXL2PGA           0x13
+#define TWL4030_REG_VRXPGA             0x14
+#define TWL4030_REG_VSTPGA             0x15
+#define TWL4030_REG_VRX2ARXPGA         0x16
+#define TWL4030_REG_AVDAC_CTL          0x17
+#define TWL4030_REG_ARX2VTXPGA         0x18
+#define TWL4030_REG_ARXL1_APGA_CTL     0x19
+#define TWL4030_REG_ARXR1_APGA_CTL     0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL     0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL     0x1C
+#define TWL4030_REG_ATX2ARXPGA         0x1D
+#define TWL4030_REG_BT_IF              0x1E
+#define TWL4030_REG_BTPGA              0x1F
+#define TWL4030_REG_BTSTPGA            0x20
+#define TWL4030_REG_EAR_CTL            0x21
+#define TWL4030_REG_HS_SEL             0x22
+#define TWL4030_REG_HS_GAIN_SET                0x23
+#define TWL4030_REG_HS_POPN_SET                0x24
+#define TWL4030_REG_PREDL_CTL          0x25
+#define TWL4030_REG_PREDR_CTL          0x26
+#define TWL4030_REG_PRECKL_CTL         0x27
+#define TWL4030_REG_PRECKR_CTL         0x28
+#define TWL4030_REG_HFL_CTL            0x29
+#define TWL4030_REG_HFR_CTL            0x2A
+#define TWL4030_REG_ALC_CTL            0x2B
+#define TWL4030_REG_ALC_SET1           0x2C
+#define TWL4030_REG_ALC_SET2           0x2D
+#define TWL4030_REG_BOOST_CTL          0x2E
+#define TWL4030_REG_SOFTVOL_CTL                0x2F
+#define TWL4030_REG_DTMF_FREQSEL       0x30
+#define TWL4030_REG_DTMF_TONEXT1H      0x31
+#define TWL4030_REG_DTMF_TONEXT1L      0x32
+#define TWL4030_REG_DTMF_TONEXT2H      0x33
+#define TWL4030_REG_DTMF_TONEXT2L      0x34
+#define TWL4030_REG_DTMF_TONOFF                0x35
+#define TWL4030_REG_DTMF_WANONOFF      0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H  0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M  0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L  0x39
+#define TWL4030_REG_APLL_CTL           0x3A
+#define TWL4030_REG_DTMF_CTL           0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2      0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1      0x3D
+#define TWL4030_REG_MISC_SET_1         0x3E
+#define TWL4030_REG_PCMBTMUX           0x3F
+#define TWL4030_REG_RX_PATH_SEL                0x43
+#define TWL4030_REG_VDL_APGA_CTL       0x44
+#define TWL4030_REG_VIBRA_CTL          0x45
+#define TWL4030_REG_VIBRA_SET          0x46
+#define TWL4030_REG_VIBRA_PWM_SET      0x47
+#define TWL4030_REG_ANAMIC_GAIN                0x48
+#define TWL4030_REG_MISC_SET_2         0x49
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+#define TWL4030_APLL_RATE              0xF0
+#define TWL4030_APLL_RATE_8000         0x00
+#define TWL4030_APLL_RATE_11025                0x10
+#define TWL4030_APLL_RATE_12000                0x20
+#define TWL4030_APLL_RATE_16000                0x40
+#define TWL4030_APLL_RATE_22050                0x50
+#define TWL4030_APLL_RATE_24000                0x60
+#define TWL4030_APLL_RATE_32000                0x80
+#define TWL4030_APLL_RATE_44100                0x90
+#define TWL4030_APLL_RATE_48000                0xA0
+#define TWL4030_APLL_RATE_96000                0xE0
+#define TWL4030_SEL_16K                        0x08
+#define TWL4030_CODECPDZ               0x02
+#define TWL4030_OPT_MODE               0x01
+#define TWL4030_OPTION_1               (1 << 0)
+#define TWL4030_OPTION_2               (0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+#define TWL4030_ATXL1_EN               (1 << 0)
+#define TWL4030_ATXR1_EN               (1 << 1)
+#define TWL4030_ATXL2_VTXL_EN          (1 << 2)
+#define TWL4030_ATXR2_VTXR_EN          (1 << 3)
+#define TWL4030_ARXL1_VRX_EN           (1 << 4)
+#define TWL4030_ARXR1_EN               (1 << 5)
+#define TWL4030_ARXL2_EN               (1 << 6)
+#define TWL4030_ARXR2_EN               (1 << 7)
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+#define TWL4030_MICBIAS2_CTL           0x40
+#define TWL4030_MICBIAS1_CTL           0x20
+#define TWL4030_HSMICBIAS_EN           0x04
+#define TWL4030_MICBIAS2_EN            0x02
+#define TWL4030_MICBIAS1_EN            0x01
+
+/* ANAMICL (0x05) Fields */
+#define TWL4030_CNCL_OFFSET_START      0x80
+#define TWL4030_OFFSET_CNCL_SEL                0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1   0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2   0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX    0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL    0x60
+#define TWL4030_MICAMPL_EN             0x10
+#define TWL4030_CKMIC_EN               0x08
+#define TWL4030_AUXL_EN                        0x04
+#define TWL4030_HSMIC_EN               0x02
+#define TWL4030_MAINMIC_EN             0x01
+
+/* ANAMICR (0x06) Fields */
+#define TWL4030_MICAMPR_EN             0x10
+#define TWL4030_AUXR_EN                        0x04
+#define TWL4030_SUBMIC_EN              0x01
+
+/* AVADC_CTL (0x07) Fields */
+#define TWL4030_ADCL_EN                        0x08
+#define TWL4030_AVADC_CLK_PRIORITY     0x04
+#define TWL4030_ADCR_EN                        0x02
+
+/* TWL4030_REG_ADCMICSEL (0x08) Fields */
+#define TWL4030_DIGMIC1_EN             0x08
+#define TWL4030_TX2IN_SEL              0x04
+#define TWL4030_DIGMIC0_EN             0x02
+#define TWL4030_TX1IN_SEL              0x01
+
+/* AUDIO_IF (0x0E) Fields */
+#define TWL4030_AIF_SLAVE_EN           0x80
+#define TWL4030_DATA_WIDTH             0x60
+#define TWL4030_DATA_WIDTH_16S_16W     0x00
+#define TWL4030_DATA_WIDTH_32S_16W     0x40
+#define TWL4030_DATA_WIDTH_32S_24W     0x60
+#define TWL4030_AIF_FORMAT             0x18
+#define TWL4030_AIF_FORMAT_CODEC       0x00
+#define TWL4030_AIF_FORMAT_LEFT                0x08
+#define TWL4030_AIF_FORMAT_RIGHT       0x10
+#define TWL4030_AIF_FORMAT_TDM         0x18
+#define TWL4030_AIF_TRI_EN             0x04
+#define TWL4030_CLK256FS_EN            0x02
+#define TWL4030_AIF_EN                 0x01
+
+/* VOICE_IF (0x0F) Fields */
+#define TWL4030_VIF_SLAVE_EN           0x80
+#define TWL4030_VIF_DIN_EN             0x40
+#define TWL4030_VIF_DOUT_EN            0x20
+#define TWL4030_VIF_SWAP               0x10
+#define TWL4030_VIF_FORMAT             0x08
+#define TWL4030_VIF_TRI_EN             0x04
+#define TWL4030_VIF_SUB_EN             0x02
+#define TWL4030_VIF_EN                 0x01
+
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN               0x30
+
+/* HS_GAIN_SET (0x23) Fields */
+#define TWL4030_HSR_GAIN               0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN      0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB      0x04
+#define TWL4030_HSR_GAIN_0DB           0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB     0x0C
+#define TWL4030_HSL_GAIN               0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN      0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB      0x01
+#define TWL4030_HSL_GAIN_0DB           0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB     0x03
+
+/* HS_POPN_SET (0x24) Fields */
+#define TWL4030_VMID_EN                        0x40
+#define        TWL4030_EXTMUTE                 0x20
+#define TWL4030_RAMP_DELAY             0x1C
+#define TWL4030_RAMP_DELAY_20MS                0x00
+#define TWL4030_RAMP_DELAY_40MS                0x04
+#define TWL4030_RAMP_DELAY_81MS                0x08
+#define TWL4030_RAMP_DELAY_161MS       0x0C
+#define TWL4030_RAMP_DELAY_323MS       0x10
+#define TWL4030_RAMP_DELAY_645MS       0x14
+#define TWL4030_RAMP_DELAY_1291MS      0x18
+#define TWL4030_RAMP_DELAY_2581MS      0x1C
+#define TWL4030_RAMP_EN                        0x02
+
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN             0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN             0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN            0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN            0x30
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN           0x04
+#define TWL4030_HF_CTL_LOOP_EN         0x08
+#define TWL4030_HF_CTL_RAMP_EN         0x10
+#define TWL4030_HF_CTL_REF_EN          0x20
+
+/* APLL_CTL (0x3A) Fields */
+#define TWL4030_APLL_EN                        0x10
+#define TWL4030_APLL_INFREQ            0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ   0x05
+#define TWL4030_APLL_INFREQ_26000KHZ   0x06
+#define TWL4030_APLL_INFREQ_38400KHZ   0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+#define TWL4030_CLK64_EN               0x80
+#define TWL4030_SCRAMBLE_EN            0x40
+#define TWL4030_FMLOOP_EN              0x20
+#define TWL4030_SMOOTH_ANAVOL_EN       0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN      0x01
+
+/* VIBRA_CTL (0x45) */
+#define TWL4030_VIBRA_EN               0x01
+#define TWL4030_VIBRA_DIR              0x02
+#define TWL4030_VIBRA_AUDIO_SEL_L1     (0x00 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R1     (0x01 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_L2     (0x02 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R2     (0x03 << 2)
+#define TWL4030_VIBRA_SEL              0x10
+#define TWL4030_VIBRA_DIR_SEL          0x20
+
+/* TWL4030 codec resource IDs */
+enum twl4030_codec_res {
+       TWL4030_CODEC_RES_POWER = 0,
+       TWL4030_CODEC_RES_APLL,
+       TWL4030_CODEC_RES_MAX,
+};
+
+int twl4030_codec_disable_resource(enum twl4030_codec_res id);
+int twl4030_codec_enable_resource(enum twl4030_codec_res id);
+unsigned int twl4030_codec_get_mclk(void);
+
+#endif /* End of __TWL4030_CODEC_H__ */
index 97ca9af414dc095b5786f785b5c46475c810f7a8..ca24e7f7a3f5b83551efa2b1c62cfaba5991dd28 100644 (file)
@@ -30,6 +30,7 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_DSP_A           3 /* L data MSB after FRM LRC */
 #define SND_SOC_DAIFMT_DSP_B           4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97            5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM             6 /* Pulse density modulation */
 
 /* left and right justified also known as MSB and LSB respectively */
 #define SND_SOC_DAIFMT_MSB             SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,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 +115,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 +141,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 +153,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 c1410e3191e3f6a43987efe818537a096401470e..c5c95e1da65b69752b44ba717e28d5e9dfb3caea 100644 (file)
        .get = snd_soc_dapm_get_enum_double, \
        .put = snd_soc_dapm_put_enum_double, \
        .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum)                   \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_enum_virt, \
+       .put = snd_soc_dapm_put_enum_virt, \
+       .private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
@@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,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 */
@@ -349,6 +363,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 475cb7ed6beccb8cadc8fd952aaf48516e766929..13b117aac5d9bdb2ad328c3df75af4dba1c79201 100644 (file)
@@ -223,15 +223,14 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
                               int addr_bits, int data_bits,
                               enum snd_soc_control_type control);
 
-#ifdef CONFIG_PM
-int snd_soc_suspend_device(struct device *dev);
-int snd_soc_resume_device(struct device *dev);
-#endif
-
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_init_card(struct snd_soc_device *socdev);
+
+/* Utility functions to get clock rates from various things */
+int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
+int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
+int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
 
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -333,6 +332,8 @@ struct snd_soc_jack_gpio {
        int debounce_time;
        struct snd_soc_jack *jack;
        struct work_struct work;
+
+       int (*jack_status_check)(void);
 };
 #endif
 
@@ -413,6 +414,7 @@ struct snd_soc_codec {
        unsigned int num_dai;
 
 #ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_codec_root;
        struct dentry *debugfs_reg;
        struct dentry *debugfs_pop_time;
        struct dentry *debugfs_dapm;
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
new file mode 100644 (file)
index 0000000..5858d06
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Platform header for Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:     Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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 __TLV320DAC33_PLAT_H
+#define __TLV320DAC33_PLAT_H
+
+struct tlv320dac33_platform_data {
+       int power_gpio;
+};
+
+#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h
new file mode 100644 (file)
index 0000000..e8c901e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * TPA6130A2 driver platform header
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef TPA6130A2_PLAT_H
+#define TPA6130A2_PLAT_H
+
+struct tpa6130a2_platform_data {
+       int power_gpio;
+};
+
+#endif
index 0c5eac01bf2e5ffbc8d4691987c7097b90dce90a..1470141d416734841fb30b065d59aa8e607bf81d 100644 (file)
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
 
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
 obj-$(CONFIG_SND_SOC)  += codecs/
index 9eb610c2ba917d42f8e81afc0972879302986928..9df4c68ef000440cfeccd0cd0123de35c03e2a76 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 885ba012557e2d3edc4751520ae192e504d47159..e028744c32ce9928c9160cc4f79f10642fa1d693 100644 (file)
@@ -207,7 +207,7 @@ static int __init at91sam9g20ek_init(void)
        struct clk *pllb;
        int ret;
 
-       if (!machine_is_at91sam9g20ek())
+       if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
                return -ENODEV;
 
        /*
index 594c6c5b7838174d168b9a0e1833584bb38a1b97..fe9f4657c959bd1e1b9ed1b4bd45adadba0f421b 100644 (file)
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *     Manuel Lauss <mano@roarinelk.homelinux.net>
+ *     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
@@ -332,6 +332,30 @@ static int au1xpsc_pcm_new(struct snd_card *card,
 }
 
 static int au1xpsc_pcm_probe(struct platform_device *pdev)
+{
+       if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
+               return -ENODEV;
+
+       return 0;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+       .name           = "au1xpsc-pcm-dbdma",
+       .probe          = au1xpsc_pcm_probe,
+       .remove         = au1xpsc_pcm_remove,
+       .pcm_ops        = &au1xpsc_pcm_ops,
+       .pcm_new        = au1xpsc_pcm_new,
+       .pcm_free       = au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
 {
        struct resource *r;
        int ret;
@@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev)
        }
        (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
 
-       return 0;
+       ret = snd_soc_register_platform(&au1xpsc_soc_platform);
+       if (!ret)
+               return ret;
 
 out2:
        kfree(au1xpsc_audio_pcmdma[PCM_RX]);
@@ -376,10 +402,12 @@ out1:
        return ret;
 }
 
-static int au1xpsc_pcm_remove(struct platform_device *pdev)
+static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
 {
        int i;
 
+       snd_soc_unregister_platform(&au1xpsc_soc_platform);
+
        for (i = 0; i < 2; i++) {
                if (au1xpsc_audio_pcmdma[i]) {
                        au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
@@ -391,32 +419,83 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev)
        return 0;
 }
 
-/* au1xpsc audio platform */
-struct snd_soc_platform au1xpsc_soc_platform = {
-       .name           = "au1xpsc-pcm-dbdma",
-       .probe          = au1xpsc_pcm_probe,
-       .remove         = au1xpsc_pcm_remove,
-       .pcm_ops        = &au1xpsc_pcm_ops,
-       .pcm_new        = au1xpsc_pcm_new,
-       .pcm_free       = au1xpsc_pcm_free_dma_buffers,
+static struct platform_driver au1xpsc_pcm_driver = {
+       .driver = {
+               .name   = "au1xpsc-pcm",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = au1xpsc_pcm_drvprobe,
+       .remove         = __devexit_p(au1xpsc_pcm_drvremove),
 };
-EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
 
-static int __init au1xpsc_audio_dbdma_init(void)
+static int __init au1xpsc_audio_dbdma_load(void)
 {
        au1xpsc_audio_pcmdma[PCM_TX] = NULL;
        au1xpsc_audio_pcmdma[PCM_RX] = NULL;
-       return snd_soc_register_platform(&au1xpsc_soc_platform);
+       return platform_driver_register(&au1xpsc_pcm_driver);
 }
 
-static void __exit au1xpsc_audio_dbdma_exit(void)
+static void __exit au1xpsc_audio_dbdma_unload(void)
 {
-       snd_soc_unregister_platform(&au1xpsc_soc_platform);
+       platform_driver_unregister(&au1xpsc_pcm_driver);
+}
+
+module_init(au1xpsc_audio_dbdma_load);
+module_exit(au1xpsc_audio_dbdma_unload);
+
+
+struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
+{
+       struct resource *res, *r;
+       struct platform_device *pd;
+       int id[2];
+       int ret;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               return NULL;
+       id[0] = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               return NULL;
+       id[1] = r->start;
+
+       res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
+       if (!res)
+               return NULL;
+
+       res[0].start = res[0].end = id[0];
+       res[1].start = res[1].end = id[1];
+       res[0].flags = res[1].flags = IORESOURCE_DMA;
+
+       pd = platform_device_alloc("au1xpsc-pcm", -1);
+       if (!pd)
+               goto out;
+
+       pd->resource = res;
+       pd->num_resources = 2;
+
+       ret = platform_device_add(pd);
+       if (!ret)
+               return pd;
+
+out:
+       kfree(res);
+       return NULL;
 }
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
 
-module_init(au1xpsc_audio_dbdma_init);
-module_exit(au1xpsc_audio_dbdma_exit);
+void au1xpsc_pcm_destroy(struct platform_device *dmapd)
+{
+       if (dmapd) {
+               kfree(dmapd->resource);
+               dmapd->resource = NULL;
+               platform_device_unregister(dmapd);
+       }
+}
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
index a521aa90ddee4ae05e7e8d3d8cfd96a7e360ffef..340311d7fed55055c44fa58520878920ffba3dfe 100644 (file)
@@ -61,7 +61,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-       unsigned short data, retry, tmo;
+       unsigned short retry, tmo;
+       unsigned long data;
 
        au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
        au_sync();
@@ -74,20 +75,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
                          AC97_CDC(pscdata));
                au_sync();
 
-               tmo = 2000;
-               while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
-                       && --tmo)
-                       udelay(2);
+               tmo = 20;
+               do {
+                       udelay(21);
+                       if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+                               break;
+               } while (--tmo);
 
-               data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+               data = au_readl(AC97_CDC(pscdata));
 
                au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
                au_sync();
 
                mutex_unlock(&pscdata->lock);
+
+               if (reg != ((data >> 16) & 0x7f))
+                       tmo = 1;        /* wrong register, try again */
+
        } while (--retry && !tmo);
 
-       return retry ? data : 0xffff;
+       return retry ? data & 0xffff : 0xffff;
 }
 
 /* AC97 controller writes to codec register */
@@ -109,10 +116,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
                          AC97_CDC(pscdata));
                au_sync();
 
-               tmo = 2000;
-               while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
-                      && --tmo)
-                       udelay(2);
+               tmo = 20;
+               do {
+                       udelay(21);
+                       if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+                               break;
+               } while (--tmo);
 
                au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
                au_sync();
@@ -195,7 +204,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
        unsigned long r, ro, stat;
-       int chans, stype = SUBSTREAM_TYPE(substream);
+       int chans, t, stype = SUBSTREAM_TYPE(substream);
 
        chans = params_channels(params);
 
@@ -237,8 +246,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
                au_sync();
 
                /* ...wait for it... */
-               while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
-                       asm volatile ("nop");
+               t = 100;
+               while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
+                       msleep(1);
+
+               if (!t)
+                       printk(KERN_ERR "PSC-AC97: can't disable!\n");
 
                /* ...write config... */
                au_writel(r, AC97_CFG(pscdata));
@@ -249,8 +262,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
                au_sync();
 
                /* ...and wait for ready bit */
-               while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
-                       asm volatile ("nop");
+               t = 100;
+               while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
+                       msleep(1);
+
+               if (!t)
+                       printk(KERN_ERR "PSC-AC97: can't enable!\n");
 
                mutex_unlock(&pscdata->lock);
 
@@ -299,20 +316,56 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
 
 static int au1xpsc_ac97_probe(struct platform_device *pdev,
                              struct snd_soc_dai *dai)
+{
+       return au1xpsc_ac97_workdata ? 0 : -ENODEV;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+                               struct snd_soc_dai *dai)
+{
+}
+
+static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+       .trigger        = au1xpsc_ac97_trigger,
+       .hw_params      = au1xpsc_ac97_hw_params,
+};
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+       .name                   = "au1xpsc_ac97",
+       .ac97_control           = 1,
+       .probe                  = au1xpsc_ac97_probe,
+       .remove                 = au1xpsc_ac97_remove,
+       .playback = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .capture = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .ops = &au1xpsc_ac97_dai_ops,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
 {
        int ret;
        struct resource *r;
        unsigned long sel;
+       struct au1xpsc_audio_data *wd;
 
        if (au1xpsc_ac97_workdata)
                return -EBUSY;
 
-       au1xpsc_ac97_workdata =
-               kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
-       if (!au1xpsc_ac97_workdata)
+       wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+       if (!wd)
                return -ENOMEM;
 
-       mutex_init(&au1xpsc_ac97_workdata->lock);
+       mutex_init(&wd->lock);
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!r) {
@@ -321,81 +374,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
        }
 
        ret = -EBUSY;
-       au1xpsc_ac97_workdata->ioarea =
-               request_mem_region(r->start, r->end - r->start + 1,
+       wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
                                        "au1xpsc_ac97");
-       if (!au1xpsc_ac97_workdata->ioarea)
+       if (!wd->ioarea)
                goto out0;
 
-       au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
-       if (!au1xpsc_ac97_workdata->mmio)
+       wd->mmio = ioremap(r->start, 0xffff);
+       if (!wd->mmio)
                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;
+       wd->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)
-        */
-       sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+       /* preserve PSC clock source set up by platform  */
+       sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
-       au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+       au_writel(0, PSC_SEL(wd));
        au_sync();
-       au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+       au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
        au_sync();
-       /* next up: cold reset.  Dont check for PSC-ready now since
-        * there may not be any codec clock yet.
-        */
 
-       return 0;
+       ret = snd_soc_register_dai(&au1xpsc_ac97_dai);
+       if (ret)
+               goto out1;
 
+       wd->dmapd = au1xpsc_pcm_add(pdev);
+       if (wd->dmapd) {
+               platform_set_drvdata(pdev, wd);
+               au1xpsc_ac97_workdata = wd;     /* MDEV */
+               return 0;
+       }
+
+       snd_soc_unregister_dai(&au1xpsc_ac97_dai);
 out1:
-       release_resource(au1xpsc_ac97_workdata->ioarea);
-       kfree(au1xpsc_ac97_workdata->ioarea);
+       release_resource(wd->ioarea);
+       kfree(wd->ioarea);
 out0:
-       kfree(au1xpsc_ac97_workdata);
-       au1xpsc_ac97_workdata = NULL;
+       kfree(wd);
        return ret;
 }
 
-static void au1xpsc_ac97_remove(struct platform_device *pdev,
-                               struct snd_soc_dai *dai)
+static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
 {
+       struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+       if (wd->dmapd)
+               au1xpsc_pcm_destroy(wd->dmapd);
+
+       snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+
        /* disable PSC completely */
-       au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+       au_writel(0, AC97_CFG(wd));
        au_sync();
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
 
-       iounmap(au1xpsc_ac97_workdata->mmio);
-       release_resource(au1xpsc_ac97_workdata->ioarea);
-       kfree(au1xpsc_ac97_workdata->ioarea);
-       kfree(au1xpsc_ac97_workdata);
-       au1xpsc_ac97_workdata = NULL;
+       iounmap(wd->mmio);
+       release_resource(wd->ioarea);
+       kfree(wd->ioarea);
+       kfree(wd);
+
+       au1xpsc_ac97_workdata = NULL;   /* MDEV */
+
+       return 0;
 }
 
-static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
+#ifdef CONFIG_PM
+static int au1xpsc_ac97_drvsuspend(struct device *dev)
 {
+       struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
        /* save interesting registers and disable PSC */
-       au1xpsc_ac97_workdata->pm[0] =
-                       au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+       wd->pm[0] = au_readl(PSC_SEL(wd));
 
-       au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+       au_writel(0, AC97_CFG(wd));
        au_sync();
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
 
        return 0;
 }
 
-static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
+static int au1xpsc_ac97_drvresume(struct device *dev)
 {
+       struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
        /* restore PSC clock config */
-       au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
-                       PSC_SEL(au1xpsc_ac97_workdata));
+       au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
        au_sync();
 
        /* after this point the ac97 core will cold-reset the codec.
@@ -405,48 +472,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
        return 0;
 }
 
-static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
-       .trigger        = au1xpsc_ac97_trigger,
-       .hw_params      = au1xpsc_ac97_hw_params,
+static struct dev_pm_ops au1xpscac97_pmops = {
+       .suspend        = au1xpsc_ac97_drvsuspend,
+       .resume         = au1xpsc_ac97_drvresume,
 };
 
-struct snd_soc_dai au1xpsc_ac97_dai = {
-       .name                   = "au1xpsc_ac97",
-       .ac97_control           = 1,
-       .probe                  = au1xpsc_ac97_probe,
-       .remove                 = au1xpsc_ac97_remove,
-       .suspend                = au1xpsc_ac97_suspend,
-       .resume                 = au1xpsc_ac97_resume,
-       .playback = {
-               .rates          = AC97_RATES,
-               .formats        = AC97_FMTS,
-               .channels_min   = 2,
-               .channels_max   = 2,
-       },
-       .capture = {
-               .rates          = AC97_RATES,
-               .formats        = AC97_FMTS,
-               .channels_min   = 2,
-               .channels_max   = 2,
+#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
+
+#else
+
+#define AU1XPSCAC97_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_ac97_driver = {
+       .driver = {
+               .name   = "au1xpsc_ac97",
+               .owner  = THIS_MODULE,
+               .pm     = AU1XPSCAC97_PMOPS,
        },
-       .ops = &au1xpsc_ac97_dai_ops,
+       .probe          = au1xpsc_ac97_drvprobe,
+       .remove         = __devexit_p(au1xpsc_ac97_drvremove),
 };
-EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
 
-static int __init au1xpsc_ac97_init(void)
+static int __init au1xpsc_ac97_load(void)
 {
        au1xpsc_ac97_workdata = NULL;
-       return snd_soc_register_dai(&au1xpsc_ac97_dai);
+       return platform_driver_register(&au1xpsc_ac97_driver);
 }
 
-static void __exit au1xpsc_ac97_exit(void)
+static void __exit au1xpsc_ac97_unload(void)
 {
-       snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+       platform_driver_unregister(&au1xpsc_ac97_driver);
 }
 
-module_init(au1xpsc_ac97_init);
-module_exit(au1xpsc_ac97_exit);
+module_init(au1xpsc_ac97_load);
+module_exit(au1xpsc_ac97_unload);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
+MODULE_AUTHOR("Manuel Lauss");
+
index bb589327ee32e495e706638af92022e9803b5947..0cf2ca61c7764bf38a2df63f088a675fee317265 100644 (file)
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *     Manuel Lauss <mano@roarinelk.homelinux.net>
+ *     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
@@ -264,17 +264,53 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 
 static int au1xpsc_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
+{
+       return  au1xpsc_i2s_workdata ? 0 : -ENODEV;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+                              struct snd_soc_dai *dai)
+{
+}
+
+static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+       .trigger        = au1xpsc_i2s_trigger,
+       .hw_params      = au1xpsc_i2s_hw_params,
+       .set_fmt        = au1xpsc_i2s_set_fmt,
+};
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+       .name                   = "au1xpsc_i2s",
+       .probe                  = au1xpsc_i2s_probe,
+       .remove                 = au1xpsc_i2s_remove,
+       .playback = {
+               .rates          = AU1XPSC_I2S_RATES,
+               .formats        = AU1XPSC_I2S_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 8,    /* 2 without external help */
+       },
+       .capture = {
+               .rates          = AU1XPSC_I2S_RATES,
+               .formats        = AU1XPSC_I2S_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 8,    /* 2 without external help */
+       },
+       .ops = &au1xpsc_i2s_dai_ops,
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev)
 {
        struct resource *r;
        unsigned long sel;
        int ret;
+       struct au1xpsc_audio_data *wd;
 
        if (au1xpsc_i2s_workdata)
                return -EBUSY;
 
-       au1xpsc_i2s_workdata =
-               kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
-       if (!au1xpsc_i2s_workdata)
+       wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+       if (!wd)
                return -ENOMEM;
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -284,131 +320,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev,
        }
 
        ret = -EBUSY;
-       au1xpsc_i2s_workdata->ioarea =
-               request_mem_region(r->start, r->end - r->start + 1,
+       wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
                                        "au1xpsc_i2s");
-       if (!au1xpsc_i2s_workdata->ioarea)
+       if (!wd->ioarea)
                goto out0;
 
-       au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
-       if (!au1xpsc_i2s_workdata->mmio)
+       wd->mmio = ioremap(r->start, 0xffff);
+       if (!wd->mmio)
                goto out1;
 
        /* preserve PSC clock source set up by platform (dev.platform_data
         * is already occupied by soc layer)
         */
-       sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+       sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
-       au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
-       au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+       au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
+       au_writel(0, I2S_CFG(wd));
        au_sync();
 
        /* preconfigure: set max rx/tx fifo depths */
-       au1xpsc_i2s_workdata->cfg |=
-                       PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+       wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
 
        /* don't wait for I2S core to become ready now; clocks may not
         * be running yet; depending on clock input for PSC a wait might
         * time out.
         */
 
-       return 0;
+       ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
+       if (ret)
+               goto out1;
 
+       /* finally add the DMA device for this PSC */
+       wd->dmapd = au1xpsc_pcm_add(pdev);
+       if (wd->dmapd) {
+               platform_set_drvdata(pdev, wd);
+               au1xpsc_i2s_workdata = wd;
+               return 0;
+       }
+
+       snd_soc_unregister_dai(&au1xpsc_i2s_dai);
 out1:
-       release_resource(au1xpsc_i2s_workdata->ioarea);
-       kfree(au1xpsc_i2s_workdata->ioarea);
+       release_resource(wd->ioarea);
+       kfree(wd->ioarea);
 out0:
-       kfree(au1xpsc_i2s_workdata);
-       au1xpsc_i2s_workdata = NULL;
+       kfree(wd);
        return ret;
 }
 
-static void au1xpsc_i2s_remove(struct platform_device *pdev,
-                              struct snd_soc_dai *dai)
+static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
 {
-       au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+       struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+       if (wd->dmapd)
+               au1xpsc_pcm_destroy(wd->dmapd);
+
+       snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+
+       au_writel(0, I2S_CFG(wd));
        au_sync();
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
 
-       iounmap(au1xpsc_i2s_workdata->mmio);
-       release_resource(au1xpsc_i2s_workdata->ioarea);
-       kfree(au1xpsc_i2s_workdata->ioarea);
-       kfree(au1xpsc_i2s_workdata);
-       au1xpsc_i2s_workdata = NULL;
+       iounmap(wd->mmio);
+       release_resource(wd->ioarea);
+       kfree(wd->ioarea);
+       kfree(wd);
+
+       au1xpsc_i2s_workdata = NULL;    /* MDEV */
+
+       return 0;
 }
 
-static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
+#ifdef CONFIG_PM
+static int au1xpsc_i2s_drvsuspend(struct device *dev)
 {
+       struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
        /* save interesting register and disable PSC */
-       au1xpsc_i2s_workdata->pm[0] =
-               au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+       wd->pm[0] = au_readl(PSC_SEL(wd));
 
-       au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+       au_writel(0, I2S_CFG(wd));
        au_sync();
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
 
        return 0;
 }
 
-static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_drvresume(struct device *dev)
 {
+       struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
        /* select I2S mode and PSC clock */
-       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+       au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
        au_sync();
-       au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+       au_writel(0, PSC_SEL(wd));
        au_sync();
-       au_writel(au1xpsc_i2s_workdata->pm[0],
-                       PSC_SEL(au1xpsc_i2s_workdata));
+       au_writel(wd->pm[0], PSC_SEL(wd));
        au_sync();
 
        return 0;
 }
 
-static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
-       .trigger        = au1xpsc_i2s_trigger,
-       .hw_params      = au1xpsc_i2s_hw_params,
-       .set_fmt        = au1xpsc_i2s_set_fmt,
+static struct dev_pm_ops au1xpsci2s_pmops = {
+       .suspend        = au1xpsc_i2s_drvsuspend,
+       .resume         = au1xpsc_i2s_drvresume,
 };
 
-struct snd_soc_dai au1xpsc_i2s_dai = {
-       .name                   = "au1xpsc_i2s",
-       .probe                  = au1xpsc_i2s_probe,
-       .remove                 = au1xpsc_i2s_remove,
-       .suspend                = au1xpsc_i2s_suspend,
-       .resume                 = au1xpsc_i2s_resume,
-       .playback = {
-               .rates          = AU1XPSC_I2S_RATES,
-               .formats        = AU1XPSC_I2S_FMTS,
-               .channels_min   = 2,
-               .channels_max   = 8,    /* 2 without external help */
-       },
-       .capture = {
-               .rates          = AU1XPSC_I2S_RATES,
-               .formats        = AU1XPSC_I2S_FMTS,
-               .channels_min   = 2,
-               .channels_max   = 8,    /* 2 without external help */
+#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
+
+#else
+
+#define AU1XPSCI2S_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_i2s_driver = {
+       .driver         = {
+               .name   = "au1xpsc_i2s",
+               .owner  = THIS_MODULE,
+               .pm     = AU1XPSCI2S_PMOPS,
        },
-       .ops = &au1xpsc_i2s_dai_ops,
+       .probe          = au1xpsc_i2s_drvprobe,
+       .remove         = __devexit_p(au1xpsc_i2s_drvremove),
 };
-EXPORT_SYMBOL(au1xpsc_i2s_dai);
 
-static int __init au1xpsc_i2s_init(void)
+static int __init au1xpsc_i2s_load(void)
 {
        au1xpsc_i2s_workdata = NULL;
-       return snd_soc_register_dai(&au1xpsc_i2s_dai);
+       return platform_driver_register(&au1xpsc_i2s_driver);
 }
 
-static void __exit au1xpsc_i2s_exit(void)
+static void __exit au1xpsc_i2s_unload(void)
 {
-       snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+       platform_driver_unregister(&au1xpsc_i2s_driver);
 }
 
-module_init(au1xpsc_i2s_init);
-module_exit(au1xpsc_i2s_exit);
+module_init(au1xpsc_i2s_load);
+module_exit(au1xpsc_i2s_unload);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
index 3f474e8ed4f6bd60a685ceed44d2ffdd4ab89eb8..32d3807d3f5a0d523cdf30a0fde24208f7112db4 100644 (file)
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *     Manuel Lauss <mano@roarinelk.homelinux.net>
+ *     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
@@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai;
 extern struct snd_soc_platform au1xpsc_soc_platform;
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
+/* DBDMA helpers */
+extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
+extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
+
 struct au1xpsc_audio_data {
        void __iomem *mmio;
 
@@ -30,6 +34,7 @@ struct au1xpsc_audio_data {
        unsigned long pm[2];
        struct resource *ioarea;
        struct mutex lock;
+       struct platform_device *dmapd;
 };
 
 #define PCM_TX 0
index cd361e304b0f010e2a2bdf018517ea9234fca8b3..0f45a3f56be8f3ff4ae335941d30c0fda62d24b8 100644 (file)
@@ -52,6 +52,7 @@ static int bf5xx_ad1836_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, 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 |
@@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
        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;
 }
 
index 08269e91810ca6e3bf0189d1cf3844a66e111728..2ef1e5013b8c68292b0fac642f8823af33869c72 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 084b68884adaa7d407e0e67ded3aa07b41edd9ee..3e6ada0dd1c42ef1b07ee144aca320fea9ebeaf5 100644 (file)
@@ -49,7 +49,6 @@ struct bf5xx_i2s_port {
        u16 rcr1;
        u16 tcr2;
        u16 rcr2;
-       int counter;
        int configured;
 };
 
@@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        return ret;
 }
 
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
-{
-       pr_debug("%s enter\n", __func__);
-
-       /*this counter is used for counting how many pcm streams are opened*/
-       bf5xx_i2s.counter++;
-       return 0;
-}
-
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
-       bf5xx_i2s.counter--;
        /* No active stream, SPORT is allowed to be configured again. */
-       if (!bf5xx_i2s.counter)
+       if (!dai->active)
                bf5xx_i2s.configured = 0;
 }
 
@@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
        SNDRV_PCM_FMTBIT_S32_LE)
 
 static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
-       .startup        = bf5xx_i2s_startup,
        .shutdown       = bf5xx_i2s_shutdown,
        .hw_params      = bf5xx_i2s_hw_params,
        .set_fmt        = bf5xx_i2s_set_dai_fmt,
index ccb5e823bd18762c0295f0d01d2d1acb825e7f3e..a8c73cbbd68512198e47b25f478aba52496dc6d1 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 ff546e91a22ef711484aaf07b155a23607667257..4b360124083e7962482e045a57963776ca3c7d22 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 618ec3d90cd408193e0ded528fa7615c9d90925a..04189a18c1bad941dc0a77d6d66134b7492109a7 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 0edca93af3b07f47348dbc126d805bc135606171..52b005f8fed413ca052c2e1bc3dca9e7155c6ea2 100644 (file)
@@ -15,10 +15,12 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AD1836 if SPI_MASTER
        select SND_SOC_AD1938 if SPI_MASTER
        select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+       select SND_SOC_ADS117X
        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
@@ -28,6 +30,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_TLV320AIC23 if I2C
        select SND_SOC_TLV320AIC26 if SPI_MASTER
        select SND_SOC_TLV320AIC3X if I2C
+       select SND_SOC_TPA6130A2 if I2C
+       select SND_SOC_TLV320DAC33 if I2C
        select SND_SOC_TWL4030 if TWL4030_CORE
        select SND_SOC_UDA134X
        select SND_SOC_UDA1380 if I2C
@@ -36,6 +40,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8523 if I2C
        select SND_SOC_WM8580 if I2C
+       select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
+       select SND_SOC_WM8727
        select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@@ -86,6 +92,9 @@ config SND_SOC_AD1980
 
 config SND_SOC_AD73311
        tristate
+       
+config SND_SOC_ADS117X
+       tristate
 
 config SND_SOC_AK4104
        tristate
@@ -96,6 +105,9 @@ config SND_SOC_AK4535
 config SND_SOC_AK4642
        tristate
 
+config SND_SOC_AK4671
+       tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
        tristate
@@ -136,7 +148,11 @@ config SND_SOC_TLV320AIC26
 config SND_SOC_TLV320AIC3X
        tristate
 
+config SND_SOC_TLV320DAC33
+       tristate
+
 config SND_SOC_TWL4030
+       select TWL4030_CODEC
        tristate
 
 config SND_SOC_UDA134X
@@ -160,6 +176,12 @@ config SND_SOC_WM8523
 config SND_SOC_WM8580
        tristate
 
+config SND_SOC_WM8711
+       tristate
+
+config SND_SOC_WM8727
+       tristate
+
 config SND_SOC_WM8728
        tristate
 
@@ -220,3 +242,6 @@ config SND_SOC_WM9713
 # Amp
 config SND_SOC_MAX9877
        tristate
+
+config SND_SOC_TPA6130A2
+       tristate
index fb4af28486bae91ebbae8539b1b7f5d5c2f9fd2a..dbaecb133ac72da0331e66cd7c33d5605d370862 100644 (file)
@@ -3,9 +3,11 @@ snd-soc-ad1836-objs := ad1836.o
 snd-soc-ad1938-objs := ad1938.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
+snd-soc-ads117x-objs := ads117x.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
@@ -16,6 +18,7 @@ snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
@@ -24,6 +27,8 @@ snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8523-objs := wm8523.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8711-objs := wm8711.o
+snd-soc-wm8727-objs := wm8727.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
@@ -47,15 +52,18 @@ snd-soc-wm-hubs-objs := wm_hubs.o
 
 # Amp
 snd-soc-max9877-objs := max9877.o
+snd-soc-tpa6130a2-objs := tpa6130a2.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)       += snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AD1836)   += snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD1938)   += snd-soc-ad1938.o
 obj-$(CONFIG_SND_SOC_AD1980)   += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADS117X)  += snd-soc-ads117x.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
@@ -66,6 +74,7 @@ obj-$(CONFIG_SND_SOC_STAC9766)        += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320DAC33)      += snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_UDA134X)  += snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)  += snd-soc-uda1380.o
@@ -74,6 +83,8 @@ obj-$(CONFIG_SND_SOC_WM8400)  += snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)   += snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8523)   += snd-soc-wm8523.o
 obj-$(CONFIG_SND_SOC_WM8580)   += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8711)   += snd-soc-wm8711.o
+obj-$(CONFIG_SND_SOC_WM8727)   += snd-soc-wm8727.o
 obj-$(CONFIG_SND_SOC_WM8728)   += snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)   += snd-soc-wm8750.o
@@ -97,3 +108,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS)        += snd-soc-wm-hubs.o
 
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
index 932299bb5d1e006a7f0108bd7d7ab0000b3969bc..69bd0acc81c8c45a26adc0cc99d2c32f2289e21a 100644 (file)
@@ -117,9 +117,6 @@ static int ac97_soc_probe(struct platform_device *pdev)
        if (ret < 0)
                goto bus_err;
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0)
-               goto bus_err;
        return 0;
 
 bus_err:
index c48485f2c55db3632f2ae18f412a7ab341b13316..2c18e3d1b71e3aa993719f32c6a911ef9295d4f4 100644 (file)
@@ -385,19 +385,7 @@ static int ad1836_probe(struct platform_device *pdev)
        snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
                                  ARRAY_SIZE(ad1836_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-       snd_soc_dapm_new_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;
-       }
-
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index 34b30efc3cb009977ff7cb30ab413412eda275e0..5d489186c05bb036d5a98aa2bbaa4b0537ad38df 100644 (file)
@@ -592,21 +592,9 @@ static int ad1938_probe(struct platform_device *pdev)
        snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
                                  ARRAY_SIZE(ad1938_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-       snd_soc_dapm_new_widgets(codec);
 
        ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card: %d\n", ret);
-               goto card_err;
-       }
-
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index d7440a982d22794928940c3590cde1765fe56d9a..39c0f7584e656ab78f71d6d70e28abac9ec6d131 100644 (file)
@@ -257,11 +257,6 @@ static int ad1980_soc_probe(struct platform_device *pdev)
 
        snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
                                ARRAY_SIZE(ad1980_snd_ac97_controls));
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "ad1980: failed to register card\n");
-               goto reset_err;
-       }
 
        return 0;
 
index e61dac5e7b8fadac8b04c1e554af3fbd1f793e64..d2fcc601722c17b23d24d144a502009063d7d728 100644 (file)
@@ -64,16 +64,8 @@ static int ad73311_soc_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "ad73311: failed to register card\n");
-               goto register_err;
-       }
-
        return ret;
 
-register_err:
-       snd_soc_free_pcms(socdev);
 pcm_err:
        kfree(socdev->card->codec);
        socdev->card->codec = NULL;
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
new file mode 100644 (file)
index 0000000..cc96411
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * ads117x.c  --  Driver for ads1174/8 ADC chips
+ *
+ * Copyright 2009 ShotSpotter Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "ads117x.h"
+
+#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+struct snd_soc_dai ads117x_dai = {
+/* ADC */
+       .name = "ADS117X ADC",
+       .id = 1,
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 32,
+               .rates = ADS117X_RATES,
+               .formats = ADS117X_FORMATS,},
+};
+EXPORT_SYMBOL_GPL(ads117x_dai);
+
+static int ads117x_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret;
+
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+
+       socdev->card->codec = codec;
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+       codec->name = "ADS117X";
+       codec->owner = THIS_MODULE;
+       codec->dai = &ads117x_dai;
+       codec->num_dai = 1;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "ads117x: failed to create pcms\n");
+               kfree(codec);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ads117x_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       snd_soc_free_pcms(socdev);
+       kfree(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ads117x = {
+       .probe =        ads117x_probe,
+       .remove =       ads117x_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x);
+
+static __devinit int ads117x_platform_probe(struct platform_device *pdev)
+{
+       ads117x_dai.dev = &pdev->dev;
+       return snd_soc_register_dai(&ads117x_dai);
+}
+
+static int __devexit ads117x_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dai(&ads117x_dai);
+       return 0;
+}
+
+static struct platform_driver ads117x_codec_driver = {
+       .driver = {
+                       .name = "ads117x",
+                       .owner = THIS_MODULE,
+       },
+
+       .probe = ads117x_platform_probe,
+       .remove = __devexit_p(ads117x_platform_remove),
+};
+
+static int __init ads117x_init(void)
+{
+       return platform_driver_register(&ads117x_codec_driver);
+}
+module_init(ads117x_init);
+
+static void __exit ads117x_exit(void)
+{
+       platform_driver_unregister(&ads117x_codec_driver);
+}
+module_exit(ads117x_exit);
+
+MODULE_DESCRIPTION("ASoC ads117x driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ads117x.h b/sound/soc/codecs/ads117x.h
new file mode 100644 (file)
index 0000000..dbcf50e
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * ads117x.h  --  Driver for ads1174/8 ADC chips
+ *
+ * Copyright 2009 ShotSpotter Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ *  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.
+ */
+extern struct snd_soc_dai ads117x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ads117x;
index 4d47bc4f74284764ec24434167371d3245009df8..3a14c6fc4f5ee45ece90bbf092bf4ba9e30cfc09 100644 (file)
@@ -313,14 +313,6 @@ static int ak4104_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* Register the socdev */
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card\n");
-               snd_soc_free_pcms(socdev);
-               return ret;
-       }
-
        return 0;
 }
 
index 0abec0d29a964ec457e65e7f618feb4dd6bacd72..ff966567e2bad1e2f5a3ae39b782205cb7122399 100644 (file)
@@ -294,7 +294,6 @@ static int ak4535_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -485,17 +484,9 @@ static int ak4535_init(struct snd_soc_device *socdev)
        snd_soc_add_controls(codec, ak4535_snd_controls,
                                ARRAY_SIZE(ak4535_snd_controls));
        ak4535_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "ak4535: 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);
 
index e057c7b578df980067e129827da14011551f8fe0..b69861d521610d9c4ba16ec7e4d8542202e798fc 100644 (file)
@@ -442,18 +442,9 @@ static int ak4642_probe(struct platform_device *pdev)
                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;
 
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644 (file)
index 0000000..82fca28
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * 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));
+
+       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);
+
+       ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return ret;
+
+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 ca1e24a8f12aaf4e54dc92a50ddbc2141ebd5161..ffe122d1cd76af149d510fe3c80fff4380922bbd 100644 (file)
@@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
        SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
        SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
        SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+       SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
        SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
        SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
        SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
@@ -598,13 +599,6 @@ static int cs4270_probe(struct platform_device *pdev)
                goto error_free_pcms;
        }
 
-       /* And finally, register the socdev */
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card\n");
-               goto error_free_pcms;
-       }
-
        return 0;
 
 error_free_pcms:
@@ -802,22 +796,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
  * and all registers are written back to the hardware when resuming.
  */
 
-static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
-{
-       struct cs4270_private *cs4270 = i2c_get_clientdata(client);
-       struct snd_soc_codec *codec = &cs4270->codec;
-
-       return snd_soc_suspend_device(codec->dev);
-}
-
-static int cs4270_i2c_resume(struct i2c_client *client)
-{
-       struct cs4270_private *cs4270 = i2c_get_clientdata(client);
-       struct snd_soc_codec *codec = &cs4270->codec;
-
-       return snd_soc_resume_device(codec->dev);
-}
-
 static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
        struct snd_soc_codec *codec = cs4270_codec;
@@ -853,8 +831,6 @@ static int cs4270_soc_resume(struct platform_device *pdev)
        return snd_soc_write(codec, CS4270_PWRCTL, reg);
 }
 #else
-#define cs4270_i2c_suspend     NULL
-#define cs4270_i2c_resume      NULL
 #define cs4270_soc_suspend     NULL
 #define cs4270_soc_resume      NULL
 #endif /* CONFIG_PM */
@@ -873,8 +849,6 @@ static struct i2c_driver cs4270_i2c_driver = {
        .id_table = cs4270_id,
        .probe = cs4270_i2c_probe,
        .remove = cs4270_i2c_remove,
-       .suspend = cs4270_i2c_suspend,
-       .resume = cs4270_i2c_resume,
 };
 
 /*
index 38eac9c866e1627a60350d9e9e3a0cc78d28a664..e000cdfec1ec20550abe154c2939bad4365e0e7b 100644 (file)
@@ -93,7 +93,6 @@ static int cx20442_add_widgets(struct snd_soc_codec *codec)
        snd_soc_dapm_add_routes(codec, cx20442_audio_map,
                                ARRAY_SIZE(cx20442_audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -355,17 +354,6 @@ static int cx20442_codec_probe(struct platform_device *pdev)
 
        cx20442_add_widgets(codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to register card\n");
-               goto card_err;
-       }
-
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index 5cda9e6b5a74ae5a468f7a8e2eb9ccac6274fb13..2afcd0a8669d308a1ab658cbbfa7f823961fdf8a 100644 (file)
@@ -90,13 +90,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       /* Register Card. */
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "pcm3008: failed to register card\n");
-               goto card_err;
-       }
-
        /* DEM1  DEM0  DE-EMPHASIS_MODE
         * Low   Low   De-emphasis 44.1 kHz ON
         * Low   High  De-emphasis OFF
@@ -136,8 +129,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
 
 gpio_err:
        pcm3008_gpio_free(setup);
-card_err:
-       snd_soc_free_pcms(socdev);
 pcm_err:
        kfree(socdev->card->codec);
 
index c550750c79c0654db16d480e4abee954cc71e09f..d2ff1cde6883c31412559540c7f3a37dcc2d06f8 100644 (file)
@@ -210,7 +210,6 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -613,17 +612,9 @@ static int ssm2602_init(struct snd_soc_device *socdev)
        snd_soc_add_controls(codec, ssm2602_snd_controls,
                                ARRAY_SIZE(ssm2602_snd_controls));
        ssm2602_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               pr_err("ssm2602: 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;
index befc6488c39a54f27cb9d9b27e7a4ca1b7451bd7..bbc72c2ddfca0292f536638cfd62dc278610b045 100644 (file)
@@ -418,9 +418,6 @@ static int stac9766_codec_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
                             ARRAY_SIZE(stac9766_snd_ac97_controls));
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0)
-               goto reset_err;
        return 0;
 
 reset_err:
index 6b24d8bb02bb0915d415de444270608ae48d62c0..a091ce77810890326059fb2030e77b2f5ef0003f 100644 (file)
@@ -395,7 +395,6 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
        /* set up audio path interconnects */
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -707,17 +706,9 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
        snd_soc_add_controls(codec, tlv320aic23_snd_controls,
                                ARRAY_SIZE(tlv320aic23_snd_controls));
        tlv320aic23_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "tlv320aic23: 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;
index 3387d9e736ea23e23e075e0e1a7409c8deebaeab..357b609196e35aecf223be365a383591bec5cd7a 100644 (file)
@@ -356,18 +356,7 @@ static int aic26_probe(struct platform_device *pdev)
                        ARRAY_SIZE(aic26_snd_controls));
        WARN_ON(err < 0);
 
-       /* CODEC is setup, we can register the card now */
-       dev_dbg(&pdev->dev, "Registering card\n");
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "aic26: failed to register card\n");
-               goto card_err;
-       }
        return 0;
-
- card_err:
-       snd_soc_free_pcms(socdev);
-       return ret;
 }
 
 static int aic26_remove(struct platform_device *pdev)
index 3395cf945d565b694dc1849a32027b48802c1e99..2b4dc2b0b01724aa91507e330860d9d7bc7e0eda 100644 (file)
@@ -753,7 +753,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
        /* set up audio path interconnects */
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -1405,18 +1404,8 @@ static int aic3x_probe(struct platform_device *pdev)
 
        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;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
new file mode 100644 (file)
index 0000000..2a013e4
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:     Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.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 <sound/tlv.h>
+
+#include <sound/tlv320dac33-plat.h>
+#include "tlv320dac33.h"
+
+#define DAC33_BUFFER_SIZE_BYTES                24576   /* bytes, 12288 16 bit words,
+                                                * 6144 stereo */
+#define DAC33_BUFFER_SIZE_SAMPLES      6144
+
+#define NSAMPLE_MAX            5700
+
+#define LATENCY_TIME_MS                20
+
+static struct snd_soc_codec *tlv320dac33_codec;
+
+enum dac33_state {
+       DAC33_IDLE = 0,
+       DAC33_PREFILL,
+       DAC33_PLAYBACK,
+       DAC33_FLUSH,
+};
+
+struct tlv320dac33_priv {
+       struct mutex mutex;
+       struct workqueue_struct *dac33_wq;
+       struct work_struct work;
+       struct snd_soc_codec codec;
+       int power_gpio;
+       int chip_power;
+       int irq;
+       unsigned int refclk;
+
+       unsigned int alarm_threshold;   /* set to be half of LATENCY_TIME_MS */
+       unsigned int nsample_min;       /* nsample should not be lower than
+                                        * this */
+       unsigned int nsample_max;       /* nsample should not be higher than
+                                        * this */
+       unsigned int nsample_switch;    /* Use FIFO or bypass FIFO switch */
+       unsigned int nsample;           /* burst read amount from host */
+
+       enum dac33_state state;
+};
+
+static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
+0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */
+0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */
+0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */
+0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */
+0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */
+0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */
+0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */
+0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */
+0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */
+0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */
+0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */
+0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */
+0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */
+0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */
+0x00, 0x00,             /* 0x38 - 0x39 */
+/* Registers 0x3a - 0x3f are reserved  */
+            0x00, 0x00, /* 0x3a - 0x3b */
+0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */
+
+0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */
+0x00, 0x80,             /* 0x44 - 0x45 */
+/* Registers 0x46 - 0x47 are reserved  */
+            0x80, 0x80, /* 0x46 - 0x47 */
+
+0x80, 0x00, 0x00,       /* 0x48 - 0x4a */
+/* Registers 0x4b - 0x7c are reserved  */
+                  0x00, /* 0x4b        */
+0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */
+0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */
+0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */
+0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */
+0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */
+0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */
+0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */
+0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */
+0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */
+0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */
+0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */
+0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */
+0x00,                   /* 0x7c        */
+
+      0xda, 0x33, 0x03, /* 0x7d - 0x7f */
+};
+
+/* Register read and write */
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+                                               unsigned reg)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= DAC33_CACHEREGNUM)
+               return 0;
+
+       return cache[reg];
+}
+
+static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+                                        u8 reg, u8 value)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= DAC33_CACHEREGNUM)
+               return;
+
+       cache[reg] = value;
+}
+
+static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+                     u8 *value)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       int val;
+
+       *value = reg & 0xff;
+
+       /* If powered off, return the cached value */
+       if (dac33->chip_power) {
+               val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+               if (val < 0) {
+                       dev_err(codec->dev, "Read failed (%d)\n", val);
+                       value[0] = dac33_read_reg_cache(codec, reg);
+               } else {
+                       value[0] = val;
+                       dac33_write_reg_cache(codec, reg, val);
+               }
+       } else {
+               value[0] = dac33_read_reg_cache(codec, reg);
+       }
+
+       return 0;
+}
+
+static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+                      unsigned int value)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       u8 data[2];
+       int ret = 0;
+
+       /*
+        * data is
+        *   D15..D8 dac33 register offset
+        *   D7...D0 register data
+        */
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       dac33_write_reg_cache(codec, data[0], data[1]);
+       if (dac33->chip_power) {
+               ret = codec->hw_write(codec->control_data, data, 2);
+               if (ret != 2)
+                       dev_err(codec->dev, "Write failed (%d)\n", ret);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
+                      unsigned int value)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       int ret;
+
+       mutex_lock(&dac33->mutex);
+       ret = dac33_write(codec, reg, value);
+       mutex_unlock(&dac33->mutex);
+
+       return ret;
+}
+
+#define DAC33_I2C_ADDR_AUTOINC 0x80
+static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+                      unsigned int value)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       u8 data[3];
+       int ret = 0;
+
+       /*
+        * data is
+        *   D23..D16 dac33 register offset
+        *   D15..D8  register data MSB
+        *   D7...D0  register data LSB
+        */
+       data[0] = reg & 0xff;
+       data[1] = (value >> 8) & 0xff;
+       data[2] = value & 0xff;
+
+       dac33_write_reg_cache(codec, data[0], data[1]);
+       dac33_write_reg_cache(codec, data[0] + 1, data[2]);
+
+       if (dac33->chip_power) {
+               /* We need to set autoincrement mode for 16 bit writes */
+               data[0] |= DAC33_I2C_ADDR_AUTOINC;
+               ret = codec->hw_write(codec->control_data, data, 3);
+               if (ret != 3)
+                       dev_err(codec->dev, "Write failed (%d)\n", ret);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+static void dac33_restore_regs(struct snd_soc_codec *codec)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       u8 *cache = codec->reg_cache;
+       u8 data[2];
+       int i, ret;
+
+       if (!dac33->chip_power)
+               return;
+
+       for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
+               data[0] = i;
+               data[1] = cache[i];
+               /* Skip the read only registers */
+               if ((i >= DAC33_INT_OSC_STATUS &&
+                               i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
+                   (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
+                   i == DAC33_DAC_STATUS_FLAGS ||
+                   i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
+                   i == DAC33_SRC_EST_REF_CLK_RATIO_B)
+                       continue;
+               ret = codec->hw_write(codec->control_data, data, 2);
+               if (ret != 2)
+                       dev_err(codec->dev, "Write failed (%d)\n", ret);
+       }
+       for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
+               data[0] = i;
+               data[1] = cache[i];
+               ret = codec->hw_write(codec->control_data, data, 2);
+               if (ret != 2)
+                       dev_err(codec->dev, "Write failed (%d)\n", ret);
+       }
+       for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
+               data[0] = i;
+               data[1] = cache[i];
+               ret = codec->hw_write(codec->control_data, data, 2);
+               if (ret != 2)
+                       dev_err(codec->dev, "Write failed (%d)\n", ret);
+       }
+}
+
+static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+{
+       u8 reg;
+
+       reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+       if (power)
+               reg |= DAC33_PDNALLB;
+       else
+               reg &= ~DAC33_PDNALLB;
+       dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+{
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+
+       mutex_lock(&dac33->mutex);
+       if (power) {
+               if (dac33->power_gpio >= 0) {
+                       gpio_set_value(dac33->power_gpio, 1);
+                       dac33->chip_power = 1;
+                       /* Restore registers */
+                       dac33_restore_regs(codec);
+               }
+               dac33_soft_power(codec, 1);
+       } else {
+               dac33_soft_power(codec, 0);
+               if (dac33->power_gpio >= 0) {
+                       gpio_set_value(dac33->power_gpio, 0);
+                       dac33->chip_power = 0;
+               }
+       }
+       mutex_unlock(&dac33->mutex);
+
+}
+
+static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+
+       ucontrol->value.integer.value[0] = dac33->nsample;
+
+       return 0;
+}
+
+static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       int ret = 0;
+
+       if (dac33->nsample == ucontrol->value.integer.value[0])
+               return 0;
+
+       if (ucontrol->value.integer.value[0] < dac33->nsample_min ||
+           ucontrol->value.integer.value[0] > dac33->nsample_max)
+               ret = -EINVAL;
+       else
+               dac33->nsample = ucontrol->value.integer.value[0];
+
+       return ret;
+}
+
+static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+
+       ucontrol->value.integer.value[0] = dac33->nsample_switch;
+
+       return 0;
+}
+
+static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       int ret = 0;
+
+       if (dac33->nsample_switch == ucontrol->value.integer.value[0])
+               return 0;
+       /* Do not allow changes while stream is running*/
+       if (codec->active)
+               return -EPERM;
+
+       if (ucontrol->value.integer.value[0] < 0 ||
+           ucontrol->value.integer.value[0] > 1)
+               ret = -EINVAL;
+       else
+               dac33->nsample_switch = ucontrol->value.integer.value[0];
+
+       return ret;
+}
+
+/*
+ * DACL/R digital volume control:
+ * from 0 dB to -63.5 in 0.5 dB steps
+ * Need to be inverted later on:
+ * 0x00 == 0 dB
+ * 0x7f == -63.5 dB
+ */
+static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0);
+
+static const struct snd_kcontrol_new dac33_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("DAC Digital Playback Volume",
+               DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL,
+               0, 0x7f, 1, dac_digivol_tlv),
+       SOC_DOUBLE_R("DAC Digital Playback Switch",
+                DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1),
+       SOC_DOUBLE_R("Line to Line Out Volume",
+                DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
+};
+
+static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
+       SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
+                dac33_get_nsample, dac33_set_nsample),
+       SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0,
+                dac33_get_nsample_switch, dac33_set_nsample_switch),
+};
+
+/* Analog bypass */
+static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
+       SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
+
+static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
+       SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1);
+
+static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("LEFT_LO"),
+       SND_SOC_DAPM_OUTPUT("RIGHT_LO"),
+
+       SND_SOC_DAPM_INPUT("LINEL"),
+       SND_SOC_DAPM_INPUT("LINER"),
+
+       SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0),
+       SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0),
+
+       /* Analog bypass */
+       SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0,
+                               &dac33_dapm_abypassl_control),
+       SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0,
+                               &dac33_dapm_abypassr_control),
+
+       SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power",
+                        DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
+                        DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Analog bypass */
+       {"Analog Left Bypass", "Switch", "LINEL"},
+       {"Analog Right Bypass", "Switch", "LINER"},
+
+       {"Output Left Amp Power", NULL, "DACL"},
+       {"Output Right Amp Power", NULL, "DACR"},
+
+       {"Output Left Amp Power", NULL, "Analog Left Bypass"},
+       {"Output Right Amp Power", NULL, "Analog Right Bypass"},
+
+       /* output */
+       {"LEFT_LO", NULL, "Output Left Amp Power"},
+       {"RIGHT_LO", NULL, "Output Right Amp Power"},
+};
+
+static int dac33_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, dac33_dapm_widgets,
+                                 ARRAY_SIZE(dac33_dapm_widgets));
+
+       /* set up audio path interconnects */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       return 0;
+}
+
+static int dac33_set_bias_level(struct snd_soc_codec *codec,
+                               enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               dac33_soft_power(codec, 1);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF)
+                       dac33_hard_power(codec, 1);
+               dac33_soft_power(codec, 0);
+               break;
+       case SND_SOC_BIAS_OFF:
+               dac33_hard_power(codec, 0);
+               break;
+       }
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static void dac33_work(struct work_struct *work)
+{
+       struct snd_soc_codec *codec;
+       struct tlv320dac33_priv *dac33;
+       u8 reg;
+
+       dac33 = container_of(work, struct tlv320dac33_priv, work);
+       codec = &dac33->codec;
+
+       mutex_lock(&dac33->mutex);
+       switch (dac33->state) {
+       case DAC33_PREFILL:
+               dac33->state = DAC33_PLAYBACK;
+               dac33_write16(codec, DAC33_NSAMPLE_MSB,
+                               DAC33_THRREG(dac33->nsample));
+               dac33_write16(codec, DAC33_PREFILL_MSB,
+                               DAC33_THRREG(dac33->alarm_threshold));
+               break;
+       case DAC33_PLAYBACK:
+               dac33_write16(codec, DAC33_NSAMPLE_MSB,
+                               DAC33_THRREG(dac33->nsample));
+               break;
+       case DAC33_IDLE:
+               break;
+       case DAC33_FLUSH:
+               dac33->state = DAC33_IDLE;
+               /* Mask all interrupts from dac33 */
+               dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+
+               /* flush fifo */
+               reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+               reg |= DAC33_FIFOFLUSH;
+               dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+               break;
+       }
+       mutex_unlock(&dac33->mutex);
+}
+
+static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
+{
+       struct snd_soc_codec *codec = dev;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+
+       queue_work(dac33->dac33_wq, &dac33->work);
+
+       return IRQ_HANDLED;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       unsigned int pwr_ctrl;
+
+       /* Stop pending workqueue */
+       if (dac33->nsample_switch)
+               cancel_work_sync(&dac33->work);
+
+       mutex_lock(&dac33->mutex);
+       pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+       pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
+       dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+       mutex_unlock(&dac33->mutex);
+}
+
+static void dac33_oscwait(struct snd_soc_codec *codec)
+{
+       int timeout = 20;
+       u8 reg;
+
+       do {
+               msleep(1);
+               dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+       } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
+       if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
+               dev_err(codec->dev,
+                       "internal oscillator calibration failed\n");
+}
+
+static int dac33_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* Check parameters for validity */
+       switch (params_rate(params)) {
+       case 44100:
+       case 48000:
+               break;
+       default:
+               dev_err(codec->dev, "unsupported rate %d\n",
+                       params_rate(params));
+               return -EINVAL;
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       default:
+               dev_err(codec->dev, "unsupported format %d\n",
+                       params_format(params));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define CALC_OSCSET(rate, refclk) ( \
+       ((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+#define CALC_RATIOSET(rate, refclk) ( \
+       ((((refclk  * 100000) / rate) * 16384) + 50000) / 100000)
+
+/*
+ * tlv320dac33 is strict on the sequence of the register writes, if the register
+ * writes happens in different order, than dac33 might end up in unknown state.
+ * Use the known, working sequence of register writes to initialize the dac33.
+ */
+static int dac33_prepare_chip(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
+       u8 aictrl_a, fifoctrl_a;
+
+       switch (substream->runtime->rate) {
+       case 44100:
+       case 48000:
+               oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk);
+               ratioset = CALC_RATIOSET(substream->runtime->rate,
+                                        dac33->refclk);
+               break;
+       default:
+               dev_err(codec->dev, "unsupported rate %d\n",
+                       substream->runtime->rate);
+               return -EINVAL;
+       }
+
+
+       aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+       aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+       fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+       fifoctrl_a &= ~DAC33_WIDTH;
+       switch (substream->runtime->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16);
+               fifoctrl_a |= DAC33_WIDTH;
+               break;
+       default:
+               dev_err(codec->dev, "unsupported format %d\n",
+                       substream->runtime->format);
+               return -EINVAL;
+       }
+
+       mutex_lock(&dac33->mutex);
+       dac33_soft_power(codec, 1);
+
+       reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+       dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+
+       /* Write registers 0x08 and 0x09 (MSB, LSB) */
+       dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+
+       /* calib time: 128 is a nice number ;) */
+       dac33_write(codec, DAC33_CALIB_TIME, 128);
+
+       /* adjustment treshold & step */
+       dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+                                                DAC33_ADJSTEP(1));
+
+       /* div=4 / gain=1 / div */
+       dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
+
+       pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+       pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
+       dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+
+       dac33_oscwait(codec);
+
+       if (dac33->nsample_switch) {
+               /* 50-51 : ASRC Control registers */
+               dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
+               dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
+
+               /* Write registers 0x34 and 0x35 (MSB, LSB) */
+               dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+
+               /* Set interrupts to high active */
+               dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
+
+               dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+                           DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+               dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+       } else {
+               /* 50-51 : ASRC Control registers */
+               dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
+               dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
+       }
+
+       if (dac33->nsample_switch)
+               fifoctrl_a &= ~DAC33_FBYPAS;
+       else
+               fifoctrl_a |= DAC33_FBYPAS;
+       dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
+
+       dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+       reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+       if (dac33->nsample_switch)
+               reg_tmp &= ~DAC33_BCLKON;
+       else
+               reg_tmp |= DAC33_BCLKON;
+       dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+
+       if (dac33->nsample_switch) {
+               /* 20: BCLK divide ratio */
+               dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
+
+               dac33_write16(codec, DAC33_ATHR_MSB,
+                             DAC33_THRREG(dac33->alarm_threshold));
+       } else {
+               dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+       }
+
+       mutex_unlock(&dac33->mutex);
+
+       return 0;
+}
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       unsigned int nsample_limit;
+
+       /* Number of samples (16bit, stereo) in one period */
+       dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
+
+       /* Number of samples (16bit, stereo) in ALSA buffer */
+       dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
+       /* Subtract one period from the total */
+       dac33->nsample_max -= dac33->nsample_min;
+
+       /* Number of samples for LATENCY_TIME_MS / 2 */
+       dac33->alarm_threshold = substream->runtime->rate /
+                                (1000 / (LATENCY_TIME_MS / 2));
+
+       /* Find and fix up the lowest nsmaple limit */
+       nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
+
+       if (dac33->nsample_min < nsample_limit)
+               dac33->nsample_min = nsample_limit;
+
+       if (dac33->nsample < dac33->nsample_min)
+               dac33->nsample = dac33->nsample_min;
+
+       /*
+        * Find and fix up the highest nsmaple limit
+        * In order to not overflow the DAC33 buffer substract the
+        * alarm_threshold value from the size of the DAC33 buffer
+        */
+       nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
+
+       if (dac33->nsample_max > nsample_limit)
+               dac33->nsample_max = nsample_limit;
+
+       if (dac33->nsample > dac33->nsample_max)
+               dac33->nsample = dac33->nsample_max;
+}
+
+static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       dac33_calculate_times(substream);
+       dac33_prepare_chip(substream);
+
+       return 0;
+}
+
+static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (dac33->nsample_switch) {
+                       dac33->state = DAC33_PREFILL;
+                       queue_work(dac33->dac33_wq, &dac33->work);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (dac33->nsample_switch) {
+                       dac33->state = DAC33_FLUSH;
+                       queue_work(dac33->dac33_wq, &dac33->work);
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
+       u8 ioc_reg, asrcb_reg;
+
+       ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+       asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+       switch (clk_id) {
+       case TLV320DAC33_MCLK:
+               ioc_reg |= DAC33_REFSEL;
+               asrcb_reg |= DAC33_SRCREFSEL;
+               break;
+       case TLV320DAC33_SLEEPCLK:
+               ioc_reg &= ~DAC33_REFSEL;
+               asrcb_reg &= ~DAC33_SRCREFSEL;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+               break;
+       }
+       dac33->refclk = freq;
+
+       dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg);
+       dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg);
+
+       return 0;
+}
+
+static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                            unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 aictrl_a, aictrl_b;
+
+       aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+       aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* Codec Master */
+               aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* Codec Slave */
+               aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       aictrl_a &= ~DAC33_AFMT_MASK;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               aictrl_a |= DAC33_AFMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               aictrl_a |= DAC33_AFMT_DSP;
+               aictrl_b &= ~DAC33_DATA_DELAY_MASK;
+               aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               aictrl_a |= DAC33_AFMT_DSP;
+               aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               aictrl_a |= DAC33_AFMT_RIGHT_J;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aictrl_a |= DAC33_AFMT_LEFT_J;
+               break;
+       default:
+               dev_err(codec->dev, "Unsupported format (%u)\n",
+                       fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+               return -EINVAL;
+       }
+
+       dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+       dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+       return 0;
+}
+
+static void dac33_init_chip(struct snd_soc_codec *codec)
+{
+       /* 44-46: DAC Control Registers */
+       /* A : DAC sample rate Fsref/1.5 */
+       dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1));
+       /* B : DAC src=normal, not muted */
+       dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+                                            DAC33_DACSRCL_LEFT);
+       /* C : (defaults) */
+       dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+       /* 64-65 : L&R DAC power control
+        Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
+       dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+       dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+
+       /* 73 : volume soft stepping control,
+        clock source = internal osc (?) */
+       dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+       /* 66 : LOP/LOM Modes */
+       dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
+
+       /* 68 : LOM inverted from LOP */
+       dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
+
+       dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
+}
+
+static int dac33_soc_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct tlv320dac33_priv *dac33;
+       int ret = 0;
+
+       BUG_ON(!tlv320dac33_codec);
+
+       codec = tlv320dac33_codec;
+       socdev->card->codec = codec;
+       dac33 = codec->private_data;
+
+       /* Power up the codec */
+       dac33_hard_power(codec, 1);
+       /* Set default configuration */
+       dac33_init_chip(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\n");
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, dac33_snd_controls,
+                            ARRAY_SIZE(dac33_snd_controls));
+       /* Only add the nSample controls, if we have valid IRQ number */
+       if (dac33->irq >= 0)
+               snd_soc_add_controls(codec, dac33_nsample_snd_controls,
+                                    ARRAY_SIZE(dac33_nsample_snd_controls));
+
+       dac33_add_widgets(codec);
+
+       /* power on device */
+       dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+
+pcm_err:
+       dac33_hard_power(codec, 0);
+       return ret;
+}
+
+static int dac33_soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+static int dac33_soc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int dac33_soc_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       dac33_set_bias_level(codec, codec->suspend_bias_level);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_tlv320dac33 = {
+       .probe = dac33_soc_probe,
+       .remove = dac33_soc_remove,
+       .suspend = dac33_soc_suspend,
+       .resume = dac33_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
+
+#define DAC33_RATES    (SNDRV_PCM_RATE_44100 | \
+                        SNDRV_PCM_RATE_48000)
+#define DAC33_FORMATS  SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops dac33_dai_ops = {
+       .shutdown       = dac33_shutdown,
+       .hw_params      = dac33_hw_params,
+       .prepare        = dac33_pcm_prepare,
+       .trigger        = dac33_pcm_trigger,
+       .set_sysclk     = dac33_set_dai_sysclk,
+       .set_fmt        = dac33_set_dai_fmt,
+};
+
+struct snd_soc_dai dac33_dai = {
+       .name = "tlv320dac33",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = DAC33_RATES,
+               .formats = DAC33_FORMATS,},
+       .ops = &dac33_dai_ops,
+};
+EXPORT_SYMBOL_GPL(dac33_dai);
+
+static int dac33_i2c_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct tlv320dac33_platform_data *pdata;
+       struct tlv320dac33_priv *dac33;
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (client->dev.platform_data == NULL) {
+               dev_err(&client->dev, "Platform data not set\n");
+               return -ENODEV;
+       }
+       pdata = client->dev.platform_data;
+
+       dac33 = kzalloc(sizeof(struct tlv320dac33_priv), GFP_KERNEL);
+       if (dac33 == NULL)
+               return -ENOMEM;
+
+       codec = &dac33->codec;
+       codec->private_data = dac33;
+       codec->control_data = client;
+
+       mutex_init(&codec->mutex);
+       mutex_init(&dac33->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "tlv320dac33";
+       codec->owner = THIS_MODULE;
+       codec->read = dac33_read_reg_cache;
+       codec->write = dac33_write_locked;
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = dac33_set_bias_level;
+       codec->dai = &dac33_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
+       codec->reg_cache = kmemdup(dac33_reg, ARRAY_SIZE(dac33_reg),
+                                  GFP_KERNEL);
+       if (codec->reg_cache == NULL) {
+               ret = -ENOMEM;
+               goto error_reg;
+       }
+
+       i2c_set_clientdata(client, dac33);
+
+       dac33->power_gpio = pdata->power_gpio;
+       dac33->irq = client->irq;
+       dac33->nsample = NSAMPLE_MAX;
+       /* Disable FIFO use by default */
+       dac33->nsample_switch = 0;
+
+       tlv320dac33_codec = codec;
+
+       codec->dev = &client->dev;
+       dac33_dai.dev = codec->dev;
+
+       /* Check if the reset GPIO number is valid and request it */
+       if (dac33->power_gpio >= 0) {
+               ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
+               if (ret < 0) {
+                       dev_err(codec->dev,
+                               "Failed to request reset GPIO (%d)\n",
+                               dac33->power_gpio);
+                       snd_soc_unregister_dai(&dac33_dai);
+                       snd_soc_unregister_codec(codec);
+                       goto error_gpio;
+               }
+               gpio_direction_output(dac33->power_gpio, 0);
+       } else {
+               dac33->chip_power = 1;
+       }
+
+       /* Check if the IRQ number is valid and request it */
+       if (dac33->irq >= 0) {
+               ret = request_irq(dac33->irq, dac33_interrupt_handler,
+                                 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+                                 codec->name, codec);
+               if (ret < 0) {
+                       dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+                                               dac33->irq, ret);
+                       dac33->irq = -1;
+               }
+               if (dac33->irq != -1) {
+                       /* Setup work queue */
+                       dac33->dac33_wq = create_rt_workqueue("tlv320dac33");
+                       if (dac33->dac33_wq == NULL) {
+                               free_irq(dac33->irq, &dac33->codec);
+                               ret = -ENOMEM;
+                               goto error_wq;
+                       }
+
+                       INIT_WORK(&dac33->work, dac33_work);
+               }
+       }
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto error_codec;
+       }
+
+       ret = snd_soc_register_dai(&dac33_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               goto error_codec;
+       }
+
+       /* Shut down the codec for now */
+       dac33_hard_power(codec, 0);
+
+       return ret;
+
+error_codec:
+       if (dac33->irq >= 0) {
+               free_irq(dac33->irq, &dac33->codec);
+               destroy_workqueue(dac33->dac33_wq);
+       }
+error_wq:
+       if (dac33->power_gpio >= 0)
+               gpio_free(dac33->power_gpio);
+error_gpio:
+       kfree(codec->reg_cache);
+error_reg:
+       tlv320dac33_codec = NULL;
+       kfree(dac33);
+
+       return ret;
+}
+
+static int dac33_i2c_remove(struct i2c_client *client)
+{
+       struct tlv320dac33_priv *dac33;
+
+       dac33 = i2c_get_clientdata(client);
+       dac33_hard_power(&dac33->codec, 0);
+
+       if (dac33->power_gpio >= 0)
+               gpio_free(dac33->power_gpio);
+       if (dac33->irq >= 0)
+               free_irq(dac33->irq, &dac33->codec);
+
+       destroy_workqueue(dac33->dac33_wq);
+       snd_soc_unregister_dai(&dac33_dai);
+       snd_soc_unregister_codec(&dac33->codec);
+       kfree(dac33->codec.reg_cache);
+       kfree(dac33);
+       tlv320dac33_codec = NULL;
+
+       return 0;
+}
+
+static const struct i2c_device_id tlv320dac33_i2c_id[] = {
+       {
+               .name = "tlv320dac33",
+               .driver_data = 0,
+       },
+       { },
+};
+
+static struct i2c_driver tlv320dac33_i2c_driver = {
+       .driver = {
+               .name = "tlv320dac33",
+               .owner = THIS_MODULE,
+       },
+       .probe          = dac33_i2c_probe,
+       .remove         = __devexit_p(dac33_i2c_remove),
+       .id_table       = tlv320dac33_i2c_id,
+};
+
+static int __init dac33_module_init(void)
+{
+       int r;
+       r = i2c_add_driver(&tlv320dac33_i2c_driver);
+       if (r < 0) {
+               printk(KERN_ERR "DAC33: driver registration failed\n");
+               return r;
+       }
+       return 0;
+}
+module_init(dac33_module_init);
+
+static void __exit dac33_module_exit(void)
+{
+       i2c_del_driver(&tlv320dac33_i2c_driver);
+}
+module_exit(dac33_module_exit);
+
+
+MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
new file mode 100644 (file)
index 0000000..eb8ae07
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:     Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TLV320DAC33_H
+#define __TLV320DAC33_H
+
+#define DAC33_PAGE_SELECT              0x00
+#define DAC33_PWR_CTRL                 0x01
+#define DAC33_PLL_CTRL_A               0x02
+#define DAC33_PLL_CTRL_B               0x03
+#define DAC33_PLL_CTRL_C               0x04
+#define DAC33_PLL_CTRL_D               0x05
+#define DAC33_PLL_CTRL_E               0x06
+#define DAC33_INT_OSC_CTRL             0x07
+#define DAC33_INT_OSC_FREQ_RAT_A       0x08
+#define DAC33_INT_OSC_FREQ_RAT_B       0x09
+#define DAC33_INT_OSC_DAC_RATIO_SET    0x0A
+#define DAC33_CALIB_TIME               0x0B
+#define DAC33_INT_OSC_CTRL_B           0x0C
+#define DAC33_INT_OSC_CTRL_C           0x0D
+#define DAC33_INT_OSC_STATUS           0x0E
+#define DAC33_INT_OSC_DAC_RATIO_READ   0x0F
+#define DAC33_INT_OSC_FREQ_RAT_READ_A  0x10
+#define DAC33_INT_OSC_FREQ_RAT_READ_B  0x11
+#define DAC33_SER_AUDIOIF_CTRL_A       0x12
+#define DAC33_SER_AUDIOIF_CTRL_B       0x13
+#define DAC33_SER_AUDIOIF_CTRL_C       0x14
+#define DAC33_FIFO_CTRL_A              0x15
+#define DAC33_UTHR_MSB                 0x16
+#define DAC33_UTHR_LSB                 0x17
+#define DAC33_ATHR_MSB                 0x18
+#define DAC33_ATHR_LSB                 0x19
+#define DAC33_LTHR_MSB                 0x1A
+#define DAC33_LTHR_LSB                 0x1B
+#define DAC33_PREFILL_MSB              0x1C
+#define DAC33_PREFILL_LSB              0x1D
+#define DAC33_NSAMPLE_MSB              0x1E
+#define DAC33_NSAMPLE_LSB              0x1F
+#define DAC33_FIFO_WPTR_MSB            0x20
+#define DAC33_FIFO_WPTR_LSB            0x21
+#define DAC33_FIFO_RPTR_MSB            0x22
+#define DAC33_FIFO_RPTR_LSB            0x23
+#define DAC33_FIFO_DEPTH_MSB           0x24
+#define DAC33_FIFO_DEPTH_LSB           0x25
+#define DAC33_SAMPLES_REMAINING_MSB    0x26
+#define DAC33_SAMPLES_REMAINING_LSB    0x27
+#define DAC33_FIFO_IRQ_FLAG            0x28
+#define DAC33_FIFO_IRQ_MASK            0x29
+#define DAC33_FIFO_IRQ_MODE_A          0x2A
+#define DAC33_FIFO_IRQ_MODE_B          0x2B
+#define DAC33_DAC_CTRL_A               0x2C
+#define DAC33_DAC_CTRL_B               0x2D
+#define DAC33_DAC_CTRL_C               0x2E
+#define DAC33_LDAC_DIG_VOL_CTRL                0x2F
+#define DAC33_RDAC_DIG_VOL_CTRL                0x30
+#define DAC33_DAC_STATUS_FLAGS         0x31
+#define DAC33_ASRC_CTRL_A              0x32
+#define DAC33_ASRC_CTRL_B              0x33
+#define DAC33_SRC_REF_CLK_RATIO_A      0x34
+#define DAC33_SRC_REF_CLK_RATIO_B      0x35
+#define DAC33_SRC_EST_REF_CLK_RATIO_A  0x36
+#define DAC33_SRC_EST_REF_CLK_RATIO_B  0x37
+#define DAC33_INTP_CTRL_A              0x38
+#define DAC33_INTP_CTRL_B              0x39
+/* Registers 0x3A - 0x3F Reserved */
+#define DAC33_LDAC_PWR_CTRL            0x40
+#define DAC33_RDAC_PWR_CTRL            0x41
+#define DAC33_OUT_AMP_CM_CTRL          0x42
+#define DAC33_OUT_AMP_PWR_CTRL         0x43
+#define DAC33_OUT_AMP_CTRL             0x44
+#define DAC33_LINEL_TO_LLO_VOL         0x45
+/* Registers 0x45 - 0x47 Reserved */
+#define DAC33_LINER_TO_RLO_VOL         0x48
+#define DAC33_ANA_VOL_SOFT_STEP_CTRL   0x49
+#define DAC33_OSC_TRIM                 0x4A
+/* Registers 0x4B - 0x7C Reserved */
+#define DAC33_DEVICE_ID_MSB            0x7D
+#define DAC33_DEVICE_ID_LSB            0x7E
+#define DAC33_DEVICE_REV_ID            0x7F
+
+#define DAC33_CACHEREGNUM               128
+
+/* Bit definitions */
+
+/* DAC33_PWR_CTRL (0x01) */
+#define DAC33_DACRPDNB                 (0x01 << 0)
+#define DAC33_DACLPDNB                 (0x01 << 1)
+#define DAC33_OSCPDNB                  (0x01 << 2)
+#define DAC33_PLLPDNB                  (0x01 << 3)
+#define DAC33_PDNALLB                  (0x01 << 4)
+#define DAC33_SOFT_RESET               (0x01 << 7)
+
+/* DAC33_INT_OSC_CTRL (0x07) */
+#define DAC33_REFSEL                   (0x01 << 1)
+
+/* DAC33_INT_OSC_CTRL_B (0x0C) */
+#define DAC33_ADJSTEP(x)               (x << 0)
+#define DAC33_ADJTHRSHLD(x)            (x << 4)
+
+/* DAC33_INT_OSC_CTRL_C (0x0D) */
+#define DAC33_REFDIV(x)                        (x << 4)
+
+/* DAC33_INT_OSC_STATUS (0x0E) */
+#define DAC33_OSCSTATUS_IDLE_CALIB     (0x00)
+#define DAC33_OSCSTATUS_NORMAL         (0x01)
+#define DAC33_OSCSTATUS_ADJUSTMENT     (0x03)
+#define DAC33_OSCSTATUS_NOT_USED       (0x02)
+
+/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
+#define DAC33_MSWCLK                   (0x01 << 0)
+#define DAC33_MSBCLK                   (0x01 << 1)
+#define DAC33_AFMT_MASK                        (0x03 << 2)
+#define DAC33_AFMT_I2S                 (0x00 << 2)
+#define DAC33_AFMT_DSP                 (0x01 << 2)
+#define DAC33_AFMT_RIGHT_J             (0x02 << 2)
+#define DAC33_AFMT_LEFT_J              (0x03 << 2)
+#define DAC33_WLEN_MASK                        (0x03 << 4)
+#define DAC33_WLEN_16                  (0x00 << 4)
+#define DAC33_WLEN_20                  (0x01 << 4)
+#define DAC33_WLEN_24                  (0x02 << 4)
+#define DAC33_WLEN_32                  (0x03 << 4)
+#define DAC33_NCYCL_MASK               (0x03 << 6)
+#define DAC33_NCYCL_16                 (0x00 << 6)
+#define DAC33_NCYCL_20                 (0x01 << 6)
+#define DAC33_NCYCL_24                 (0x02 << 6)
+#define DAC33_NCYCL_32                 (0x03 << 6)
+
+/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
+#define DAC33_DATA_DELAY_MASK          (0x03 << 2)
+#define DAC33_DATA_DELAY(x)            (x << 2)
+#define DAC33_BCLKON                   (0x01 << 5)
+
+/* DAC33_FIFO_CTRL_A (0x15) */
+#define DAC33_WIDTH                            (0x01 << 0)
+#define DAC33_FBYPAS                           (0x01 << 1)
+#define DAC33_FAUTO                            (0x01 << 2)
+#define DAC33_FIFOFLUSH                        (0x01 << 3)
+
+/*
+ * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
+ * 13-bit values
+*/
+#define DAC33_THRREG(x)                        (((x) & 0x1FFF) << 3)
+
+/* DAC33_FIFO_IRQ_MASK (0x29) */
+#define DAC33_MNS                      (0x01 << 0)
+#define DAC33_MPS                      (0x01 << 1)
+#define DAC33_MAT                      (0x01 << 2)
+#define DAC33_MLT                      (0x01 << 3)
+#define DAC33_MUT                      (0x01 << 4)
+#define DAC33_MUF                      (0x01 << 5)
+#define DAC33_MOF                      (0x01 << 6)
+
+#define DAC33_FIFO_IRQ_MODE_MASK       (0x03)
+#define DAC33_FIFO_IRQ_MODE_RISING     (0x00)
+#define DAC33_FIFO_IRQ_MODE_FALLING    (0x01)
+#define DAC33_FIFO_IRQ_MODE_LEVEL      (0x02)
+#define DAC33_FIFO_IRQ_MODE_EDGE       (0x03)
+
+/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
+#define DAC33_UTM(x)                   (x << 0)
+#define DAC33_UFM(x)                   (x << 2)
+#define DAC33_OFM(x)                   (x << 4)
+
+/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
+#define DAC33_NSM(x)                   (x << 0)
+#define DAC33_PSM(x)                   (x << 2)
+#define DAC33_ATM(x)                   (x << 4)
+#define DAC33_LTM(x)                   (x << 6)
+
+/* DAC33_DAC_CTRL_A (0x2C) */
+#define DAC33_DACRATE(x)               (x << 0)
+#define DAC33_DACDUAL                  (0x01 << 4)
+#define DAC33_DACLKSEL_MASK            (0x03 << 5)
+#define DAC33_DACLKSEL_INTSOC          (0x00 << 5)
+#define DAC33_DACLKSEL_PLL             (0x01 << 5)
+#define DAC33_DACLKSEL_MCLK            (0x02 << 5)
+#define DAC33_DACLKSEL_BCLK            (0x03 << 5)
+
+/* DAC33_DAC_CTRL_B (0x2D) */
+#define DAC33_DACSRCR_MASK             (0x03 << 0)
+#define DAC33_DACSRCR_MUTE             (0x00 << 0)
+#define DAC33_DACSRCR_RIGHT            (0x01 << 0)
+#define DAC33_DACSRCR_LEFT             (0x02 << 0)
+#define DAC33_DACSRCR_MONOMIX          (0x03 << 0)
+#define DAC33_DACSRCL_MASK             (0x03 << 2)
+#define DAC33_DACSRCL_MUTE             (0x00 << 2)
+#define DAC33_DACSRCL_LEFT             (0x01 << 2)
+#define DAC33_DACSRCL_RIGHT            (0x02 << 2)
+#define DAC33_DACSRCL_MONOMIX          (0x03 << 2)
+#define DAC33_DVOLSTEP_MASK            (0x03 << 4)
+#define DAC33_DVOLSTEP_SS_PERFS                (0x00 << 4)
+#define DAC33_DVOLSTEP_SS_PER2FS       (0x01 << 4)
+#define DAC33_DVOLSTEP_SS_DISABLED     (0x02 << 4)
+#define DAC33_DVOLCTRL_MASK            (0x03 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6)
+#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL        (0x01 << 6)
+#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6)
+
+/* DAC33_DAC_CTRL_C (0x2E) */
+#define DAC33_DEEMENR                  (0x01 << 0)
+#define DAC33_EFFENR                   (0x01 << 1)
+#define DAC33_DEEMENL                  (0x01 << 2)
+#define DAC33_EFFENL                   (0x01 << 3)
+#define DAC33_EN3D                     (0x01 << 4)
+#define DAC33_RESYNMUTE                        (0x01 << 5)
+#define DAC33_RESYNEN                  (0x01 << 6)
+
+/* DAC33_ASRC_CTRL_A (0x32) */
+#define DAC33_SRCBYP                   (0x01 << 0)
+#define DAC33_SRCLKSEL_MASK            (0x03 << 1)
+#define DAC33_SRCLKSEL_INTSOC          (0x00 << 1)
+#define DAC33_SRCLKSEL_PLL             (0x01 << 1)
+#define DAC33_SRCLKSEL_MCLK            (0x02 << 1)
+#define DAC33_SRCLKSEL_BCLK            (0x03 << 1)
+#define DAC33_SRCLKDIV(x)              (x << 3)
+
+/* DAC33_ASRC_CTRL_B (0x33) */
+#define DAC33_SRCSETUP(x)              (x << 0)
+#define DAC33_SRCREFSEL                        (0x01 << 4)
+#define DAC33_SRCREFDIV(x)             (x << 5)
+
+/* DAC33_INTP_CTRL_A (0x38) */
+#define DAC33_INTPSEL                  (0x01 << 0)
+#define DAC33_INTPM_MASK               (0x03 << 1)
+#define DAC33_INTPM_ALOW_OPENDRAIN     (0x00 << 1)
+#define DAC33_INTPM_ALOW               (0x01 << 1)
+#define DAC33_INTPM_AHIGH              (0x02 << 1)
+
+/* DAC33_LDAC_PWR_CTRL (0x40) */
+/* DAC33_RDAC_PWR_CTRL (0x41) */
+#define DAC33_DACLRNUM                 (0x01 << 2)
+#define DAC33_LROUT_GAIN(x)            (x << 0)
+
+/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
+#define DAC33_VOLCLKSEL                        (0x01 << 0)
+#define DAC33_VOLCLKEN                 (0x01 << 1)
+#define DAC33_VOLBYPASS                        (0x01 << 2)
+
+#define TLV320DAC33_MCLK               0
+#define TLV320DAC33_SLEEPCLK           1
+
+extern struct snd_soc_dai dac33_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33;
+
+#endif /* __TLV320DAC33_H */
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
new file mode 100644 (file)
index 0000000..6b650c1
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <sound/tpa6130a2-plat.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tpa6130a2.h"
+
+static struct i2c_client *tpa6130a2_client;
+
+/* This struct is used to save the context */
+struct tpa6130a2_data {
+       struct mutex mutex;
+       unsigned char regs[TPA6130A2_CACHEREGNUM];
+       int power_gpio;
+       unsigned char power_state;
+};
+
+static int tpa6130a2_i2c_read(int reg)
+{
+       struct tpa6130a2_data *data;
+       int val;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       /* If powered off, return the cached value */
+       if (data->power_state) {
+               val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
+               if (val < 0)
+                       dev_err(&tpa6130a2_client->dev, "Read failed\n");
+               else
+                       data->regs[reg] = val;
+       } else {
+               val = data->regs[reg];
+       }
+
+       return val;
+}
+
+static int tpa6130a2_i2c_write(int reg, u8 value)
+{
+       struct tpa6130a2_data *data;
+       int val = 0;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       if (data->power_state) {
+               val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
+               if (val < 0)
+                       dev_err(&tpa6130a2_client->dev, "Write failed\n");
+       }
+
+       /* Either powered on or off, we save the context */
+       data->regs[reg] = value;
+
+       return val;
+}
+
+static u8 tpa6130a2_read(int reg)
+{
+       struct tpa6130a2_data *data;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       return data->regs[reg];
+}
+
+static void tpa6130a2_initialize(void)
+{
+       struct tpa6130a2_data *data;
+       int i;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       for (i = 1; i < TPA6130A2_REG_VERSION; i++)
+               tpa6130a2_i2c_write(i, data->regs[i]);
+}
+
+static void tpa6130a2_power(int power)
+{
+       struct  tpa6130a2_data *data;
+       u8      val;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       mutex_lock(&data->mutex);
+       if (power) {
+               /* Power on */
+               if (data->power_gpio >= 0) {
+                       gpio_set_value(data->power_gpio, 1);
+                       data->power_state = 1;
+                       tpa6130a2_initialize();
+               }
+               /* Clear SWS */
+               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+               val &= ~TPA6130A2_SWS;
+               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+       } else {
+               /* set SWS */
+               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+               val |= TPA6130A2_SWS;
+               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+               /* Power off */
+               if (data->power_gpio >= 0) {
+                       gpio_set_value(data->power_gpio, 0);
+                       data->power_state = 0;
+               }
+       }
+       mutex_unlock(&data->mutex);
+}
+
+static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct tpa6130a2_data *data;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+       unsigned int invert = mc->invert;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       mutex_lock(&data->mutex);
+
+       ucontrol->value.integer.value[0] =
+               (tpa6130a2_read(reg) >> shift) & mask;
+
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       mask - ucontrol->value.integer.value[0];
+
+       mutex_unlock(&data->mutex);
+       return 0;
+}
+
+static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct tpa6130a2_data *data;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+       unsigned int invert = mc->invert;
+       unsigned int val = (ucontrol->value.integer.value[0] & mask);
+       unsigned int val_reg;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       if (invert)
+               val = mask - val;
+
+       mutex_lock(&data->mutex);
+
+       val_reg = tpa6130a2_read(reg);
+       if (((val_reg >> shift) & mask) == val) {
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+
+       val_reg &= ~(mask << shift);
+       val_reg |= val << shift;
+       tpa6130a2_i2c_write(reg, val_reg);
+
+       mutex_unlock(&data->mutex);
+
+       return 1;
+}
+
+/*
+ * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
+ * down in gain.
+ */
+static const unsigned int tpa6130_tlv[] = {
+       TLV_DB_RANGE_HEAD(10),
+       0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
+       2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
+       4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
+       6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
+       8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
+       10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
+       12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
+       14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
+       21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
+       38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
+};
+
+static const struct snd_kcontrol_new tpa6130a2_controls[] = {
+       SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+                      TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
+                      tpa6130a2_get_reg, tpa6130a2_set_reg,
+                      tpa6130_tlv),
+};
+
+/*
+ * Enable or disable channel (left or right)
+ * The bit number for mute and amplifier are the same per channel:
+ * bit 6: Right channel
+ * bit 7: Left channel
+ * in both registers.
+ */
+static void tpa6130a2_channel_enable(u8 channel, int enable)
+{
+       struct  tpa6130a2_data *data;
+       u8      val;
+
+       BUG_ON(tpa6130a2_client == NULL);
+       data = i2c_get_clientdata(tpa6130a2_client);
+
+       if (enable) {
+               /* Enable channel */
+               /* Enable amplifier */
+               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+               val |= channel;
+               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+               /* Unmute channel */
+               val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+               val &= ~channel;
+               tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+       } else {
+               /* Disable channel */
+               /* Mute channel */
+               val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+               val |= channel;
+               tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+
+               /* Disable amplifier */
+               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+               val &= ~channel;
+               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+       }
+}
+
+static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0);
+               break;
+       }
+       return 0;
+}
+
+static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0);
+               break;
+       }
+       return 0;
+}
+
+static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               tpa6130a2_power(1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               tpa6130a2_power(0);
+               break;
+       }
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+       SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM,
+                       0, 0, NULL, 0, tpa6130a2_left_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM,
+                       0, 0, NULL, 0, tpa6130a2_right_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM,
+                       0, 0, tpa6130a2_supply_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       /* Outputs */
+       SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
+       SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"},
+       {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"},
+
+       {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"},
+       {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"},
+};
+
+int tpa6130a2_add_controls(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
+                               ARRAY_SIZE(tpa6130a2_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       return snd_soc_add_controls(codec, tpa6130a2_controls,
+                               ARRAY_SIZE(tpa6130a2_controls));
+
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+
+static int tpa6130a2_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct device *dev;
+       struct tpa6130a2_data *data;
+       struct tpa6130a2_platform_data *pdata;
+       int ret;
+
+       dev = &client->dev;
+
+       if (client->dev.platform_data == NULL) {
+               dev_err(dev, "Platform data not set\n");
+               dump_stack();
+               return -ENODEV;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL) {
+               dev_err(dev, "Can not allocate memory\n");
+               return -ENOMEM;
+       }
+
+       tpa6130a2_client = client;
+
+       i2c_set_clientdata(tpa6130a2_client, data);
+
+       pdata = client->dev.platform_data;
+       data->power_gpio = pdata->power_gpio;
+
+       mutex_init(&data->mutex);
+
+       /* Set default register values */
+       data->regs[TPA6130A2_REG_CONTROL] =     TPA6130A2_SWS;
+       data->regs[TPA6130A2_REG_VOL_MUTE] =    TPA6130A2_MUTE_R |
+                                               TPA6130A2_MUTE_L;
+
+       if (data->power_gpio >= 0) {
+               ret = gpio_request(data->power_gpio, "tpa6130a2 enable");
+               if (ret < 0) {
+                       dev_err(dev, "Failed to request power GPIO (%d)\n",
+                               data->power_gpio);
+                       goto fail;
+               }
+               gpio_direction_output(data->power_gpio, 0);
+       } else {
+               data->power_state = 1;
+               tpa6130a2_initialize();
+       }
+
+       tpa6130a2_power(1);
+
+       /* Read version */
+       ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
+                                TPA6130A2_VERSION_MASK;
+       if ((ret != 1) && (ret != 2))
+               dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+
+       /* Disable the chip */
+       tpa6130a2_power(0);
+
+       return 0;
+fail:
+       kfree(data);
+       i2c_set_clientdata(tpa6130a2_client, NULL);
+       tpa6130a2_client = NULL;
+
+       return ret;
+}
+
+static int tpa6130a2_remove(struct i2c_client *client)
+{
+       struct tpa6130a2_data *data = i2c_get_clientdata(client);
+
+       tpa6130a2_power(0);
+
+       if (data->power_gpio >= 0)
+               gpio_free(data->power_gpio);
+       kfree(data);
+       tpa6130a2_client = NULL;
+
+       return 0;
+}
+
+static const struct i2c_device_id tpa6130a2_id[] = {
+       { "tpa6130a2", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static struct i2c_driver tpa6130a2_i2c_driver = {
+       .driver = {
+               .name = "tpa6130a2",
+               .owner = THIS_MODULE,
+       },
+       .probe = tpa6130a2_probe,
+       .remove = __devexit_p(tpa6130a2_remove),
+       .id_table = tpa6130a2_id,
+};
+
+static int __init tpa6130a2_init(void)
+{
+       return i2c_add_driver(&tpa6130a2_i2c_driver);
+}
+
+static void __exit tpa6130a2_exit(void)
+{
+       i2c_del_driver(&tpa6130a2_i2c_driver);
+}
+
+MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
+MODULE_LICENSE("GPL");
+
+module_init(tpa6130a2_init);
+module_exit(tpa6130a2_exit);
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
new file mode 100644 (file)
index 0000000..57e867f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * ALSA SoC TPA6130A2 amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TPA6130A2_H__
+#define __TPA6130A2_H__
+
+/* Register addresses */
+#define TPA6130A2_REG_CONTROL          0x01
+#define TPA6130A2_REG_VOL_MUTE         0x02
+#define TPA6130A2_REG_OUT_IMPEDANCE    0x03
+#define TPA6130A2_REG_VERSION          0x04
+
+#define TPA6130A2_CACHEREGNUM  (TPA6130A2_REG_VERSION + 1)
+
+/* Register bits */
+/* TPA6130A2_REG_CONTROL (0x01) */
+#define TPA6130A2_SWS                  (0x01 << 0)
+#define TPA6130A2_TERMAL               (0x01 << 1)
+#define TPA6130A2_MODE(x)              (x << 4)
+#define TPA6130A2_MODE_STEREO          (0x00)
+#define TPA6130A2_MODE_DUAL_MONO       (0x01)
+#define TPA6130A2_MODE_BRIDGE          (0x02)
+#define TPA6130A2_MODE_MASK            (0x03)
+#define TPA6130A2_HP_EN_R              (0x01 << 6)
+#define TPA6130A2_HP_EN_L              (0x01 << 7)
+
+/* TPA6130A2_REG_VOL_MUTE (0x02) */
+#define TPA6130A2_VOLUME(x)            ((x & 0x3f) << 0)
+#define TPA6130A2_MUTE_R               (0x01 << 6)
+#define TPA6130A2_MUTE_L               (0x01 << 7)
+
+/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
+#define TPA6130A2_HIZ_R                        (0x01 << 0)
+#define TPA6130A2_HIZ_L                        (0x01 << 1)
+
+/* TPA6130A2_REG_VERSION (0x04) */
+#define TPA6130A2_VERSION_MASK         (0x0f)
+
+extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
+
+#endif /* __TPA6130A2_H__ */
index 4df7c6c61c76da631cd213a25a927e35e7710bc2..5f1681f6ca766531d07270e82c1f81494f0d86a4 100644 (file)
@@ -120,9 +120,10 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
 
 /* codec private data */
 struct twl4030_priv {
-       unsigned int bypass_state;
+       struct snd_soc_codec codec;
+
        unsigned int codec_powered;
-       unsigned int codec_muted;
+       unsigned int apll_enabled;
 
        struct snd_pcm_substream *master_substream;
        struct snd_pcm_substream *slave_substream;
@@ -183,19 +184,20 @@ static int twl4030_write(struct snd_soc_codec *codec,
 static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
 {
        struct twl4030_priv *twl4030 = codec->private_data;
-       u8 mode;
+       int mode;
 
        if (enable == twl4030->codec_powered)
                return;
 
-       mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
        if (enable)
-               mode |= TWL4030_CODECPDZ;
+               mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
        else
-               mode &= ~TWL4030_CODECPDZ;
+               mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
 
-       twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-       twl4030->codec_powered = enable;
+       if (mode >= 0) {
+               twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
+               twl4030->codec_powered = enable;
+       }
 
        /* REVISIT: this delay is present in TI sample drivers */
        /* but there seems to be no TRM requirement for it     */
@@ -212,31 +214,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
 
        /* set all audio section registers to reasonable defaults */
        for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
-               twl4030_write(codec, i, cache[i]);
+               if (i != TWL4030_REG_APLL_CTL)
+                       twl4030_write(codec, i, cache[i]);
 
 }
 
-static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
 {
        struct twl4030_priv *twl4030 = codec->private_data;
-       u8 reg_val;
+       int status;
 
-       if (mute == twl4030->codec_muted)
+       if (enable == twl4030->apll_enabled)
                return;
 
-       if (mute) {
-               /* Disable PLL */
-               reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
-               reg_val &= ~TWL4030_APLL_EN;
-               twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
-       } else {
+       if (enable)
                /* Enable PLL */
-               reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
-               reg_val |= TWL4030_APLL_EN;
-               twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
-       }
+               status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+       else
+               /* Disable PLL */
+               status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
+
+       if (status >= 0)
+               twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
 
-       twl4030->codec_muted = mute;
+       twl4030->apll_enabled = enable;
 }
 
 static void twl4030_power_up(struct snd_soc_codec *codec)
@@ -613,6 +614,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int vibramux_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
+       return 0;
+}
+
+static int apll_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               twl4030_apll_enable(w->codec, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               twl4030_apll_enable(w->codec, 0);
+               break;
+       }
+       return 0;
+}
+
 static void headset_ramp(struct snd_soc_codec *codec, int ramp)
 {
        struct snd_soc_device *socdev = codec->socdev;
@@ -724,67 +746,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static int bypass_event(struct snd_soc_dapm_widget *w,
-               struct snd_kcontrol *kcontrol, int event)
-{
-       struct soc_mixer_control *m =
-               (struct soc_mixer_control *)w->kcontrols->private_value;
-       struct twl4030_priv *twl4030 = w->codec->private_data;
-       unsigned char reg, misc;
-
-       reg = twl4030_read_reg_cache(w->codec, m->reg);
-
-       /*
-        * bypass_state[0:3] - analog HiFi bypass
-        * bypass_state[4]   - analog voice bypass
-        * bypass_state[5]   - digital voice bypass
-        * bypass_state[6:7] - digital HiFi bypass
-        */
-       if (m->reg == TWL4030_REG_VSTPGA) {
-               /* Voice digital bypass */
-               if (reg)
-                       twl4030->bypass_state |= (1 << 5);
-               else
-                       twl4030->bypass_state &= ~(1 << 5);
-       } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
-               /* Analog bypass */
-               if (reg & (1 << m->shift))
-                       twl4030->bypass_state |=
-                               (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
-               else
-                       twl4030->bypass_state &=
-                               ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
-       } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
-               /* Analog voice bypass */
-               if (reg & (1 << m->shift))
-                       twl4030->bypass_state |= (1 << 4);
-               else
-                       twl4030->bypass_state &= ~(1 << 4);
-       } else {
-               /* Digital bypass */
-               if (reg & (0x7 << m->shift))
-                       twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
-               else
-                       twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
-       }
-
-       /* Enable master analog loopback mode if any analog switch is enabled*/
-       misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
-       if (twl4030->bypass_state & 0x1F)
-               misc |= TWL4030_FMLOOP_EN;
-       else
-               misc &= ~TWL4030_FMLOOP_EN;
-       twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
-
-       if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
-               if (twl4030->bypass_state)
-                       twl4030_codec_mute(w->codec, 0);
-               else
-                       twl4030_codec_mute(w->codec, 1);
-       }
-       return 0;
-}
-
 /*
  * Some of the gain controls in TWL (mostly those which are associated with
  * the outputs) are implemented in an interesting way:
@@ -1192,32 +1153,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
                        SND_SOC_NOPM, 0, 0),
 
        /* Analog bypasses */
-       SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_abypassr1_control, bypass_event,
-                       SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_abypassl1_control,
-                       bypass_event, SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_abypassr2_control,
-                       bypass_event, SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_abypassl2_control,
-                       bypass_event, SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_abypassv_control,
-                       bypass_event, SND_SOC_DAPM_POST_REG),
+       SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassr1_control),
+       SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassl1_control),
+       SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassr2_control),
+       SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassl2_control),
+       SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassv_control),
+
+       /* Master analog loopback switch */
+       SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
+                           NULL, 0),
 
        /* Digital bypasses */
-       SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_dbypassl_control, bypass_event,
-                       SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_dbypassr_control, bypass_event,
-                       SND_SOC_DAPM_POST_REG),
-       SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
-                       &twl4030_dapm_dbypassv_control, bypass_event,
-                       SND_SOC_DAPM_POST_REG),
+       SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_dbypassl_control),
+       SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_dbypassr_control),
+       SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_dbypassv_control),
 
        /* Digital mixers, power control for the physical DACs */
        SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
@@ -1243,6 +1200,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
                        TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
 
+       SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
+                           SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
        /* Output MIXER controls */
        /* Earpiece */
        SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1308,8 +1268,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
                        0, 0, NULL, 0, handsfreerpga_event,
                        SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
        /* Vibra */
-       SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
-               &twl4030_dapm_vibra_control),
+       SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+                          &twl4030_dapm_vibra_control, vibramux_event,
+                          SND_SOC_DAPM_PRE_PMU),
        SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
                &twl4030_dapm_vibrapath_control),
 
@@ -1369,6 +1330,13 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Digital R2 Playback Mixer", NULL, "DAC Right2"},
        {"Digital Voice Playback Mixer", NULL, "DAC Voice"},
 
+       /* Supply for the digital part (APLL) */
+       {"Digital R1 Playback Mixer", NULL, "APLL Enable"},
+       {"Digital L1 Playback Mixer", NULL, "APLL Enable"},
+       {"Digital R2 Playback Mixer", NULL, "APLL Enable"},
+       {"Digital L2 Playback Mixer", NULL, "APLL Enable"},
+       {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
+
        {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
        {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
        {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
@@ -1482,6 +1450,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"ADC Virtual Left2", NULL, "TX2 Capture Route"},
        {"ADC Virtual Right2", NULL, "TX2 Capture Route"},
 
+       {"ADC Virtual Left1", NULL, "APLL Enable"},
+       {"ADC Virtual Right1", NULL, "APLL Enable"},
+       {"ADC Virtual Left2", NULL, "APLL Enable"},
+       {"ADC Virtual Right2", NULL, "APLL Enable"},
+
        /* Analog bypass routes */
        {"Right1 Analog Loopback", "Switch", "Analog Right"},
        {"Left1 Analog Loopback", "Switch", "Analog Left"},
@@ -1489,6 +1462,13 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left2 Analog Loopback", "Switch", "Analog Left"},
        {"Voice Analog Loopback", "Switch", "Analog Left"},
 
+       /* Supply for the Analog loopbacks */
+       {"Right1 Analog Loopback", NULL, "FM Loop Enable"},
+       {"Left1 Analog Loopback", NULL, "FM Loop Enable"},
+       {"Right2 Analog Loopback", NULL, "FM Loop Enable"},
+       {"Left2 Analog Loopback", NULL, "FM Loop Enable"},
+       {"Voice Analog Loopback", NULL, "FM Loop Enable"},
+
        {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
        {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
        {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
@@ -1513,32 +1493,20 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
 static int twl4030_set_bias_level(struct snd_soc_codec *codec,
                                  enum snd_soc_bias_level level)
 {
-       struct twl4030_priv *twl4030 = codec->private_data;
-
        switch (level) {
        case SND_SOC_BIAS_ON:
-               twl4030_codec_mute(codec, 0);
                break;
        case SND_SOC_BIAS_PREPARE:
-               twl4030_power_up(codec);
-               if (twl4030->bypass_state)
-                       twl4030_codec_mute(codec, 0);
-               else
-                       twl4030_codec_mute(codec, 1);
                break;
        case SND_SOC_BIAS_STANDBY:
-               twl4030_power_up(codec);
-               if (twl4030->bypass_state)
-                       twl4030_codec_mute(codec, 0);
-               else
-                       twl4030_codec_mute(codec, 1);
+               if (codec->bias_level == SND_SOC_BIAS_OFF)
+                       twl4030_power_up(codec);
                break;
        case SND_SOC_BIAS_OFF:
                twl4030_power_down(codec);
@@ -1785,29 +1753,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct twl4030_priv *twl4030 = codec->private_data;
-       u8 infreq;
 
        switch (freq) {
        case 19200000:
-               infreq = TWL4030_APLL_INFREQ_19200KHZ;
-               twl4030->sysclk = 19200;
-               break;
        case 26000000:
-               infreq = TWL4030_APLL_INFREQ_26000KHZ;
-               twl4030->sysclk = 26000;
-               break;
        case 38400000:
-               infreq = TWL4030_APLL_INFREQ_38400KHZ;
-               twl4030->sysclk = 38400;
                break;
        default:
-               printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
-                       freq);
+               dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq);
                return -EINVAL;
        }
 
-       infreq |= TWL4030_APLL_EN;
-       twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+       if ((freq / 1000) != twl4030->sysclk) {
+               dev_err(codec->dev,
+                       "Mismatch in APLL mclk: %u (configured: %u)\n",
+                       freq, twl4030->sysclk * 1000);
+               return -EINVAL;
+       }
 
        return 0;
 }
@@ -1905,18 +1867,16 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
-       u8 infreq;
+       struct twl4030_priv *twl4030 = codec->private_data;
        u8 mode;
 
        /* If the system master clock is not 26MHz, the voice PCM interface is
         * not avilable.
         */
-       infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
-               & TWL4030_APLL_INFREQ;
-
-       if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
-               printk(KERN_ERR "TWL4030 voice startup: "
-                       "MCLK is not 26MHz, call set_sysclk() on init\n");
+       if (twl4030->sysclk != 26000) {
+               dev_err(codec->dev, "The board is configured for %u Hz, while"
+                       "the Voice interface needs 26MHz APLL mclk\n",
+                       twl4030->sysclk * 1000);
                return -EINVAL;
        }
 
@@ -1989,21 +1949,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-       u8 infreq;
+       struct twl4030_priv *twl4030 = codec->private_data;
 
-       switch (freq) {
-       case 26000000:
-               infreq = TWL4030_APLL_INFREQ_26000KHZ;
-               break;
-       default:
-               printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
-                       freq);
+       if (freq != 26000000) {
+               dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice"
+                       "interface needs 26MHz APLL mclk\n", freq);
+               return -EINVAL;
+       }
+       if ((freq / 1000) != twl4030->sysclk) {
+               dev_err(codec->dev,
+                       "Mismatch in APLL mclk: %u (configured: %u)\n",
+                       freq, twl4030->sysclk * 1000);
                return -EINVAL;
        }
-
-       infreq |= TWL4030_APLL_EN;
-       twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
-
        return 0;
 }
 
@@ -2121,7 +2079,7 @@ struct snd_soc_dai twl4030_dai[] = {
 };
 EXPORT_SYMBOL_GPL(twl4030_dai);
 
-static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
@@ -2131,7 +2089,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
        return 0;
 }
 
-static int twl4030_resume(struct platform_device *pdev)
+static int twl4030_soc_resume(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
@@ -2141,147 +2099,181 @@ static int twl4030_resume(struct platform_device *pdev)
        return 0;
 }
 
-/*
- * initialize the driver
- * register the mixer and dsp interfaces with the kernel
- */
+static struct snd_soc_codec *twl4030_codec;
 
-static int twl4030_init(struct snd_soc_device *socdev)
+static int twl4030_soc_probe(struct platform_device *pdev)
 {
-       struct snd_soc_codec *codec = socdev->card->codec;
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct twl4030_setup_data *setup = socdev->codec_data;
-       struct twl4030_priv *twl4030 = codec->private_data;
-       int ret = 0;
+       struct snd_soc_codec *codec;
+       struct twl4030_priv *twl4030;
+       int ret;
 
-       printk(KERN_INFO "TWL4030 Audio Codec init \n");
+       BUG_ON(!twl4030_codec);
 
-       codec->name = "twl4030";
-       codec->owner = THIS_MODULE;
-       codec->read = twl4030_read_reg_cache;
-       codec->write = twl4030_write;
-       codec->set_bias_level = twl4030_set_bias_level;
-       codec->dai = twl4030_dai;
-       codec->num_dai = ARRAY_SIZE(twl4030_dai),
-       codec->reg_cache_size = sizeof(twl4030_reg);
-       codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
-                                       GFP_KERNEL);
-       if (codec->reg_cache == NULL)
-               return -ENOMEM;
+       codec = twl4030_codec;
+       twl4030 = codec->private_data;
+       socdev->card->codec = codec;
 
        /* Configuration for headset ramp delay from setup data */
        if (setup) {
                unsigned char hs_pop;
 
-               if (setup->sysclk)
-                       twl4030->sysclk = setup->sysclk;
-               else
-                       twl4030->sysclk = 26000;
+               if (setup->sysclk != twl4030->sysclk)
+                       dev_warn(&pdev->dev,
+                                "Mismatch in APLL mclk: %u (configured: %u)\n",
+                                setup->sysclk, twl4030->sysclk);
 
                hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
                hs_pop &= ~TWL4030_RAMP_DELAY;
                hs_pop |= (setup->ramp_delay_value << 2);
                twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-       } else {
-               twl4030->sysclk = 26000;
        }
 
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0) {
-               printk(KERN_ERR "twl4030: failed to create pcms\n");
-               goto pcm_err;
+               dev_err(&pdev->dev, "failed to create pcms\n");
+               return ret;
        }
 
-       twl4030_init_chip(codec);
-
-       /* power on device */
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_add_controls(codec, twl4030_snd_controls,
                                ARRAY_SIZE(twl4030_snd_controls));
        twl4030_add_widgets(codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "twl4030: failed to register card\n");
-               goto card_err;
-       }
+       return 0;
+}
 
-       return ret;
+static int twl4030_soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
 
-card_err:
+       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
-pcm_err:
-       kfree(codec->reg_cache);
-       return ret;
-}
+       kfree(codec->private_data);
+       kfree(codec);
 
-static struct snd_soc_device *twl4030_socdev;
+       return 0;
+}
 
-static int twl4030_probe(struct platform_device *pdev)
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
        struct snd_soc_codec *codec;
        struct twl4030_priv *twl4030;
+       int ret;
 
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
-               return -ENOMEM;
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform_data is missing\n");
+               return -EINVAL;
+       }
 
        twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
        if (twl4030 == NULL) {
-               kfree(codec);
+               dev_err(&pdev->dev, "Can not allocate memroy\n");
                return -ENOMEM;
        }
 
+       codec = &twl4030->codec;
        codec->private_data = twl4030;
-       socdev->card->codec = codec;
+       codec->dev = &pdev->dev;
+       twl4030_dai[0].dev = &pdev->dev;
+       twl4030_dai[1].dev = &pdev->dev;
+
        mutex_init(&codec->mutex);
        INIT_LIST_HEAD(&codec->dapm_widgets);
        INIT_LIST_HEAD(&codec->dapm_paths);
 
-       twl4030_socdev = socdev;
-       twl4030_init(socdev);
+       codec->name = "twl4030";
+       codec->owner = THIS_MODULE;
+       codec->read = twl4030_read_reg_cache;
+       codec->write = twl4030_write;
+       codec->set_bias_level = twl4030_set_bias_level;
+       codec->dai = twl4030_dai;
+       codec->num_dai = ARRAY_SIZE(twl4030_dai),
+       codec->reg_cache_size = sizeof(twl4030_reg);
+       codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+                                       GFP_KERNEL);
+       if (codec->reg_cache == NULL) {
+               ret = -ENOMEM;
+               goto error_cache;
+       }
+
+       platform_set_drvdata(pdev, twl4030);
+       twl4030_codec = codec;
+
+       /* Set the defaults, and power up the codec */
+       twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
+       twl4030_init_chip(codec);
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto error_codec;
+       }
+
+       ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               goto error_codec;
+       }
 
        return 0;
+
+error_codec:
+       twl4030_power_down(codec);
+       kfree(codec->reg_cache);
+error_cache:
+       kfree(twl4030);
+       return ret;
 }
 
-static int twl4030_remove(struct platform_device *pdev)
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = socdev->card->codec;
+       struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
 
-       printk(KERN_INFO "TWL4030 Audio Codec remove\n");
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-       kfree(codec->private_data);
-       kfree(codec);
+       kfree(twl4030);
 
+       twl4030_codec = NULL;
        return 0;
 }
 
-struct snd_soc_codec_device soc_codec_dev_twl4030 = {
-       .probe = twl4030_probe,
-       .remove = twl4030_remove,
-       .suspend = twl4030_suspend,
-       .resume = twl4030_resume,
+MODULE_ALIAS("platform:twl4030_codec_audio");
+
+static struct platform_driver twl4030_codec_driver = {
+       .probe          = twl4030_codec_probe,
+       .remove         = __devexit_p(twl4030_codec_remove),
+       .driver         = {
+               .name   = "twl4030_codec_audio",
+               .owner  = THIS_MODULE,
+       },
 };
-EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
 
 static int __init twl4030_modinit(void)
 {
-       return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+       return platform_driver_register(&twl4030_codec_driver);
 }
 module_init(twl4030_modinit);
 
 static void __exit twl4030_exit(void)
 {
-       snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+       platform_driver_unregister(&twl4030_codec_driver);
 }
 module_exit(twl4030_exit);
 
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+       .probe = twl4030_soc_probe,
+       .remove = twl4030_soc_remove,
+       .suspend = twl4030_soc_suspend,
+       .resume = twl4030_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
 MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
 MODULE_AUTHOR("Steve Sakoman");
 MODULE_LICENSE("GPL");
index 2b4bfa23f985d5c2a1f74e8cad4f607410a8f23b..dd6396ec9c79751e0fb8f6639d317d78f4e97434 100644 (file)
 #ifndef __TWL4030_AUDIO_H__
 #define __TWL4030_AUDIO_H__
 
-#define TWL4030_REG_CODEC_MODE         0x1
-#define TWL4030_REG_OPTION             0x2
-#define TWL4030_REG_UNKNOWN            0x3
-#define TWL4030_REG_MICBIAS_CTL                0x4
-#define TWL4030_REG_ANAMICL            0x5
-#define TWL4030_REG_ANAMICR            0x6
-#define TWL4030_REG_AVADC_CTL          0x7
-#define TWL4030_REG_ADCMICSEL          0x8
-#define TWL4030_REG_DIGMIXING          0x9
-#define TWL4030_REG_ATXL1PGA           0xA
-#define TWL4030_REG_ATXR1PGA           0xB
-#define TWL4030_REG_AVTXL2PGA          0xC
-#define TWL4030_REG_AVTXR2PGA          0xD
-#define TWL4030_REG_AUDIO_IF           0xE
-#define TWL4030_REG_VOICE_IF           0xF
-#define TWL4030_REG_ARXR1PGA           0x10
-#define TWL4030_REG_ARXL1PGA           0x11
-#define TWL4030_REG_ARXR2PGA           0x12
-#define TWL4030_REG_ARXL2PGA           0x13
-#define TWL4030_REG_VRXPGA             0x14
-#define TWL4030_REG_VSTPGA             0x15
-#define TWL4030_REG_VRX2ARXPGA         0x16
-#define TWL4030_REG_AVDAC_CTL          0x17
-#define TWL4030_REG_ARX2VTXPGA         0x18
-#define TWL4030_REG_ARXL1_APGA_CTL     0x19
-#define TWL4030_REG_ARXR1_APGA_CTL     0x1A
-#define TWL4030_REG_ARXL2_APGA_CTL     0x1B
-#define TWL4030_REG_ARXR2_APGA_CTL     0x1C
-#define TWL4030_REG_ATX2ARXPGA         0x1D
-#define TWL4030_REG_BT_IF              0x1E
-#define TWL4030_REG_BTPGA              0x1F
-#define TWL4030_REG_BTSTPGA            0x20
-#define TWL4030_REG_EAR_CTL            0x21
-#define TWL4030_REG_HS_SEL             0x22
-#define TWL4030_REG_HS_GAIN_SET                0x23
-#define TWL4030_REG_HS_POPN_SET                0x24
-#define TWL4030_REG_PREDL_CTL          0x25
-#define TWL4030_REG_PREDR_CTL          0x26
-#define TWL4030_REG_PRECKL_CTL         0x27
-#define TWL4030_REG_PRECKR_CTL         0x28
-#define TWL4030_REG_HFL_CTL            0x29
-#define TWL4030_REG_HFR_CTL            0x2A
-#define TWL4030_REG_ALC_CTL            0x2B
-#define TWL4030_REG_ALC_SET1           0x2C
-#define TWL4030_REG_ALC_SET2           0x2D
-#define TWL4030_REG_BOOST_CTL          0x2E
-#define TWL4030_REG_SOFTVOL_CTL                0x2F
-#define TWL4030_REG_DTMF_FREQSEL       0x30
-#define TWL4030_REG_DTMF_TONEXT1H      0x31
-#define TWL4030_REG_DTMF_TONEXT1L      0x32
-#define TWL4030_REG_DTMF_TONEXT2H      0x33
-#define TWL4030_REG_DTMF_TONEXT2L      0x34
-#define TWL4030_REG_DTMF_TONOFF                0x35
-#define TWL4030_REG_DTMF_WANONOFF      0x36
-#define TWL4030_REG_I2S_RX_SCRAMBLE_H  0x37
-#define TWL4030_REG_I2S_RX_SCRAMBLE_M  0x38
-#define TWL4030_REG_I2S_RX_SCRAMBLE_L  0x39
-#define TWL4030_REG_APLL_CTL           0x3A
-#define TWL4030_REG_DTMF_CTL           0x3B
-#define TWL4030_REG_DTMF_PGA_CTL2      0x3C
-#define TWL4030_REG_DTMF_PGA_CTL1      0x3D
-#define TWL4030_REG_MISC_SET_1         0x3E
-#define TWL4030_REG_PCMBTMUX           0x3F
-#define TWL4030_REG_RX_PATH_SEL                0x43
-#define TWL4030_REG_VDL_APGA_CTL       0x44
-#define TWL4030_REG_VIBRA_CTL          0x45
-#define TWL4030_REG_VIBRA_SET          0x46
-#define TWL4030_REG_VIBRA_PWM_SET      0x47
-#define TWL4030_REG_ANAMIC_GAIN                0x48
-#define TWL4030_REG_MISC_SET_2         0x49
-#define TWL4030_REG_SW_SHADOW          0x4A
+/* Register descriptions are here */
+#include <linux/mfd/twl4030-codec.h>
 
+/* Sgadow register used by the audio driver */
+#define TWL4030_REG_SW_SHADOW          0x4A
 #define TWL4030_CACHEREGNUM    (TWL4030_REG_SW_SHADOW + 1)
 
-/* Bitfield Definitions */
-
-/* TWL4030_CODEC_MODE (0x01) Fields */
-
-#define TWL4030_APLL_RATE              0xF0
-#define TWL4030_APLL_RATE_8000         0x00
-#define TWL4030_APLL_RATE_11025                0x10
-#define TWL4030_APLL_RATE_12000                0x20
-#define TWL4030_APLL_RATE_16000                0x40
-#define TWL4030_APLL_RATE_22050                0x50
-#define TWL4030_APLL_RATE_24000                0x60
-#define TWL4030_APLL_RATE_32000                0x80
-#define TWL4030_APLL_RATE_44100                0x90
-#define TWL4030_APLL_RATE_48000                0xA0
-#define TWL4030_APLL_RATE_96000                0xE0
-#define TWL4030_SEL_16K                        0x08
-#define TWL4030_CODECPDZ               0x02
-#define TWL4030_OPT_MODE               0x01
-#define TWL4030_OPTION_1               (1 << 0)
-#define TWL4030_OPTION_2               (0 << 0)
-
-/* TWL4030_OPTION (0x02) Fields */
-
-#define TWL4030_ATXL1_EN               (1 << 0)
-#define TWL4030_ATXR1_EN               (1 << 1)
-#define TWL4030_ATXL2_VTXL_EN          (1 << 2)
-#define TWL4030_ATXR2_VTXR_EN          (1 << 3)
-#define TWL4030_ARXL1_VRX_EN           (1 << 4)
-#define TWL4030_ARXR1_EN               (1 << 5)
-#define TWL4030_ARXL2_EN               (1 << 6)
-#define TWL4030_ARXR2_EN               (1 << 7)
-
-/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
-
-#define TWL4030_MICBIAS2_CTL           0x40
-#define TWL4030_MICBIAS1_CTL           0x20
-#define TWL4030_HSMICBIAS_EN           0x04
-#define TWL4030_MICBIAS2_EN            0x02
-#define TWL4030_MICBIAS1_EN            0x01
-
-/* ANAMICL (0x05) Fields */
-
-#define TWL4030_CNCL_OFFSET_START      0x80
-#define TWL4030_OFFSET_CNCL_SEL                0x60
-#define TWL4030_OFFSET_CNCL_SEL_ARX1   0x00
-#define TWL4030_OFFSET_CNCL_SEL_ARX2   0x20
-#define TWL4030_OFFSET_CNCL_SEL_VRX    0x40
-#define TWL4030_OFFSET_CNCL_SEL_ALL    0x60
-#define TWL4030_MICAMPL_EN             0x10
-#define TWL4030_CKMIC_EN               0x08
-#define TWL4030_AUXL_EN                        0x04
-#define TWL4030_HSMIC_EN               0x02
-#define TWL4030_MAINMIC_EN             0x01
-
-/* ANAMICR (0x06) Fields */
-
-#define TWL4030_MICAMPR_EN             0x10
-#define TWL4030_AUXR_EN                        0x04
-#define TWL4030_SUBMIC_EN              0x01
-
-/* AVADC_CTL (0x07) Fields */
-
-#define TWL4030_ADCL_EN                        0x08
-#define TWL4030_AVADC_CLK_PRIORITY     0x04
-#define TWL4030_ADCR_EN                        0x02
-
-/* TWL4030_REG_ADCMICSEL (0x08) Fields */
-
-#define TWL4030_DIGMIC1_EN             0x08
-#define TWL4030_TX2IN_SEL              0x04
-#define TWL4030_DIGMIC0_EN             0x02
-#define TWL4030_TX1IN_SEL              0x01
-
-/* AUDIO_IF (0x0E) Fields */
-
-#define TWL4030_AIF_SLAVE_EN           0x80
-#define TWL4030_DATA_WIDTH             0x60
-#define TWL4030_DATA_WIDTH_16S_16W     0x00
-#define TWL4030_DATA_WIDTH_32S_16W     0x40
-#define TWL4030_DATA_WIDTH_32S_24W     0x60
-#define TWL4030_AIF_FORMAT             0x18
-#define TWL4030_AIF_FORMAT_CODEC       0x00
-#define TWL4030_AIF_FORMAT_LEFT                0x08
-#define TWL4030_AIF_FORMAT_RIGHT       0x10
-#define TWL4030_AIF_FORMAT_TDM         0x18
-#define TWL4030_AIF_TRI_EN             0x04
-#define TWL4030_CLK256FS_EN            0x02
-#define TWL4030_AIF_EN                 0x01
-
-/* VOICE_IF (0x0F) Fields */
-
-#define TWL4030_VIF_SLAVE_EN           0x80
-#define TWL4030_VIF_DIN_EN             0x40
-#define TWL4030_VIF_DOUT_EN            0x20
-#define TWL4030_VIF_SWAP               0x10
-#define TWL4030_VIF_FORMAT             0x08
-#define TWL4030_VIF_TRI_EN             0x04
-#define TWL4030_VIF_SUB_EN             0x02
-#define TWL4030_VIF_EN                 0x01
-
-/* EAR_CTL (0x21) */
-#define TWL4030_EAR_GAIN               0x30
-
-/* HS_GAIN_SET (0x23) Fields */
-
-#define TWL4030_HSR_GAIN               0x0C
-#define TWL4030_HSR_GAIN_PWR_DOWN      0x00
-#define TWL4030_HSR_GAIN_PLUS_6DB      0x04
-#define TWL4030_HSR_GAIN_0DB           0x08
-#define TWL4030_HSR_GAIN_MINUS_6DB     0x0C
-#define TWL4030_HSL_GAIN               0x03
-#define TWL4030_HSL_GAIN_PWR_DOWN      0x00
-#define TWL4030_HSL_GAIN_PLUS_6DB      0x01
-#define TWL4030_HSL_GAIN_0DB           0x02
-#define TWL4030_HSL_GAIN_MINUS_6DB     0x03
-
-/* HS_POPN_SET (0x24) Fields */
-
-#define TWL4030_VMID_EN                        0x40
-#define        TWL4030_EXTMUTE                 0x20
-#define TWL4030_RAMP_DELAY             0x1C
-#define TWL4030_RAMP_DELAY_20MS                0x00
-#define TWL4030_RAMP_DELAY_40MS                0x04
-#define TWL4030_RAMP_DELAY_81MS                0x08
-#define TWL4030_RAMP_DELAY_161MS       0x0C
-#define TWL4030_RAMP_DELAY_323MS       0x10
-#define TWL4030_RAMP_DELAY_645MS       0x14
-#define TWL4030_RAMP_DELAY_1291MS      0x18
-#define TWL4030_RAMP_DELAY_2581MS      0x1C
-#define TWL4030_RAMP_EN                        0x02
-
-/* PREDL_CTL (0x25) */
-#define TWL4030_PREDL_GAIN             0x30
-
-/* PREDR_CTL (0x26) */
-#define TWL4030_PREDR_GAIN             0x30
-
-/* PRECKL_CTL (0x27) */
-#define TWL4030_PRECKL_GAIN            0x30
-
-/* PRECKR_CTL (0x28) */
-#define TWL4030_PRECKR_GAIN            0x30
-
-/* HFL_CTL (0x29, 0x2A) Fields */
-#define TWL4030_HF_CTL_HB_EN           0x04
-#define TWL4030_HF_CTL_LOOP_EN         0x08
-#define TWL4030_HF_CTL_RAMP_EN         0x10
-#define TWL4030_HF_CTL_REF_EN          0x20
-
-/* APLL_CTL (0x3A) Fields */
-
-#define TWL4030_APLL_EN                        0x10
-#define TWL4030_APLL_INFREQ            0x0F
-#define TWL4030_APLL_INFREQ_19200KHZ   0x05
-#define TWL4030_APLL_INFREQ_26000KHZ   0x06
-#define TWL4030_APLL_INFREQ_38400KHZ   0x0F
-
-/* REG_MISC_SET_1 (0x3E) Fields */
-
-#define TWL4030_CLK64_EN               0x80
-#define TWL4030_SCRAMBLE_EN            0x40
-#define TWL4030_FMLOOP_EN              0x20
-#define TWL4030_SMOOTH_ANAVOL_EN       0x02
-#define TWL4030_DIGMIC_LR_SWAP_EN      0x01
-
 /* TWL4030_REG_SW_SHADOW (0x4A) Fields */
 #define TWL4030_HFL_EN                 0x01
 #define TWL4030_HFR_EN                 0x02
@@ -279,3 +47,5 @@ struct twl4030_setup_data {
 };
 
 #endif /* End of __TWL4030_AUDIO_H__ */
+
+
index c33b92edbded03774fc1cc16bfb7f1bda82414dd..aa40d985138fafd4893fa09f47a003c7c975f393 100644 (file)
@@ -562,17 +562,8 @@ static int uda134x_soc_probe(struct platform_device *pdev)
                goto pcm_err;
        }
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "UDA134X: failed to register card\n");
-               goto card_err;
-       }
-
        return 0;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        kfree(codec->reg_cache);
 reg_err:
index 92ec034421543e06f494b7c5075cc3a8767f2a95..a2763c2e7348cea2422b8b0f88f86a3002e40da2 100644 (file)
@@ -378,7 +378,6 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -713,17 +712,9 @@ static int uda1380_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, uda1380_snd_controls,
                                ARRAY_SIZE(uda1380_snd_controls));
        uda1380_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index 593d5b9c9f033ca79e0ee97ab907d29ab05ac0db..f82125d9e85a750e0aefd4d9d3ed7db9f7e30ecb 100644 (file)
@@ -800,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec)
                return ret;
        }
 
-       return snd_soc_dapm_new_widgets(codec);
+       return 0;
 }
 
 static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@@ -1101,7 +1101,7 @@ 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;
@@ -1501,18 +1501,7 @@ static int wm8350_probe(struct platform_device *pdev)
 
        wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to register card\n");
-               goto card_err;
-       }
-
        return 0;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-       return ret;
 }
 
 static int wm8350_remove(struct platform_device *pdev)
@@ -1680,21 +1669,6 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
-{
-       return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8350_codec_resume(struct platform_device *pdev)
-{
-       return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8350_codec_suspend NULL
-#define wm8350_codec_resume NULL
-#endif
-
 static struct platform_driver wm8350_codec_driver = {
        .driver = {
                   .name = "wm8350-codec",
@@ -1702,8 +1676,6 @@ static struct platform_driver wm8350_codec_driver = {
                   },
        .probe = wm8350_codec_probe,
        .remove = __devexit_p(wm8350_codec_remove),
-       .suspend = wm8350_codec_suspend,
-       .resume = wm8350_codec_resume,
 };
 
 static __init int wm8350_init(void)
index b9ef4d9152211a7649249e0f6a96c50f484147b0..b432f4d4a3247bb4df2763bcc5c00a97779a7ed1 100644 (file)
@@ -915,7 +915,6 @@ static int wm8400_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -1011,7 +1010,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;
@@ -1399,17 +1399,6 @@ static int wm8400_probe(struct platform_device *pdev)
        wm8400_add_controls(codec);
        wm8400_add_widgets(codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to register card\n");
-               goto card_err;
-       }
-
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -1558,21 +1547,6 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8400_pdev_resume(struct platform_device *pdev)
-{
-       return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8400_pdev_suspend NULL
-#define wm8400_pdev_resume NULL
-#endif
-
 static struct platform_driver wm8400_codec_driver = {
        .driver = {
                .name = "wm8400-codec",
@@ -1580,8 +1554,6 @@ static struct platform_driver wm8400_codec_driver = {
        },
        .probe = wm8400_codec_probe,
        .remove = __exit_p(wm8400_codec_remove),
-       .suspend = wm8400_pdev_suspend,
-       .resume = wm8400_pdev_resume,
 };
 
 static int __init wm8400_codec_init(void)
index 060d5d06ba955019baf1209595a72734d6866657..265e68c75df8e2908bcf4bcba8724c8c12599013 100644 (file)
@@ -219,7 +219,6 @@ static int wm8510_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -271,8 +270,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;
@@ -604,16 +603,9 @@ static int wm8510_init(struct snd_soc_device *socdev,
        snd_soc_add_controls(codec, wm8510_snd_controls,
                                ARRAY_SIZE(wm8510_snd_controls));
        wm8510_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8510: failed to register card\n");
-               goto card_err;
-       }
+
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        kfree(codec->reg_cache);
        return ret;
index 25870a4652fb67e281dfdb75b28efdb40d983afb..d3a61d7ea0c509414e4a113def31b547fba33d6b 100644 (file)
@@ -117,7 +117,6 @@ static int wm8523_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -448,17 +447,9 @@ static int wm8523_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8523_snd_controls,
                             ARRAY_SIZE(wm8523_snd_controls));
        wm8523_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -638,21 +629,6 @@ static __devexit int wm8523_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8523_i2c_resume(struct i2c_client *i2c)
-{
-       return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8523_i2c_suspend NULL
-#define wm8523_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8523_i2c_id[] = {
        { "wm8523", 0 },
        { }
@@ -666,8 +642,6 @@ static struct i2c_driver wm8523_i2c_driver = {
        },
        .probe =    wm8523_i2c_probe,
        .remove =   __devexit_p(wm8523_i2c_remove),
-       .suspend =  wm8523_i2c_suspend,
-       .resume =   wm8523_i2c_resume,
        .id_table = wm8523_i2c_id,
 };
 #endif
index 6bded8c78150d7f64c52cf7cc4eca8fd89df0866..d077df6f5e7515a6cb98ab727bf83d4529b05274 100644 (file)
@@ -315,7 +315,6 @@ static int wm8580_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -407,8 +406,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;
@@ -800,17 +799,9 @@ static int wm8580_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8580_snd_controls,
                             ARRAY_SIZE(wm8580_snd_controls));
        wm8580_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -961,21 +952,6 @@ static int wm8580_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8580_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8580_i2c_suspend NULL
-#define wm8580_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8580_i2c_id[] = {
        { "wm8580", 0 },
        { }
@@ -989,8 +965,6 @@ static struct i2c_driver wm8580_i2c_driver = {
        },
        .probe =    wm8580_i2c_probe,
        .remove =   wm8580_i2c_remove,
-       .suspend =  wm8580_i2c_suspend,
-       .resume =   wm8580_i2c_resume,
        .id_table = wm8580_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
new file mode 100644 (file)
index 0000000..24a3560
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * wm8711.c  --  WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ *
+ * 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/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.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/tlv.h>
+#include <sound/initval.h>
+
+#include "wm8711.h"
+
+static struct snd_soc_codec *wm8711_codec;
+
+/* codec private data */
+struct wm8711_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM8711_CACHEREGNUM];
+       unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
+       0x0079, 0x0079, 0x000a, 0x0008,
+       0x009f, 0x000a, 0x0000, 0x0000
+};
+
+#define wm8711_reset(c)        snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+       7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+       &wm8711_output_mixer_controls[0],
+       ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       /* output mixer */
+       {"Output Mixer", "Line Bypass Switch", "Line Input"},
+       {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+       /* outputs */
+       {"RHPOUT", NULL, "Output Mixer"},
+       {"ROUT", NULL, "Output Mixer"},
+       {"LHPOUT", NULL, "Output Mixer"},
+       {"LOUT", NULL, "Output Mixer"},
+};
+
+static int wm8711_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
+                                 ARRAY_SIZE(wm8711_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       return 0;
+}
+
+struct _coeff_div {
+       u32 mclk;
+       u32 rate;
+       u16 fs;
+       u8 sr:4;
+       u8 bosr:1;
+       u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+       /* 48k */
+       {12288000, 48000, 256, 0x0, 0x0, 0x0},
+       {18432000, 48000, 384, 0x0, 0x1, 0x0},
+       {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+       /* 32k */
+       {12288000, 32000, 384, 0x6, 0x0, 0x0},
+       {18432000, 32000, 576, 0x6, 0x1, 0x0},
+       {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+       /* 8k */
+       {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+       {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+       {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+       {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+       {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+       /* 96k */
+       {12288000, 96000, 128, 0x7, 0x0, 0x0},
+       {18432000, 96000, 192, 0x7, 0x1, 0x0},
+       {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+       /* 44.1k */
+       {11289600, 44100, 256, 0x8, 0x0, 0x0},
+       {16934400, 44100, 384, 0x8, 0x1, 0x0},
+       {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+       /* 88.2k */
+       {11289600, 88200, 128, 0xf, 0x0, 0x0},
+       {16934400, 88200, 192, 0xf, 0x1, 0x0},
+       {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+               if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+                       return i;
+       }
+       return 0;
+}
+
+static int wm8711_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;
+       struct wm8711_priv *wm8711 = codec->private_data;
+       u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
+       int i = get_coeff(wm8711->sysclk, params_rate(params));
+       u16 srate = (coeff_div[i].sr << 2) |
+               (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+       snd_soc_write(codec, WM8711_SRATE, srate);
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x0004;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x0008;
+               break;
+       }
+
+       snd_soc_write(codec, WM8711_IFACE, iface);
+       return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       /* set active */
+       snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+       return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       /* deactivate */
+       if (!codec->active) {
+               udelay(50);
+               snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+       }
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+       if (mute)
+               snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+       else
+               snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+       return 0;
+}
+
+static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8711_priv *wm8711 = codec->private_data;
+
+       switch (freq) {
+       case 11289600:
+       case 12000000:
+       case 12288000:
+       case 16934400:
+       case 18432000:
+               wm8711->sysclk = freq;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface |= 0x0040;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x0013;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= 0x0090;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= 0x0080;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= 0x0010;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set iface */
+       snd_soc_write(codec, WM8711_IFACE, iface);
+       return 0;
+}
+
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+       enum snd_soc_bias_level level)
+{
+       u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               snd_soc_write(codec, WM8711_PWR, reg);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+               snd_soc_write(codec, WM8711_PWR, 0xffff);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8711_ops = {
+       .prepare = wm8711_pcm_prepare,
+       .hw_params = wm8711_hw_params,
+       .shutdown = wm8711_shutdown,
+       .digital_mute = wm8711_mute,
+       .set_sysclk = wm8711_set_dai_sysclk,
+       .set_fmt = wm8711_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8711_dai = {
+       .name = "WM8711",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8711_RATES,
+               .formats = WM8711_FORMATS,
+       },
+       .ops = &wm8711_ops,
+};
+EXPORT_SYMBOL_GPL(wm8711_dai);
+
+static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8711_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 data[2];
+       u16 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
+               data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+               data[1] = cache[i] & 0x00ff;
+               codec->hw_write(codec->control_data, data, 2);
+       }
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       wm8711_set_bias_level(codec, codec->suspend_bias_level);
+       return 0;
+}
+
+static int wm8711_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8711_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8711_codec;
+       codec = wm8711_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, wm8711_snd_controls,
+                            ARRAY_SIZE(wm8711_snd_controls));
+       wm8711_add_widgets(codec);
+
+       return ret;
+
+pcm_err:
+       return ret;
+}
+
+/* power down chip */
+static int wm8711_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_wm8711 = {
+       .probe =        wm8711_probe,
+       .remove =       wm8711_remove,
+       .suspend =      wm8711_suspend,
+       .resume =       wm8711_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
+
+static int wm8711_register(struct wm8711_priv *wm8711,
+                          enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &wm8711->codec;
+       u16 reg;
+
+       if (wm8711_codec) {
+               dev_err(codec->dev, "Another WM8711 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8711;
+       codec->name = "WM8711";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8711_set_bias_level;
+       codec->dai = &wm8711_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8711_CACHEREGNUM;
+       codec->reg_cache = &wm8711->reg_cache;
+
+       memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       ret = wm8711_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               goto err;
+       }
+
+       wm8711_dai.dev = codec->dev;
+
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Latch the update bits */
+       reg = snd_soc_read(codec, WM8711_LOUT1V);
+       snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
+       reg = snd_soc_read(codec, WM8711_ROUT1V);
+       snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
+
+       wm8711_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(&wm8711_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(wm8711);
+       return ret;
+}
+
+static void wm8711_unregister(struct wm8711_priv *wm8711)
+{
+       wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8711_dai);
+       snd_soc_unregister_codec(&wm8711->codec);
+       kfree(wm8711);
+       wm8711_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8711_spi_probe(struct spi_device *spi)
+{
+       struct snd_soc_codec *codec;
+       struct wm8711_priv *wm8711;
+
+       wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+       if (wm8711 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8711->codec;
+       codec->control_data = spi;
+       codec->dev = &spi->dev;
+
+       dev_set_drvdata(&spi->dev, wm8711);
+
+       return wm8711_register(wm8711, SND_SOC_SPI);
+}
+
+static int __devexit wm8711_spi_remove(struct spi_device *spi)
+{
+       struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
+
+       wm8711_unregister(wm8711);
+
+       return 0;
+}
+
+static struct spi_driver wm8711_spi_driver = {
+       .driver = {
+               .name   = "wm8711",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = wm8711_spi_probe,
+       .remove         = __devexit_p(wm8711_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8711_priv *wm8711;
+       struct snd_soc_codec *codec;
+
+       wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+       if (wm8711 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8711->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8711);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8711_register(wm8711, SND_SOC_I2C);
+}
+
+static __devexit int wm8711_i2c_remove(struct i2c_client *client)
+{
+       struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
+       wm8711_unregister(wm8711);
+       return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+       { "wm8711", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+       .driver = {
+               .name = "WM8711 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8711_i2c_probe,
+       .remove =   __devexit_p(wm8711_i2c_remove),
+       .id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8711_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+                      ret);
+       }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&wm8711_spi_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+                      ret);
+       }
+#endif
+       return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
new file mode 100644 (file)
index 0000000..381e84a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * wm8711.h  --  WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V   0x02
+#define WM8711_ROUT1V   0x03
+#define WM8711_APANA    0x04
+#define WM8711_APDIGI   0x05
+#define WM8711_PWR      0x06
+#define WM8711_IFACE    0x07
+#define WM8711_SRATE    0x08
+#define WM8711_ACTIVE   0x09
+#define WM8711_RESET   0x0f
+
+#define WM8711_CACHEREGNUM     8
+
+#define WM8711_SYSCLK  0
+#define WM8711_DAI             0
+
+struct wm8711_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8711_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
+
+#endif
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
new file mode 100644 (file)
index 0000000..d8ffbd6
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * wm8727.c
+ *
+ *  Created on: 15-Oct-2009
+ *      Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ *  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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "wm8727.h"
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+#define WM8727_RATES  (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+                       SNDRV_PCM_RATE_192000)
+
+
+struct snd_soc_dai wm8727_dai = {
+       .name = "WM8727",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = WM8727_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+               },
+};
+EXPORT_SYMBOL_GPL(wm8727_dai);
+
+static int wm8727_soc_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+       mutex_init(&codec->mutex);
+       codec->name = "WM8727";
+       codec->owner = THIS_MODULE;
+       codec->dai = &wm8727_dai;
+       codec->num_dai = 1;
+       socdev->card->codec = codec;
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               printk(KERN_ERR "wm8727: failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       return ret;
+
+pcm_err:
+       kfree(socdev->card->codec);
+       socdev->card->codec = NULL;
+       return ret;
+}
+
+static int wm8727_soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec == NULL)
+               return 0;
+       snd_soc_free_pcms(socdev);
+       kfree(codec);
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8727 = {
+       .probe =        wm8727_soc_probe,
+       .remove =       wm8727_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
+
+
+static __devinit int wm8727_platform_probe(struct platform_device *pdev)
+{
+       wm8727_dai.dev = &pdev->dev;
+       return snd_soc_register_dai(&wm8727_dai);
+}
+
+static int __devexit wm8727_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dai(&wm8727_dai);
+       return 0;
+}
+
+static struct platform_driver wm8727_codec_driver = {
+       .driver = {
+                       .name = "wm8727-codec",
+                       .owner = THIS_MODULE,
+       },
+
+       .probe = wm8727_platform_probe,
+       .remove = __devexit_p(wm8727_platform_remove),
+};
+
+static int __init wm8727_init(void)
+{
+       return platform_driver_register(&wm8727_codec_driver);
+}
+module_init(wm8727_init);
+
+static void __exit wm8727_exit(void)
+{
+       platform_driver_unregister(&wm8727_codec_driver);
+}
+module_exit(wm8727_exit);
+
+MODULE_DESCRIPTION("ASoC wm8727 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8727.h b/sound/soc/codecs/wm8727.h
new file mode 100644 (file)
index 0000000..ee19aa7
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * wm8727.h
+ *
+ *  Created on: 15-Oct-2009
+ *      Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ *  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 WM8727_H_
+#define WM8727_H_
+
+extern struct snd_soc_dai wm8727_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8727;
+
+#endif /* WM8727_H_ */
index 16e969a762c3934bd9af11b5e240a0f5aa1f0b75..3fb653ba363a1da03ab8d702ab6add9b45e4cb34 100644 (file)
@@ -74,8 +74,6 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
-
        return 0;
 }
 
@@ -287,17 +285,9 @@ static int wm8728_init(struct snd_soc_device *socdev,
        snd_soc_add_controls(codec, wm8728_snd_controls,
                                ARRAY_SIZE(wm8728_snd_controls));
        wm8728_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8728: failed to register card\n");
-               goto card_err;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        kfree(codec->reg_cache);
        return ret;
index d3fd4f28d96eebd48edee92abdca17fe7f406ac6..3a497810f9391c36f9918f0a3c93e20dce5779e5 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 static struct snd_soc_codec *wm8731_codec;
 struct snd_soc_codec_device soc_codec_dev_wm8731;
 
+#define WM8731_NUM_SUPPLIES 4
+static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
+       "AVDD",
+       "HPVDD",
+       "DCVDD",
+       "DBVDD",
+};
+
 /* codec private data */
 struct wm8731_priv {
        struct snd_soc_codec codec;
+       struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
        u16 reg_cache[WM8731_CACHEREGNUM];
        unsigned int sysclk;
 };
@@ -149,7 +159,6 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -422,9 +431,12 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8731_priv *wm8731 = codec->private_data;
 
        snd_soc_write(codec, WM8731_ACTIVE, 0x0);
        wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+                              wm8731->supplies);
        return 0;
 }
 
@@ -432,10 +444,16 @@ static int wm8731_resume(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
-       int i;
+       struct wm8731_priv *wm8731 = codec->private_data;
+       int i, ret;
        u8 data[2];
        u16 *cache = codec->reg_cache;
 
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+                                   wm8731->supplies);
+       if (ret != 0)
+               return ret;
+
        /* Sync reg_cache with the hardware */
        for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
                data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
@@ -444,6 +462,7 @@ static int wm8731_resume(struct platform_device *pdev)
        }
        wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        wm8731_set_bias_level(codec, codec->suspend_bias_level);
+
        return 0;
 }
 #else
@@ -475,17 +494,9 @@ static int wm8731_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8731_snd_controls,
                             ARRAY_SIZE(wm8731_snd_controls));
        wm8731_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -512,7 +523,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
 static int wm8731_register(struct wm8731_priv *wm8731,
                           enum snd_soc_control_type control)
 {
-       int ret;
+       int ret, i;
        struct snd_soc_codec *codec = &wm8731->codec;
 
        if (wm8731_codec) {
@@ -543,10 +554,27 @@ static int wm8731_register(struct wm8731_priv *wm8731,
                goto err;
        }
 
+       for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+               wm8731->supplies[i].supply = wm8731_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+                                wm8731->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+                                   wm8731->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_regulator_get;
+       }
+
        ret = wm8731_reset(codec);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        wm8731_dai.dev = codec->dev;
@@ -567,7 +595,7 @@ static int wm8731_register(struct wm8731_priv *wm8731,
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        ret = snd_soc_register_dai(&wm8731_dai);
@@ -581,6 +609,10 @@ static int wm8731_register(struct wm8731_priv *wm8731,
 
 err_codec:
        snd_soc_unregister_codec(codec);
+err_regulator_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+err_regulator_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 err:
        kfree(wm8731);
        return ret;
@@ -591,6 +623,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
        wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
        snd_soc_unregister_dai(&wm8731_dai);
        snd_soc_unregister_codec(&wm8731->codec);
+       regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+       regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
        kfree(wm8731);
        wm8731_codec = NULL;
 }
@@ -623,21 +657,6 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8731_spi_resume(struct spi_device *spi)
-{
-       return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8731_spi_suspend NULL
-#define wm8731_spi_resume NULL
-#endif
-
 static struct spi_driver wm8731_spi_driver = {
        .driver = {
                .name   = "wm8731",
@@ -645,8 +664,6 @@ static struct spi_driver wm8731_spi_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = wm8731_spi_probe,
-       .suspend        = wm8731_spi_suspend,
-       .resume         = wm8731_spi_resume,
        .remove         = __devexit_p(wm8731_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -679,21 +696,6 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8731_i2c_resume(struct i2c_client *i2c)
-{
-       return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8731_i2c_suspend NULL
-#define wm8731_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8731_i2c_id[] = {
        { "wm8731", 0 },
        { }
@@ -707,8 +709,6 @@ static struct i2c_driver wm8731_i2c_driver = {
        },
        .probe =    wm8731_i2c_probe,
        .remove =   __devexit_p(wm8731_i2c_remove),
-       .suspend =  wm8731_i2c_suspend,
-       .resume =   wm8731_i2c_resume,
        .id_table = wm8731_i2c_id,
 };
 #endif
index 4ba1e7e93fb4dbfc99cb28e04e7baa82318102f3..475c67ac7818e39e417d21c9a43fb96ab0833841 100644 (file)
@@ -403,7 +403,6 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -772,16 +771,8 @@ static int wm8750_init(struct snd_soc_device *socdev,
        snd_soc_add_controls(codec, wm8750_snd_controls,
                                ARRAY_SIZE(wm8750_snd_controls));
        wm8750_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8750: failed to register card\n");
-               goto card_err;
-       }
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        kfree(codec->reg_cache);
        return ret;
index 5ad677ce80da46baa8789c2d3072c3205474bb2b..d6850dacda29fbc9e36f3155fb9cd0557d59168e 100644 (file)
@@ -673,7 +673,6 @@ static int wm8753_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -724,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;
@@ -1583,18 +1582,9 @@ static int wm8753_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8753_snd_controls,
                             ARRAY_SIZE(wm8753_snd_controls));
        wm8753_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8753: failed to register card\n");
-               goto card_err;
-       }
 
        return 0;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-
 pcm_err:
        return ret;
 }
@@ -1767,21 +1757,6 @@ static int wm8753_i2c_remove(struct i2c_client *client)
         return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8753_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8753_i2c_suspend NULL
-#define wm8753_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8753_i2c_id[] = {
        { "wm8753", 0 },
        { }
@@ -1795,8 +1770,6 @@ static struct i2c_driver wm8753_i2c_driver = {
        },
        .probe =    wm8753_i2c_probe,
        .remove =   wm8753_i2c_remove,
-       .suspend =  wm8753_i2c_suspend,
-       .resume =   wm8753_i2c_resume,
        .id_table = wm8753_i2c_id,
 };
 #endif
@@ -1852,22 +1825,6 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8753_spi_resume(struct spi_device *spi)
-{
-       return snd_soc_resume_device(&spi->dev);
-}
-
-#else
-#define wm8753_spi_suspend NULL
-#define wm8753_spi_resume NULL
-#endif
-
 static struct spi_driver wm8753_spi_driver = {
        .driver = {
                .name   = "wm8753",
@@ -1876,8 +1833,6 @@ static struct spi_driver wm8753_spi_driver = {
        },
        .probe          = wm8753_spi_probe,
        .remove         = __devexit_p(wm8753_spi_remove),
-       .suspend        = wm8753_spi_suspend,
-       .resume         = wm8753_spi_resume,
 };
 #endif
 
index a9829aa26e53591a0574d44f884c5fccee5f0b29..ab2c0da1809102396cc357ca78be1d3946a356a2 100644 (file)
@@ -447,17 +447,8 @@ static int wm8776_probe(struct platform_device *pdev)
                                  ARRAY_SIZE(wm8776_dapm_widgets));
        snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card: %d\n", ret);
-               goto card_err;
-       }
-
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -616,21 +607,6 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8776_spi_resume(struct spi_device *spi)
-{
-       return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8776_spi_suspend NULL
-#define wm8776_spi_resume NULL
-#endif
-
 static struct spi_driver wm8776_spi_driver = {
        .driver = {
                .name   = "wm8776",
@@ -638,8 +614,6 @@ static struct spi_driver wm8776_spi_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = wm8776_spi_probe,
-       .suspend        = wm8776_spi_suspend,
-       .resume         = wm8776_spi_resume,
        .remove         = __devexit_p(wm8776_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -673,21 +647,6 @@ static __devexit int wm8776_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8776_i2c_resume(struct i2c_client *i2c)
-{
-       return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8776_i2c_suspend NULL
-#define wm8776_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8776_i2c_id[] = {
        { "wm8776", 0 },
        { }
@@ -701,8 +660,6 @@ static struct i2c_driver wm8776_i2c_driver = {
        },
        .probe =    wm8776_i2c_probe,
        .remove =   __devexit_p(wm8776_i2c_remove),
-       .suspend =  wm8776_i2c_suspend,
-       .resume =   wm8776_i2c_resume,
        .id_table = wm8776_i2c_id,
 };
 #endif
index 5e9c855c0036eb72e79741ff4b90d13b3b7ae3c3..c9438dd62df34afc439d17728d9aaa18e81d9f53 100644 (file)
@@ -618,8 +618,6 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
-
        return 0;
 }
 
@@ -814,8 +812,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);
 }
@@ -1312,21 +1310,6 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8900_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8900_i2c_suspend NULL
-#define wm8900_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8900_i2c_id[] = {
        { "wm8900", 0 },
        { }
@@ -1340,8 +1323,6 @@ static struct i2c_driver wm8900_i2c_driver = {
        },
        .probe = wm8900_i2c_probe,
        .remove = __devexit_p(wm8900_i2c_remove),
-       .suspend = wm8900_i2c_suspend,
-       .resume = wm8900_i2c_resume,
        .id_table = wm8900_i2c_id,
 };
 
@@ -1370,17 +1351,6 @@ static int wm8900_probe(struct platform_device *pdev)
                                ARRAY_SIZE(wm8900_snd_controls));
        wm8900_add_widgets(codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Failed to register card\n");
-               goto card_err;
-       }
-
-       return ret;
-
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index fe1307b500cf5cf30af59b9f83c0310447c08f33..b8cae17586425012f9456f350d154d368e7fee6e 100644 (file)
@@ -919,8 +919,6 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
-       snd_soc_dapm_new_widgets(codec);
-
        return 0;
 }
 
@@ -1655,21 +1653,6 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8903_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8903_i2c_suspend NULL
-#define wm8903_i2c_resume NULL
-#endif
-
 /* i2c codec control layer */
 static const struct i2c_device_id wm8903_i2c_id[] = {
        { "wm8903", 0 },
@@ -1684,8 +1667,6 @@ static struct i2c_driver wm8903_i2c_driver = {
        },
        .probe    = wm8903_i2c_probe,
        .remove   = __devexit_p(wm8903_i2c_remove),
-       .suspend  = wm8903_i2c_suspend,
-       .resume   = wm8903_i2c_resume,
        .id_table = wm8903_i2c_id,
 };
 
@@ -1712,17 +1693,8 @@ static int wm8903_probe(struct platform_device *pdev)
                                ARRAY_SIZE(wm8903_snd_controls));
        wm8903_add_widgets(socdev->card->codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "wm8903: failed to register card\n");
-               goto card_err;
-       }
-
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        return ret;
 }
index 1ef2454c52054357789cb3b14e1fea77072b86a6..3d850b97037a69fed4961ea6eea3591e53617587 100644 (file)
@@ -298,7 +298,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
        ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
        if (ret)
                goto error_ret;
-       ret = snd_soc_dapm_new_widgets(codec);
 
 error_ret:
        return ret;
@@ -536,8 +535,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;
@@ -731,12 +730,6 @@ static int wm8940_probe(struct platform_device *pdev)
        if (ret)
                goto error_free_pcms;
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card: %d\n", ret);
-               goto error_free_pcms;
-       }
-
        return ret;
 
 error_free_pcms:
@@ -877,21 +870,6 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8940_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8940_i2c_suspend NULL
-#define wm8940_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8940_i2c_id[] = {
        { "wm8940", 0 },
        { }
@@ -905,8 +883,6 @@ static struct i2c_driver wm8940_i2c_driver = {
        },
        .probe = wm8940_i2c_probe,
        .remove = __devexit_p(wm8940_i2c_remove),
-       .suspend = wm8940_i2c_suspend,
-       .resume = wm8940_i2c_resume,
        .id_table = wm8940_i2c_id,
 };
 
index f59703be61c84b6213600edab224c0f71ab63bf6..d07bcc1e1c603ed9a86e06c2e293bdb51358d271 100644 (file)
@@ -307,7 +307,6 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -540,8 +539,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;
@@ -713,17 +712,9 @@ static int wm8960_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8960_snd_controls,
                             ARRAY_SIZE(wm8960_snd_controls));
        wm8960_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -883,21 +874,6 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8960_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8960_i2c_suspend NULL
-#define wm8960_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8960_i2c_id[] = {
        { "wm8960", 0 },
        { }
@@ -911,8 +887,6 @@ static struct i2c_driver wm8960_i2c_driver = {
        },
        .probe =    wm8960_i2c_probe,
        .remove =   __devexit_p(wm8960_i2c_remove),
-       .suspend =  wm8960_i2c_suspend,
-       .resume =   wm8960_i2c_resume,
        .id_table = wm8960_i2c_id,
 };
 
index 50303208589905758341658105cd9d04196ed451..a8007d58813f77b4687223fe40f48820e4b71561 100644 (file)
@@ -986,19 +986,9 @@ static int wm8961_probe(struct platform_device *pdev)
        snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
                                  ARRAY_SIZE(wm8961_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-       snd_soc_dapm_new_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -1206,21 +1196,6 @@ static __devexit int wm8961_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8961_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8961_i2c_suspend NULL
-#define wm8961_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8961_i2c_id[] = {
        { "wm8961", 0 },
        { }
@@ -1234,8 +1209,6 @@ static struct i2c_driver wm8961_i2c_driver = {
        },
        .probe =    wm8961_i2c_probe,
        .remove =   __devexit_p(wm8961_i2c_remove),
-       .suspend =  wm8961_i2c_suspend,
-       .resume =   wm8961_i2c_resume,
        .id_table = wm8961_i2c_id,
 };
 
index d66efb0546eadf3846aa81adaff2003bd8675dcf..d9540d55fc89b6b96ec108f896cc7572f8c74176 100644 (file)
@@ -338,8 +338,6 @@ static int wm8971_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
-
        return 0;
 }
 
@@ -703,16 +701,9 @@ static int wm8971_init(struct snd_soc_device *socdev,
        snd_soc_add_controls(codec, wm8971_snd_controls,
                                ARRAY_SIZE(wm8971_snd_controls));
        wm8971_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8971: failed to register card\n");
-               goto card_err;
-       }
+
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        kfree(codec->reg_cache);
        return ret;
index 98d663afc97d290e9d954eb08d37e007b16fbfae..81c57b5c591c5d35676eae9d3dfa5f1858326149 100644 (file)
@@ -276,41 +276,42 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
 struct pll_ {
-       unsigned int pre_div:4; /* prescale - 1 */
+       unsigned int pre_div:1;
        unsigned int n:4;
        unsigned int k;
 };
 
-static struct pll_ pll_div;
-
 /* The size in bits of the pll divide multiplied by 10
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 24) * 10)
 
-static void pll_factors(unsigned int target, unsigned int source)
+static void pll_factors(struct pll_ *pll_div,
+                       unsigned int target, unsigned int source)
 {
        unsigned long long Kpart;
        unsigned int K, Ndiv, Nmod;
 
+       /* There is a fixed divide by 4 in the output path */
+       target *= 4;
+
        Ndiv = target / source;
        if (Ndiv < 6) {
-               source >>= 1;
-               pll_div.pre_div = 1;
+               source /= 2;
+               pll_div->pre_div = 1;
                Ndiv = target / source;
        } else
-               pll_div.pre_div = 0;
+               pll_div->pre_div = 0;
 
        if ((Ndiv < 6) || (Ndiv > 12))
                printk(KERN_WARNING
                        "WM8974 N value %u outwith recommended range!\n",
                        Ndiv);
 
-       pll_div.n = Ndiv;
+       pll_div->n = Ndiv;
        Nmod = target % source;
        Kpart = FIXED_PLL_SIZE * (long long)Nmod;
 
@@ -325,13 +326,14 @@ static void pll_factors(unsigned int target, unsigned int source)
        /* Move down to proper range now rounding is done */
        K /= 10;
 
-       pll_div.k = K;
+       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;
+       struct pll_ pll_div;
        u16 reg;
 
        if (freq_in == 0 || freq_out == 0) {
@@ -345,7 +347,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
                return 0;
        }
 
-       pll_factors(freq_out*4, freq_in);
+       pll_factors(&pll_div, freq_out, freq_in);
 
        snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
        snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
@@ -638,17 +640,9 @@ static int wm8974_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm8974_snd_controls,
                             ARRAY_SIZE(wm8974_snd_controls));
        wm8974_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
index 3f530f8a972af3547a9da579e4db2f8f519e4e58..2862e4dced27fc2ff20a338c3f4461674a4dcf21 100644 (file)
@@ -790,19 +790,9 @@ static int wm8988_probe(struct platform_device *pdev)
        snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
                                  ARRAY_SIZE(wm8988_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
-       snd_soc_dapm_new_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -944,21 +934,6 @@ static int wm8988_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8988_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8988_i2c_suspend NULL
-#define wm8988_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8988_i2c_id[] = {
        { "wm8988", 0 },
        { }
@@ -972,8 +947,6 @@ static struct i2c_driver wm8988_i2c_driver = {
        },
        .probe = wm8988_i2c_probe,
        .remove = wm8988_i2c_remove,
-       .suspend = wm8988_i2c_suspend,
-       .resume = wm8988_i2c_resume,
        .id_table = wm8988_i2c_id,
 };
 #endif
@@ -1006,21 +979,6 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8988_spi_resume(struct spi_device *spi)
-{
-       return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8988_spi_suspend NULL
-#define wm8988_spi_resume NULL
-#endif
-
 static struct spi_driver wm8988_spi_driver = {
        .driver = {
                .name   = "wm8988",
@@ -1029,8 +987,6 @@ static struct spi_driver wm8988_spi_driver = {
        },
        .probe          = wm8988_spi_probe,
        .remove         = __devexit_p(wm8988_spi_remove),
-       .suspend        = wm8988_spi_suspend,
-       .resume         = wm8988_spi_resume,
 };
 #endif
 
index 2d702db4131db36f17d854621c99767777c64c17..341481e0e83004c285bdaf634838647cfcc12a14 100644 (file)
@@ -920,7 +920,6 @@ static int wm8990_add_widgets(struct snd_soc_codec *codec)
        /* set up the WM8990 audio map */
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -972,8 +971,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;
@@ -1409,16 +1408,9 @@ static int wm8990_init(struct snd_soc_device *socdev)
        snd_soc_add_controls(codec, wm8990_snd_controls,
                                ARRAY_SIZE(wm8990_snd_controls));
        wm8990_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm8990: 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;
index d9987999e92caf330a91896dadf68eae20406ea9..5e32f2ed5fc295e1d7fbf17197c7f68d55714f41 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;
@@ -1464,19 +1464,8 @@ static int wm8993_probe(struct platform_device *pdev)
        wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
                                    wm8993->pdata.lineout2_diff);
 
-       snd_soc_dapm_new_widgets(codec);
-
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               dev_err(codec->dev, "failed to register card\n");
-               goto card_err;
-       }
-
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 err:
        return ret;
 }
@@ -1572,33 +1561,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
        /* Use automatic clock configuration */
        snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
 
-       if (!wm8993->pdata.lineout1_diff)
-               snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
-                                   WM8993_LINEOUT1_MODE,
-                                   WM8993_LINEOUT1_MODE);
-       if (!wm8993->pdata.lineout2_diff)
-               snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
-                                   WM8993_LINEOUT2_MODE,
-                                   WM8993_LINEOUT2_MODE);
-
-       if (wm8993->pdata.lineout1fb)
-               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-                                   WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
-
-       if (wm8993->pdata.lineout2fb)
-               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-                                   WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
-
-       /* Apply the microphone bias/detection configuration - the
-        * platform data is directly applicable to the register. */
-       snd_soc_update_bits(codec, WM8993_MICBIAS,
-                           WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
-                           WM8993_MICB1_LVL | WM8993_MICB2_LVL,
-                           wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
-                           wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
-                           wm8993->pdata.micbias1_lvl |
-                           wm8993->pdata.micbias1_lvl << 1);
-
+       wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+                                     wm8993->pdata.lineout2_diff,
+                                     wm8993->pdata.lineout1fb,
+                                     wm8993->pdata.lineout2fb,
+                                     wm8993->pdata.jd_scthr,
+                                     wm8993->pdata.jd_thr,
+                                     wm8993->pdata.micbias1_lvl,
+                                     wm8993->pdata.micbias2_lvl);
+                            
        ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        if (ret != 0)
                goto err;
index 686e5aa972067106f1f88056c5d367020a0c1356..c468497314ba4291314e856e26d3bb50c9c9b5b7 100644 (file)
@@ -1262,19 +1262,9 @@ static int wm9081_probe(struct platform_device *pdev)
        snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets,
                                  ARRAY_SIZE(wm9081_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-       snd_soc_dapm_new_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;
-       }
 
        return ret;
 
-card_err:
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
 pcm_err:
        return ret;
 }
@@ -1452,21 +1442,6 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-       return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm9081_i2c_resume(struct i2c_client *client)
-{
-       return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm9081_i2c_suspend NULL
-#define wm9081_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm9081_i2c_id[] = {
        { "wm9081", 0 },
        { }
@@ -1480,8 +1455,6 @@ static struct i2c_driver wm9081_i2c_driver = {
        },
        .probe =    wm9081_i2c_probe,
        .remove =   __devexit_p(wm9081_i2c_remove),
-       .suspend =  wm9081_i2c_suspend,
-       .resume =   wm9081_i2c_resume,
        .id_table = wm9081_i2c_id,
 };
 
index e7d2840d9e59d22e344b6c9792fe1be6ec9b1fd9..dfffc6c778c0ec2495831487da2c870130e5932c 100644 (file)
@@ -205,7 +205,6 @@ static int wm9705_add_widgets(struct snd_soc_codec *codec)
        snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets,
                                        ARRAY_SIZE(wm9705_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
-       snd_soc_dapm_new_widgets(codec);
 
        return 0;
 }
@@ -403,16 +402,8 @@ static int wm9705_soc_probe(struct platform_device *pdev)
                                ARRAY_SIZE(wm9705_snd_ac97_controls));
        wm9705_add_widgets(codec);
 
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9705: failed to register card\n");
-               goto reset_err;
-       }
-
        return 0;
 
-reset_err:
-       snd_soc_free_pcms(socdev);
 pcm_err:
        snd_soc_free_ac97_codec(codec);
 codec_err:
index 1fd4e88f50cff74db693676172518601671532ab..2a0872273007df81ec836a9a4ef5f39aa1c8bcc0 100644 (file)
@@ -436,7 +436,6 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -695,17 +694,9 @@ static int wm9712_soc_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
                                ARRAY_SIZE(wm9712_snd_ac97_controls));
        wm9712_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9712: failed to register card\n");
-               goto reset_err;
-       }
 
        return 0;
 
-reset_err:
-       snd_soc_free_pcms(socdev);
-
 pcm_err:
        snd_soc_free_ac97_codec(codec);
 
index abed37acf78714be6b7f5dd534df94dc6c2be319..00bac315fb3bb97de2df341e8db10a09380b312c 100644 (file)
@@ -625,7 +625,6 @@ static int wm9713_add_widgets(struct snd_soc_codec *codec)
 
        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-       snd_soc_dapm_new_widgets(codec);
        return 0;
 }
 
@@ -800,8 +799,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);
@@ -1247,13 +1246,8 @@ static int wm9713_soc_probe(struct platform_device *pdev)
        snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
                                ARRAY_SIZE(wm9713_snd_ac97_controls));
        wm9713_add_widgets(codec);
-       ret = snd_soc_init_card(socdev);
-       if (ret < 0)
-               goto reset_err;
-       return 0;
 
-reset_err:
-       snd_soc_free_pcms(socdev);
+       return 0;
 
 pcm_err:
        snd_soc_free_ac97_codec(codec);
index e542027eea89017d0a8aa22639eb45f307c4f543..810a563d0ebf2ddb88fa25d8b3f3fb72344ff65a 100644 (file)
@@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
 
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+                                 int lineout1_diff, int lineout2_diff,
+                                 int lineout1fb, int lineout2fb,
+                                 int jd_scthr, int jd_thr, int micbias1_lvl,
+                                 int micbias2_lvl)
+{
+       if (!lineout1_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+                                   WM8993_LINEOUT1_MODE,
+                                   WM8993_LINEOUT1_MODE);
+       if (!lineout2_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+                                   WM8993_LINEOUT2_MODE,
+                                   WM8993_LINEOUT2_MODE);
+
+       if (lineout1fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+       if (lineout2fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+       snd_soc_update_bits(codec, WM8993_MICBIAS,
+                           WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+                           WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+                           jd_scthr << WM8993_JD_SCTHR_SHIFT |
+                           jd_thr << WM8993_JD_THR_SHIFT |
+                           micbias1_lvl |
+                           micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
 MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index ec09cb6a29392a491e53aab36fce691660a269fd..36d3fba1de8b560ee414d5092461c844e0fa3f86 100644 (file)
@@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
 
 extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
 extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
+                                        int lineout1_diff, int lineout2_diff,
+                                        int lineout1fb, int lineout2fb,
+                                        int jd_scthr, int jd_thr,
+                                        int micbias1_lvl, int micbias2_lvl);
 
 #endif
index 4dfd4ad9d90e35f1abe920b02de591613557095b..047ee39418c0a4279c14f09faa1daab7ef27523e 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 67414f65940577c745913ba7050f854b7527e0b7..7ccbe6684fc2b30c96babcf5c6e497c5ef708cd9 100644 (file)
@@ -45,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
@@ -176,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,
@@ -243,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 4ae707048021886c45533ec6c5f6ca5cadf3999c..6362ca05506e0e011312ff023446b2eb0a7da440 100644 (file)
@@ -97,12 +97,24 @@ enum {
        DAVINCI_MCBSP_WORD_32,
 };
 
+static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+       [SNDRV_PCM_FORMAT_S8]           = 1,
+       [SNDRV_PCM_FORMAT_S16_LE]       = 2,
+       [SNDRV_PCM_FORMAT_S32_LE]       = 4,
+};
+
+static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+       [SNDRV_PCM_FORMAT_S8]           = DAVINCI_MCBSP_WORD_8,
+       [SNDRV_PCM_FORMAT_S16_LE]       = DAVINCI_MCBSP_WORD_16,
+       [SNDRV_PCM_FORMAT_S32_LE]       = DAVINCI_MCBSP_WORD_32,
+};
+
+static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+       [SNDRV_PCM_FORMAT_S8]           = SNDRV_PCM_FORMAT_S16_LE,
+       [SNDRV_PCM_FORMAT_S16_LE]       = SNDRV_PCM_FORMAT_S32_LE,
+};
+
 struct davinci_mcbsp_dev {
-       /*
-        * dma_params must be first because rtd->dai->cpu_dai->private_data
-        * is cast to a pointer of an array of struct davinci_pcm_dma_params in
-        * davinci_pcm_open.
-        */
        struct davinci_pcm_dma_params   dma_params[2];
        void __iomem                    *base;
 #define MOD_DSP_A      0
@@ -110,6 +122,27 @@ struct davinci_mcbsp_dev {
        int                             mode;
        u32                             pcr;
        struct clk                      *clk;
+       /*
+        * Combining both channels into 1 element will at least double the
+        * amount of time between servicing the dma channel, increase
+        * effiency, and reduce the chance of overrun/underrun. But,
+        * it will result in the left & right channels being swapped.
+        *
+        * If relabeling the left and right channels is not possible,
+        * you may want to let the codec know to swap them back.
+        *
+        * It may allow x10 the amount of time to service dma requests,
+        * if the codec is master and is using an unnecessarily fast bit clock
+        * (ie. tlvaic23b), independent of the sample rate. So, having an
+        * entire frame at once means it can be serviced at the sample rate
+        * instead of the bit clock rate.
+        *
+        * In the now unlikely case that an underrun still
+        * occurs, both the left and right samples will be repeated
+        * so that no pops are heard, and the left and right channels
+        * won't end up being swapped because of the underrun.
+        */
+       unsigned enable_channel_combine:1;
 };
 
 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -349,6 +382,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        int mcbsp_word_length;
        unsigned int rcr, xcr, srgr;
        u32 spcr;
+       snd_pcm_format_t fmt;
+       unsigned element_cnt = 1;
 
        /* general line settings */
        spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -378,27 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
        }
        /* Determine xfer data type */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
-               dma_params->data_type = 1;
-               mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
-               break;
-       case SNDRV_PCM_FORMAT_S16_LE:
-               dma_params->data_type = 2;
-               mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
-               break;
-       case SNDRV_PCM_FORMAT_S32_LE:
-               dma_params->data_type = 4;
-               mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
-               break;
-       default:
+       fmt = params_format(params);
+       if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
                printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
                return -EINVAL;
        }
 
-       dma_params->acnt  = dma_params->data_type;
-       rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
-       xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
+       if (params_channels(params) == 2) {
+               element_cnt = 2;
+               if (double_fmt[fmt] && dev->enable_channel_combine) {
+                       element_cnt = 1;
+                       fmt = double_fmt[fmt];
+               }
+       }
+       dma_params->acnt = dma_params->data_type = data_type[fmt];
+       dma_params->fifo_level = 0;
+       mcbsp_word_length = asp_word_length[fmt];
+       rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+       xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
 
        rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
                DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -513,7 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev)
                ret = -ENOMEM;
                goto err_release_region;
        }
-
+       if (pdata) {
+               dev->enable_channel_combine = pdata->enable_channel_combine;
+               dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size =
+                       pdata->sram_size_playback;
+               dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
+                       pdata->sram_size_capture;
+       }
        dev->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(dev->clk)) {
                ret = -ENODEV;
@@ -547,6 +585,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
 
        davinci_i2s_dai.private_data = dev;
+       davinci_i2s_dai.dma_data = dev->dma_params;
        ret = snd_soc_register_dai(&davinci_i2s_dai);
        if (ret != 0)
                goto err_free_mem;
index 5d1f98a4c97869fa41d446327a879f383b11136c..0a302e1080d9ec06553d77dfb12125b4fcece380 100644 (file)
@@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        struct davinci_pcm_dma_params *dma_params =
                                        &dev->dma_params[substream->stream];
        int word_length;
-       u8 numevt;
+       u8 fifo_level;
 
        davinci_hw_common_param(dev, substream->stream);
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               numevt = dev->txnumevt;
+               fifo_level = dev->txnumevt;
        else
-               numevt = dev->rxnumevt;
-
-       if (!numevt)
-               numevt = 1;
+               fifo_level = dev->rxnumevt;
 
        if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
                davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       if (dev->version == MCASP_VERSION_2) {
-               dma_params->data_type *= numevt;
-               dma_params->acnt = 4 * numevt;
-       } else
+       if (dev->version == MCASP_VERSION_2 && !fifo_level)
+               dma_params->acnt = 4;
+       else
                dma_params->acnt = dma_params->data_type;
 
+       dma_params->fifo_level = fifo_level;
        davinci_config_channel_size(dev, word_length);
 
        return 0;
@@ -907,6 +904,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
        dma_data->channel = res->start;
        davinci_mcasp_dai[pdata->op_mode].private_data = dev;
+       davinci_mcasp_dai[pdata->op_mode].dma_data = dev->dma_params;
        davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev;
        ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]);
 
index 9d179cc88f7b84a9614753c0af34aef28a16c74d..582c9249ef09b4cb0a4a53222ab7dd4b323a58cb 100644 (file)
@@ -39,11 +39,6 @@ enum {
 };
 
 struct davinci_audio_dev {
-       /*
-        * dma_params must be first because rtd->dai->cpu_dai->private_data
-        * is cast to a pointer of an array of struct davinci_pcm_dma_params in
-        * davinci_pcm_open.
-        */
        struct davinci_pcm_dma_params dma_params[2];
        void __iomem *base;
        int sample_rate;
index c73a915f233f3d397639bfcb669ffbd834f3aee1..ad4d7f47a86b01f985ffebeea5818f27d1f67a3c 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.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
 
 #include <asm/dma.h>
 #include <mach/edma.h>
+#include <mach/sram.h>
 
 #include "davinci-pcm.h"
 
-static struct snd_pcm_hardware davinci_pcm_hardware = {
+#ifdef DEBUG
+static void print_buf_info(int slot, char *name)
+{
+       struct edmacc_param p;
+       if (slot < 0)
+               return;
+       edma_read_slot(slot, &p);
+       printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n",
+                       name, slot, p.opt, p.src, p.a_b_cnt, p.dst);
+       printk(KERN_DEBUG "    src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n",
+                       p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt);
+}
+#else
+static void print_buf_info(int slot, char *name)
+{
+}
+#endif
+
+static struct snd_pcm_hardware pcm_hardware_playback = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
                 SNDRV_PCM_INFO_PAUSE),
@@ -48,102 +68,432 @@ static struct snd_pcm_hardware davinci_pcm_hardware = {
        .fifo_size = 0,
 };
 
+static struct snd_pcm_hardware pcm_hardware_capture = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_PAUSE),
+       .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+       .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+                 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+                 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+                 SNDRV_PCM_RATE_KNOT),
+       .rate_min = 8000,
+       .rate_max = 96000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 128 * 1024,
+       .period_bytes_min = 32,
+       .period_bytes_max = 8 * 1024,
+       .periods_min = 16,
+       .periods_max = 255,
+       .fifo_size = 0,
+};
+
+/*
+ * How ping/pong works....
+ *
+ * Playback:
+ * ram_params - copys 2*ping_size from start of SDRAM to iram,
+ *     links to ram_link2
+ * ram_link2 - copys rest of SDRAM to iram in ping_size units,
+ *     links to ram_link
+ * ram_link - copys entire SDRAM to iram in ping_size uints,
+ *     links to self
+ *
+ * asp_params - same as asp_link[0]
+ * asp_link[0] - copys from lower half of iram to asp port
+ *     links to asp_link[1], triggers iram copy event on completion
+ * asp_link[1] - copys from upper half of iram to asp port
+ *     links to asp_link[0], triggers iram copy event on completion
+ *     triggers interrupt only needed to let upper SOC levels update position
+ *     in stream on completion
+ *
+ * When playback is started:
+ *     ram_params started
+ *     asp_params started
+ *
+ * Capture:
+ * ram_params - same as ram_link,
+ *     links to ram_link
+ * ram_link - same as playback
+ *     links to self
+ *
+ * asp_params - same as playback
+ * asp_link[0] - same as playback
+ * asp_link[1] - same as playback
+ *
+ * When capture is started:
+ *     asp_params started
+ */
 struct davinci_runtime_data {
        spinlock_t lock;
        int period;             /* current DMA period */
-       int master_lch;         /* Master DMA channel */
-       int slave_lch;          /* linked parameter RAM reload slot */
+       int asp_channel;        /* Master DMA channel */
+       int asp_link[2];        /* asp parameter link channel, ping/pong */
        struct davinci_pcm_dma_params *params;  /* DMA params */
+       int ram_channel;
+       int ram_link;
+       int ram_link2;
+       struct edmacc_param asp_params;
+       struct edmacc_param ram_params;
 };
 
+/*
+ * Not used with ping/pong
+ */
 static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
 {
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int lch = prtd->slave_lch;
+       int link = prtd->asp_link[0];
        unsigned int period_size;
        unsigned int dma_offset;
        dma_addr_t dma_pos;
        dma_addr_t src, dst;
        unsigned short src_bidx, dst_bidx;
+       unsigned short src_cidx, dst_cidx;
        unsigned int data_type;
        unsigned short acnt;
        unsigned int count;
+       unsigned int fifo_level;
 
        period_size = snd_pcm_lib_period_bytes(substream);
        dma_offset = prtd->period * period_size;
        dma_pos = runtime->dma_addr + dma_offset;
+       fifo_level = prtd->params->fifo_level;
 
        pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
-               "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
+               "dma_ptr = %x period_size=%x\n", link, dma_pos, period_size);
 
        data_type = prtd->params->data_type;
        count = period_size / data_type;
+       if (fifo_level)
+               count /= fifo_level;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                src = dma_pos;
                dst = prtd->params->dma_addr;
                src_bidx = data_type;
                dst_bidx = 0;
+               src_cidx = data_type * fifo_level;
+               dst_cidx = 0;
        } else {
                src = prtd->params->dma_addr;
                dst = dma_pos;
                src_bidx = 0;
                dst_bidx = data_type;
+               src_cidx = 0;
+               dst_cidx = data_type * fifo_level;
        }
 
        acnt = prtd->params->acnt;
-       edma_set_src(lch, src, INCR, W8BIT);
-       edma_set_dest(lch, dst, INCR, W8BIT);
-       edma_set_src_index(lch, src_bidx, 0);
-       edma_set_dest_index(lch, dst_bidx, 0);
-       edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+       edma_set_src(link, src, INCR, W8BIT);
+       edma_set_dest(link, dst, INCR, W8BIT);
+
+       edma_set_src_index(link, src_bidx, src_cidx);
+       edma_set_dest_index(link, dst_bidx, dst_cidx);
+
+       if (!fifo_level)
+               edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC);
+       else
+               edma_set_transfer_params(link, acnt, fifo_level, count,
+                                                       fifo_level, ABSYNC);
 
        prtd->period++;
        if (unlikely(prtd->period >= runtime->periods))
                prtd->period = 0;
 }
 
-static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data)
+static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
 {
        struct snd_pcm_substream *substream = data;
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
 
-       pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
+       print_buf_info(prtd->ram_channel, "i ram_channel");
+       pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status);
 
        if (unlikely(ch_status != DMA_COMPLETE))
                return;
 
        if (snd_pcm_running(substream)) {
+               if (prtd->ram_channel < 0) {
+                       /* No ping/pong must fix up link dma data*/
+                       spin_lock(&prtd->lock);
+                       davinci_pcm_enqueue_dma(substream);
+                       spin_unlock(&prtd->lock);
+               }
                snd_pcm_period_elapsed(substream);
+       }
+}
+
+static int allocate_sram(struct snd_pcm_substream *substream, unsigned size,
+               struct snd_pcm_hardware *ppcm)
+{
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct snd_dma_buffer *iram_dma = NULL;
+       dma_addr_t iram_phys = 0;
+       void *iram_virt = NULL;
+
+       if (buf->private_data || !size)
+               return 0;
+
+       ppcm->period_bytes_max = size;
+       iram_virt = sram_alloc(size, &iram_phys);
+       if (!iram_virt)
+               goto exit1;
+       iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
+       if (!iram_dma)
+               goto exit2;
+       iram_dma->area = iram_virt;
+       iram_dma->addr = iram_phys;
+       memset(iram_dma->area, 0, size);
+       iram_dma->bytes = size;
+       buf->private_data = iram_dma;
+       return 0;
+exit2:
+       if (iram_virt)
+               sram_free(iram_virt, size);
+exit1:
+       return -ENOMEM;
+}
+
+/*
+ * Only used with ping/pong.
+ * This is called after runtime->dma_addr, period_bytes and data_type are valid
+ */
+static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
+{
+       unsigned short ram_src_cidx, ram_dst_cidx;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct davinci_runtime_data *prtd = runtime->private_data;
+       struct snd_dma_buffer *iram_dma =
+               (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+       struct davinci_pcm_dma_params *params = prtd->params;
+       unsigned int data_type = params->data_type;
+       unsigned int acnt = params->acnt;
+       /* divide by 2 for ping/pong */
+       unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
+       int link = prtd->asp_link[1];
+       unsigned int fifo_level = prtd->params->fifo_level;
+       unsigned int count;
+       if ((data_type == 0) || (data_type > 4)) {
+               printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type);
+               return -EINVAL;
+       }
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
+               ram_src_cidx = ping_size;
+               ram_dst_cidx = -ping_size;
+               edma_set_src(link, asp_src_pong, INCR, W8BIT);
+
+               link = prtd->asp_link[0];
+               edma_set_src_index(link, data_type, data_type * fifo_level);
+               link = prtd->asp_link[1];
+               edma_set_src_index(link, data_type, data_type * fifo_level);
+
+               link = prtd->ram_link;
+               edma_set_src(link, runtime->dma_addr, INCR, W32BIT);
+       } else {
+               dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
+               ram_src_cidx = -ping_size;
+               ram_dst_cidx = ping_size;
+               edma_set_dest(link, asp_dst_pong, INCR, W8BIT);
+
+               link = prtd->asp_link[0];
+               edma_set_dest_index(link, data_type, data_type * fifo_level);
+               link = prtd->asp_link[1];
+               edma_set_dest_index(link, data_type, data_type * fifo_level);
+
+               link = prtd->ram_link;
+               edma_set_dest(link, runtime->dma_addr, INCR, W32BIT);
+       }
 
-               spin_lock(&prtd->lock);
-               davinci_pcm_enqueue_dma(substream);
-               spin_unlock(&prtd->lock);
+       if (!fifo_level) {
+               count = ping_size / data_type;
+               edma_set_transfer_params(prtd->asp_link[0], acnt, count,
+                               1, 0, ASYNC);
+               edma_set_transfer_params(prtd->asp_link[1], acnt, count,
+                               1, 0, ASYNC);
+       } else {
+               count = ping_size / (data_type * fifo_level);
+               edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
+                               count, fifo_level, ABSYNC);
+               edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level,
+                               count, fifo_level, ABSYNC);
        }
+
+       link = prtd->ram_link;
+       edma_set_src_index(link, ping_size, ram_src_cidx);
+       edma_set_dest_index(link, ping_size, ram_dst_cidx);
+       edma_set_transfer_params(link, ping_size, 2,
+                       runtime->periods, 2, ASYNC);
+
+       /* init master params */
+       edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+       edma_read_slot(prtd->ram_link, &prtd->ram_params);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               struct edmacc_param p_ram;
+               /* Copy entire iram buffer before playback started */
+               prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
+               /* 0 dst_bidx */
+               prtd->ram_params.src_dst_bidx = (ping_size << 1);
+               /* 0 dst_cidx */
+               prtd->ram_params.src_dst_cidx = (ping_size << 1);
+               prtd->ram_params.ccnt = 1;
+
+               /* Skip 1st period */
+               edma_read_slot(prtd->ram_link, &p_ram);
+               p_ram.src += (ping_size << 1);
+               p_ram.ccnt -= 1;
+               edma_write_slot(prtd->ram_link2, &p_ram);
+               /*
+                * When 1st started, ram -> iram dma channel will fill the
+                * entire iram.  Then, whenever a ping/pong asp buffer finishes,
+                * 1/2 iram will be filled.
+                */
+               prtd->ram_params.link_bcntrld =
+                       EDMA_CHAN_SLOT(prtd->ram_link2) << 5;
+       }
+       return 0;
+}
+
+/* 1 asp tx or rx channel using 2 parameter channels
+ * 1 ram to/from iram channel using 1 parameter channel
+ *
+ * Playback
+ * ram copy channel kicks off first,
+ * 1st ram copy of entire iram buffer completion kicks off asp channel
+ * asp tcc always kicks off ram copy of 1/2 iram buffer
+ *
+ * Record
+ * asp channel starts, tcc kicks off ram copy
+ */
+static int request_ping_pong(struct snd_pcm_substream *substream,
+               struct davinci_runtime_data *prtd,
+               struct snd_dma_buffer *iram_dma)
+{
+       dma_addr_t asp_src_ping;
+       dma_addr_t asp_dst_ping;
+       int link;
+       struct davinci_pcm_dma_params *params = prtd->params;
+
+       /* Request ram master channel */
+       link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
+                                 davinci_pcm_dma_irq, substream,
+                                 EVENTQ_1);
+       if (link < 0)
+               goto exit1;
+
+       /* Request ram link channel */
+       link = prtd->ram_link = edma_alloc_slot(
+                       EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+       if (link < 0)
+               goto exit2;
+
+       link = prtd->asp_link[1] = edma_alloc_slot(
+                       EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+       if (link < 0)
+               goto exit3;
+
+       prtd->ram_link2 = -1;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               link = prtd->ram_link2 = edma_alloc_slot(
+                       EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+               if (link < 0)
+                       goto exit4;
+       }
+       /* circle ping-pong buffers */
+       edma_link(prtd->asp_link[0], prtd->asp_link[1]);
+       edma_link(prtd->asp_link[1], prtd->asp_link[0]);
+       /* circle ram buffers */
+       edma_link(prtd->ram_link, prtd->ram_link);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               asp_src_ping = iram_dma->addr;
+               asp_dst_ping = params->dma_addr;        /* fifo */
+       } else {
+               asp_src_ping = params->dma_addr;        /* fifo */
+               asp_dst_ping = iram_dma->addr;
+       }
+       /* ping */
+       link = prtd->asp_link[0];
+       edma_set_src(link, asp_src_ping, INCR, W16BIT);
+       edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
+       edma_set_src_index(link, 0, 0);
+       edma_set_dest_index(link, 0, 0);
+
+       edma_read_slot(link, &prtd->asp_params);
+       prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
+       prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
+       edma_write_slot(link, &prtd->asp_params);
+
+       /* pong */
+       link = prtd->asp_link[1];
+       edma_set_src(link, asp_src_ping, INCR, W16BIT);
+       edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
+       edma_set_src_index(link, 0, 0);
+       edma_set_dest_index(link, 0, 0);
+
+       edma_read_slot(link, &prtd->asp_params);
+       prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
+       /* interrupt after every pong completion */
+       prtd->asp_params.opt |= TCINTEN | TCCHEN |
+               EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
+       edma_write_slot(link, &prtd->asp_params);
+
+       /* ram */
+       link = prtd->ram_link;
+       edma_set_src(link, iram_dma->addr, INCR, W32BIT);
+       edma_set_dest(link, iram_dma->addr, INCR, W32BIT);
+       pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
+               "for asp:%u %u %u\n", __func__,
+               prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
+               prtd->asp_channel, prtd->asp_link[0],
+               prtd->asp_link[1]);
+       return 0;
+exit4:
+       edma_free_channel(prtd->asp_link[1]);
+       prtd->asp_link[1] = -1;
+exit3:
+       edma_free_channel(prtd->ram_link);
+       prtd->ram_link = -1;
+exit2:
+       edma_free_channel(prtd->ram_channel);
+       prtd->ram_channel = -1;
+exit1:
+       return link;
 }
 
 static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
 {
+       struct snd_dma_buffer *iram_dma;
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
-       struct edmacc_param p_ram;
-       int ret;
+       struct davinci_pcm_dma_params *params = prtd->params;
+       int link;
 
-       /* Request master DMA channel */
-       ret = edma_alloc_channel(prtd->params->channel,
-                                 davinci_pcm_dma_irq, substream,
-                                 EVENTQ_0);
-       if (ret < 0)
-               return ret;
-       prtd->master_lch = ret;
+       if (!params)
+               return -ENODEV;
 
-       /* Request parameter RAM reload slot */
-       ret = edma_alloc_slot(EDMA_CTLR(prtd->master_lch), EDMA_SLOT_ANY);
-       if (ret < 0) {
-               edma_free_channel(prtd->master_lch);
-               return ret;
+       /* Request asp master DMA channel */
+       link = prtd->asp_channel = edma_alloc_channel(params->channel,
+                       davinci_pcm_dma_irq, substream, EVENTQ_0);
+       if (link < 0)
+               goto exit1;
+
+       /* Request asp link channels */
+       link = prtd->asp_link[0] = edma_alloc_slot(
+                       EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+       if (link < 0)
+               goto exit2;
+
+       iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+       if (iram_dma) {
+               if (request_ping_pong(substream, prtd, iram_dma) == 0)
+                       return 0;
+               printk(KERN_WARNING "%s: dma channel allocation failed,"
+                               "not using sram\n", __func__);
        }
-       prtd->slave_lch = ret;
 
        /* Issue transfer completion IRQ when the channel completes a
         * transfer, then always reload from the same slot (by a kind
@@ -154,12 +504,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
         * the buffer and its length (ccnt) ... use it as a template
         * so davinci_pcm_enqueue_dma() takes less time in IRQ.
         */
-       edma_read_slot(prtd->slave_lch, &p_ram);
-       p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->master_lch));
-       p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5;
-       edma_write_slot(prtd->slave_lch, &p_ram);
-
+       edma_read_slot(link, &prtd->asp_params);
+       prtd->asp_params.opt |= TCINTEN |
+               EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
+       prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5;
+       edma_write_slot(link, &prtd->asp_params);
        return 0;
+exit2:
+       edma_free_channel(prtd->asp_channel);
+       prtd->asp_channel = -1;
+exit1:
+       return link;
 }
 
 static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -173,12 +528,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               edma_start(prtd->master_lch);
+               edma_resume(prtd->asp_channel);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               edma_stop(prtd->master_lch);
+               edma_pause(prtd->asp_channel);
                break;
        default:
                ret = -EINVAL;
@@ -193,15 +548,37 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
 {
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
-       struct edmacc_param temp;
 
+       if (prtd->ram_channel >= 0) {
+               int ret = ping_pong_dma_setup(substream);
+               if (ret < 0)
+                       return ret;
+
+               edma_write_slot(prtd->ram_channel, &prtd->ram_params);
+               edma_write_slot(prtd->asp_channel, &prtd->asp_params);
+
+               print_buf_info(prtd->ram_channel, "ram_channel");
+               print_buf_info(prtd->ram_link, "ram_link");
+               print_buf_info(prtd->ram_link2, "ram_link2");
+               print_buf_info(prtd->asp_channel, "asp_channel");
+               print_buf_info(prtd->asp_link[0], "asp_link[0]");
+               print_buf_info(prtd->asp_link[1], "asp_link[1]");
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /* copy 1st iram buffer */
+                       edma_start(prtd->ram_channel);
+               }
+               edma_start(prtd->asp_channel);
+               return 0;
+       }
        prtd->period = 0;
        davinci_pcm_enqueue_dma(substream);
 
        /* Copy self-linked parameter RAM entry into master channel */
-       edma_read_slot(prtd->slave_lch, &temp);
-       edma_write_slot(prtd->master_lch, &temp);
+       edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+       edma_write_slot(prtd->asp_channel, &prtd->asp_params);
        davinci_pcm_enqueue_dma(substream);
+       edma_start(prtd->asp_channel);
 
        return 0;
 }
@@ -212,20 +589,53 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct davinci_runtime_data *prtd = runtime->private_data;
        unsigned int offset;
-       dma_addr_t count;
-       dma_addr_t src, dst;
+       int asp_count;
+       dma_addr_t asp_src, asp_dst;
 
        spin_lock(&prtd->lock);
-
-       edma_get_position(prtd->master_lch, &src, &dst);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               count = src - runtime->dma_addr;
-       else
-               count = dst - runtime->dma_addr;
-
+       if (prtd->ram_channel >= 0) {
+               int ram_count;
+               int mod_ram;
+               dma_addr_t ram_src, ram_dst;
+               unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /* reading ram before asp should be safe
+                        * as long as the asp transfers less than a ping size
+                        * of bytes between the 2 reads
+                        */
+                       edma_get_position(prtd->ram_channel,
+                                       &ram_src, &ram_dst);
+                       edma_get_position(prtd->asp_channel,
+                                       &asp_src, &asp_dst);
+                       asp_count = asp_src - prtd->asp_params.src;
+                       ram_count = ram_src - prtd->ram_params.src;
+                       mod_ram = ram_count % period_size;
+                       mod_ram -= asp_count;
+                       if (mod_ram < 0)
+                               mod_ram += period_size;
+                       else if (mod_ram == 0) {
+                               if (snd_pcm_running(substream))
+                                       mod_ram += period_size;
+                       }
+                       ram_count -= mod_ram;
+                       if (ram_count < 0)
+                               ram_count += period_size * runtime->periods;
+               } else {
+                       edma_get_position(prtd->ram_channel,
+                                       &ram_src, &ram_dst);
+                       ram_count = ram_dst - prtd->ram_params.dst;
+               }
+               asp_count = ram_count;
+       } else {
+               edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       asp_count = asp_src - runtime->dma_addr;
+               else
+                       asp_count = asp_dst - runtime->dma_addr;
+       }
        spin_unlock(&prtd->lock);
 
-       offset = bytes_to_frames(runtime, count);
+       offset = bytes_to_frames(runtime, asp_count);
        if (offset >= runtime->buffer_size)
                offset = 0;
 
@@ -236,14 +646,19 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct davinci_runtime_data *prtd;
+       struct snd_pcm_hardware *ppcm;
        int ret = 0;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->private_data;
-       struct davinci_pcm_dma_params *params = &pa[substream->stream];
-       if (!params)
+       struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data;
+       struct davinci_pcm_dma_params *params;
+       if (!pa)
                return -ENODEV;
+       params = &pa[substream->stream];
 
-       snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
+       ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                       &pcm_hardware_playback : &pcm_hardware_capture;
+       allocate_sram(substream, params->sram_size, ppcm);
+       snd_soc_set_runtime_hwparams(substream, ppcm);
        /* ensure that buffer size is a multiple of period size */
        ret = snd_pcm_hw_constraint_integer(runtime,
                                                SNDRV_PCM_HW_PARAM_PERIODS);
@@ -256,6 +671,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
 
        spin_lock_init(&prtd->lock);
        prtd->params = params;
+       prtd->asp_channel = -1;
+       prtd->asp_link[0] = prtd->asp_link[1] = -1;
+       prtd->ram_channel = -1;
+       prtd->ram_link = -1;
+       prtd->ram_link2 = -1;
 
        runtime->private_data = prtd;
 
@@ -273,10 +693,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct davinci_runtime_data *prtd = runtime->private_data;
 
-       edma_unlink(prtd->slave_lch);
-
-       edma_free_slot(prtd->slave_lch);
-       edma_free_channel(prtd->master_lch);
+       if (prtd->ram_channel >= 0)
+               edma_stop(prtd->ram_channel);
+       if (prtd->asp_channel >= 0)
+               edma_stop(prtd->asp_channel);
+       if (prtd->asp_link[0] >= 0)
+               edma_unlink(prtd->asp_link[0]);
+       if (prtd->asp_link[1] >= 0)
+               edma_unlink(prtd->asp_link[1]);
+       if (prtd->ram_link >= 0)
+               edma_unlink(prtd->ram_link);
+
+       if (prtd->asp_link[0] >= 0)
+               edma_free_slot(prtd->asp_link[0]);
+       if (prtd->asp_link[1] >= 0)
+               edma_free_slot(prtd->asp_link[1]);
+       if (prtd->asp_channel >= 0)
+               edma_free_channel(prtd->asp_channel);
+       if (prtd->ram_link >= 0)
+               edma_free_slot(prtd->ram_link);
+       if (prtd->ram_link2 >= 0)
+               edma_free_slot(prtd->ram_link2);
+       if (prtd->ram_channel >= 0)
+               edma_free_channel(prtd->ram_channel);
 
        kfree(prtd);
 
@@ -318,11 +757,11 @@ static struct snd_pcm_ops davinci_pcm_ops = {
        .mmap =         davinci_pcm_mmap,
 };
 
-static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
+               size_t size)
 {
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
        struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = davinci_pcm_hardware.buffer_bytes_max;
 
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
        buf->dev.dev = pcm->card->dev;
@@ -347,6 +786,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
        int stream;
 
        for (stream = 0; stream < 2; stream++) {
+               struct snd_dma_buffer *iram_dma;
                substream = pcm->streams[stream].substream;
                if (!substream)
                        continue;
@@ -358,6 +798,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
                dma_free_writecombine(pcm->card->dev, buf->bytes,
                                      buf->area, buf->addr);
                buf->area = NULL;
+               iram_dma = (struct snd_dma_buffer *)buf->private_data;
+               if (iram_dma) {
+                       sram_free(iram_dma->area, iram_dma->bytes);
+                       kfree(iram_dma);
+               }
        }
 }
 
@@ -375,14 +820,16 @@ static int davinci_pcm_new(struct snd_card *card,
 
        if (dai->playback.channels_min) {
                ret = davinci_pcm_preallocate_dma_buffer(pcm,
-                       SNDRV_PCM_STREAM_PLAYBACK);
+                       SNDRV_PCM_STREAM_PLAYBACK,
+                       pcm_hardware_playback.buffer_bytes_max);
                if (ret)
                        return ret;
        }
 
        if (dai->capture.channels_min) {
                ret = davinci_pcm_preallocate_dma_buffer(pcm,
-                       SNDRV_PCM_STREAM_CAPTURE);
+                       SNDRV_PCM_STREAM_CAPTURE,
+                       pcm_hardware_capture.buffer_bytes_max);
                if (ret)
                        return ret;
        }
index 8746606efc893114a10f4135fe2420006da96321..0764944cf10f4250dd802400c77bde414eac3226 100644 (file)
@@ -20,9 +20,11 @@ struct davinci_pcm_dma_params {
        int channel;                    /* sync dma channel ID */
        unsigned short acnt;
        dma_addr_t dma_addr;            /* device physical address for DMA */
+       unsigned sram_size;
        enum dma_event_q eventq_no;     /* event queue number */
        unsigned char data_type;        /* xfer data type */
        unsigned char convert_mono_stereo;
+       unsigned int fifo_level;
 };
 
 
index 6096d22283e6e5043386724cf11caf54b597f536..30ed568afb2ef5c24316e1fe1ae24c34a795adac 100644 (file)
@@ -58,47 +58,15 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
        /* Prepare and enqueue the next buffer descriptor */
        bd = bcom_prepare_next_buffer(s->bcom_task);
        bd->status = s->period_bytes;
-       bd->data[0] = s->period_next_pt;
+       bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes);
        bcom_submit_next_buffer(s->bcom_task, NULL);
 
        /* Update for next period */
-       s->period_next_pt += s->period_bytes;
-       if (s->period_next_pt >= s->period_end)
-               s->period_next_pt = s->period_start;
-}
-
-static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
-{
-       if (s->appl_ptr > s->runtime->control->appl_ptr) {
-               /*
-                * In this case s->runtime->control->appl_ptr has wrapped around.
-                * Play the data to the end of the boundary, then wrap our own
-                * appl_ptr back around.
-                */
-               while (s->appl_ptr < s->runtime->boundary) {
-                       if (bcom_queue_full(s->bcom_task))
-                               return;
-
-                       s->appl_ptr += s->period_size;
-
-                       psc_dma_bcom_enqueue_next_buffer(s);
-               }
-               s->appl_ptr -= s->runtime->boundary;
-       }
-
-       while (s->appl_ptr < s->runtime->control->appl_ptr) {
-
-               if (bcom_queue_full(s->bcom_task))
-                       return;
-
-               s->appl_ptr += s->period_size;
-
-               psc_dma_bcom_enqueue_next_buffer(s);
-       }
+       s->period_next = (s->period_next + 1) % s->runtime->periods;
 }
 
 /* Bestcomm DMA irq handler */
-static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
+static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
 {
        struct psc_dma_stream *s = _psc_dma_stream;
 
@@ -108,34 +76,8 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
        while (bcom_buffer_done(s->bcom_task)) {
                bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
 
-               s->period_current_pt += s->period_bytes;
-               if (s->period_current_pt >= s->period_end)
-                       s->period_current_pt = s->period_start;
-       }
-       psc_dma_bcom_enqueue_tx(s);
-       spin_unlock(&s->psc_dma->lock);
-
-       /* If the stream is active, then also inform the PCM middle layer
-        * of the period finished event. */
-       if (s->active)
-               snd_pcm_period_elapsed(s->stream);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream)
-{
-       struct psc_dma_stream *s = _psc_dma_stream;
-
-       spin_lock(&s->psc_dma->lock);
-       /* For each finished period, dequeue the completed period buffer
-        * and enqueue a new one in it's place. */
-       while (bcom_buffer_done(s->bcom_task)) {
-               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-
-               s->period_current_pt += s->period_bytes;
-               if (s->period_current_pt >= s->period_end)
-                       s->period_current_pt = s->period_start;
+               s->period_current = (s->period_current+1) % s->runtime->periods;
+               s->period_count++;
 
                psc_dma_bcom_enqueue_next_buffer(s);
        }
@@ -166,54 +108,38 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct psc_dma_stream *s;
+       struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
        struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
        u16 imr;
        unsigned long flags;
        int i;
 
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-               s = &psc_dma->capture;
-       else
-               s = &psc_dma->playback;
-
-       dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)"
-               " stream_id=%i\n",
-               substream, cmd, substream->pstr->stream);
-
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+               dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n",
+                       substream->pstr->stream, runtime->frame_bits,
+                       (int)runtime->period_size, runtime->periods);
                s->period_bytes = frames_to_bytes(runtime,
                                                  runtime->period_size);
-               s->period_start = virt_to_phys(runtime->dma_area);
-               s->period_end = s->period_start +
-                               (s->period_bytes * runtime->periods);
-               s->period_next_pt = s->period_start;
-               s->period_current_pt = s->period_start;
-               s->period_size = runtime->period_size;
+               s->period_next = 0;
+               s->period_current = 0;
                s->active = 1;
-
-               /* track appl_ptr so that we have a better chance of detecting
-                * end of stream and not over running it.
-                */
+               s->period_count = 0;
                s->runtime = runtime;
-               s->appl_ptr = s->runtime->control->appl_ptr -
-                               (runtime->period_size * runtime->periods);
 
                /* Fill up the bestcomm bd queue and enable DMA.
                 * This will begin filling the PSC's fifo.
                 */
                spin_lock_irqsave(&psc_dma->lock, flags);
 
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
                        bcom_gen_bd_rx_reset(s->bcom_task);
-                       for (i = 0; i < runtime->periods; i++)
-                               if (!bcom_queue_full(s->bcom_task))
-                                       psc_dma_bcom_enqueue_next_buffer(s);
-               } else {
+               else
                        bcom_gen_bd_tx_reset(s->bcom_task);
-                       psc_dma_bcom_enqueue_tx(s);
-               }
+
+               for (i = 0; i < runtime->periods; i++)
+                       if (!bcom_queue_full(s->bcom_task))
+                               psc_dma_bcom_enqueue_next_buffer(s);
 
                bcom_enable(s->bcom_task);
                spin_unlock_irqrestore(&psc_dma->lock, flags);
@@ -223,6 +149,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
+               dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n",
+                       substream->pstr->stream, s->period_count);
                s->active = 0;
 
                spin_lock_irqsave(&psc_dma->lock, flags);
@@ -236,7 +164,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
                break;
 
        default:
-               dev_dbg(psc_dma->dev, "invalid command\n");
+               dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n",
+                       substream->pstr->stream, cmd);
                return -EINVAL;
        }
 
@@ -343,7 +272,7 @@ psc_dma_pointer(struct snd_pcm_substream *substream)
        else
                s = &psc_dma->playback;
 
-       count = s->period_current_pt - s->period_start;
+       count = s->period_current * s->period_bytes;
 
        return bytes_to_frames(substream->runtime, count);
 }
@@ -532,11 +461,9 @@ int mpc5200_audio_dma_create(struct of_device *op)
 
        rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,
                         "psc-dma-status", psc_dma);
-       rc |= request_irq(psc_dma->capture.irq,
-                         &psc_dma_bcom_irq_rx, IRQF_SHARED,
+       rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED,
                          "psc-dma-capture", &psc_dma->capture);
-       rc |= request_irq(psc_dma->playback.irq,
-                         &psc_dma_bcom_irq_tx, IRQF_SHARED,
+       rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED,
                          "psc-dma-playback", &psc_dma->playback);
        if (rc) {
                ret = -ENODEV;
index 8d396bb9d9fe405db9c10dc12d14e2dedabfaf58..22208b373fb90fd64bd294d4f8eeb6a90ab841b1 100644 (file)
  * @psc_dma:           pointer back to parent psc_dma data structure
  * @bcom_task:         bestcomm task structure
  * @irq:               irq number for bestcomm task
- * @period_start:      physical address of start of DMA region
  * @period_end:                physical address of end of DMA region
  * @period_next_pt:    physical address of next DMA buffer to enqueue
  * @period_bytes:      size of DMA period in bytes
+ * @ac97_slot_bits:    Enable bits for turning on the correct AC97 slot
  */
 struct psc_dma_stream {
        struct snd_pcm_runtime *runtime;
-       snd_pcm_uframes_t appl_ptr;
-
        int active;
        struct psc_dma *psc_dma;
        struct bcom_task *bcom_task;
        int irq;
        struct snd_pcm_substream *stream;
-       dma_addr_t period_start;
-       dma_addr_t period_end;
-       dma_addr_t period_next_pt;
-       dma_addr_t period_current_pt;
+       int period_next;
+       int period_current;
        int period_bytes;
-       int period_size;
+       int period_count;
+
+       /* AC97 state */
+       u32 ac97_slot_bits;
 };
 
 /**
@@ -73,6 +72,15 @@ struct psc_dma {
        } stats;
 };
 
+/* Utility for retrieving psc_dma_stream structure from a substream */
+inline struct psc_dma_stream *
+to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma)
+{
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+               return &psc_dma->capture;
+       return &psc_dma->playback;
+}
+
 int mpc5200_audio_dma_create(struct of_device *op);
 int mpc5200_audio_dma_destroy(struct of_device *op);
 
index c4ae3e096bb9154376a3a3760e3cf4897cad65a3..3dbc7f7cd7b9a01a9b89cc3512a46159cc804133 100644 (file)
@@ -130,6 +130,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
                                 struct snd_soc_dai *cpu_dai)
 {
        struct psc_dma *psc_dma = cpu_dai->private_data;
+       struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
 
        dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
                " periods=%i buffer_size=%i  buffer_bytes=%i channels=%i"
@@ -140,20 +141,10 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
                params_channels(params), params_rate(params),
                params_format(params));
 
-
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               if (params_channels(params) == 1)
-                       psc_dma->slots |= 0x00000100;
-               else
-                       psc_dma->slots |= 0x00000300;
-       } else {
-               if (params_channels(params) == 1)
-                       psc_dma->slots |= 0x01000000;
-               else
-                       psc_dma->slots |= 0x03000000;
-       }
-       out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
-
+       /* Determine the set of enable bits to turn on */
+       s->ac97_slot_bits = (params_channels(params) == 1) ? 0x100 : 0x300;
+       if (substream->pstr->stream != SNDRV_PCM_STREAM_CAPTURE)
+               s->ac97_slot_bits <<= 16;
        return 0;
 }
 
@@ -163,6 +154,8 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream,
 {
        struct psc_dma *psc_dma = cpu_dai->private_data;
 
+       dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream);
+
        if (params_channels(params) == 1)
                out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000);
        else
@@ -176,14 +169,24 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma);
 
        switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               dev_dbg(psc_dma->dev, "AC97 START: stream=%i\n",
+                       substream->pstr->stream);
+
+               /* Set the slot enable bits */
+               psc_dma->slots |= s->ac97_slot_bits;
+               out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+               break;
+
        case SNDRV_PCM_TRIGGER_STOP:
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-                       psc_dma->slots &= 0xFFFF0000;
-               else
-                       psc_dma->slots &= 0x0000FFFF;
+               dev_dbg(psc_dma->dev, "AC97 STOP: stream=%i\n",
+                       substream->pstr->stream);
 
+               /* Clear the slot enable bits */
+               psc_dma->slots &= ~(s->ac97_slot_bits);
                out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
                break;
        }
index e4dcb539108a2657f54de4cac4f356f4bde413c3..0267d2d91685775ff6f5e957df424794180ae781 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 653a362425dfd265b70068361495c0404908df60..61952aa6cd5ac34a8a14e0324ccf2d036872eae8 100644 (file)
@@ -43,12 +43,13 @@ config SND_OMAP_SOC_OSK5912
          Say Y if you want to add support for SoC audio on osk5912.
 
 config SND_OMAP_SOC_OVERO
-       tristate "SoC Audio support for Gumstix Overo"
-       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
+       tristate "SoC Audio support for Gumstix Overo and CompuLab CM-T35"
+       depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_OVERO || MACH_CM_T35)
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TWL4030
        help
-         Say Y if you want to add support for SoC audio on the Gumstix Overo.
+         Say Y if you want to add support for SoC audio on the
+         Gumstix Overo or CompuLab CM-T35
 
 config SND_OMAP_SOC_OMAP2EVM
        tristate "SoC Audio support for OMAP2EVM board"
@@ -66,6 +67,15 @@ config SND_OMAP_SOC_OMAP3EVM
        help
          Say Y if you want to add support for SoC audio on the omap3evm board.
 
+config SND_OMAP_SOC_AM3517EVM
+       tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
+       depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TLV320AIC23
+       help
+         Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
+         EVM.
+
 config SND_OMAP_SOC_SDP3430
        tristate "SoC Audio support for Texas Instruments SDP3430"
        depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
@@ -99,3 +109,10 @@ config SND_OMAP_SOC_ZOOM2
        help
          Say Y if you want to add support for Soc audio on Zoom2 board.
 
+config SND_OMAP_SOC_IGEP0020
+       tristate "SoC Audio support for IGEP v2"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_IGEP0020
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for Soc audio on IGEP v2 board.
index 02d69471dcb5ae051fe8879f927fa0216802bb11..d49458a29bb71f415dc0c53201841a41bcbc726b 100644 (file)
@@ -12,10 +12,12 @@ snd-soc-osk5912-objs := osk5912.o
 snd-soc-overo-objs := overo.o
 snd-soc-omap2evm-objs := omap2evm.o
 snd-soc-omap3evm-objs := omap3evm.o
+snd-soc-am3517evm-objs := am3517evm.o
 snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap3pandora-objs := omap3pandora.o
 snd-soc-omap3beagle-objs := omap3beagle.o
 snd-soc-zoom2-objs := zoom2.o
+snd-soc-igep0020-objs := igep0020.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
@@ -23,7 +25,9 @@ obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
 obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
 obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
+obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o
 obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
 obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
+obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
new file mode 100644 (file)
index 0000000..135901b
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * am3517evm.c  -- ALSA SoC support for OMAP3517 / AM3517 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#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 <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK    12000000
+
+static int am3517evm_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 codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_DSP_B |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_DSP_B |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+                       CODEC_CLOCK, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
+                               SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n");
+               return ret;
+       }
+
+       snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+                               SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops am3517evm_ops = {
+       .hw_params = am3517evm_hw_params,
+};
+
+/* am3517evm machine dapm widgets */
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Line Out", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+       SND_SOC_DAPM_MIC("Mic In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Line Out connected to LLOUT, RLOUT */
+       {"Line Out", NULL, "LOUT"},
+       {"Line Out", NULL, "ROUT"},
+
+       {"LLINEIN", NULL, "Line In"},
+       {"RLINEIN", NULL, "Line In"},
+
+       {"MICIN", NULL, "Mic In"},
+};
+
+static int am3517evm_aic23_init(struct snd_soc_codec *codec)
+{
+       /* Add am3517-evm specific widgets */
+       snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+                                 ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+       /* Set up davinci-evm specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       /* always connected */
+       snd_soc_dapm_enable_pin(codec, "Line Out");
+       snd_soc_dapm_enable_pin(codec, "Line In");
+       snd_soc_dapm_enable_pin(codec, "Mic In");
+
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link am3517evm_dai = {
+       .name = "TLV320AIC23",
+       .stream_name = "AIC23",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &tlv320aic23_dai,
+       .init = am3517evm_aic23_init,
+       .ops = &am3517evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_am3517evm = {
+       .name = "am3517evm",
+       .platform = &omap_soc_platform,
+       .dai_link = &am3517evm_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device am3517evm_snd_devdata = {
+       .card = &snd_soc_am3517evm,
+       .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *am3517evm_snd_device;
+
+static int __init am3517evm_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap3517evm()) {
+               pr_err("Not OMAP3517 / AM3517 EVM!\n");
+               return -ENODEV;
+       }
+       pr_info("OMAP3517 / AM3517 EVM SoC init\n");
+
+       am3517evm_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!am3517evm_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata);
+       am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev;
+       *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */
+
+       ret = platform_device_add(am3517evm_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(am3517evm_snd_device);
+
+       return ret;
+}
+
+static void __exit am3517evm_soc_exit(void)
+{
+       platform_device_unregister(am3517evm_snd_device);
+}
+
+module_init(am3517evm_soc_init);
+module_exit(am3517evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM");
+MODULE_LICENSE("GPL v2");
index 5a5166ac727940105870bc6cd8d6c7f8f965ebb7..ae0fc9b135d40aefe8f0ade0fba72522a92c2851 100644 (file)
@@ -40,7 +40,7 @@
 
 
 /* Board specific DAPM widgets */
- const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
        /* Handset */
        SND_SOC_DAPM_MIC("Mouthpiece", NULL),
        SND_SOC_DAPM_HP("Earpiece", NULL),
@@ -81,7 +81,7 @@ static const char *ams_delta_audio_mode[] =
                                                (1 << AMS_DELTA_SPEAKER))
 #define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
 
-unsigned short ams_delta_audio_mode_pins[] = {
+static const unsigned short ams_delta_audio_mode_pins[] = {
        AMS_DELTA_MIXED,
        AMS_DELTA_HANDSET,
        AMS_DELTA_HANDSFREE,
diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c
new file mode 100644 (file)
index 0000000..3583c42
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * igep0020.c  --  SoC audio for IGEP v2
+ *
+ * Based on sound/soc/omap/overo.c by Steve Sakoman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/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 <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int igep2_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 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) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu 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) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops igep2_ops = {
+       .hw_params = igep2_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link igep2_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+       .ops = &igep2_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_igep2 = {
+       .name = "igep2",
+       .platform = &omap_soc_platform,
+       .dai_link = &igep2_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device igep2_snd_devdata = {
+       .card = &snd_soc_card_igep2,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *igep2_snd_device;
+
+static int __init igep2_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_igep0020()) {
+               pr_debug("Not IGEP v2!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "IGEP v2 SoC init\n");
+
+       igep2_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!igep2_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(igep2_snd_device, &igep2_snd_devdata);
+       igep2_snd_devdata.dev = &igep2_snd_device->dev;
+       *(unsigned int *)igep2_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(igep2_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(igep2_snd_device);
+
+       return ret;
+}
+module_init(igep2_soc_init);
+
+static void __exit igep2_soc_exit(void)
+{
+       platform_device_unregister(igep2_snd_device);
+}
+module_exit(igep2_soc_exit);
+
+MODULE_AUTHOR("Enric Balletbo i Serra <eballetbo@iseebcn.com>");
+MODULE_DESCRIPTION("ALSA SoC IGEP v2");
+MODULE_LICENSE("GPL");
index 3341f49402ca634226eeaa7b7e231b4c98f80fd7..45be94201c89747080df9eb9d322165c33097b2f 100644 (file)
@@ -49,6 +49,8 @@ struct omap_mcbsp_data {
         */
        int                             active;
        int                             configured;
+       unsigned int                    in_freq;
+       int                             clk_div;
 };
 
 #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id)
@@ -257,7 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
        int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
        unsigned long port;
-       unsigned int format;
+       unsigned int format, div, framesize, master;
 
        if (cpu_class_is_omap1()) {
                dma = omap1_dma_reqs[bus_id][substream->stream];
@@ -294,28 +296,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
 
        format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
        wpf = channels = params_channels(params);
-       switch (channels) {
-       case 2:
-               if (format == SND_SOC_DAIFMT_I2S) {
-                       /* Use dual-phase frames */
-                       regs->rcr2      |= RPHASE;
-                       regs->xcr2      |= XPHASE;
-                       /* Set 1 word per (McBSP) frame for phase1 and phase2 */
-                       wpf--;
-                       regs->rcr2      |= RFRLEN2(wpf - 1);
-                       regs->xcr2      |= XFRLEN2(wpf - 1);
-               }
-       case 1:
-       case 4:
-               /* Set word per (McBSP) frame for phase1 */
-               regs->rcr1      |= RFRLEN1(wpf - 1);
-               regs->xcr1      |= XFRLEN1(wpf - 1);
-               break;
-       default:
-               /* Unsupported number of channels */
-               return -EINVAL;
+       if (channels == 2 && format == SND_SOC_DAIFMT_I2S) {
+               /* Use dual-phase frames */
+               regs->rcr2      |= RPHASE;
+               regs->xcr2      |= XPHASE;
+               /* Set 1 word per (McBSP) frame for phase1 and phase2 */
+               wpf--;
+               regs->rcr2      |= RFRLEN2(wpf - 1);
+               regs->xcr2      |= XFRLEN2(wpf - 1);
        }
 
+       regs->rcr1      |= RFRLEN1(wpf - 1);
+       regs->xcr1      |= XFRLEN1(wpf - 1);
+
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                /* Set word lengths */
@@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
+       /* In McBSP master modes, FRAME (i.e. sample rate) is generated
+        * by _counting_ BCLKs. Calculate frame size in BCLKs */
+       master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+       if (master ==   SND_SOC_DAIFMT_CBS_CFS) {
+               div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1;
+               framesize = (mcbsp_data->in_freq / div) / params_rate(params);
+
+               if (framesize < wlen * channels) {
+                       printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
+                                       "channels\n", __func__);
+                       return -EINVAL;
+               }
+       } else
+               framesize = wlen * channels;
+
        /* Set FS period and length in terms of bit clock periods */
        switch (format) {
        case SND_SOC_DAIFMT_I2S:
-               regs->srgr2     |= FPER(wlen * channels - 1);
-               regs->srgr1     |= FWID(wlen - 1);
+               regs->srgr2     |= FPER(framesize - 1);
+               regs->srgr1     |= FWID((framesize >> 1) - 1);
                break;
        case SND_SOC_DAIFMT_DSP_A:
        case SND_SOC_DAIFMT_DSP_B:
-               regs->srgr2     |= FPER(wlen * channels - 1);
+               regs->srgr2     |= FPER(framesize - 1);
                regs->srgr1     |= FWID(0);
                break;
        }
@@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
        if (div_id != OMAP_MCBSP_CLKGDV)
                return -ENODEV;
 
+       mcbsp_data->clk_div = div;
        regs->srgr1     |= CLKGDV(div - 1);
 
        return 0;
@@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
        int err = 0;
 
+       mcbsp_data->in_freq = freq;
+
        switch (clk_id) {
        case OMAP_MCBSP_SYSCLK_CLK:
                regs->srgr2     |= CLKSM;
@@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
        .id = (link_id),                                        \
        .playback = {                                           \
                .channels_min = 1,                              \
-               .channels_max = 4,                              \
+               .channels_max = 16,                             \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
        .capture = {                                            \
                .channels_min = 1,                              \
-               .channels_max = 4,                              \
+               .channels_max = 16,                             \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
index 13aa380de162daa7deab8abbdf8fb3231ddca418..f484dcd634089562727d189f14615cbf84246d60 100644 (file)
@@ -93,10 +93,17 @@ static struct snd_soc_card snd_soc_omap3evm = {
        .num_links = 1,
 };
 
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+       .ramp_delay_value = 4,
+       .sysclk = 26000,
+};
+
 /* Audio subsystem */
 static struct snd_soc_device omap3evm_snd_devdata = {
        .card = &snd_soc_omap3evm,
        .codec_dev = &soc_codec_dev_twl4030,
+       .codec_data = &twl4030_setup,
 };
 
 static struct platform_device *omap3evm_snd_device;
index 0cd06f5dd356f24214d91d714ecfbd991478ff81..71b2c161158d5fb5c3271bdf0211efc2a9656719 100644 (file)
 
 #define PREFIX "ASoC omap3pandora: "
 
-static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
-       struct snd_soc_dai *cpu_dai, unsigned int fmt)
+static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, unsigned int fmt)
 {
+       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 codec DAI configuration */
@@ -68,8 +71,9 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
        }
 
        /* Set McBSP clock to external */
-       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0,
-                                           SND_SOC_CLOCK_IN);
+       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
+                                    256 * params_rate(params),
+                                    SND_SOC_CLOCK_IN);
        if (ret < 0) {
                pr_err(PREFIX "can't set cpu system clock\n");
                return ret;
@@ -87,11 +91,7 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
 static int omap3pandora_out_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;
-
-       return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+       return omap3pandora_cmn_hw_params(substream, params,
                                          SND_SOC_DAIFMT_I2S |
                                          SND_SOC_DAIFMT_IB_NF |
                                          SND_SOC_DAIFMT_CBS_CFS);
@@ -100,11 +100,7 @@ static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream,
 static int omap3pandora_in_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;
-
-       return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+       return omap3pandora_cmn_hw_params(substream, params,
                                          SND_SOC_DAIFMT_I2S |
                                          SND_SOC_DAIFMT_NB_NF |
                                          SND_SOC_DAIFMT_CBS_CFS);
index ec4f8fd8b3a2ae9a1e79a3cb91c033a2b1ae4cf6..97a4d6308bd61aff46efa7ee98a66bbba1c9d07e 100644 (file)
@@ -107,8 +107,8 @@ static int __init overo_soc_init(void)
 {
        int ret;
 
-       if (!machine_is_overo()) {
-               pr_debug("Not Overo!\n");
+       if (!(machine_is_overo() || machine_is_cm_t35())) {
+               pr_debug("Incomatible machine!\n");
                return -ENODEV;
        }
        printk(KERN_INFO "overo SoC init\n");
index dcb3181bb3406a66fc1c5843a8b9be743a87fc78..d4f4031afa33b45412b88b389649b981ede841fa 100644 (file)
@@ -90,7 +90,8 @@ config SND_PXA2XX_SOC_E800
 
 config SND_PXA2XX_SOC_EM_X270
        tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
-       depends on SND_PXA2XX_SOC && MACH_EM_X270
+       depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
+                       MACH_CM_X300)
        select SND_PXA2XX_SOC_AC97
        select SND_SOC_WM9712
        help
index 9f7c61e23dafbdf597a84779b231878cf6d9b12a..4c8d99a8d38667804ccb80b62a78268f8f55f439 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 d11a6d7e384abdfeab68eb47120a251f8734fe47..3bd7712f029b9024315bc3cc3f8bf5b6f6988235 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;
@@ -760,13 +760,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                         .channels_min = 1,
-                        .channels_max = 2,
+                        .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -780,13 +780,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -801,13 +801,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -822,13 +822,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
index 9a386b4c4ed13f09118bd27ed8a53cd8a3f63f45..dd678ae24398bb7f336257a362b43e54eeb25a5f 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 923428fc1adb9184b5b42d54fd9fcaf50144afeb..b489f1ae103dfec0c146d1db59d423472defaea4 100644 (file)
@@ -24,6 +24,9 @@ config SND_S3C64XX_SOC_I2S
        select SND_S3C_I2SV2_SOC
        select S3C64XX_DMA
 
+config SND_S3C_SOC_PCM
+       tristate
+
 config SND_S3C2443_SOC_AC97
        tristate
        select S3C2410_DMA
@@ -56,6 +59,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
index 99f5a7dd3fc667b3fd1bc4703651839e586bac0e..b744657733d736f8ad606a14395ee059bf69a812 100644 (file)
@@ -1,10 +1,11 @@
 # S3c24XX Platform Support
-snd-soc-s3c24xx-objs := s3c24xx-pcm.o
+snd-soc-s3c24xx-objs := s3c-dma.o
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
 snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
 snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o
 snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
 snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
+snd-soc-s3c-pcm-objs := s3c-pcm.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
@@ -12,6 +13,7 @@ obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
 obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
 obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o
 obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
+obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o
 
 # S3C24XX Machine Support
 snd-soc-jive-wm8750-objs := jive_wm8750.o
@@ -23,6 +25,7 @@ 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
@@ -33,4 +36,5 @@ 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 93e6c87b7399085722169857e6756eed4fbe7ff1..59dc2c6b56d92d7ffae8bfc4b0840cf07ad1a5bf 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <asm/mach-types.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c2412-i2s.h"
 
 #include "../codecs/wm8750.h"
index 12c71482d258b35c70d22ecf9e9c18441b57a575..d00d359a03e6fd8b30631051b2169474a8a6327f 100644 (file)
@@ -24,7 +24,7 @@
 #include <sound/soc-dapm.h>
 
 #include "../codecs/ac97.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-ac97.h"
 
 static struct snd_soc_card ln2440sbc;
index 0c52e36ddd87d865002afb1a28ef10d2066292d6..dea83d30a5c9e821fe4cada5fae2e6981d1b3188 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/io.h>
 #include <mach/gta02.h>
 #include "../codecs/wm8753.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 
 static struct snd_soc_card neo1973_gta02;
@@ -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;
@@ -133,7 +133,7 @@ static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -183,7 +183,7 @@ static int neo1973_gta02_voice_hw_params(
                return ret;
 
        /* configue and enable PLL for 12.288MHz output */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
                iis_clkrate / 4, 12288000);
        if (ret < 0)
                return ret;
@@ -197,7 +197,7 @@ static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_gta02_voice_ops = {
index 906709e6dd5f36182d2ec3ef7db497747d05c627..0cb4f86f6d1eb65be9f5e4864f70b1a42e830fe8 100644 (file)
@@ -29,7 +29,6 @@
 #include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
 #include <mach/hardware.h>
-#include <plat/audio.h>
 #include <linux/io.h>
 #include <mach/spi-gpio.h>
 
@@ -37,7 +36,7 @@
 
 #include "../codecs/wm8753.h"
 #include "lm4857.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 
 /* define the scenarios */
@@ -137,7 +136,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;
@@ -153,7 +152,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
        pr_debug("Entered %s\n", __func__);
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -203,7 +202,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* configue and enable PLL for 12.288MHz output */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
                iis_clkrate / 4, 12288000);
        if (ret < 0)
                return ret;
@@ -219,7 +218,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
        pr_debug("Entered %s\n", __func__);
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_voice_ops = {
similarity index 82%
rename from sound/soc/s3c24xx/s3c24xx-pcm.c
rename to sound/soc/s3c24xx/s3c-dma.c
index 1f35c6fcf5fd7bb1a8553bd6b7677f36a5c3788c..7725e26d6c914c99ad84ce89864320c944abbd00 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * s3c24xx-pcm.c  --  ALSA Soc Audio Layer
+ * s3c-dma.c  --  ALSA Soc Audio Layer
  *
  * (c) 2006 Wolfson Microelectronics PLC.
  * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
 #include <asm/dma.h>
 #include <mach/hardware.h>
 #include <mach/dma.h>
-#include <plat/audio.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 
-static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
+static const struct snd_pcm_hardware s3c_dma_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                    SNDRV_PCM_INFO_MMAP |
@@ -63,15 +62,15 @@ struct s3c24xx_runtime_data {
        dma_addr_t dma_start;
        dma_addr_t dma_pos;
        dma_addr_t dma_end;
-       struct s3c24xx_pcm_dma_params *params;
+       struct s3c_dma_params *params;
 };
 
-/* s3c24xx_pcm_enqueue
+/* s3c_dma_enqueue
  *
  * place a dma buffer onto the queue for the dma system
  * to handle.
 */
-static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
+static void s3c_dma_enqueue(struct snd_pcm_substream *substream)
 {
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
        dma_addr_t pos = prtd->dma_pos;
@@ -80,12 +79,13 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
 
        pr_debug("Entered %s\n", __func__);
 
-       if (s3c_dma_has_circular()) {
+       if (s3c_dma_has_circular())
                limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
-       else
+       else
                limit = prtd->dma_limit;
 
-       pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit);
+       pr_debug("%s: loaded %d, limit %d\n",
+                               __func__, prtd->dma_loaded, limit);
 
        while (prtd->dma_loaded < limit) {
                unsigned long len = prtd->dma_period;
@@ -133,19 +133,19 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
        spin_lock(&prtd->lock);
        if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
                prtd->dma_loaded--;
-               s3c24xx_pcm_enqueue(substream);
+               s3c_dma_enqueue(substream);
        }
 
        spin_unlock(&prtd->lock);
 }
 
-static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
+static int s3c_dma_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
+       struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data;
        unsigned long totbytes = params_buffer_bytes(params);
        int ret = 0;
 
@@ -198,7 +198,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
+static int s3c_dma_hw_free(struct snd_pcm_substream *substream)
 {
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
 
@@ -215,7 +215,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
+static int s3c_dma_prepare(struct snd_pcm_substream *substream)
 {
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
        int ret = 0;
@@ -248,12 +248,12 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
        prtd->dma_pos = prtd->dma_start;
 
        /* enqueue dma buffers */
-       s3c24xx_pcm_enqueue(substream);
+       s3c_dma_enqueue(substream);
 
        return ret;
 }
 
-static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
        int ret = 0;
@@ -288,7 +288,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static snd_pcm_uframes_t
-s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
+s3c_dma_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
@@ -323,7 +323,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
        return bytes_to_frames(substream->runtime, res);
 }
 
-static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
+static int s3c_dma_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct s3c24xx_runtime_data *prtd;
@@ -331,7 +331,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);
+       snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware);
 
        prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
        if (prtd == NULL)
@@ -343,7 +343,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
+static int s3c_dma_close(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
@@ -351,14 +351,14 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
        pr_debug("Entered %s\n", __func__);
 
        if (!prtd)
-               pr_debug("s3c24xx_pcm_close called with prtd == NULL\n");
+               pr_debug("s3c_dma_close called with prtd == NULL\n");
 
        kfree(prtd);
 
        return 0;
 }
 
-static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
+static int s3c_dma_mmap(struct snd_pcm_substream *substream,
        struct vm_area_struct *vma)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -371,23 +371,23 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
                                     runtime->dma_bytes);
 }
 
-static struct snd_pcm_ops s3c24xx_pcm_ops = {
-       .open           = s3c24xx_pcm_open,
-       .close          = s3c24xx_pcm_close,
+static struct snd_pcm_ops s3c_dma_ops = {
+       .open           = s3c_dma_open,
+       .close          = s3c_dma_close,
        .ioctl          = snd_pcm_lib_ioctl,
-       .hw_params      = s3c24xx_pcm_hw_params,
-       .hw_free        = s3c24xx_pcm_hw_free,
-       .prepare        = s3c24xx_pcm_prepare,
-       .trigger        = s3c24xx_pcm_trigger,
-       .pointer        = s3c24xx_pcm_pointer,
-       .mmap           = s3c24xx_pcm_mmap,
+       .hw_params      = s3c_dma_hw_params,
+       .hw_free        = s3c_dma_hw_free,
+       .prepare        = s3c_dma_prepare,
+       .trigger        = s3c_dma_trigger,
+       .pointer        = s3c_dma_pointer,
+       .mmap           = s3c_dma_mmap,
 };
 
-static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 {
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
        struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
+       size_t size = s3c_dma_hardware.buffer_bytes_max;
 
        pr_debug("Entered %s\n", __func__);
 
@@ -402,7 +402,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
        return 0;
 }
 
-static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm)
 {
        struct snd_pcm_substream *substream;
        struct snd_dma_buffer *buf;
@@ -425,9 +425,9 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
        }
 }
 
-static u64 s3c24xx_pcm_dmamask = DMA_BIT_MASK(32);
+static u64 s3c_dma_mask = DMA_BIT_MASK(32);
 
-static int s3c24xx_pcm_new(struct snd_card *card,
+static int s3c_dma_new(struct snd_card *card,
        struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
        int ret = 0;
@@ -435,19 +435,19 @@ static int s3c24xx_pcm_new(struct snd_card *card,
        pr_debug("Entered %s\n", __func__);
 
        if (!card->dev->dma_mask)
-               card->dev->dma_mask = &s3c24xx_pcm_dmamask;
+               card->dev->dma_mask = &s3c_dma_mask;
        if (!card->dev->coherent_dma_mask)
                card->dev->coherent_dma_mask = 0xffffffff;
 
        if (dai->playback.channels_min) {
-               ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
+               ret = s3c_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_PLAYBACK);
                if (ret)
                        goto out;
        }
 
        if (dai->capture.channels_min) {
-               ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
+               ret = s3c_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_CAPTURE);
                if (ret)
                        goto out;
@@ -458,9 +458,9 @@ static int s3c24xx_pcm_new(struct snd_card *card,
 
 struct snd_soc_platform s3c24xx_soc_platform = {
        .name           = "s3c24xx-audio",
-       .pcm_ops        = &s3c24xx_pcm_ops,
-       .pcm_new        = s3c24xx_pcm_new,
-       .pcm_free       = s3c24xx_pcm_free_dma_buffers,
+       .pcm_ops        = &s3c_dma_ops,
+       .pcm_new        = s3c_dma_new,
+       .pcm_free       = s3c_dma_free_dma_buffers,
 };
 EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
 
@@ -477,5 +477,5 @@ static void __exit s3c24xx_soc_platform_exit(void)
 module_exit(s3c24xx_soc_platform_exit);
 
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
+MODULE_DESCRIPTION("Samsung S3C Audio DMA module");
 MODULE_LICENSE("GPL");
similarity index 87%
rename from sound/soc/s3c24xx/s3c24xx-pcm.h
rename to sound/soc/s3c24xx/s3c-dma.h
index 0088c79822ead0b409aec38ea4729fb1c2758a34..69bb6bf6fc1c2a99a475313375a56969310b6f35 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  s3c24xx-pcm.h --
+ *  s3c-dma.h --
  *
  *  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
@@ -9,13 +9,13 @@
  *  ALSA PCM interface for the Samsung S3C24xx CPU
  */
 
-#ifndef _S3C24XX_PCM_H
-#define _S3C24XX_PCM_H
+#ifndef _S3C_AUDIO_H
+#define _S3C_AUDIO_H
 
 #define ST_RUNNING             (1<<0)
 #define ST_OPENED              (1<<1)
 
-struct s3c24xx_pcm_dma_params {
+struct s3c_dma_params {
        struct s3c2410_dma_client *client;      /* stream identifier */
        int channel;                            /* Channel ID */
        dma_addr_t dma_addr;
index 9bc4aa35caab477f918f6f574fadb8ffbf75f319..e994d8374fe62dc313aca6077e8bed20c5fb186f 100644 (file)
 
 #include <plat/regs-s3c2412-iis.h>
 
-#include <plat/audio.h>
 #include <mach/dma.h>
 
 #include "s3c-i2s-v2.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 
 #undef S3C_IIS_V2_SUPPORTED
 
@@ -312,12 +311,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:
@@ -392,7 +394,7 @@ 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 *)
+       int channel = ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->channel;
 
        pr_debug("Entered %s\n", __func__);
@@ -467,6 +469,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);
@@ -626,7 +653,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 f66854a77fb225c880cf172501a4c56456356eb2..ecf8eaaed1db7a4c61e561c831a186d5b6a3c6e2 100644 (file)
@@ -49,8 +49,8 @@ struct s3c_i2sv2_info {
 
        unsigned char    master;
 
-       struct s3c24xx_pcm_dma_params   *dma_playback;
-       struct s3c24xx_pcm_dma_params   *dma_capture;
+       struct s3c_dma_params   *dma_playback;
+       struct s3c_dma_params   *dma_capture;
 
        u32              suspend_iismod;
        u32              suspend_iiscon;
diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c
new file mode 100644 (file)
index 0000000..9e61a7c
--- /dev/null
@@ -0,0 +1,552 @@
+/* sound/soc/s3c24xx/s3c-pcm.c
+ *
+ * ALSA SoC Audio Layer - S3C PCM-Controller driver
+ *
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * based upon I2S drivers by Ben Dooks.
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/audio.h>
+#include <plat/dma.h>
+
+#include "s3c-dma.h"
+#include "s3c-pcm.h"
+
+static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
+       .name           = "PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c_pcm_dma_client_in = {
+       .name           = "PCM Stereo in"
+};
+
+static struct s3c_dma_params s3c_pcm_stereo_out[] = {
+       [0] = {
+               .client         = &s3c_pcm_dma_client_out,
+               .dma_size       = 4,
+       },
+       [1] = {
+               .client         = &s3c_pcm_dma_client_out,
+               .dma_size       = 4,
+       },
+};
+
+static struct s3c_dma_params s3c_pcm_stereo_in[] = {
+       [0] = {
+               .client         = &s3c_pcm_dma_client_in,
+               .dma_size       = 4,
+       },
+       [1] = {
+               .client         = &s3c_pcm_dma_client_in,
+               .dma_size       = 4,
+       },
+};
+
+static struct s3c_pcm_info s3c_pcm[2];
+
+static inline struct s3c_pcm_info *to_info(struct snd_soc_dai *cpu_dai)
+{
+       return cpu_dai->private_data;
+}
+
+static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
+{
+       void __iomem *regs = pcm->regs;
+       u32 ctl, clkctl;
+
+       clkctl = readl(regs + S3C_PCM_CLKCTL);
+       ctl = readl(regs + S3C_PCM_CTL);
+       ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
+                        << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
+
+       if (on) {
+               ctl |= S3C_PCM_CTL_TXDMA_EN;
+               ctl |= S3C_PCM_CTL_TXFIFO_EN;
+               ctl |= S3C_PCM_CTL_ENABLE;
+               ctl |= (0x20<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
+               clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+       } else {
+               ctl &= ~S3C_PCM_CTL_TXDMA_EN;
+               ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
+
+               if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
+                       ctl &= ~S3C_PCM_CTL_ENABLE;
+                       if (!pcm->idleclk)
+                               clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+               }
+       }
+
+       writel(clkctl, regs + S3C_PCM_CLKCTL);
+       writel(ctl, regs + S3C_PCM_CTL);
+}
+
+static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
+{
+       void __iomem *regs = pcm->regs;
+       u32 ctl, clkctl;
+
+       ctl = readl(regs + S3C_PCM_CTL);
+       clkctl = readl(regs + S3C_PCM_CLKCTL);
+
+       if (on) {
+               ctl |= S3C_PCM_CTL_RXDMA_EN;
+               ctl |= S3C_PCM_CTL_RXFIFO_EN;
+               ctl |= S3C_PCM_CTL_ENABLE;
+               clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+       } else {
+               ctl &= ~S3C_PCM_CTL_RXDMA_EN;
+               ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
+
+               if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
+                       ctl &= ~S3C_PCM_CTL_ENABLE;
+                       if (!pcm->idleclk)
+                               clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
+               }
+       }
+
+       writel(clkctl, regs + S3C_PCM_CLKCTL);
+       writel(ctl, regs + S3C_PCM_CTL);
+}
+
+static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct s3c_pcm_info *pcm = to_info(rtd->dai->cpu_dai);
+       unsigned long flags;
+
+       dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               spin_lock_irqsave(&pcm->lock, flags);
+
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       s3c_pcm_snd_rxctrl(pcm, 1);
+               else
+                       s3c_pcm_snd_txctrl(pcm, 1);
+
+               spin_unlock_irqrestore(&pcm->lock, flags);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               spin_lock_irqsave(&pcm->lock, flags);
+
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       s3c_pcm_snd_rxctrl(pcm, 0);
+               else
+                       s3c_pcm_snd_txctrl(pcm, 0);
+
+               spin_unlock_irqrestore(&pcm->lock, flags);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *socdai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai_link *dai = rtd->dai;
+       struct s3c_pcm_info *pcm = to_info(dai->cpu_dai);
+       void __iomem *regs = pcm->regs;
+       struct clk *clk;
+       int sclk_div, sync_div;
+       unsigned long flags;
+       u32 clkctl;
+
+       dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dai->cpu_dai->dma_data = pcm->dma_playback;
+       else
+               dai->cpu_dai->dma_data = pcm->dma_capture;
+
+       /* Strictly check for sample size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&pcm->lock, flags);
+
+       /* Get hold of the PCMSOURCE_CLK */
+       clkctl = readl(regs + S3C_PCM_CLKCTL);
+       if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
+               clk = pcm->pclk;
+       else
+               clk = pcm->cclk;
+
+       /* Set the SCLK divider */
+       sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
+                                       params_rate(params) / 2 - 1;
+
+       clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
+                       << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
+       clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
+                       << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
+
+       /* Set the SYNC divider */
+       sync_div = pcm->sclk_per_fs - 1;
+
+       clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
+                               << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
+       clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
+                               << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
+
+       writel(clkctl, regs + S3C_PCM_CLKCTL);
+
+       spin_unlock_irqrestore(&pcm->lock, flags);
+
+       dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \
+                               SCLK_DIV=%d SYNC_DIV=%d\n",
+                               clk_get_rate(clk), pcm->sclk_per_fs,
+                               sclk_div, sync_div);
+
+       return 0;
+}
+
+static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
+                              unsigned int fmt)
+{
+       struct s3c_pcm_info *pcm = to_info(cpu_dai);
+       void __iomem *regs = pcm->regs;
+       unsigned long flags;
+       int ret = 0;
+       u32 ctl;
+
+       dev_dbg(pcm->dev, "Entered %s\n", __func__);
+
+       spin_lock_irqsave(&pcm->lock, flags);
+
+       ctl = readl(regs + S3C_PCM_CTL);
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               /* Nothing to do, NB_NF by default */
+               break;
+       default:
+               dev_err(pcm->dev, "Unsupported clock inversion!\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* Nothing to do, Master by default */
+               break;
+       default:
+               dev_err(pcm->dev, "Unsupported master/slave format!\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+       case SND_SOC_DAIFMT_CONT:
+               pcm->idleclk = 1;
+               break;
+       case SND_SOC_DAIFMT_GATED:
+               pcm->idleclk = 0;
+               break;
+       default:
+               dev_err(pcm->dev, "Invalid Clock gating request!\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
+               ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
+               ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
+               break;
+       default:
+               dev_err(pcm->dev, "Unsupported data format!\n");
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       writel(ctl, regs + S3C_PCM_CTL);
+
+exit:
+       spin_unlock_irqrestore(&pcm->lock, flags);
+
+       return ret;
+}
+
+static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
+                                               int div_id, int div)
+{
+       struct s3c_pcm_info *pcm = to_info(cpu_dai);
+
+       switch (div_id) {
+       case S3C_PCM_SCLK_PER_FS:
+               pcm->sclk_per_fs = div;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct s3c_pcm_info *pcm = to_info(cpu_dai);
+       void __iomem *regs = pcm->regs;
+       u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
+
+       switch (clk_id) {
+       case S3C_PCM_CLKSRC_PCLK:
+               clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
+               break;
+
+       case S3C_PCM_CLKSRC_MUX:
+               clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
+
+               if (clk_get_rate(pcm->cclk) != freq)
+                       clk_set_rate(pcm->cclk, freq);
+
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       writel(clkctl, regs + S3C_PCM_CLKCTL);
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops s3c_pcm_dai_ops = {
+       .set_sysclk     = s3c_pcm_set_sysclk,
+       .set_clkdiv     = s3c_pcm_set_clkdiv,
+       .trigger        = s3c_pcm_trigger,
+       .hw_params      = s3c_pcm_hw_params,
+       .set_fmt        = s3c_pcm_set_fmt,
+};
+
+#define S3C_PCM_RATES  SNDRV_PCM_RATE_8000_96000
+
+#define S3C_PCM_DECLARE(n)                     \
+{                                                              \
+       .name            = "samsung-pcm",                       \
+       .id              = (n),                         \
+       .symmetric_rates = 1,                                   \
+       .ops = &s3c_pcm_dai_ops,                                \
+       .playback = {                                           \
+               .channels_min   = 2,                            \
+               .channels_max   = 2,                            \
+               .rates          = S3C_PCM_RATES,                \
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE,      \
+       },                                                      \
+       .capture = {                                            \
+               .channels_min   = 2,                            \
+               .channels_max   = 2,                            \
+               .rates          = S3C_PCM_RATES,                \
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE,      \
+       },                                                      \
+}
+
+struct snd_soc_dai s3c_pcm_dai[] = {
+       S3C_PCM_DECLARE(0),
+       S3C_PCM_DECLARE(1),
+};
+EXPORT_SYMBOL_GPL(s3c_pcm_dai);
+
+static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev)
+{
+       struct s3c_pcm_info *pcm;
+       struct snd_soc_dai *dai;
+       struct resource *mem_res, *dmatx_res, *dmarx_res;
+       struct s3c_audio_pdata *pcm_pdata;
+       int ret;
+
+       /* Check for valid device index */
+       if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
+               dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+               return -EINVAL;
+       }
+
+       pcm_pdata = pdev->dev.platform_data;
+
+       /* Check for availability of necessary resource */
+       dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!dmatx_res) {
+               dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
+               return -ENXIO;
+       }
+
+       dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!dmarx_res) {
+               dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
+               return -ENXIO;
+       }
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem_res) {
+               dev_err(&pdev->dev, "Unable to get register resource\n");
+               return -ENXIO;
+       }
+
+       if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
+               dev_err(&pdev->dev, "Unable to configure gpio\n");
+               return -EINVAL;
+       }
+
+       pcm = &s3c_pcm[pdev->id];
+       pcm->dev = &pdev->dev;
+
+       spin_lock_init(&pcm->lock);
+
+       dai = &s3c_pcm_dai[pdev->id];
+       dai->dev = &pdev->dev;
+
+       /* Default is 128fs */
+       pcm->sclk_per_fs = 128;
+
+       pcm->cclk = clk_get(&pdev->dev, "audio-bus");
+       if (IS_ERR(pcm->cclk)) {
+               dev_err(&pdev->dev, "failed to get audio-bus\n");
+               ret = PTR_ERR(pcm->cclk);
+               goto err1;
+       }
+       clk_enable(pcm->cclk);
+
+       /* record our pcm structure for later use in the callbacks */
+       dai->private_data = pcm;
+
+       if (!request_mem_region(mem_res->start,
+                               resource_size(mem_res), "samsung-pcm")) {
+               dev_err(&pdev->dev, "Unable to request register region\n");
+               ret = -EBUSY;
+               goto err2;
+       }
+
+       pcm->regs = ioremap(mem_res->start, 0x100);
+       if (pcm->regs == NULL) {
+               dev_err(&pdev->dev, "cannot ioremap registers\n");
+               ret = -ENXIO;
+               goto err3;
+       }
+
+       pcm->pclk = clk_get(&pdev->dev, "pcm");
+       if (IS_ERR(pcm->pclk)) {
+               dev_err(&pdev->dev, "failed to get pcm_clock\n");
+               ret = -ENOENT;
+               goto err4;
+       }
+       clk_enable(pcm->pclk);
+
+       ret = snd_soc_register_dai(dai);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "failed to get pcm_clock\n");
+               goto err5;
+       }
+
+       s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
+                                                       + S3C_PCM_RXFIFO;
+       s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
+                                                       + S3C_PCM_TXFIFO;
+
+       s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
+       s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
+
+       pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
+       pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
+
+       return 0;
+
+err5:
+       clk_disable(pcm->pclk);
+       clk_put(pcm->pclk);
+err4:
+       iounmap(pcm->regs);
+err3:
+       release_mem_region(mem_res->start, resource_size(mem_res));
+err2:
+       clk_disable(pcm->cclk);
+       clk_put(pcm->cclk);
+err1:
+       return ret;
+}
+
+static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev)
+{
+       struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
+       struct resource *mem_res;
+
+       iounmap(pcm->regs);
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem_res->start, resource_size(mem_res));
+
+       clk_disable(pcm->cclk);
+       clk_disable(pcm->pclk);
+       clk_put(pcm->pclk);
+       clk_put(pcm->cclk);
+
+       return 0;
+}
+
+static struct platform_driver s3c_pcm_driver = {
+       .probe  = s3c_pcm_dev_probe,
+       .remove = s3c_pcm_dev_remove,
+       .driver = {
+               .name = "samsung-pcm",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init s3c_pcm_init(void)
+{
+       return platform_driver_register(&s3c_pcm_driver);
+}
+module_init(s3c_pcm_init);
+
+static void __exit s3c_pcm_exit(void)
+{
+       platform_driver_unregister(&s3c_pcm_driver);
+}
+module_exit(s3c_pcm_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("S3C PCM Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-pcm.h b/sound/soc/s3c24xx/s3c-pcm.h
new file mode 100644 (file)
index 0000000..69ff997
--- /dev/null
@@ -0,0 +1,123 @@
+/*  sound/soc/s3c24xx/s3c-pcm.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
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __S3C_PCM_H
+#define __S3C_PCM_H __FILE__
+
+/*Register Offsets */
+#define S3C_PCM_CTL    (0x00)
+#define S3C_PCM_CLKCTL (0x04)
+#define S3C_PCM_TXFIFO (0x08)
+#define S3C_PCM_RXFIFO (0x0C)
+#define S3C_PCM_IRQCTL (0x10)
+#define S3C_PCM_IRQSTAT        (0x14)
+#define S3C_PCM_FIFOSTAT       (0x18)
+#define S3C_PCM_CLRINT (0x20)
+
+/* PCM_CTL Bit-Fields */
+#define S3C_PCM_CTL_TXDIPSTICK_MASK            (0x3f)
+#define S3C_PCM_CTL_TXDIPSTICK_SHIFT   (13)
+#define S3C_PCM_CTL_RXDIPSTICK_MSK             (0x3f<<7)
+#define S3C_PCM_CTL_TXDMA_EN           (0x1<<6)
+#define S3C_PCM_CTL_RXDMA_EN           (0x1<<5)
+#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC  (0x1<<4)
+#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC  (0x1<<3)
+#define S3C_PCM_CTL_TXFIFO_EN          (0x1<<2)
+#define S3C_PCM_CTL_RXFIFO_EN          (0x1<<1)
+#define S3C_PCM_CTL_ENABLE                     (0x1<<0)
+
+/* PCM_CLKCTL Bit-Fields */
+#define S3C_PCM_CLKCTL_SERCLK_EN               (0x1<<19)
+#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK  (0x1<<18)
+#define S3C_PCM_CLKCTL_SCLKDIV_MASK            (0x1ff)
+#define S3C_PCM_CLKCTL_SYNCDIV_MASK            (0x1ff)
+#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT   (9)
+#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT   (0)
+
+/* PCM_TXFIFO Bit-Fields */
+#define S3C_PCM_TXFIFO_DVALID  (0x1<<16)
+#define S3C_PCM_TXFIFO_DATA_MSK        (0xffff<<0)
+
+/* PCM_RXFIFO Bit-Fields */
+#define S3C_PCM_RXFIFO_DVALID  (0x1<<16)
+#define S3C_PCM_RXFIFO_DATA_MSK        (0xffff<<0)
+
+/* PCM_IRQCTL Bit-Fields */
+#define S3C_PCM_IRQCTL_IRQEN           (0x1<<14)
+#define S3C_PCM_IRQCTL_WRDEN           (0x1<<12)
+#define S3C_PCM_IRQCTL_TXEMPTYEN               (0x1<<11)
+#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN  (0x1<<10)
+#define S3C_PCM_IRQCTL_TXFULLEN                (0x1<<9)
+#define S3C_PCM_IRQCTL_TXALMSTFULLEN   (0x1<<8)
+#define S3C_PCM_IRQCTL_TXSTARVEN               (0x1<<7)
+#define S3C_PCM_IRQCTL_TXERROVRFLEN            (0x1<<6)
+#define S3C_PCM_IRQCTL_RXEMPTEN                (0x1<<5)
+#define S3C_PCM_IRQCTL_RXALMSTEMPTEN   (0x1<<4)
+#define S3C_PCM_IRQCTL_RXFULLEN                (0x1<<3)
+#define S3C_PCM_IRQCTL_RXALMSTFULLEN   (0x1<<2)
+#define S3C_PCM_IRQCTL_RXSTARVEN               (0x1<<1)
+#define S3C_PCM_IRQCTL_RXERROVRFLEN            (0x1<<0)
+
+/* PCM_IRQSTAT Bit-Fields */
+#define S3C_PCM_IRQSTAT_IRQPND         (0x1<<13)
+#define S3C_PCM_IRQSTAT_WRD_XFER               (0x1<<12)
+#define S3C_PCM_IRQSTAT_TXEMPTY                (0x1<<11)
+#define S3C_PCM_IRQSTAT_TXALMSTEMPTY   (0x1<<10)
+#define S3C_PCM_IRQSTAT_TXFULL         (0x1<<9)
+#define S3C_PCM_IRQSTAT_TXALMSTFULL            (0x1<<8)
+#define S3C_PCM_IRQSTAT_TXSTARV                (0x1<<7)
+#define S3C_PCM_IRQSTAT_TXERROVRFL             (0x1<<6)
+#define S3C_PCM_IRQSTAT_RXEMPT         (0x1<<5)
+#define S3C_PCM_IRQSTAT_RXALMSTEMPT            (0x1<<4)
+#define S3C_PCM_IRQSTAT_RXFULL         (0x1<<3)
+#define S3C_PCM_IRQSTAT_RXALMSTFULL            (0x1<<2)
+#define S3C_PCM_IRQSTAT_RXSTARV                (0x1<<1)
+#define S3C_PCM_IRQSTAT_RXERROVRFL             (0x1<<0)
+
+/* PCM_FIFOSTAT Bit-Fields */
+#define S3C_PCM_FIFOSTAT_TXCNT_MSK             (0x3f<<14)
+#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY   (0x1<<13)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY      (0x1<<12)
+#define S3C_PCM_FIFOSTAT_TXFIFOFULL            (0x1<<11)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL       (0x1<<10)
+#define S3C_PCM_FIFOSTAT_RXCNT_MSK             (0x3f<<4)
+#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY   (0x1<<3)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY      (0x1<<2)
+#define S3C_PCM_FIFOSTAT_RXFIFOFULL            (0x1<<1)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL       (0x1<<0)
+
+#define S3C_PCM_CLKSRC_PCLK    0
+#define S3C_PCM_CLKSRC_MUX     1
+
+#define S3C_PCM_SCLK_PER_FS    0
+
+/**
+ * struct s3c_pcm_info - S3C PCM Controller information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device register block.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ */
+struct s3c_pcm_info {
+       spinlock_t lock;
+       struct device   *dev;
+       void __iomem    *regs;
+
+       unsigned int sclk_per_fs;
+
+       /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
+       unsigned int idleclk;
+
+       struct clk      *pclk;
+       struct clk      *cclk;
+
+       struct s3c_dma_params   *dma_playback;
+       struct s3c_dma_params   *dma_capture;
+};
+
+#endif /* __S3C_PCM_H */
index a587ec40b449689df4d27a214d16c5d0ef28f7c4..359e59346ba2ae243bbfb867e1a694dea37768b8 100644 (file)
 
 #include <plat/regs-s3c2412-iis.h>
 
-#include <plat/audio.h>
 #include <mach/regs-gpio.h>
 #include <mach/dma.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c2412-i2s.h"
 
 #define S3C2412_I2S_DEBUG 0
@@ -51,14 +50,14 @@ static struct s3c2410_dma_client s3c2412_dma_client_in = {
        .name           = "I2S PCM Stereo in"
 };
 
-static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = {
+static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
        .client         = &s3c2412_dma_client_out,
        .channel        = DMACH_I2S_OUT,
        .dma_addr       = S3C2410_PA_IIS + S3C2412_IISTXD,
        .dma_size       = 4,
 };
 
-static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = {
+static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
        .client         = &s3c2412_dma_client_in,
        .channel        = DMACH_I2S_IN,
        .dma_addr       = S3C2410_PA_IIS + S3C2412_IISRXD,
index fc1beb0930b988ef4377703cbdd531b805aea2c0..0191e3acb0b42804e1547905424b97243e6eeaa1 100644 (file)
 #include <plat/regs-ac97.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-clock.h>
-#include <plat/audio.h>
 #include <asm/dma.h>
 #include <mach/dma.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-ac97.h"
 
 struct s3c24xx_ac97_info {
@@ -189,21 +188,21 @@ static struct s3c2410_dma_client s3c2443_dma_client_micin = {
        .name = "AC97 Mic Mono in"
 };
 
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
+static struct s3c_dma_params s3c2443_ac97_pcm_stereo_out = {
        .client         = &s3c2443_dma_client_out,
        .channel        = DMACH_PCM_OUT,
        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
        .dma_size       = 4,
 };
 
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
+static struct s3c_dma_params s3c2443_ac97_pcm_stereo_in = {
        .client         = &s3c2443_dma_client_in,
        .channel        = DMACH_PCM_IN,
        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
        .dma_size       = 4,
 };
 
-static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
+static struct s3c_dma_params s3c2443_ac97_mic_mono_in = {
        .client         = &s3c2443_dma_client_micin,
        .channel        = DMACH_MIC_IN,
        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
@@ -291,7 +290,7 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        u32 ac_glbctrl;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       int channel = ((struct s3c24xx_pcm_dma_params *)
+       int channel = ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->channel;
 
        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
@@ -340,7 +339,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
 {
        u32 ac_glbctrl;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       int channel = ((struct s3c24xx_pcm_dma_params *)
+       int channel = ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->channel;
 
        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
index 40e2c4790f0de95b08d0e846678422151909e366..0bc5950b9f0299d1dd6d7c6bdf830e2f12380747 100644 (file)
 #include <mach/hardware.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-clock.h>
-#include <plat/audio.h>
+
 #include <asm/dma.h>
 #include <mach/dma.h>
 
 #include <plat/regs-iis.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 
 static struct s3c2410_dma_client s3c24xx_dma_client_out = {
@@ -49,14 +49,14 @@ static struct s3c2410_dma_client s3c24xx_dma_client_in = {
        .name = "I2S PCM Stereo in"
 };
 
-static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {
+static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
        .client         = &s3c24xx_dma_client_out,
        .channel        = DMACH_I2S_OUT,
        .dma_addr       = S3C2410_PA_IIS + S3C2410_IISFIFO,
        .dma_size       = 2,
 };
 
-static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
+static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
        .client         = &s3c24xx_dma_client_in,
        .channel        = DMACH_I2S_IN,
        .dma_addr       = S3C2410_PA_IIS + S3C2410_IISFIFO,
@@ -258,12 +258,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
                iismod &= ~S3C2410_IISMOD_16BIT;
-               ((struct s3c24xx_pcm_dma_params *)
+               ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->dma_size = 1;
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
                iismod |= S3C2410_IISMOD_16BIT;
-               ((struct s3c24xx_pcm_dma_params *)
+               ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->dma_size = 2;
                break;
        default:
@@ -280,7 +280,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        int ret = 0;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       int channel = ((struct s3c24xx_pcm_dma_params *)
+       int channel = ((struct s3c_dma_params *)
                  rtd->dai->cpu_dai->dma_data)->channel;
 
        pr_debug("Entered %s\n", __func__);
index 1966e0d5652dea6b1a9529f31a7d25d12d20e6e8..507b2ed5d58b1898eabace347dec2411d3f6059c 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <plat/audio-simtec.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
index 8346bd96eaf55f897e5da9336731fcd58c008522..bdf8951af8e38fac1a7e88ff8be274ce4e7e1815 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <plat/audio-simtec.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
index 25797e0961753a6b171c1629cce576dad328c9a4..185c0acb5ce6b8f7fff39c3fd008477ad4c6d11e 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <plat/audio-simtec.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
index c215d32d6322e410d32465a8b012d54e260694f9..052d59659c296f2916261970ae9f4ff675b7a5cf 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <plat/regs-iis.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-i2s.h"
 #include "../codecs/uda134x.h"
 
index 105a77eeded0e75eec4d618c7ad5844cba0ce442..cc7edb5f792d86fef69a04ae713587721f2272f9 100644 (file)
 #include <plat/gpio-bank-d.h>
 #include <plat/gpio-bank-e.h>
 #include <plat/gpio-cfg.h>
-#include <plat/audio.h>
 
 #include <mach/map.h>
 #include <mach/dma.h>
 
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c64xx-i2s.h"
 
 static struct s3c2410_dma_client s3c64xx_dma_client_out = {
@@ -47,7 +46,7 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = {
        .name           = "I2S PCM Stereo in"
 };
 
-static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
        [0] = {
                .channel        = DMACH_I2S0_OUT,
                .client         = &s3c64xx_dma_client_out,
@@ -62,7 +61,7 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
        },
 };
 
-static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
        [0] = {
                .channel        = DMACH_I2S0_IN,
                .client         = &s3c64xx_dma_client_in,
@@ -99,6 +98,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 +123,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 02148cee26139fe1ca4df20df734d9314ca43486..abe7253b55fc37bf72447c69ea7e80feeb9b23b0 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[];
 
index a2a4f5323c177b83e600a8271640407170556aa3..12b783b12fcbd3fefa50599156b3d7ea775f3428 100644 (file)
@@ -20,7 +20,7 @@
 #include <sound/soc-dapm.h>
 
 #include "../codecs/ac97.h"
-#include "s3c24xx-pcm.h"
+#include "s3c-dma.h"
 #include "s3c24xx-ac97.h"
 
 static struct snd_soc_card smdk2443;
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644 (file)
index 0000000..efe4901
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ *  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 "s3c-dma.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 its 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;
+
+       ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
+                                       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));
+
+       /* Enabling the microphone requires the fitting of a 0R
+        * resistor to connect the line from the microphone jack.
+        */
+       snd_soc_dapm_disable_pin(codec, "MicIn");
+
+       /* 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));
+
+       /* 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 83b8028e209db1510b696988363e85444d483197..0eb1722f6581d831f0121e7299a22fbee4813226 100644 (file)
@@ -423,7 +423,7 @@ static void s6000_pcm_free(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
+static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
 
 static int s6000_pcm_new(struct snd_card *card,
                         struct snd_soc_dai *dai, struct snd_pcm *pcm)
@@ -435,7 +435,7 @@ static int s6000_pcm_new(struct snd_card *card,
        if (!card->dev->dma_mask)
                card->dev->dma_mask = &s6000_pcm_dmamask;
        if (!card->dev->coherent_dma_mask)
-               card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+               card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 
        if (params->dma_in) {
                s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
index 9154b4363db386f45ea5f1cb188596536b7f3d94..9e69765865547b98da7043da493a41c8f63f9078 100644 (file)
@@ -23,7 +23,6 @@ config SND_SOC_SH4_SSI
 config SND_SOC_SH4_FSI
        tristate "SH4 FSI support"
        depends on CPU_SUBTYPE_SH7724
-        select SH_DMA
        help
          This option enables FSI sound support
 
index 44123248b6301a5547871b1ded1f64acaddfec12..e1a3d1a2b4c8832c794e75822e43e7f591e22d40 100644 (file)
@@ -26,8 +26,6 @@
 #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
@@ -97,7 +95,6 @@ struct fsi_priv {
 
        int fifo_max;
        int chan;
-       int dma_chan;
 
        int byte_offset;
        int period_len;
@@ -308,62 +305,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
        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;
-}
-
 /************************************************************************
 
 
@@ -435,44 +376,6 @@ static void fsi_soft_all_reset(void)
        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)
 {
@@ -481,6 +384,8 @@ static int fsi_data_push(struct fsi_priv *fsi)
        int send;
        int fifo_free;
        int width;
+       u8 *start;
+       int i;
 
        if (!fsi                        ||
            !fsi->substream             ||
@@ -515,12 +420,22 @@ static int fsi_data_push(struct fsi_priv *fsi)
        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
+       start = runtime->dma_area;
+       start += fsi->byte_offset;
+
+       switch (width) {
+       case 2:
+               for (i = 0; i < send; i++)
+                       fsi_reg_write(fsi, DODT,
+                                     ((u32)*((u16 *)start + i) << 8));
+               break;
+       case 4:
+               for (i = 0; i < send; i++)
+                       fsi_reg_write(fsi, DODT, *((u32 *)start + i));
+               break;
+       default:
                return -EINVAL;
+       }
 
        fsi->byte_offset += send * width;
 
@@ -532,6 +447,75 @@ static int fsi_data_push(struct fsi_priv *fsi)
        return 0;
 }
 
+static int fsi_data_pop(struct fsi_priv *fsi)
+{
+       struct snd_pcm_runtime *runtime;
+       struct snd_pcm_substream *substream = NULL;
+       int free;
+       int fifo_fill;
+       int width;
+       u8 *start;
+       int i;
+
+       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 free space for alsa */
+       free = (fsi->buffer_len - fsi->byte_offset) / width;
+
+       /* get recv size */
+       fifo_fill = fsi_get_fifo_residue(fsi, 0);
+
+       if (free < fifo_fill)
+               fifo_fill = free;
+
+       start = runtime->dma_area;
+       start += fsi->byte_offset;
+
+       switch (width) {
+       case 2:
+               for (i = 0; i < fifo_fill; i++)
+                       *((u16 *)start + i) =
+                               (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+               break;
+       case 4:
+               for (i = 0; i < fifo_fill; i++)
+                       *((u32 *)start + i) = fsi_reg_read(fsi, DIDT);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       fsi->byte_offset += fifo_fill * width;
+
+       fsi_irq_enable(fsi, 0);
+
+       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;
@@ -545,6 +529,10 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
                fsi_data_push(&master->fsia);
        if (int_st & INT_B_OUT)
                fsi_data_push(&master->fsib);
+       if (int_st & INT_A_IN)
+               fsi_data_pop(&master->fsia);
+       if (int_st & INT_B_IN)
+               fsi_data_pop(&master->fsib);
 
        fsi_master_write(INT_ST, 0x0000000);
 
@@ -664,8 +652,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
        }
 
        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
@@ -699,16 +685,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
        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);
+               ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                fsi_irq_disable(fsi, is_play);
@@ -780,10 +762,9 @@ 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);
+       location = (fsi->byte_offset - 1);
        if (location < 0)
                location = 0;
 
@@ -845,7 +826,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
                        .channels_min   = 1,
                        .channels_max   = 8,
                },
-               /* capture not supported */
+               .capture = {
+                       .rates          = FSI_RATES,
+                       .formats        = FSI_FMTS,
+                       .channels_min   = 1,
+                       .channels_max   = 8,
+               },
                .ops = &fsi_dai_ops,
        },
        {
@@ -857,7 +843,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
                        .channels_min   = 1,
                        .channels_max   = 8,
                },
-               /* capture not supported */
+               .capture = {
+                       .rates          = FSI_RATES,
+                       .formats        = FSI_FMTS,
+                       .channels_min   = 1,
+                       .channels_max   = 8,
+               },
                .ops = &fsi_dai_ops,
        },
 };
@@ -912,22 +903,13 @@ static int fsi_probe(struct platform_device *pdev)
        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;
+               goto exit_iounmap;
        }
 
        fsi_soc_dai[0].dev              = &pdev->dev;
@@ -938,7 +920,7 @@ static int fsi_probe(struct platform_device *pdev)
        ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
        if (ret) {
                dev_err(&pdev->dev, "irq request err\n");
-               goto exit_free_dma;
+               goto exit_iounmap;
        }
 
        ret = snd_soc_register_platform(&fsi_soc_platform);
@@ -951,8 +933,6 @@ static int fsi_probe(struct platform_device *pdev)
 
 exit_free_irq:
        free_irq(irq, master);
-exit_free_dma:
-       fsi_free_dma_chan();
 exit_iounmap:
        iounmap(master->base);
 exit_kfree:
@@ -969,8 +949,6 @@ static int fsi_remove(struct platform_device *pdev)
 
        clk_put(master->clk);
 
-       fsi_free_dma_chan();
-
        free_irq(master->irq, master);
 
        iounmap(master->base);
index c8ceddc2a26c8c6af4c1f1b93135fd7b5f8e8e98..d2505e8b06c9783dcb89a6226f7f2e3393c6bf63 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 0a1b2f64bbee38a18947a9fa161eb922dc0f17a0..ef8f28284cb900372c7bf536a4d835b6e1ab9306 100644 (file)
@@ -37,7 +37,6 @@
 #include <sound/initval.h>
 
 static DEFINE_MUTEX(pcm_mutex);
-static DEFINE_MUTEX(io_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
@@ -81,6 +80,173 @@ static int run_delayed_work(struct delayed_work *dwork)
        return ret;
 }
 
+/* codec register dump */
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
+{
+       int i, step = 1, count = 0;
+
+       if (!codec->reg_cache_size)
+               return 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       count += sprintf(buf, "%s registers\n", codec->name);
+       for (i = 0; i < codec->reg_cache_size; i += step) {
+               if (codec->readable_register && !codec->readable_register(i))
+                       continue;
+
+               count += sprintf(buf + count, "%2x: ", i);
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               if (codec->display_register)
+                       count += codec->display_register(codec, buf + count,
+                                                        PAGE_SIZE - count, i);
+               else
+                       count += snprintf(buf + count, PAGE_SIZE - count,
+                                         "%4x", codec->read(codec, i));
+
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+               if (count >= PAGE_SIZE - 1)
+                       break;
+       }
+
+       /* Truncate count; min() would cause a warning */
+       if (count >= PAGE_SIZE)
+               count = PAGE_SIZE - 1;
+
+       return count;
+}
+static ssize_t codec_reg_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct snd_soc_device *devdata = dev_get_drvdata(dev);
+       return soc_codec_reg_show(devdata->card->codec, buf);
+}
+
+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       struct snd_soc_codec *codec = file->private_data;
+       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       ret = soc_codec_reg_show(codec, buf);
+       if (ret >= 0)
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+               const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size;
+       char *start = buf;
+       unsigned long reg, value;
+       int step = 1;
+       struct snd_soc_codec *codec = file->private_data;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       while (*start == ' ')
+               start++;
+       reg = simple_strtoul(start, &start, 16);
+       if ((reg >= codec->reg_cache_size) || (reg % step))
+               return -EINVAL;
+       while (*start == ' ')
+               start++;
+       if (strict_strtoul(start, 16, &value))
+               return -EINVAL;
+       codec->write(codec, reg, value);
+       return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+       .open = codec_reg_open_file,
+       .read = codec_reg_read_file,
+       .write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+       char codec_root[128];
+
+       if (codec->dev)
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s.%s", codec->name, dev_name(codec->dev));
+       else
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s", codec->name);
+
+       codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+                                                      debugfs_root);
+       if (!codec->debugfs_codec_root) {
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec debugfs directory\n");
+               return;
+       }
+
+       codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+                                                codec->debugfs_codec_root,
+                                                codec, &codec_reg_fops);
+       if (!codec->debugfs_reg)
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec register debugfs file\n");
+
+       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+                                                    codec->debugfs_codec_root,
+                                                    &codec->pop_time);
+       if (!codec->debugfs_pop_time)
+               printk(KERN_WARNING
+                      "Failed to create pop time debugfs file\n");
+
+       codec->debugfs_dapm = debugfs_create_dir("dapm",
+                                                codec->debugfs_codec_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_codec_root);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 #ifdef CONFIG_SND_SOC_AC97_BUS
 /* unregister ac97 codec */
 static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -790,45 +956,6 @@ static int soc_resume(struct device *dev)
 
        return 0;
 }
-
-/**
- * snd_soc_suspend_device: Notify core of device suspend
- *
- * @dev: Device being suspended.
- *
- * In order to ensure that the entire audio subsystem is suspended in a
- * coordinated fashion ASoC devices should suspend themselves when
- * called by ASoC.  When the standard kernel suspend process asks the
- * device to suspend it should call this function to initiate a suspend
- * of the entire ASoC card.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_suspend_device(struct device *dev)
-{
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
-
-/**
- * snd_soc_resume_device: Notify core of device resume
- *
- * @dev: Device being resumed.
- *
- * In order to ensure that the entire audio subsystem is resumed in a
- * coordinated fashion ASoC devices should resume themselves when called
- * by ASoC.  When the standard kernel resume process asks the device
- * to resume it should call this function.  Once all the components of
- * the card have notified that they are ready to be resumed the card
- * will be resumed.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_resume_device(struct device *dev)
-{
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_resume_device);
 #else
 #define soc_suspend    NULL
 #define soc_resume     NULL
@@ -843,6 +970,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                                                    struct platform_device,
                                                    dev);
        struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+       struct snd_soc_codec *codec;
        struct snd_soc_platform *platform;
        struct snd_soc_dai *dai;
        int i, found, ret, ac97;
@@ -931,6 +1059,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                if (ret < 0)
                        goto cpu_dai_err;
        }
+       codec = card->codec;
 
        if (platform->probe) {
                ret = platform->probe(pdev);
@@ -945,10 +1074,69 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
+       for (i = 0; i < card->num_links; i++) {
+               if (card->dai_link[i].init) {
+                       ret = card->dai_link[i].init(codec);
+                       if (ret < 0) {
+                               printk(KERN_ERR "asoc: failed to init %s\n",
+                                       card->dai_link[i].stream_name);
+                               continue;
+                       }
+               }
+               if (card->dai_link[i].codec_dai->ac97_control)
+                       ac97 = 1;
+       }
+
+       snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+                "%s",  card->name);
+       snprintf(codec->card->longname, sizeof(codec->card->longname),
+                "%s (%s)", card->name, codec->name);
+
+       /* Make sure all DAPM widgets are instantiated */
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_card_register(codec->card);
+       if (ret < 0) {
+               printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
+                               codec->name);
+               goto card_err;
+       }
+
+       mutex_lock(&codec->mutex);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+       /* Only instantiate AC97 if not already done by the adaptor
+        * for the generic AC97 subsystem.
+        */
+       if (ac97 && strcmp(codec->name, "AC97") != 0) {
+               ret = soc_ac97_dev_register(codec);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: AC97 device register failed\n");
+                       snd_card_free(codec->card);
+                       mutex_unlock(&codec->mutex);
+                       goto card_err;
+               }
+       }
+#endif
+
+       ret = snd_soc_dapm_sys_add(card->socdev->dev);
+       if (ret < 0)
+               printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
+
+       ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg);
+       if (ret < 0)
+               printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
+
+       soc_init_codec_debugfs(codec);
+       mutex_unlock(&codec->mutex);
+
        card->instantiated = 1;
 
        return;
 
+card_err:
+       if (platform->remove)
+               platform->remove(pdev);
+
 platform_err:
        if (codec_dev->remove)
                codec_dev->remove(pdev);
@@ -1151,157 +1339,6 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
 
-/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
-{
-       int i, step = 1, count = 0;
-
-       if (!codec->reg_cache_size)
-               return 0;
-
-       if (codec->reg_cache_step)
-               step = codec->reg_cache_step;
-
-       count += sprintf(buf, "%s registers\n", codec->name);
-       for (i = 0; i < codec->reg_cache_size; i += step) {
-               if (codec->readable_register && !codec->readable_register(i))
-                       continue;
-
-               count += sprintf(buf + count, "%2x: ", i);
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
-               if (codec->display_register)
-                       count += codec->display_register(codec, buf + count,
-                                                        PAGE_SIZE - count, i);
-               else
-                       count += snprintf(buf + count, PAGE_SIZE - count,
-                                         "%4x", codec->read(codec, i));
-
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
-               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
-               if (count >= PAGE_SIZE - 1)
-                       break;
-       }
-
-       /* Truncate count; min() would cause a warning */
-       if (count >= PAGE_SIZE)
-               count = PAGE_SIZE - 1;
-
-       return count;
-}
-static ssize_t codec_reg_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       struct snd_soc_device *devdata = dev_get_drvdata(dev);
-       return soc_codec_reg_show(devdata->card->codec, buf);
-}
-
-static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return 0;
-}
-
-static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
-                              size_t count, loff_t *ppos)
-{
-       ssize_t ret;
-       struct snd_soc_codec *codec = file->private_data;
-       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       ret = soc_codec_reg_show(codec, buf);
-       if (ret >= 0)
-               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
-       kfree(buf);
-       return ret;
-}
-
-static ssize_t codec_reg_write_file(struct file *file,
-               const char __user *user_buf, size_t count, loff_t *ppos)
-{
-       char buf[32];
-       int buf_size;
-       char *start = buf;
-       unsigned long reg, value;
-       int step = 1;
-       struct snd_soc_codec *codec = file->private_data;
-
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       if (codec->reg_cache_step)
-               step = codec->reg_cache_step;
-
-       while (*start == ' ')
-               start++;
-       reg = simple_strtoul(start, &start, 16);
-       if ((reg >= codec->reg_cache_size) || (reg % step))
-               return -EINVAL;
-       while (*start == ' ')
-               start++;
-       if (strict_strtoul(start, 16, &value))
-               return -EINVAL;
-       codec->write(codec, reg, value);
-       return buf_size;
-}
-
-static const struct file_operations codec_reg_fops = {
-       .open = codec_reg_open_file,
-       .read = codec_reg_read_file,
-       .write = codec_reg_write_file,
-};
-
-static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-       codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
-                                                debugfs_root, codec,
-                                                &codec_reg_fops);
-       if (!codec->debugfs_reg)
-               printk(KERN_WARNING
-                      "ASoC: Failed to create codec register debugfs file\n");
-
-       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
-                                                    debugfs_root,
-                                                    &codec->pop_time);
-       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);
-}
-
-#else
-
-static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-
-static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-#endif
-
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
  * @codec: audio codec
@@ -1369,18 +1406,40 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
        int change;
        unsigned int old, new;
 
-       mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
        new = (old & ~mask) | value;
        change = old != new;
        if (change)
                snd_soc_write(codec, reg, new);
 
-       mutex_unlock(&io_mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_update_bits);
 
+/**
+ * snd_soc_update_bits_locked - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value, and takes the codec mutex.
+ *
+ * Returns 1 for change else 0.
+ */
+static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+                               unsigned short reg, unsigned int mask,
+                               unsigned int value)
+{
+       int change;
+
+       mutex_lock(&codec->mutex);
+       change = snd_soc_update_bits(codec, reg, mask, value);
+       mutex_unlock(&codec->mutex);
+
+       return change;
+}
+
 /**
  * snd_soc_test_bits - test register for change
  * @codec: audio codec
@@ -1399,11 +1458,9 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
        int change;
        unsigned int old, new;
 
-       mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
        new = (old & ~mask) | value;
        change = old != new;
-       mutex_unlock(&io_mutex);
 
        return change;
 }
@@ -1450,89 +1507,16 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
                        mutex_unlock(&codec->mutex);
                        return ret;
                }
-       }
-
-       mutex_unlock(&codec->mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
-
-/**
- * snd_soc_init_card - register sound card
- * @socdev: the SoC audio device
- *
- * Register a SoC sound card. Also registers an AC97 device if the
- * codec is AC97 for ad hoc devices.
- *
- * Returns 0 for success, else error.
- */
-int snd_soc_init_card(struct snd_soc_device *socdev)
-{
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_codec *codec = card->codec;
-       int ret = 0, i, ac97 = 0, err = 0;
-
-       for (i = 0; i < card->num_links; i++) {
-               if (card->dai_link[i].init) {
-                       err = card->dai_link[i].init(codec);
-                       if (err < 0) {
-                               printk(KERN_ERR "asoc: failed to init %s\n",
-                                       card->dai_link[i].stream_name);
-                               continue;
-                       }
-               }
                if (card->dai_link[i].codec_dai->ac97_control) {
-                       ac97 = 1;
                        snd_ac97_dev_add_pdata(codec->ac97,
                                card->dai_link[i].cpu_dai->ac97_pdata);
                }
        }
-       snprintf(codec->card->shortname, sizeof(codec->card->shortname),
-                "%s",  card->name);
-       snprintf(codec->card->longname, sizeof(codec->card->longname),
-                "%s (%s)", card->name, codec->name);
-
-       /* Make sure all DAPM widgets are instantiated */
-       snd_soc_dapm_new_widgets(codec);
-
-       ret = snd_card_register(codec->card);
-       if (ret < 0) {
-               printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
-                               codec->name);
-               goto out;
-       }
-
-       mutex_lock(&codec->mutex);
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       /* Only instantiate AC97 if not already done by the adaptor
-        * for the generic AC97 subsystem.
-        */
-       if (ac97 && strcmp(codec->name, "AC97") != 0) {
-               ret = soc_ac97_dev_register(codec);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: AC97 device register failed\n");
-                       snd_card_free(codec->card);
-                       mutex_unlock(&codec->mutex);
-                       goto out;
-               }
-       }
-#endif
-
-       err = snd_soc_dapm_sys_add(socdev->dev);
-       if (err < 0)
-               printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
-
-       err = device_create_file(socdev->dev, &dev_attr_codec_reg);
-       if (err < 0)
-               printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
 
-       soc_init_codec_debugfs(codec);
        mutex_unlock(&codec->mutex);
-
-out:
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_init_card);
+EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
 
 /**
  * snd_soc_free_pcms - free sound card and pcms
@@ -1734,7 +1718,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= (bitmask - 1) << e->shift_r;
        }
 
-       return snd_soc_update_bits(codec, e->reg, mask, val);
+       return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
@@ -1808,7 +1792,7 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       return snd_soc_update_bits(codec, e->reg, mask, val);
+       return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
@@ -1969,7 +1953,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
                val_mask |= mask << rshift;
                val |= val2 << rshift;
        }
-       return snd_soc_update_bits(codec, reg, val_mask, val);
+       return snd_soc_update_bits_locked(codec, reg, val_mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
@@ -2075,11 +2059,11 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        val = val << shift;
        val2 = val2 << shift;
 
-       err = snd_soc_update_bits(codec, reg, val_mask, val);
+       err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
        if (err < 0)
                return err;
 
-       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+       err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
        return err;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
@@ -2158,7 +2142,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
        val = (ucontrol->value.integer.value[0]+min) & 0xff;
        val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
 
-       return snd_soc_update_bits(codec, reg, 0xffff, val);
+       return snd_soc_update_bits_locked(codec, reg, 0xffff, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
@@ -2205,16 +2189,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;
 }
@@ -2258,6 +2244,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 66d4c165f99b468fe2fe051b6bd5782684705852..0d294ef7259006e0681a9b1bb2092c608fde1925 100644 (file)
@@ -719,6 +719,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;
@@ -1152,6 +1156,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                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",
@@ -1159,6 +1166,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                        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",
@@ -1206,8 +1216,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
 
 /* 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,
-                                int mux, int val, struct soc_enum *e)
+                                struct snd_kcontrol *kcontrol, int change,
+                                int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
@@ -1216,7 +1226,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
            widget->id != snd_soc_dapm_value_mux)
                return -ENODEV;
 
-       if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+       if (!change)
                return 0;
 
        /* find dapm widget path assoc with kcontrol */
@@ -1401,10 +1411,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 */
@@ -1428,6 +1441,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);
@@ -1528,8 +1542,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,
@@ -1766,7 +1779,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, mux;
+       unsigned int val, mux, change;
        unsigned int mask, bitmask;
        int ret = 0;
 
@@ -1786,20 +1799,21 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
-       dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-       if (widget->event) {
-               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0)
-                               goto out;
-               }
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_POST_REG);
-       } else
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_PRE_REG);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
        mutex_unlock(&widget->codec->mutex);
@@ -1807,6 +1821,54 @@ out:
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
+/**
+ * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = widget->value;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
+
+/**
+ * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e =
+               (struct soc_enum *)kcontrol->private_value;
+       int change;
+       int ret = 0;
+
+       if (ucontrol->value.enumerated.item[0] >= e->max)
+               return -EINVAL;
+
+       mutex_lock(&widget->codec->mutex);
+
+       change = widget->value != ucontrol->value.enumerated.item[0];
+       widget->value = ucontrol->value.enumerated.item[0];
+       dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+
+       mutex_unlock(&widget->codec->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
+
 /**
  * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
  *                                     callback
@@ -1865,7 +1927,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, mux;
+       unsigned int val, mux, change;
        unsigned int mask;
        int ret = 0;
 
@@ -1883,20 +1945,21 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
-       dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-       if (widget->event) {
-               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0)
-                               goto out;
-               }
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_POST_REG);
-       } else
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_PRE_REG);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
        mutex_unlock(&widget->codec->mutex);
index 1d455ab79490a70fdb0833638205a387ecccb8aa..3c07a94c2e300cd0f75a60617411894971f81d7d 100644 (file)
@@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new);
  */
 void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
 {
-       struct snd_soc_codec *codec = jack->card->codec;
+       struct snd_soc_codec *codec;
        struct snd_soc_jack_pin *pin;
        int enable;
        int oldstatus;
@@ -67,6 +67,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
                WARN_ON_ONCE(!jack);
                return;
        }
+       codec = jack->card->codec;
 
        mutex_lock(&codec->mutex);
 
@@ -162,6 +163,9 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
        else
                report = 0;
 
+       if (gpio->jack_status_check)
+               report = gpio->jack_status_check();
+
        snd_soc_jack_report(jack, report, gpio->report);
 }
 
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
new file mode 100644 (file)
index 0000000..b16aaae
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * soc-util.c  --  ALSA SoC Audio Layer utility functions
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *         Liam Girdwood <lrg@slimlogic.co.uk>
+ *         
+ *
+ *  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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots)
+{
+       return sample_size * channels * tdm_slots;
+}
+EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size);
+
+int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params)
+{
+       int sample_size;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+       case SNDRV_PCM_FORMAT_S16_BE:
+               sample_size = 16;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+       case SNDRV_PCM_FORMAT_S20_3BE:
+               sample_size = 20;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S24_BE:
+               sample_size = 24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+       case SNDRV_PCM_FORMAT_S32_BE:
+               sample_size = 32;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return snd_soc_calc_frame_size(sample_size, params_channels(params),
+                                      1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size);
+
+int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
+{
+       int ret;
+
+       ret = snd_soc_params_to_frame_size(params);
+
+       if (ret > 0)
+               return ret * params_rate(params);
+       else
+               return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);