]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge source.mvista.com:/home/git/linux-omap-2.6
authorTony Lindgren <tony@atomide.com>
Wed, 22 Mar 2006 16:06:12 +0000 (08:06 -0800)
committerTony Lindgren <tony@atomide.com>
Wed, 22 Mar 2006 16:06:12 +0000 (08:06 -0800)
17 files changed:
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-osk.c
include/asm-arm/arch-omap/omap-alsa.h [moved from sound/arm/omap-aic23.h with 58% similarity]
sound/arm/Kconfig
sound/arm/Makefile
sound/arm/omap-aic23.c [deleted file]
sound/arm/omap/Makefile [new file with mode: 0644]
sound/arm/omap/omap-alsa-aic23-mixer.c [moved from sound/arm/omap-alsa-mixer.c with 97% similarity]
sound/arm/omap/omap-alsa-aic23.c [new file with mode: 0644]
sound/arm/omap/omap-alsa-aic23.h [new file with mode: 0644]
sound/arm/omap/omap-alsa-dma.c [moved from sound/arm/omap-alsa-dma.c with 88% similarity]
sound/arm/omap/omap-alsa-dma.h [moved from sound/arm/omap-alsa-dma.h with 64% similarity]
sound/arm/omap/omap-alsa-tsc2101-mixer.c [new file with mode: 0644]
sound/arm/omap/omap-alsa-tsc2101-mixer.h [new file with mode: 0644]
sound/arm/omap/omap-alsa-tsc2101.c [new file with mode: 0644]
sound/arm/omap/omap-alsa-tsc2101.h [new file with mode: 0644]
sound/arm/omap/omap-alsa.c [new file with mode: 0644]

index 4364d51ebd6d37ff80ccdd8dcc2c797e8c7aee58..51634ce12cb1326a2e015c1e8244e75810c2163c 100644 (file)
@@ -40,8 +40,9 @@
 #include <asm/arch/irda.h>
 #include <asm/arch/usb.h>
 #include <asm/arch/keypad.h>
-#include <asm/arch/dma.h>
 #include <asm/arch/common.h>
+#include <asm/arch/mcbsp.h>
+#include <asm/arch/omap-alsa.h>
 
 extern int omap_gpio_init(void);
 
@@ -285,6 +286,41 @@ static struct platform_device h2_lcd_device = {
        .id             = -1,
 };
 
+static struct omap_mcbsp_reg_cfg mcbsp_regs = { 
+       .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+       .spcr1 = RINTM(3) | RRST,
+       .rcr2  = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+                RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1),
+       .rcr1  = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+       .xcr2  = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | 
+                XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
+       .xcr1  = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+       .srgr1 = FWID(15),
+       .srgr2 = GSYNC | CLKSP | FSGM | FPER(31),
+
+       .pcr0  = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP,
+       //.pcr0 = CLKXP | CLKRP,        /* mcbsp: slave */
+};
+static struct omap_alsa_codec_config alsa_config = {
+       .name                   = "H2 TSC2101",
+       .mcbsp_regs_alsa        = &mcbsp_regs,
+       .codec_configure_dev    = NULL, // tsc2101_configure,
+       .codec_set_samplerate   = NULL, // tsc2101_set_samplerate,
+       .codec_clock_setup      = NULL, // tsc2101_clock_setup,
+       .codec_clock_on         = NULL, // tsc2101_clock_on,
+       .codec_clock_off        = NULL, // tsc2101_clock_off,
+       .get_default_samplerate = NULL, // tsc2101_get_default_samplerate,
+};    
+
+static struct platform_device h2_mcbsp1_device = {
+       .name   = "omap_alsa_mcbsp",
+       .id     = 1,
+       .dev = {
+               .platform_data  = &alsa_config,
+       }, 
+};    
+
 static struct platform_device *h2_devices[] __initdata = {
        &h2_nor_device,
        &h2_nand_device,
@@ -292,6 +328,7 @@ static struct platform_device *h2_devices[] __initdata = {
        &h2_irda_device,
        &h2_kp_device,
        &h2_lcd_device,
+       &h2_mcbsp1_device,
 };
 
 static void __init h2_init_smc91x(void)
index 56c8a4b12bb8171178a13f68d161485c40d09a60..76248bbc512740fbc4f272d174b63bd7fe2aed20 100644 (file)
@@ -47,6 +47,8 @@
 #include <asm/arch/tc.h>
 #include <asm/arch/keypad.h>
 #include <asm/arch/common.h>
+#include <asm/arch/mcbsp.h>
+#include <asm/arch/omap-alsa.h>
 
 static int osk_keymap[] = {
        KEY(0, 0, KEY_F1),
@@ -149,9 +151,40 @@ static struct platform_device osk5912_cf_device = {
        .resource       = osk5912_cf_resources,
 };
 
+#define DEFAULT_BITPERSAMPLE 16
+
+static struct omap_mcbsp_reg_cfg mcbsp_regs = { 
+       .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+       .spcr1 = RINTM(3) | RRST,
+       .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+           RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
+       .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+       .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
+           XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
+       .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+       .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
+       .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
+       /*.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,*/ /* mcbsp: master */
+       .pcr0 = CLKXP | CLKRP,  /* mcbsp: slave */
+};
+
+static struct omap_alsa_codec_config alsa_config = {
+       .name                   = "OSK AIC23",
+       .mcbsp_regs_alsa        = &mcbsp_regs,
+       .codec_configure_dev    = NULL, // aic23_configure,
+       .codec_set_samplerate   = NULL, // aic23_set_samplerate,
+       .codec_clock_setup      = NULL, // aic23_clock_setup,
+       .codec_clock_on         = NULL, // aic23_clock_on,
+       .codec_clock_off        = NULL, // aic23_clock_off,
+       .get_default_samplerate = NULL, // aic23_get_default_samplerate,        
+};
+
 static struct platform_device osk5912_mcbsp1_device = {
-       .name           = "omap_mcbsp",
-       .id             = 1,
+       .name   = "omap_alsa_mcbsp",
+       .id     = 1,
+       .dev = {
+               .platform_data  = &alsa_config,
+       },
 };
 
 static struct resource osk5912_kp_resources[] = {
similarity index 58%
rename from sound/arm/omap-aic23.h
rename to include/asm-arm/arch-omap/omap-alsa.h
index c8ab42c29354c00b9e364c5c24abe5eaed9aa0f6..b4ec45a84e635c18b12b1e903570c5de2277fcfd 100644 (file)
@@ -1,7 +1,9 @@
 /*
- * sound/arm/omap-aic23.h
+ * linux/include/asm-arm/arch-omap/omap-alsa.h
  * 
- * Alsa Driver for AIC23 codec on OSK5912 platform board
+ * Alsa Driver for AIC23 and TSC2101 codecs on OMAP platform boards.
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
  *
  * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
  * Written by Daniel Petrini, David Cohen, Anderson Briglia
  *  2005/07/25 INdT-10LE Kernel Team -         Alsa driver for omap osk,
  *                                     original version based in sa1100 driver
  *                                     and omap oss driver.
- *
- *  2005-12-18   Dirk Behme      - Added L/R Channel Interchange fix as proposed by Ajaya Babu
  */
 
-#ifndef __OMAP_AIC23_H
-#define __OMAP_AIC23_H
+#ifndef __OMAP_ALSA_H
+#define __OMAP_ALSA_H
 
 #include <sound/driver.h>
 #include <asm/arch/dma.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <asm/arch/mcbsp.h>
+#include <linux/platform_device.h>
+/*
+ * Debug functions
+ */
+#undef DEBUG
+//#define DEBUG
+
+#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
+
+#ifdef DEBUG
+#define DPRINTK(ARGS...)  printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
+#else
+#define DPRINTK(ARGS...)       /* nop */
+#define ADEBUG()               /* nop */
+#define FN_IN                  /* nop */
+#define FN_OUT(n)              /* nop */
+#endif
 
-#define DEFAULT_OUTPUT_VOLUME         0x60
-#define DEFAULT_INPUT_VOLUME          0x00     /* 0 ==> mute line in */
-
-#define OUTPUT_VOLUME_MIN             LHV_MIN
-#define OUTPUT_VOLUME_MAX             LHV_MAX
-#define OUTPUT_VOLUME_RANGE           (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
-#define OUTPUT_VOLUME_MASK            OUTPUT_VOLUME_MAX
-
-#define INPUT_VOLUME_MIN             LIV_MIN
-#define INPUT_VOLUME_MAX             LIV_MAX
-#define INPUT_VOLUME_RANGE           (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
-#define INPUT_VOLUME_MASK            INPUT_VOLUME_MAX
-
-#define SIDETONE_MASK                 0x1c0
-#define SIDETONE_0                    0x100
-#define SIDETONE_6                    0x000
-#define SIDETONE_9                    0x040
-#define SIDETONE_12                   0x080
-#define SIDETONE_18                   0x0c0
-
-#define DEFAULT_ANALOG_AUDIO_CONTROL  DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
+#define DMA_BUF_SIZE   (1024 * 8)
 
 /*
  * Buffer management for alsa and dma
@@ -93,38 +94,51 @@ struct audio_stream {
 /*
  * Alsa card structure for aic23
  */
-struct snd_card_omap_aic23 {
+struct snd_card_omap_codec {
        snd_card_t *card;
        snd_pcm_t *pcm;
        long samplerate;
        struct audio_stream s[2];       /* playback & capture */
 };
 
-/*********** Function Prototypes *************************/
+/* Codec specific information and function pointers. 
+ * Codec (omap-alsa-aic23.c and omap-alsa-tsc2101.c)
+ * are responsible for defining the function pointers.
+ */
+struct omap_alsa_codec_config {
+       char    *name;
+       struct  omap_mcbsp_reg_cfg *mcbsp_regs_alsa;
+       snd_pcm_hw_constraint_list_t *hw_constraints_rates;
+       snd_pcm_hardware_t *snd_omap_alsa_playback;
+       snd_pcm_hardware_t *snd_omap_alsa_capture;
+       void    (*codec_configure_dev)(void);
+       void    (*codec_set_samplerate)(long);
+       void    (*codec_clock_setup)(void);
+       int     (*codec_clock_on)(void);
+       int     (*codec_clock_off)(void);
+       int     (*get_default_samplerate)(void);
+};
 
-void audio_dma_callback(void *);
-int snd_omap_mixer(struct snd_card_omap_aic23 *);
+/*********** Mixer function prototypes *************************/
+int snd_omap_mixer(struct snd_card_omap_codec *);
 void snd_omap_init_mixer(void);
-/* Clock functions */
-int omap_aic23_clock_on(void);
-int omap_aic23_clock_off(void);
 
 #ifdef CONFIG_PM
 void snd_omap_suspend_mixer(void);
 void snd_omap_resume_mixer(void);
 #endif
 
-/* Codec AIC23 */
-#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
-
-extern int tlv320aic23_write_value(u8 reg, u16 value);
-
-/* TLV320AIC23 is a write only device */
-static __inline__ void audio_aic23_write(u8 address, u16 data)
-{
-       tlv320aic23_write_value(address, data);
-}
+int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config);
+int snd_omap_alsa_remove(struct platform_device *pdev);
+#ifdef CONFIG_PM
+int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state);
+int snd_omap_alsa_resume(struct platform_device *pdev);
+#else
+#define snd_omap_alsa_suspend  NULL
+#define snd_omap_alsa_resume   NULL
+#endif
 
-#endif /* CONFIG_SENSORS_TLV320AIC23 */
+/*********** function prototype to function called from the dma interrupt handler ******/
+void callback_omap_alsa_sound_dma(void *);
 
 #endif
index cf071547a13d439b28d1f5c07f1f71a727e12ed0..cccdf23e8e6e4a107fcbe8789a48364b95a9c500 100644 (file)
@@ -44,5 +44,19 @@ config SND_OMAP_AIC23
 
          To compile this driver as a module, choose M here: the module
          will be called snd-omap-aic23.
+         
+config SND_OMAP_TSC2101
+       tristate "OMAP TSC2101 alsa driver"
+       depends on ARCH_OMAP && SND
+       select SND_PCM
+         select OMAP_TSC2101
+         select OMAP_UWIRE if ARCH_OMAP
+       help
+         Say Y here if you have a OMAP platform board
+         and want to use its TSC2101 audio chip. Driver has
+         been tested with H2 and iPAQ h6300.
+         To compile this driver as a module, choose M here: the module
+         will be called snd-omap-tsc2101.
 
 endmenu
index 019a9f03c2c02c13a8b51af2efc756f06d0bf329..bd12f53404a6c1c975db9754381bdc14096c532c 100644 (file)
@@ -14,5 +14,4 @@ snd-pxa2xx-pcm-objs           := pxa2xx-pcm.o
 obj-$(CONFIG_SND_PXA2XX_AC97)  += snd-pxa2xx-ac97.o
 snd-pxa2xx-ac97-objs           := pxa2xx-ac97.o
 
-obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-aic23.o
-snd-omap-aic23-objs := omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o
+obj-$(CONFIG_SND) += omap/
diff --git a/sound/arm/omap-aic23.c b/sound/arm/omap-aic23.c
deleted file mode 100644 (file)
index c92e245..0000000
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * sound/arm/omap-aic23.c
- * 
- * Alsa Driver for AIC23 codec on OSK5912 platform board
- *
- * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
- * Written by Daniel Petrini, David Cohen, Anderson Briglia
- *            {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
- *
- * Based on sa11xx-uda1341.c, 
- * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the  GNU General Public License along
- * with this program; if not, write  to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- *
- * 2005-07-29   INdT Kernel Team - Alsa driver for omap osk. Creation of new 
- *                                 file omap-aic23.c
- *
- * 2005-12-18   Dirk Behme      - Added L/R Channel Interchange fix as proposed by Ajaya Babu
- */
-
-#include <linux/config.h>
-#include <sound/driver.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-
-#ifdef CONFIG_PM
-#include <linux/pm.h>
-#endif
-
-#include <asm/hardware.h>
-#include <asm/mach-types.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/aic23.h>
-#include <asm/arch/mcbsp.h>
-#include <asm/arch/clock.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-#include <sound/memalloc.h>
-
-#include "omap-alsa-dma.h"
-#include "omap-aic23.h"
-
-#undef DEBUG
-
-#ifdef DEBUG
-#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
-#else
-#define ADEBUG()               /* nop */
-#endif
-
-/* Define to set the AIC23 as the master w.r.t McBSP */
-#define AIC23_MASTER
-
-/*
- * AUDIO related MACROS
- */
-#define DEFAULT_BITPERSAMPLE          16
-#define AUDIO_RATE_DEFAULT           44100
-#define AUDIO_MCBSP                   OMAP_MCBSP1
-#define NUMBER_SAMPLE_RATES_SUPPORTED 10
-
-
-MODULE_AUTHOR("Daniel Petrini, David Cohen, Anderson Briglia - INdT");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("OMAP AIC23 driver for ALSA");
-MODULE_SUPPORTED_DEVICE("{{AIC23,OMAP AIC23}}");
-MODULE_ALIAS("omap_mcbsp.1");
-
-static char *id = NULL;        
-MODULE_PARM_DESC(id, "OMAP OSK ALSA Driver for AIC23 chip.");
-
-static struct snd_card_omap_aic23 *omap_aic23 = NULL;
-
-static struct clk *aic23_mclk = 0;
-
-struct sample_rate_rate_reg_info {
-       u8 control;             /* SR3, SR2, SR1, SR0 and BOSR */
-       u8 divider;             /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
-};
-
-/*
- * DAC USB-mode sampling rates (MCLK = 12 MHz)
- * The rates and rate_reg_into MUST be in the same order
- */
-static unsigned int rates[] = {
-       4000, 8000, 16000, 22050,
-       24000, 32000, 44100,
-       48000, 88200, 96000,
-};
-static const struct sample_rate_rate_reg_info
- rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
-       {0x06, 1},              /*  4000 */
-       {0x06, 0},              /*  8000 */
-       {0x0C, 1},              /* 16000 */
-       {0x11, 1},              /* 22050 */
-       {0x00, 1},              /* 24000 */
-       {0x0C, 0},              /* 32000 */
-       {0x11, 0},              /* 44100 */
-       {0x00, 0},              /* 48000 */
-       {0x1F, 0},              /* 88200 */
-       {0x0E, 0},              /* 96000 */
-};
-
-/*
- *  mcbsp configuration structure
- */
-static struct omap_mcbsp_reg_cfg initial_config_mcbsp = {
-       .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
-       .spcr1 = RINTM(3) | RRST,
-       .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
-           RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
-       .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
-       .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
-           XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
-       .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
-       .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
-       .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
-#ifndef AIC23_MASTER
-       /* configure McBSP to be the I2S master */
-       .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
-#else
-       /* configure McBSP to be the I2S slave */
-       .pcr0 = CLKXP | CLKRP,
-#endif                         /* AIC23_MASTER */
-};
-
-static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-       .count = ARRAY_SIZE(rates),
-       .list = rates,
-       .mask = 0,
-};
-
-/*
- * HW interface start and stop helper functions
- */
-static int audio_ifc_start(void)
-{
-       omap_mcbsp_start(AUDIO_MCBSP);
-       return 0;
-}
-
-static int audio_ifc_stop(void)
-{
-       omap_mcbsp_stop(AUDIO_MCBSP);
-       return 0;
-}
-
-/*
- * Codec/mcbsp init and configuration section
- * codec dependent code.
- */
-
-/*
- * Sample rate changing
- */
-static void omap_aic23_set_samplerate(struct snd_card_omap_aic23
-                                     *omap_aic23, long rate)
-{
-       u8 count = 0;
-       u16 data = 0;
-
-       /* Fix the rate if it has a wrong value */
-       if (rate >= 96000)
-               rate = 96000;
-       else if (rate >= 88200)
-               rate = 88200;
-       else if (rate >= 48000)
-               rate = 48000;
-       else if (rate >= 44100)
-               rate = 44100;
-       else if (rate >= 32000)
-               rate = 32000;
-       else if (rate >= 24000)
-               rate = 24000;
-       else if (rate >= 22050)
-               rate = 22050;
-       else if (rate >= 16000)
-               rate = 16000;
-       else if (rate >= 8000)
-               rate = 8000;
-       else
-               rate = 4000;
-
-       /* Search for the right sample rate */
-       /* Verify what happens if the rate is not supported
-        * now it goes to 96Khz */
-       while ((rates[count] != rate) &&
-              (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) {
-               count++;
-       }
-
-       data = (rate_reg_info[count].divider << CLKIN_SHIFT) |
-           (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
-
-       audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
-
-       omap_aic23->samplerate = rate;
-}
-
-static inline void aic23_configure(void)
-{
-       /* Reset codec */
-       audio_aic23_write(RESET_CONTROL_ADDR, 0);
-
-       /* Initialize the AIC23 internal state */
-
-       /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
-       audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL);
-
-       /* Digital audio path control, de-emphasis control 44.1kHz */
-       audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
-
-       /* Digital audio interface, master/slave mode, I2S, 16 bit */
-#ifdef AIC23_MASTER
-       audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR,
-                         MS_MASTER | IWL_16 | FOR_DSP);
-#else
-       audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
-#endif
-
-       /* Enable digital interface */
-       audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
-
-}
-
-static void omap_aic23_audio_init(struct snd_card_omap_aic23 *omap_aic23)
-{
-       /* Setup DMA stuff */
-       omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa AIC23 out";
-       omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
-           SNDRV_PCM_STREAM_PLAYBACK;
-       omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
-           OMAP_DMA_MCBSP1_TX;
-       omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
-           audio_ifc_start;
-       omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
-           audio_ifc_stop;
-
-       omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa AIC23 in";
-       omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
-           SNDRV_PCM_STREAM_CAPTURE;
-       omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
-           OMAP_DMA_MCBSP1_RX;
-       omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
-           audio_ifc_start;
-       omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
-           audio_ifc_stop;
-
-       /* configuring the McBSP */
-       omap_mcbsp_request(AUDIO_MCBSP);
-
-       /* if configured, then stop mcbsp */
-       omap_mcbsp_stop(AUDIO_MCBSP);
-
-       omap_mcbsp_config(AUDIO_MCBSP, &initial_config_mcbsp);
-       omap_mcbsp_start(AUDIO_MCBSP);
-       aic23_configure();
-}
-
-/* 
- * DMA functions 
- * Depends on omap-aic23-dma.c functions and (omap) dma.c
- * 
- */
-#define DMA_BUF_SIZE   1024 * 8
-
-static int audio_dma_request(struct audio_stream *s,
-                            void (*callback) (void *))
-{
-       int err;
-
-       err = omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);
-       if (err < 0)
-               printk(KERN_ERR "unable to grab audio dma 0x%x\n",
-                      s->dma_dev);
-       return err;
-}
-
-static int audio_dma_free(struct audio_stream *s)
-{
-       int err = 0;
-
-       err = omap_free_sound_dma(s, &s->lch);
-       if (err < 0)
-               printk(KERN_ERR "Unable to free audio dma channels!\n");
-       return err;
-}
-
-/*
- *  This function should calculate the current position of the dma in the
- *  buffer. It will help alsa middle layer to continue update the buffer.
- *  Its correctness is crucial for good functioning.
- */
-static u_int audio_get_dma_pos(struct audio_stream *s)
-{
-       snd_pcm_substream_t *substream = s->stream;
-       snd_pcm_runtime_t *runtime = substream->runtime;
-       unsigned int offset;
-       unsigned long flags;
-       dma_addr_t count;
-       ADEBUG();
-
-       /* this must be called w/ interrupts locked as requested in dma.c */
-       spin_lock_irqsave(&s->dma_lock, flags);
-
-       /* For the current period let's see where we are */
-       count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]);
-
-       spin_unlock_irqrestore(&s->dma_lock, flags);
-
-       /* Now, the position related to the end of that period */
-       offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count);
-
-       if (offset >= runtime->buffer_size || offset < 0)
-               offset = 0;
-
-       return offset;
-}
-
-/*
- * this stops the dma and clears the dma ptrs
- */
-static void audio_stop_dma(struct audio_stream *s)
-{
-       unsigned long flags;
-       ADEBUG();
-
-       spin_lock_irqsave(&s->dma_lock, flags);
-       s->active = 0;
-       s->period = 0;
-       s->periods = 0;
-
-       /* this stops the dma channel and clears the buffer ptrs */
-       omap_audio_stop_dma(s);
-
-       omap_clear_sound_dma(s);
-
-       spin_unlock_irqrestore(&s->dma_lock, flags);
-}
-
-/*
- *  Main dma routine, requests dma according where you are in main alsa buffer
- */
-static void audio_process_dma(struct audio_stream *s)
-{
-       snd_pcm_substream_t *substream = s->stream;
-       snd_pcm_runtime_t *runtime;
-       unsigned int dma_size;
-       unsigned int offset;
-       int ret;
-
-       runtime = substream->runtime;
-       if (s->active) {
-               dma_size = frames_to_bytes(runtime, runtime->period_size);
-               offset = dma_size * s->period;
-               snd_assert(dma_size <= DMA_BUF_SIZE,);
-               ret =
-                   omap_start_sound_dma(s,
-                                        (dma_addr_t) runtime->dma_area +
-                                        offset, dma_size);
-               if (ret) {
-                       printk(KERN_ERR
-                              "audio_process_dma: cannot queue DMA buffer (%i)\n",
-                              ret);
-                       return;
-               }
-
-               s->period++;
-               s->period %= runtime->periods;
-               s->periods++;
-               s->offset = offset;
-       }
-}
-
-/* 
- *  This is called when dma IRQ occurs at the end of each transmited block
- */
-void audio_dma_callback(void *data)
-{
-       struct audio_stream *s = data;
-
-       /* 
-        * If we are getting a callback for an active stream then we inform
-        * the PCM middle layer we've finished a period
-        */
-       if (s->active)
-               snd_pcm_period_elapsed(s->stream);
-
-       spin_lock(&s->dma_lock);
-       if (s->periods > 0) {
-               s->periods--;
-       }
-       audio_process_dma(s);
-       spin_unlock(&s->dma_lock);
-}
-
-
-/* 
- * Alsa section
- * PCM settings and callbacks
- */
-
-static int snd_omap_aic23_trigger(snd_pcm_substream_t * substream, int cmd)
-{
-       struct snd_card_omap_aic23 *chip =
-           snd_pcm_substream_chip(substream);
-       int stream_id = substream->pstr->stream;
-       struct audio_stream *s = &chip->s[stream_id];
-       int err = 0;
-       ADEBUG();
-
-       /* note local interrupts are already disabled in the midlevel code */
-       spin_lock(&s->dma_lock);
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               /* requested stream startup */
-               s->active = 1;
-               audio_process_dma(s);
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               /* requested stream shutdown */
-               audio_stop_dma(s);
-               break;
-       default:
-               err = -EINVAL;
-               break;
-       }
-       spin_unlock(&s->dma_lock);
-       
-       return err;
-}
-
-static int snd_omap_aic23_prepare(snd_pcm_substream_t * substream)
-{
-       struct snd_card_omap_aic23 *chip =
-           snd_pcm_substream_chip(substream);
-       snd_pcm_runtime_t *runtime = substream->runtime;
-       struct audio_stream *s = &chip->s[substream->pstr->stream];
-
-       /* set requested samplerate */
-       omap_aic23_set_samplerate(chip, runtime->rate);
-
-       s->period = 0;
-       s->periods = 0;
-
-       return 0;
-}
-
-static snd_pcm_uframes_t snd_omap_aic23_pointer(snd_pcm_substream_t *
-                                               substream)
-{
-       struct snd_card_omap_aic23 *chip =
-           snd_pcm_substream_chip(substream);
-       
-       return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
-}
-
-/* Hardware capabilities */
-
-static snd_pcm_hardware_t snd_omap_aic23_capture = {
-       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
-       .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,
-};
-
-static snd_pcm_hardware_t snd_omap_aic23_playback = {
-       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),      
-       .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,
-};
-
-static int snd_card_omap_aic23_open(snd_pcm_substream_t * substream)
-{
-       struct snd_card_omap_aic23 *chip =
-           snd_pcm_substream_chip(substream);
-       snd_pcm_runtime_t *runtime = substream->runtime;
-       int stream_id = substream->pstr->stream;
-       int err;
-       ADEBUG();
-
-       chip->s[stream_id].stream = substream;
-       
-       omap_aic23_clock_on();
-       
-       if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
-               runtime->hw = snd_omap_aic23_playback;
-       else
-               runtime->hw = snd_omap_aic23_capture;
-       if ((err =
-            snd_pcm_hw_constraint_integer(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIODS)) <
-           0)
-               return err;
-       if ((err =
-            snd_pcm_hw_constraint_list(runtime, 0,
-                                       SNDRV_PCM_HW_PARAM_RATE,
-                                       &hw_constraints_rates)) < 0)
-               return err;
-
-       return 0;
-}
-
-static int snd_card_omap_aic23_close(snd_pcm_substream_t * substream)
-{
-       struct snd_card_omap_aic23 *chip =
-           snd_pcm_substream_chip(substream);
-       ADEBUG();
-       
-       omap_aic23_clock_off();
-       chip->s[substream->pstr->stream].stream = NULL;
-       
-       return 0;
-}
-
-/* HW params & free */
-
-static int snd_omap_aic23_hw_params(snd_pcm_substream_t * substream,
-                                   snd_pcm_hw_params_t * hw_params)
-{
-       return snd_pcm_lib_malloc_pages(substream,
-                                       params_buffer_bytes(hw_params));
-}
-
-static int snd_omap_aic23_hw_free(snd_pcm_substream_t * substream)
-{
-       return snd_pcm_lib_free_pages(substream);
-}
-
-/* pcm operations */
-
-static snd_pcm_ops_t snd_card_omap_aic23_playback_ops = {
-       .open =         snd_card_omap_aic23_open,
-       .close =        snd_card_omap_aic23_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_omap_aic23_hw_params,
-       .hw_free =      snd_omap_aic23_hw_free,
-       .prepare =      snd_omap_aic23_prepare,
-       .trigger =      snd_omap_aic23_trigger,
-       .pointer =      snd_omap_aic23_pointer,
-};
-
-static snd_pcm_ops_t snd_card_omap_aic23_capture_ops = {
-       .open =         snd_card_omap_aic23_open,
-       .close =        snd_card_omap_aic23_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_omap_aic23_hw_params,
-       .hw_free =      snd_omap_aic23_hw_free,
-       .prepare =      snd_omap_aic23_prepare,
-       .trigger =      snd_omap_aic23_trigger,
-       .pointer =      snd_omap_aic23_pointer,
-};
-
-/*
- *  Alsa init and exit section
- *  
- *  Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
- */
-static int __init snd_card_omap_aic23_pcm(struct snd_card_omap_aic23
-                                         *omap_aic23, int device)
-{
-       snd_pcm_t *pcm;
-       int err;
-       ADEBUG();
-
-       if ((err =
-            snd_pcm_new(omap_aic23->card, "AIC23 PCM", device, 1, 1,
-                        &pcm)) < 0)
-               return err;
-
-       /* sets up initial buffer with continuous allocation */
-       snd_pcm_lib_preallocate_pages_for_all(pcm,
-                                             SNDRV_DMA_TYPE_CONTINUOUS,
-                                             snd_dma_continuous_data
-                                             (GFP_KERNEL),
-                                             128 * 1024, 128 * 1024);
-
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-                       &snd_card_omap_aic23_playback_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
-                       &snd_card_omap_aic23_capture_ops);
-       pcm->private_data = omap_aic23;
-       pcm->info_flags = 0;
-       strcpy(pcm->name, "omap aic23 pcm");
-
-       omap_aic23_audio_init(omap_aic23);
-
-       /* setup DMA controller */
-       audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK],
-                         audio_dma_callback);
-       audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE],
-                         audio_dma_callback);
-
-       omap_aic23->pcm = pcm;
-
-       return 0;
-}
-
-
-#ifdef CONFIG_PM
-
-static int snd_omap_aic23_suspend(snd_card_t * card, pm_message_t state)
-{
-       struct snd_card_omap_aic23 *chip = card->private_data;
-       ADEBUG();
-
-       if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) {
-               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-               snd_pcm_suspend_all(chip->pcm);
-               /* Mutes and turn clock off */
-               omap_aic23_clock_off();
-               snd_omap_suspend_mixer();
-       }
-
-       return 0;
-}
-
-/*
- *  Prepare hardware for resume
- */
-static int snd_omap_aic23_resume(snd_card_t * card)
-{
-       struct snd_card_omap_aic23 *chip = card->private_data;
-       ADEBUG();
-       
-       if (chip->card->power_state != SNDRV_CTL_POWER_D0) {
-               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
-               omap_aic23_clock_on();
-               snd_omap_resume_mixer();
-       }
-
-       return 0;
-}
-
-/*
- * Driver suspend/resume - calls alsa functions. Some hints from aaci.c
- */
-static int omap_aic23_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       snd_card_t *card = platform_get_drvdata(pdev);
-       
-       if (card->power_state != SNDRV_CTL_POWER_D3hot) {
-               snd_omap_aic23_suspend(card, PMSG_SUSPEND);
-       }
-       return 0;
-}
-
-static int omap_aic23_resume(struct platform_device *pdev)
-{
-       snd_card_t *card = platform_get_drvdata(pdev);
-
-       if (card->power_state != SNDRV_CTL_POWER_D0) {
-               snd_omap_aic23_resume(card);
-       }
-       return 0;
-}
-
-#else
-#define snd_omap_aic23_suspend NULL
-#define snd_omap_aic23_resume  NULL
-#define omap_aic23_suspend     NULL
-#define omap_aic23_resume      NULL
-
-#endif /* CONFIG_PM */
-
-/* 
- */
-void snd_omap_aic23_free(snd_card_t * card)
-{
-       struct snd_card_omap_aic23 *chip = card->private_data;
-       ADEBUG();
-       
-       /*
-        * Turn off codec after it is done.
-        * Can't do it immediately, since it may still have
-        * buffered data.
-        */
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(2);
-
-       omap_mcbsp_stop(AUDIO_MCBSP);
-       omap_mcbsp_free(AUDIO_MCBSP);
-
-       audio_aic23_write(RESET_CONTROL_ADDR, 0);
-       audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff);
-
-       audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
-       audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
-}
-
-/*
- *  Omap MCBSP clock configuration
- *  
- *  Here we have some functions that allows clock to be enabled and
- *   disabled only when needed. Besides doing clock configuration 
- *   it allows turn on/turn off audio when necessary. 
- */
-#define CODEC_CLOCK                   12000000
-#define AUDIO_RATE_DEFAULT           44100
-
-/*
- * Do clock framework mclk search
- */
-static __init void omap_aic23_clock_setup(void)
-{
-       aic23_mclk = clk_get(0, "mclk");
-}
-
-/*
- * Do some sanity check, set clock rate, starts it and
- *  turn codec audio on 
- */
-int omap_aic23_clock_on(void)
-{
-       if (clk_get_usecount(aic23_mclk) > 0) {
-               /* MCLK is already in use */
-               printk(KERN_WARNING
-                      "MCLK in use at %d Hz. We change it to %d Hz\n",
-                      (uint) clk_get_rate(aic23_mclk),
-                      CODEC_CLOCK);
-       }
-       
-       if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) {
-               printk(KERN_ERR
-                      "Cannot set MCLK for AIC23 CODEC\n");
-               return -ECANCELED;
-       }
-
-       clk_enable(aic23_mclk);
-
-       printk(KERN_DEBUG
-               "MCLK = %d [%d], usecount = %d\n",
-              (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK,
-              clk_get_usecount(aic23_mclk));
-
-       /* Now turn the audio on */
-       audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 
-                         ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF &
-                         ~ADC_OFF & ~MIC_OFF & ~LINE_OFF);
-       
-       return 0;
-}
-/*
- * Do some sanity check, turn clock off and then turn
- *  codec audio off
- */
-int omap_aic23_clock_off(void)
-{
-       if  (clk_get_usecount(aic23_mclk) > 0) { 
-               if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) {
-                       printk(KERN_WARNING
-                              "MCLK for audio should be %d Hz. But is %d Hz\n",
-                              (uint) clk_get_rate(aic23_mclk),
-                              CODEC_CLOCK);
-               }
-
-               clk_disable(aic23_mclk);
-       }
-       
-       audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
-                         DEVICE_POWER_OFF | OUT_OFF | DAC_OFF |
-                         ADC_OFF | MIC_OFF | LINE_OFF);        
-       return 0;
-}
-
-/* module init & exit */
-
-/* 
- *  Inits alsa soudcard structure
- */
-static int __init snd_omap_aic23_probe(struct platform_device *pdev)
-{
-       int err = 0;
-       snd_card_t *card;
-       ADEBUG();
-       
-       /* gets clock from clock framework */
-       omap_aic23_clock_setup();
-
-       /* register the soundcard */
-       card = snd_card_new(-1, id, THIS_MODULE, sizeof(omap_aic23));
-       if (card == NULL)
-               return -ENOMEM;
-
-       omap_aic23 = kcalloc(1, sizeof(*omap_aic23), GFP_KERNEL);
-       if (omap_aic23 == NULL)
-               return -ENOMEM;
-
-       card->private_data = (void *) omap_aic23;
-       card->private_free = snd_omap_aic23_free;
-
-       omap_aic23->card = card;
-       omap_aic23->samplerate = AUDIO_RATE_DEFAULT;
-
-       spin_lock_init(&omap_aic23->s[0].dma_lock);
-       spin_lock_init(&omap_aic23->s[1].dma_lock);
-
-       /* mixer */
-       if ((err = snd_omap_mixer(omap_aic23)) < 0) 
-               goto nodev;
-
-       /* PCM */
-       if ((err = snd_card_omap_aic23_pcm(omap_aic23, 0)) < 0)
-               goto nodev;
-
-       strcpy(card->driver, "AIC23");
-       strcpy(card->shortname, "OSK AIC23");
-       sprintf(card->longname, "OMAP OSK with AIC23");
-
-       snd_omap_init_mixer();
-
-       snd_card_set_dev(card, &pdev->dev);
-       
-       if ((err = snd_card_register(card)) == 0) {
-               printk(KERN_INFO "OSK audio support initialized\n");
-               platform_set_drvdata(pdev, card);
-               return 0;
-       }
-       
-nodev:
-       snd_omap_aic23_free(card);
-       
-       return err;
-}
-
-static int snd_omap_aic23_remove(struct platform_device *pdev)
-{
-       snd_card_t *card = platform_get_drvdata(pdev);
-       struct snd_card_omap_aic23 *chip = card->private_data;
-       
-       snd_card_free(card);
-
-       omap_aic23 = NULL;
-       card->private_data = NULL;
-       kfree(chip);
-       
-       platform_set_drvdata(pdev, NULL);
-       
-       return 0;
-       
-}
-
-static struct platform_driver omap_alsa_driver = {
-       .probe =        snd_omap_aic23_probe,
-       .remove =       snd_omap_aic23_remove,
-       .suspend =      omap_aic23_suspend, 
-       .resume =       omap_aic23_resume, 
-       .driver = {
-               .name = "omap_mcbsp",
-       },
-};
-
-static int __init omap_aic23_init(void)
-{
-       int err;
-       ADEBUG();
-
-       err = platform_driver_register(&omap_alsa_driver);
-
-       return err;
-}
-
-static void __exit omap_aic23_exit(void)
-{
-       ADEBUG();
-       
-       platform_driver_unregister(&omap_alsa_driver);
-}
-
-module_init(omap_aic23_init);
-module_exit(omap_aic23_exit);
diff --git a/sound/arm/omap/Makefile b/sound/arm/omap/Makefile
new file mode 100644 (file)
index 0000000..354c7b4
--- /dev/null
@@ -0,0 +1,9 @@
+#
+## Makefile for ALSA OMAP
+#
+#
+obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-alsa-aic23.o
+snd-omap-alsa-aic23-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-aic23.o omap-alsa-aic23-mixer.o
+
+obj-$(CONFIG_SND_OMAP_TSC2101) += snd-omap-alsa-tsc2101.o
+snd-omap-alsa-tsc2101-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-tsc2101.o omap-alsa-tsc2101-mixer.o
similarity index 97%
rename from sound/arm/omap-alsa-mixer.c
rename to sound/arm/omap/omap-alsa-aic23-mixer.c
index 742e5b627c4f6293f75afc7f8e57e106618e057b..1fb0160d1b2085a37296fdef8aa3c8f6f0ee3010 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * sound/arm/omap-alsa-mixer.c
+ * sound/arm/omap/omap-alsa-aic23-mixer.c
  * 
  * Alsa Driver Mixer for generic codecs for omap boards
  *
 
 #include <linux/config.h>
 #include <sound/driver.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include <asm/hardware.h>
-#include <asm/mach-types.h>
-#include <asm/arch/dma.h>
 #include <asm/arch/aic23.h>
 
-#include "omap-aic23.h"
+#include <asm/arch/omap-alsa.h>
+#include "omap-alsa-aic23.h"
 #include <sound/initval.h>
 #include <sound/control.h>
 
@@ -67,7 +57,7 @@ MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
 /* Codec AIC23 */
 #if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
 
-extern __inline__ void audio_aic23_write(u8, u16);
+extern void audio_aic23_write(u8, u16);
 
 #define MIXER_NAME                  "Mixer AIC23"
 #define SND_OMAP_WRITE(reg, val)     audio_aic23_write(reg, val)
@@ -411,31 +401,6 @@ static snd_kcontrol_new_t snd_omap_controls[] = {
        OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, INSEL_MIC),
 };
 
-void snd_omap_init_mixer(void)
-{
-       u16 vol_reg;
-
-       /* Line's default values */
-       omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
-       omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
-       omap_regs[LINE_INDEX].sw = 0;
-       SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
-       SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
-       
-       /* Analog Audio Control's default values */
-       omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
-       
-       /* Headphone's default values */
-       vol_reg = LZC_ON;
-       vol_reg &= ~OUTPUT_VOLUME_MASK;
-       vol_reg |= DEFAULT_OUTPUT_VOLUME;
-       omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
-       omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
-       omap_regs[PCM_INDEX].sw = 1;
-       SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
-       SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
-}
-
 #ifdef CONFIG_PM
 
 void snd_omap_suspend_mixer(void)
@@ -474,7 +439,32 @@ void snd_omap_resume_mixer(void)
 }
 #endif
 
-int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
+void snd_omap_init_mixer(void)
+{
+       u16 vol_reg;
+
+       /* Line's default values */
+       omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
+       omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
+       omap_regs[LINE_INDEX].sw = 0;
+       SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
+       SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
+       
+       /* Analog Audio Control's default values */
+       omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
+       
+       /* Headphone's default values */
+       vol_reg = LZC_ON;
+       vol_reg &= ~OUTPUT_VOLUME_MASK;
+       vol_reg |= DEFAULT_OUTPUT_VOLUME;
+       omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
+       omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
+       omap_regs[PCM_INDEX].sw = 1;
+       SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
+       SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
+}
+
+int snd_omap_mixer(struct snd_card_omap_codec *chip)
 {
        snd_card_t *card;
        unsigned int idx;
@@ -493,4 +483,3 @@ int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
 
        return 0;
 }
-
diff --git a/sound/arm/omap/omap-alsa-aic23.c b/sound/arm/omap/omap-alsa-aic23.c
new file mode 100644 (file)
index 0000000..f490616
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * arch/arm/mach-omap1/omap-alsa-aic23.c
+ * 
+ * Alsa codec Driver for AIC23 chip on OSK5912 platform board
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ *            {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ * 
+ * Based in former alsa driver for osk and oss driver
+ *
+ * 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/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <linux/clk.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/aic23.h>
+
+#include <asm/arch/omap-alsa.h>
+#include "omap-alsa-aic23.h"
+
+static struct clk *aic23_mclk = 0;
+
+/* aic23 related */
+static const struct aic23_samplerate_reg_info
+ rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+       {4000, 0x06, 1},                /*  4000 */
+       {8000, 0x06, 0},                /*  8000 */
+       {16000, 0x0C, 1},               /* 16000 */
+       {22050, 0x11, 1},               /* 22050 */
+       {24000, 0x00, 1},               /* 24000 */
+       {32000, 0x0C, 0},               /* 32000 */
+       {44100, 0x11, 0},               /* 44100 */
+       {48000, 0x00, 0},               /* 48000 */
+       {88200, 0x1F, 0},               /* 88200 */
+       {96000, 0x0E, 0},               /* 96000 */
+};
+
+/*
+ * Hardware capabilities
+ */
+ /*
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+       4000, 8000, 16000, 22050,
+       24000, 32000, 44100,
+       48000, 88200, 96000,
+};
+
+static snd_pcm_hw_constraint_list_t aic23_hw_constraints_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list = rates,
+       .mask = 0,
+};
+
+static snd_pcm_hardware_t aic23_snd_omap_alsa_playback = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),      
+       .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,
+};
+
+static snd_pcm_hardware_t aic23_snd_omap_alsa_capture = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+       .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,
+};
+
+/*
+ * Codec/mcbsp init and configuration section
+ * codec dependent code.
+ */
+
+extern int tlv320aic23_write_value(u8 reg, u16 value);
+
+/* TLV320AIC23 is a write only device */
+void audio_aic23_write(u8 address, u16 data)
+{
+       tlv320aic23_write_value(address, data);
+}
+EXPORT_SYMBOL_GPL(audio_aic23_write);
+
+/*
+ * Sample rate changing
+ */
+void aic23_set_samplerate(long rate)
+{
+       u8 count = 0;
+       u16 data = 0;
+
+       /* Fix the rate if it has a wrong value */
+       if (rate >= 96000)
+               rate = 96000;
+       else if (rate >= 88200)
+               rate = 88200;
+       else if (rate >= 48000)
+               rate = 48000;
+       else if (rate >= 44100)
+               rate = 44100;
+       else if (rate >= 32000)
+               rate = 32000;
+       else if (rate >= 24000)
+               rate = 24000;
+       else if (rate >= 22050)
+               rate = 22050;
+       else if (rate >= 16000)
+               rate = 16000;
+       else if (rate >= 8000)
+               rate = 8000;
+       else
+               rate = 4000;
+
+       /* Search for the right sample rate */
+       /* Verify what happens if the rate is not supported
+        * now it goes to 96Khz */
+       while ((rate_reg_info[count].sample_rate != rate) &&
+              (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) {
+               count++;
+       }
+
+       data = (rate_reg_info[count].divider << CLKIN_SHIFT) |
+           (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
+
+       audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
+}
+
+inline void aic23_configure(void)
+{
+       /* Reset codec */
+       audio_aic23_write(RESET_CONTROL_ADDR, 0);
+
+       /* Initialize the AIC23 internal state */
+
+       /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
+       audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL);
+
+       /* Digital audio path control, de-emphasis control 44.1kHz */
+       audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
+
+       /* Digital audio interface, master/slave mode, I2S, 16 bit */
+#ifdef AIC23_MASTER
+       audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR,
+                         MS_MASTER | IWL_16 | FOR_DSP);
+#else
+       audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
+#endif
+
+       /* Enable digital interface */
+       audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
+}
+
+/*
+ *  Omap MCBSP clock configuration and Power Management
+ *  
+ *  Here we have some functions that allows clock to be enabled and
+ *   disabled only when needed. Besides doing clock configuration 
+ *   it allows turn on/turn off audio when necessary. 
+ */
+/*
+ * Do clock framework mclk search
+ */
+void aic23_clock_setup(void)
+{
+       aic23_mclk = clk_get(0, "mclk");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and
+ *  turn codec audio on 
+ */
+int aic23_clock_on(void)
+{
+       if (clk_get_usecount(aic23_mclk) > 0) {
+               /* MCLK is already in use */
+               printk(KERN_WARNING
+                      "MCLK in use at %d Hz. We change it to %d Hz\n",
+                      (uint) clk_get_rate(aic23_mclk),
+                      CODEC_CLOCK);
+       }
+       
+       if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) {
+               printk(KERN_ERR
+                      "Cannot set MCLK for AIC23 CODEC\n");
+               return -ECANCELED;
+       }
+
+       clk_enable(aic23_mclk);
+
+       printk(KERN_DEBUG
+               "MCLK = %d [%d], usecount = %d\n",
+              (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK,
+              clk_get_usecount(aic23_mclk));
+
+       /* Now turn the audio on */
+       audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 
+                         ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF &
+                         ~ADC_OFF & ~MIC_OFF & ~LINE_OFF);     
+       return 0;
+}
+/*
+ * Do some sanity check, turn clock off and then turn
+ *  codec audio off
+ */
+int aic23_clock_off(void)
+{
+       if  (clk_get_usecount(aic23_mclk) > 0) { 
+               if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) {
+                       printk(KERN_WARNING
+                              "MCLK for audio should be %d Hz. But is %d Hz\n",
+                              (uint) clk_get_rate(aic23_mclk),
+                              CODEC_CLOCK);
+               }
+
+               clk_disable(aic23_mclk);
+       }
+       
+       audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
+                         DEVICE_POWER_OFF | OUT_OFF | DAC_OFF |
+                         ADC_OFF | MIC_OFF | LINE_OFF);        
+       return 0;
+}
+
+int aic23_get_default_samplerate(void)
+{
+       return DEFAULT_SAMPLE_RATE;
+}
+
+static int __init snd_omap_alsa_aic23_probe(struct platform_device *pdev)
+{
+       int     ret;
+       struct  omap_alsa_codec_config *codec_cfg;
+       
+       codec_cfg = pdev->dev.platform_data;
+       if (codec_cfg != NULL) {
+               codec_cfg->hw_constraints_rates = &aic23_hw_constraints_rates;
+               codec_cfg->snd_omap_alsa_playback  = &aic23_snd_omap_alsa_playback;
+               codec_cfg->snd_omap_alsa_capture  = &aic23_snd_omap_alsa_capture;               
+               codec_cfg->codec_configure_dev  = aic23_configure;
+               codec_cfg->codec_set_samplerate = aic23_set_samplerate;
+               codec_cfg->codec_clock_setup    = aic23_clock_setup;
+               codec_cfg->codec_clock_on       = aic23_clock_on;
+               codec_cfg->codec_clock_off      = aic23_clock_off;
+               codec_cfg->get_default_samplerate = aic23_get_default_samplerate;
+               ret     = snd_omap_alsa_post_probe(pdev, codec_cfg);
+       }
+       else
+               ret = -ENODEV;
+       return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+       .probe          = snd_omap_alsa_aic23_probe,
+       .remove         = snd_omap_alsa_remove,
+       .suspend        = snd_omap_alsa_suspend,
+       .resume         = snd_omap_alsa_resume,
+       .driver = {
+               .name = "omap_alsa_mcbsp",
+       },
+};
+
+static int __init omap_alsa_aic23_init(void)
+{
+       int err;
+       
+       ADEBUG();
+       err = platform_driver_register(&omap_alsa_driver);
+
+       return err;
+}
+
+static void __exit omap_alsa_aic23_exit(void)
+{
+       ADEBUG();
+       
+       platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_aic23_init);
+module_exit(omap_alsa_aic23_exit);
diff --git a/sound/arm/omap/omap-alsa-aic23.h b/sound/arm/omap/omap-alsa-aic23.h
new file mode 100644 (file)
index 0000000..63907c4
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * sound/arm/omap-alsa-aic23.h
+ * 
+ * Alsa Driver for AIC23 codec on OSK5912 platform board
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ *            {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * 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 __OMAP_ALSA_AIC23_H
+#define __OMAP_ALSA_AIC23_H
+
+#include <sound/driver.h>
+#include <asm/arch/dma.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <asm/arch/mcbsp.h>
+
+/* Define to set the AIC23 as the master w.r.t McBSP */
+#define AIC23_MASTER
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED  10
+
+/*
+ * AUDIO related MACROS
+ */
+#ifndef DEFAULT_BITPERSAMPLE
+#define DEFAULT_BITPERSAMPLE           16
+#endif
+
+#define DEFAULT_SAMPLE_RATE            44100
+#define CODEC_CLOCK                    12000000
+#define AUDIO_MCBSP                    OMAP_MCBSP1
+
+#define DEFAULT_OUTPUT_VOLUME          0x60
+#define DEFAULT_INPUT_VOLUME           0x00    /* 0 ==> mute line in */
+
+#define OUTPUT_VOLUME_MIN              LHV_MIN
+#define OUTPUT_VOLUME_MAX              LHV_MAX
+#define OUTPUT_VOLUME_RANGE            (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
+#define OUTPUT_VOLUME_MASK             OUTPUT_VOLUME_MAX
+
+#define INPUT_VOLUME_MIN               LIV_MIN
+#define INPUT_VOLUME_MAX               LIV_MAX
+#define INPUT_VOLUME_RANGE             (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK              INPUT_VOLUME_MAX
+
+#define SIDETONE_MASK                  0x1c0
+#define SIDETONE_0                     0x100
+#define SIDETONE_6                     0x000
+#define SIDETONE_9                     0x040
+#define SIDETONE_12                    0x080
+#define SIDETONE_18                    0x0c0
+
+#define DEFAULT_ANALOG_AUDIO_CONTROL  DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
+
+struct aic23_samplerate_reg_info {
+       u32 sample_rate;
+       u8 control;             /* SR3, SR2, SR1, SR0 and BOSR */
+       u8 divider;             /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+/*
+ * Defines codec specific functions pointers that can be used from the 
+ * common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
+ */
+void define_codec_functions(struct omap_alsa_codec_config *codec_config);
+inline void aic23_configure(void);
+void aic23_set_samplerate(long rate);
+void aic23_clock_setup(void);
+int aic23_clock_on(void);
+int aic23_clock_off(void);
+int aic23_get_default_samplerate(void);
+
+#endif
similarity index 88%
rename from sound/arm/omap-alsa-dma.c
rename to sound/arm/omap/omap-alsa-dma.c
index 8530acf7694d9f87c0b8917626f3e825289d8ae8..7537d8e00bb796d719cd783da2444104f7aa6755 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * sound/arm/omap-alsa-dma.c
+ * sound/arm/omap/omap-alsa-dma.c
  *
  * Common audio DMA handling for the OMAP processors
  *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
  * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
  * 
  * Copyright (C) 2004 Texas Instruments, Inc.
 
 #include <asm/arch/mcbsp.h>
 
-#include "omap-aic23.h"
+#include <asm/arch/omap-alsa.h>
 
 #undef DEBUG
-//#define DEBUG
-#ifdef DEBUG
-#define DPRINTK(ARGS...)  printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
-#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
-#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
-#else
-
-#define DPRINTK( x... )
-#define FN_IN
-#define FN_OUT(x)
-#endif
 
 #define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
 
@@ -165,7 +156,7 @@ static void omap_sound_dma_link_lch(void *data)
        FN_OUT(0);
 }
 
-int omap_request_sound_dma(int device_id, const char *device_name,
+int omap_request_alsa_sound_dma(int device_id, const char *device_name,
                           void *data, int **channels)
 {
        int i, err = 0;
@@ -186,10 +177,11 @@ int omap_request_sound_dma(int device_id, const char *device_name,
        }
        spin_lock(&dma_list_lock);
        for (i = 0; i < nr_linked_channels; i++) {
-               err =
-                   omap_request_dma(device_id, device_name,
-                                    sound_dma_irq_handler, data,
-                                    &chan[i]);
+               err = omap_request_dma(device_id, 
+                               device_name,
+                               sound_dma_irq_handler, 
+                               data,
+                               &chan[i]);
 
                /* Handle Failure condition here */
                if (err < 0) {
@@ -223,7 +215,7 @@ int omap_request_sound_dma(int device_id, const char *device_name,
  **************************************************************************************/
 static void omap_sound_dma_unlink_lch(void *data)
 {
-       struct audio_stream *s = (struct audio_stream *) data;
+       struct audio_stream *s = (struct audio_stream *)data;
        int *chan = s->lch;
        int i;
 
@@ -243,11 +235,11 @@ static void omap_sound_dma_unlink_lch(void *data)
        FN_OUT(0);
 }
 
-int omap_free_sound_dma(void *data, int **channels)
+int omap_free_alsa_sound_dma(void *data, int **channels)
 {
-
        int i;
        int *chan = NULL;
+       
        FN_IN;
        if (unlikely(NULL == channels)) {
                BUG();
@@ -277,10 +269,11 @@ int omap_free_sound_dma(void *data, int **channels)
  * Stop all the DMA channels of the stream
  *
  **************************************************************************************/
-void omap_audio_stop_dma(struct audio_stream *s)
+void omap_stop_alsa_sound_dma(struct audio_stream *s)
 {
        int *chan = s->lch;
        int i;
+       
        FN_IN;
        if (unlikely(NULL == chan)) {
                BUG();
@@ -299,7 +292,7 @@ void omap_audio_stop_dma(struct audio_stream *s)
  * Clear any pending transfers
  *
  **************************************************************************************/
-void omap_clear_sound_dma(struct audio_stream * s)
+void omap_clear_alsa_sound_dma(struct audio_stream * s)
 {
        FN_IN;
        omap_clear_dma(s->lch[s->dma_q_head]);
@@ -307,13 +300,6 @@ void omap_clear_sound_dma(struct audio_stream * s)
        return;
 }
 
-/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/
-
-#ifdef OMAP1610_MCBSP1_BASE
-#undef OMAP1610_MCBSP1_BASE
-#endif
-#define OMAP1610_MCBSP1_BASE    0xE1011000
-
 /***************************************************************************************
  *
  * DMA related functions
@@ -325,9 +311,10 @@ static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
        int dt = 0x1;           /* data type 16 */
        int cen = 32;           /* Stereo */
        int cfn = dma_size / (2 * cen);
+       
        FN_IN;
        omap_set_dma_dest_params(channel, 0x05, 0x00,
-                                (OMAP1610_MCBSP1_BASE + 0x806),
+                                (OMAP1510_MCBSP1_BASE + 0x06),
                                 0, 0);
        omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr,
                                0, 0);
@@ -341,11 +328,11 @@ static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
 {
        int dt = 0x1;           /* data type 16 */
        int cen = 32;           /* stereo */
-       
        int cfn = dma_size / (2 * cen);
+       
        FN_IN;
        omap_set_dma_src_params(channel, 0x05, 0x00,
-                               (OMAP1610_MCBSP1_BASE + 0x802),
+                               (OMAP1510_MCBSP1_BASE + 0x02),
                                0, 0);
        omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
        omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
@@ -358,7 +345,7 @@ static int audio_start_dma_chain(struct audio_stream *s)
        int channel = s->lch[s->dma_q_head];
        FN_IN;
        if (!s->started) {
-        s->hw_stop();      /* stops McBSP Interface */
+               s->hw_stop();      /* stops McBSP Interface */
                omap_start_dma(channel);
                s->started = 1;
                s->hw_start();     /* start McBSP interface */
@@ -372,8 +359,9 @@ static int audio_start_dma_chain(struct audio_stream *s)
  * Do the initial set of work to initialize all the channels as required.
  * We shall then initate a transfer
  */
-int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,
-                        u_int dma_size)
+int omap_start_alsa_sound_dma(struct audio_stream *s, 
+                       dma_addr_t dma_ptr, 
+                       u_int dma_size)
 {
        int ret = -EPERM;
 
@@ -439,18 +427,17 @@ static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status,
        }
 
        if (ch_status & DCSR_END_BLOCK) 
-               audio_dma_callback(s);
+               callback_omap_alsa_sound_dma(s);
        FN_OUT(0);
        return;
 }
 
 MODULE_AUTHOR("Texas Instruments");
-MODULE_DESCRIPTION
-    ("Common DMA handling for Audio driver on OMAP processors");
+MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
 MODULE_LICENSE("GPL");
 
-EXPORT_SYMBOL(omap_start_sound_dma);
-EXPORT_SYMBOL(omap_clear_sound_dma);
-EXPORT_SYMBOL(omap_request_sound_dma);
-EXPORT_SYMBOL(omap_free_sound_dma);
-EXPORT_SYMBOL(omap_audio_stop_dma);
+EXPORT_SYMBOL(omap_start_alsa_sound_dma);
+EXPORT_SYMBOL(omap_clear_alsa_sound_dma);
+EXPORT_SYMBOL(omap_request_alsa_sound_dma);
+EXPORT_SYMBOL(omap_free_alsa_sound_dma);
+EXPORT_SYMBOL(omap_stop_alsa_sound_dma);
similarity index 64%
rename from sound/arm/omap-alsa-dma.h
rename to sound/arm/omap/omap-alsa-dma.h
index 2ac7650abe353380f2d75c292153549a3dc416a5..1cecc8aa55cfeea1074af2b2859e2343a57335ed 100644 (file)
@@ -1,8 +1,10 @@
 /*  
- * linux/sound/arm/omap-alsa-dma.h
+ * linux/sound/arm/omap/omap-alsa-dma.h
  *
  * Common audio DMA handling for the OMAP processors
  *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
  * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
  * 
  * Copyright (C) 2004 Texas Instruments, Inc.
 
 /************************** INCLUDES *************************************/
 
-#include "omap-aic23.h"
-
-/************************** GLOBAL MACROS *************************************/
-
-/* Provide the Macro interfaces common across platforms */
-#define DMA_REQUEST(e,s, cb)   {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);}
-#define DMA_FREE(s)            omap_free_sound_dma(s, &s->lch)
-#define DMA_CLEAR(s)           omap_clear_sound_dma(s)
+#include <asm/arch/omap-alsa.h>
 
 /************************** GLOBAL DATA STRUCTURES *********************************/
 
@@ -45,15 +40,14 @@ typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
 
 /**************** ARCH SPECIFIC FUNCIONS *******************************************/
 
-void omap_clear_sound_dma(struct audio_stream * s);
+void omap_clear_alsa_sound_dma(struct audio_stream * s);
 
-int omap_request_sound_dma(int device_id, const char *device_name,
+int omap_request_alsa_sound_dma(int device_id, const char *device_name,
                           void *data, int **channels);
-int omap_free_sound_dma(void *data, int **channels);
+int omap_free_alsa_sound_dma(void *data, int **channels);
 
-int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,
-                        u_int dma_size);
+int omap_start_alsa_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,  u_int dma_size);
 
-void omap_audio_stop_dma(struct audio_stream *s);
+void omap_stop_alsa_sound_dma(struct audio_stream *s);
 
 #endif
diff --git a/sound/arm/omap/omap-alsa-tsc2101-mixer.c b/sound/arm/omap/omap-alsa-tsc2101-mixer.c
new file mode 100644 (file)
index 0000000..9dfaea8
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2101-mixer.c
+ * 
+ * Alsa Driver for TSC2101 codec for OMAP platform boards.
+ *
+ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and 
+ *                  Everett Coleman II <gcc80x86@fuzzyneural.net>
+ *
+ * Board initialization code is based on the code in TSC2101 OSS driver.
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *     Written by Nishanth Menon and Sriram Kannan
+ * 
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2006-03-01   Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
+ *             Can switch between headset and loudspeaker playback, 
+ *             mute and unmute dgc, set dgc volume. Record source switch,
+ *             keyclick, buzzer and headset volume and handset volume control 
+ *             are still missing.
+ *             
+ */
+#include "omap-alsa-tsc2101.h"
+#include "omap-alsa-tsc2101-mixer.h"
+
+#include <linux/types.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+//#define M_DPRINTK(ARGS...)  printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define M_DPRINTK(ARGS...)             /* nop */
+
+#define DGC_DALVL_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define DGC_DARVL_EXTRACT(ARG) ((ARG & 0x007f))
+#define GET_DGC_DALMU_BIT_VALUE(ARG)  (((ARG) & TSC2101_BIT(15)) >> 15)
+#define GET_DGC_DARMU_BIT_VALUE(ARG)  (((ARG) & TSC2101_BIT(7)) >> 7)
+#define IS_DGC_DALMU_UNMUTED(ARG)  (((GET_DGC_DALMU_BIT_VALUE(ARG)) == 0))
+#define IS_DGC_DARMU_UNMUTED(ARG) (((GET_DGC_DARMU_BIT_VALUE(ARG)) == 0))
+
+#define HGC_ADPGA_HED_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define GET_DGC_HGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
+#define IS_DGC_HGCMU_UNMUTED(ARG) (((GET_DGC_HGCMU_BIT_VALUE(ARG)) == 0))
+
+#define HNGC_ADPGA_HND_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define GET_DGC_HNGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
+#define IS_DGC_HNGCMU_UNMUTED(ARG) (((GET_DGC_HNGCMU_BIT_VALUE(ARG)) == 0))
+
+static int current_playback_target     = PLAYBACK_TARGET_LOUDSPEAKER;
+static int current_rec_src             = REC_SRC_SINGLE_ENDED_MICIN_HED;
+
+/*
+ * Used for switching between TSC2101 recourd sources.
+ * Logic is adjusted from the TSC2101 OSS code.
+ */
+static int set_record_source(int val)
+{
+       u16     data;
+       int     maskedVal;
+       
+       FN_IN;
+       maskedVal       = 0xe0 & val;   
+
+       data    = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_MIXER_PGA_CTRL);
+       data    &= ~MPC_MICSEL(7); /* clear all MICSEL bits */
+       data    |= maskedVal;
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                               TSC2101_MIXER_PGA_CTRL,
+                               data);
+       current_rec_src = val;
+
+       FN_OUT(0);
+       return 0;
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to real 
+ * Digital Gain Control (DGC) value that can be written
+ * or read from the TSC2101 registry.
+ * 
+ * Note that the number "OUTPUT_VOLUME_MAX" is smaller than OUTPUT_VOLUME_MIN
+ * because DGC works as a volume decreaser. (The more bigger value is put
+ * to DGC, the more the volume of controlled channel is decreased)
+ * 
+ * In addition the TCS2101 chip would allow the maximum volume reduction be 63.5 DB
+ * but according to some tests user can not hear anything with this chip
+ * when the volume is set to be less than 25 db.
+ * Therefore this function will return a value that means 38.5 db (63.5 db - 25 db) 
+ * reduction in the channel volume, when mixer is set to 0.
+ * For mixer value 100, this will return a value that means 0 db volume reduction.
+ * ([mute_left_bit]0000000[mute_right_bit]0000000)
+*/
+int get_mixer_volume_as_dac_gain_control_volume(int vol)
+{
+       u16 retVal;
+
+       /* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
+       retVal  = ((vol * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
+       /* invert the value for getting the proper range 0 min and 100 max */
+       retVal  = OUTPUT_VOLUME_MIN - retVal;
+       
+       return retVal;
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to TSC2101 
+ * Digital Gain Control (DGC) volume. Alsa mixer volume 0
+ * is converted to value meaning the volume reduction of -38.5 db
+ * and Alsa mixer volume 100 is converted to value meaning the
+ * reduction of 0 db.
+ */
+int set_mixer_volume_as_dac_gain_control_volume(int mixerVolL, int mixerVolR) 
+{
+       u16 val;
+       int retVal;
+       int volL;
+       int volR;
+       
+       if ((mixerVolL < 0) || 
+           (mixerVolL > 100) ||
+           (mixerVolR < 0) ||
+           (mixerVolR > 100)) {
+               printk(KERN_ERR "Trying a bad mixer volume as dac gain control volume value, left (%d), right (%d)!\n", mixerVolL, mixerVolR);
+               return -EPERM;
+       }
+       M_DPRINTK("mixer volume left = %d, right = %d\n", mixerVolL, mixerVolR);        
+       volL    = get_mixer_volume_as_dac_gain_control_volume(mixerVolL);
+       volR    = get_mixer_volume_as_dac_gain_control_volume(mixerVolR);
+       
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+       /* keep the old mute bit settings */
+       val     &= ~(DGC_DALVL(OUTPUT_VOLUME_MIN) | DGC_DARVL(OUTPUT_VOLUME_MIN));
+       val     |= DGC_DALVL(volL) | DGC_DARVL(volR);
+       retVal  = 2;
+       if (retVal) {
+               omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_DAC_GAIN_CTRL, 
+                               val);
+       }
+       M_DPRINTK("to registry: left = %d, right = %d, total = %d\n", DGC_DALVL_EXTRACT(val), DGC_DARVL_EXTRACT(val), val);
+       return retVal;
+}
+
+int dac_gain_control_unmute_control(int muteLeft, int muteRight)
+{
+       u16 val;
+       int count;
+
+       count   = 0;
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+       /* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+        * so if values are same, it's time to change the registry value.
+        */
+       if (muteLeft == GET_DGC_DALMU_BIT_VALUE(val)) {
+               if (muteLeft == 0) {
+                       /* mute --> turn bit on */
+                       val     = val | DGC_DALMU;
+               }
+               else {
+                       /* unmute --> turn bit off */
+                       val     = val & ~DGC_DALMU;
+               }
+               count++;
+       } /* L */
+       if (muteRight == GET_DGC_DARMU_BIT_VALUE(val)) {
+               if (muteRight == 0) {
+                       /* mute --> turn bit on */
+                       val     = val | DGC_DARMU;
+               }
+               else {
+                       /* unmute --> turn bit off */
+                       val     = val & ~DGC_DARMU;
+               }               
+               count++;
+       } /* R */
+       if (count) {
+               omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL, val);
+               M_DPRINTK("changed value, is_unmuted left = %d, right = %d\n", 
+                       IS_DGC_DALMU_UNMUTED(val),
+                       IS_DGC_DARMU_UNMUTED(val));
+       }
+       return count;   
+}
+
+/*
+ * Converts the DGC registry value read from the TSC2101 registry to 
+ * Alsa mixer volume format (0 - 100).
+ */
+int get_dac_gain_control_volume_as_mixer_volume(u16 vol) 
+{
+       u16 retVal;     
+
+       retVal  = OUTPUT_VOLUME_MIN - vol;
+       retVal  = ((retVal - OUTPUT_VOLUME_MAX) * 100) / OUTPUT_VOLUME_RANGE;
+       /* fix scaling error */
+       if ((retVal > 0) && (retVal < 100)) {
+               retVal++;
+       }
+       return retVal;
+}
+
+/*
+ * Converts the headset gain control volume (0 - 63.5 db)
+ * to Alsa mixer volume (0 - 100)
+ */
+int get_headset_gain_control_volume_as_mixer_volume(u16 registerVal) 
+{
+       u16 retVal;
+       
+       retVal  = ((registerVal * 100) / INPUT_VOLUME_RANGE);
+       return retVal;
+}
+
+/*
+ * Converts the handset gain control volume (0 - 63.5 db)
+ * to Alsa mixer volume (0 - 100)
+ */
+int get_handset_gain_control_volume_as_mixer_volume(u16 registerVal) 
+{
+       return get_headset_gain_control_volume_as_mixer_volume(registerVal);
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to 
+ * headset gain control volume (0 - 63.5 db)
+ */
+int get_mixer_volume_as_headset_gain_control_volume(u16 mixerVal) 
+{
+       u16 retVal;
+       
+       retVal  = ((mixerVal * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;   
+       return retVal;
+}
+
+/*
+ * Writes Alsa mixer volume (0 - 100) to TSC2101 headset volume registry in
+ * a TSC2101 format. (0 - 63.5 db)
+ * In TSC2101 OSS driver this functionality was controlled with "SET_LINE" parameter.
+ */
+int set_mixer_volume_as_headset_gain_control_volume(int mixerVol) 
+{
+       int volume;
+       int retVal;
+       u16 val;
+
+       if (mixerVol < 0 || mixerVol > 100) {
+               M_DPRINTK("Trying a bad headset mixer volume value(%d)!\n", mixerVol);
+               return -EPERM;
+       }
+       M_DPRINTK("mixer volume = %d\n", mixerVol);
+       /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+       /* NOTE: 0 is minimum volume and not mute */
+       volume  = get_mixer_volume_as_headset_gain_control_volume(mixerVol);    
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HEADSET_GAIN_CTRL);
+       /* preserve the old mute settings */
+       val     &= ~(HGC_ADPGA_HED(INPUT_VOLUME_MAX));
+       val     |= HGC_ADPGA_HED(volume);
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                       TSC2101_HEADSET_GAIN_CTRL,
+                       val);   
+       retVal  = 1;
+       
+       M_DPRINTK("to registry = %d\n", val);   
+       return retVal;
+}
+
+/*
+ * Writes Alsa mixer volume (0 - 100) to TSC2101 handset volume registry in
+ * a TSC2101 format. (0 - 63.5 db)
+ * In TSC2101 OSS driver this functionality was controlled with "SET_MIC" parameter.
+ */
+int set_mixer_volume_as_handset_gain_control_volume(int mixerVol) 
+{
+       int volume;
+       int retVal;
+       u16 val;        
+
+       if (mixerVol < 0 || mixerVol > 100) {
+               M_DPRINTK("Trying a bad mic mixer volume value(%d)!\n", mixerVol);
+               return -EPERM;
+       }
+       M_DPRINTK("mixer volume = %d\n", mixerVol);
+       /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range
+        * NOTE: 0 is minimum volume and not mute 
+        */
+       volume  = get_mixer_volume_as_headset_gain_control_volume(mixerVol);
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+       /* preserve the old mute settigns */
+       val     &= ~(HNGC_ADPGA_HND(INPUT_VOLUME_MAX));
+       val     |= HNGC_ADPGA_HND(volume);
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                       TSC2101_HANDSET_GAIN_CTRL,
+                       val);
+       retVal  = 1;
+       
+       M_DPRINTK("to registry = %d\n", val);   
+       return retVal;
+}
+
+void init_record_sources(void)
+{
+       /* Mute Analog Sidetone
+        * analog sidetone gain db?
+        * Cell Phone In not connected to ADC
+        * Input selected by MICSEL connected to ADC
+        */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                         TSC2101_MIXER_PGA_CTRL,
+                         MPC_ASTMU | MPC_ASTG(0x40) | ~MPC_CPADC | MPC_MICADC);
+       /* Set record source, Select MIC_INHED input for headset */
+       set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);      
+}
+
+void set_loudspeaker_to_playback_target(void)
+{
+       u16     val;
+
+       /* power down sp1, sp2 and loudspeaker */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_CODEC_POWER_CTRL,
+                       CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);       
+       /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled
+        * 1dB AGC hysteresis
+        * MICes bias 2V
+        */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                       TSC2101_AUDIO_CTRL_4, 
+                       AC4_MB_HED(0));
+
+       /* DAC left and right routed to SPK1/SPK2
+        * SPK1/SPK2 unmuted
+        * keyclicks routed to SPK1/SPK2
+        */
+       val     = AC5_DIFFIN |
+                       AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+                       AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+                       AC5_HDSCPTC;
+       val     = val & ~AC5_HDSCPTC;
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_AUDIO_CTRL_5,
+                       val);
+       
+       /* powerdown spk1/out32n and spk2 */
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                               TSC2101_POWERDOWN_STS);
+       val     = val & ~(~PS_SPK1FL | ~PS_HNDFL | PS_LSPKFL);
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_POWERDOWN_STS,
+                       val);
+
+       /* routing selected to SPK1 goes to OUT8P/OUT84 alsa. (loudspeaker)
+        * analog sidetone routed to loudspeaker
+        * buzzer pga routed to loudspeaker
+        * keyclick routing to loudspeaker
+        * cellphone input routed to loudspeaker
+        * mic selection (control register 04h/page2) routed to cell phone output (CP_OUT)
+        * routing selected for SPK1 goes also to cellphone output (CP_OUT)
+        * OUT8P/OUT8N (loudspeakers) unmuted (0 = unmuted)
+        * Cellphone output is not muted (0 = unmuted)
+        * Enable loudspeaker short protection control (0 = enable protection)
+        * VGND short protection control (0 = enable protection)
+        */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_AUDIO_CTRL_6,
+                       AC6_SPL2LSK | AC6_AST2LSK | AC6_BUZ2LSK | AC6_KCL2LSK |
+                       AC6_CPI2LSK | AC6_MIC2CPO | AC6_SPL2CPO |
+                       ~AC6_MUTLSPK | ~AC6_MUTSPK2 | ~AC6_LDSCPTC | ~AC6_VGNDSCPTC);
+       current_playback_target = PLAYBACK_TARGET_LOUDSPEAKER;
+}
+
+void set_headphone_to_playback_target(void)
+{
+       /* power down sp1, sp2 and loudspeaker */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_CODEC_POWER_CTRL,
+                       CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);
+       /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+       /* 1dB AGC hysteresis */
+       /* MICes bias 2V */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                               TSC2101_AUDIO_CTRL_4, 
+                               AC4_MB_HED(0));
+
+       /* DAC left and right routed to SPK2 */
+       /* SPK1/2 unmuted */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_AUDIO_CTRL_5,
+                       AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+                       AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+                       AC5_HDSCPTC);
+
+       /* OUT8P/N muted, CPOUT muted */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_AUDIO_CTRL_6,
+                       AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+                       AC6_VGNDSCPTC);
+       current_playback_target = PLAYBACK_TARGET_HEADPHONE;
+}
+
+/*
+ * Checks whether the headset is detected.
+ * If headset is detected, the type is returned. Type can be
+ *     0x01    = stereo headset detected
+ *     0x02    = cellurar headset detected
+ *     0x03    = stereo + cellurar headset detected
+ * If headset is not detected 0 is returned.
+ */
+u16 get_headset_detected(void)
+{
+       u16     curDetected;
+       u16     curType;
+       u16     curVal;
+       
+       curType = 0;    /* not detected */
+       curVal  = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                                       TSC2101_AUDIO_CTRL_7);
+       curDetected     = curVal & AC7_HDDETFL;
+       if (curDetected) {
+               printk("headset detected, checking type from %d \n", curVal);
+               curType = ((curVal & 0x6000) >> 13);
+               printk("headset type detected = %d \n", curType);
+       }
+       else {
+               printk("headset not detected\n");
+       }
+       return curType;
+}
+
+void init_playback_targets(void)
+{
+       u16     val;
+
+       set_loudspeaker_to_playback_target();
+       /* Left line input volume control
+        * = SET_LINE in the OSS driver
+        */
+       set_mixer_volume_as_headset_gain_control_volume(DEFAULT_INPUT_VOLUME);
+
+       /* Set headset to be controllable by handset mixer
+        * AGC enable for handset input
+        * Handset input not muted
+        */
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HANDSET_GAIN_CTRL);
+       val     = val | HNGC_AGCEN_HND; 
+       val     = val & ~HNGC_ADMUT_HND;
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                       TSC2101_HANDSET_GAIN_CTRL,
+                       val);   
+                       
+       /* mic input volume control
+        * SET_MIC in the OSS driver 
+        */
+       set_mixer_volume_as_handset_gain_control_volume(DEFAULT_INPUT_VOLUME);
+
+       /* Left/Right headphone channel volume control
+        * Zero-cross detect on
+        */
+       set_mixer_volume_as_dac_gain_control_volume(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);      
+       /* unmute */
+       dac_gain_control_unmute_control(1, 1);
+}
+
+/*
+ * Initializes tsc2101 recourd source (to line) and playback target (to loudspeaker)
+ */
+void snd_omap_init_mixer(void)
+{      
+       FN_IN;
+       
+       /* Headset/Hook switch detect enabled */
+       omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+                       TSC2101_AUDIO_CTRL_7,
+                       AC7_DETECT);
+
+       init_record_sources();
+       init_playback_targets();
+
+       FN_OUT(0);
+}
+
+static int __pcm_playback_target_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       static char *texts[PLAYBACK_TARGET_COUNT] = {
+               "Loudspeaker", "Headphone"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = PLAYBACK_TARGET_COUNT;
+       if (uinfo->value.enumerated.item > PLAYBACK_TARGET_COUNT - 1) {
+               uinfo->value.enumerated.item = PLAYBACK_TARGET_COUNT - 1;
+       }
+       strcpy(uinfo->value.enumerated.name,
+               texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int __pcm_playback_target_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       ucontrol->value.integer.value[0] = current_playback_target;
+       return 0;
+}
+
+static int __pcm_playback_target_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       int     retVal;
+       int     curVal;
+       
+       retVal  = 0;
+       curVal  = ucontrol->value.integer.value[0];
+       if ((curVal >= 0) &&
+           (curVal < PLAYBACK_TARGET_COUNT) &&
+           (curVal != current_playback_target)) {              
+               if (curVal == 0) {
+                       set_loudspeaker_to_playback_target();           
+               }
+               else {
+                       set_headphone_to_playback_target();
+               }
+               retVal  = 1;
+       }
+       return retVal;
+}      
+
+static int __pcm_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count                    = 2;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 100;
+       return 0;
+}
+
+/*
+ * Alsa mixer interface function for getting the volume read from the DGC in a 
+ * 0 -100 alsa mixer format.
+ */
+static int __pcm_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 volL;
+       u16 volR;       
+       u16 val;
+       
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+       M_DPRINTK("registry value = %d!\n", val);
+       volL    = DGC_DALVL_EXTRACT(val);
+       volR    = DGC_DARVL_EXTRACT(val);
+       /* make sure that other bits are not on */
+       volL    = volL & ~DGC_DALMU;
+       volR    = volR & ~DGC_DARMU;
+
+       volL    = get_dac_gain_control_volume_as_mixer_volume(volL);
+       volR    = get_dac_gain_control_volume_as_mixer_volume(volR);
+       
+       ucontrol->value.integer.value[0]        = volL; /* L */
+       ucontrol->value.integer.value[1]        = volR; /* R */
+       
+       M_DPRINTK("mixer volume left = %ld, right = %ld\n", ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
+       return 0;
+}
+
+static int __pcm_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       return set_mixer_volume_as_dac_gain_control_volume(ucontrol->value.integer.value[0], 
+                                                       ucontrol->value.integer.value[1]);
+}
+
+static int __pcm_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count                    = 2;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 1;
+       return 0;
+}
+
+/* 
+ * When DGC_DALMU (bit 15) is 1, the left channel is muted.
+ * When DGC_DALMU is 0, left channel is not muted.
+ * Same logic apply also for the right channel.
+ */
+static int __pcm_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+       
+       ucontrol->value.integer.value[0]        = IS_DGC_DALMU_UNMUTED(val);
+       ucontrol->value.integer.value[1]        = IS_DGC_DARMU_UNMUTED(val);
+       return 0;
+}
+
+static int __pcm_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       return dac_gain_control_unmute_control(ucontrol->value.integer.value[0], 
+                                       ucontrol->value.integer.value[1]);
+}
+
+static int __headset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count                    = 1;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 100;
+       return 0;
+}
+
+static int __headset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 val;
+       u16 vol;
+       
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HEADSET_GAIN_CTRL);
+       M_DPRINTK("registry value = %d\n", val);
+       vol     = HGC_ADPGA_HED_EXTRACT(val);
+       vol     = vol & ~HGC_ADMUT_HED;
+
+       vol     = get_headset_gain_control_volume_as_mixer_volume(vol);
+       ucontrol->value.integer.value[0]        = vol;
+       
+       M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+static int __headset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       return set_mixer_volume_as_headset_gain_control_volume(ucontrol->value.integer.value[0]);       
+}
+
+static int __headset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count                    = 1;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 1;
+       return 0;
+}
+
+/* When HGC_ADMUT_HED (bit 15) is 1, the headset is muted.
+ * When HGC_ADMUT_HED is 0, headset is not muted.
+ */
+static int __headset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HEADSET_GAIN_CTRL);
+       ucontrol->value.integer.value[0]        = IS_DGC_HGCMU_UNMUTED(val);
+       return 0;
+}
+
+static int __headset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       int count = 0;
+       u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HEADSET_GAIN_CTRL);
+       /* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+        * so if values are same, it's time to change the registry value...
+        */
+       if (ucontrol->value.integer.value[0] == GET_DGC_HGCMU_BIT_VALUE(val)) {
+               if (ucontrol->value.integer.value[0] == 0) {
+                       /* mute --> turn bit on */
+                       val     = val | HGC_ADMUT_HED;
+               }
+               else {
+                       /* unmute --> turn bit off */
+                       val     = val & ~HGC_ADMUT_HED;
+               }
+               count++;
+               M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HGCMU_UNMUTED(val));
+       }
+       if (count) {
+               omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HEADSET_GAIN_CTRL, 
+                               val);
+       }
+       return count;
+}
+
+static int __handset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count                    = 1;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 100;
+       return 0;
+}
+
+static int __handset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 val;
+       u16 vol;
+       
+       val     = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+       M_DPRINTK("registry value = %d\n", val);
+       vol     = HNGC_ADPGA_HND_EXTRACT(val);
+       vol     = vol & ~HNGC_ADMUT_HND;
+       vol     = get_handset_gain_control_volume_as_mixer_volume(vol);
+       ucontrol->value.integer.value[0]        = vol;
+       
+       M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+static int __handset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       return set_mixer_volume_as_handset_gain_control_volume(ucontrol->value.integer.value[0]);       
+}
+
+static int __handset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+       uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count                    = 1;
+       uinfo->value.integer.min        = 0;
+       uinfo->value.integer.max        = 1;
+       return 0;
+}
+
+/* When HNGC_ADMUT_HND (bit 15) is 1, the handset is muted.
+ * When HNGC_ADMUT_HND is 0, handset is not muted.
+ */
+static int __handset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+       ucontrol->value.integer.value[0]        = IS_DGC_HNGCMU_UNMUTED(val);
+       return 0;
+}
+
+static int __handset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       int count = 0;
+       u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+       
+       /* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+        * so if values are same, it's time to change the registry value...
+        */
+       if (ucontrol->value.integer.value[0] == GET_DGC_HNGCMU_BIT_VALUE(val)) {
+               if (ucontrol->value.integer.value[0] == 0) {
+                       /* mute --> turn bit on */
+                       val     = val | HNGC_ADMUT_HND;
+               }
+               else {
+                       /* unmute --> turn bit off */
+                       val     = val & ~HNGC_ADMUT_HND;
+               }
+               M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HNGCMU_UNMUTED(val));
+               count++;
+       }
+       if (count) {
+               omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+                               TSC2101_HANDSET_GAIN_CTRL, 
+                               val);
+       }
+       return count;
+}
+
+static snd_kcontrol_new_t tsc2101_control[] __devinitdata = {
+       {
+               .name  = "Playback Playback Route",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 0,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __pcm_playback_target_info,
+               .get   = __pcm_playback_target_get,
+               .put   = __pcm_playback_target_put,
+       }, {
+               .name  = "Master Playback Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 0,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __pcm_playback_volume_info,
+               .get   = __pcm_playback_volume_get,
+               .put   = __pcm_playback_volume_put,
+       }, {
+               .name  = "Master Playback Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 0,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __pcm_playback_switch_info,
+               .get   = __pcm_playback_switch_get,
+               .put   = __pcm_playback_switch_put,
+       }, {
+               .name  = "Headset Playback Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 1,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __headset_playback_volume_info,
+               .get   = __headset_playback_volume_get,
+               .put   = __headset_playback_volume_put,
+       }, {
+               .name  = "Headset Playback Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 1,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __headset_playback_switch_info,
+               .get   = __headset_playback_switch_get,
+               .put   = __headset_playback_switch_put,
+       }, {
+               .name  = "Handset Playback Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 2,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __handset_playback_volume_info,
+               .get   = __handset_playback_volume_get,
+               .put   = __handset_playback_volume_put,
+       }, {
+               .name  = "Handset Playback Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .index = 2,
+               .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info  = __handset_playback_switch_info,
+               .get   = __handset_playback_switch_get,
+               .put   = __handset_playback_switch_put,
+       }       
+};
+
+#ifdef CONFIG_PM
+
+void snd_omap_suspend_mixer(void)
+{
+}
+
+void snd_omap_resume_mixer(void)
+{
+       snd_omap_init_mixer();
+}
+#endif
+
+int snd_omap_mixer(struct snd_card_omap_codec *tsc2101) 
+{
+       int i=0;
+       int err=0;
+
+       if (!tsc2101) {
+               return -EINVAL;
+       }
+       for (i=0; i < ARRAY_SIZE(tsc2101_control); i++) {
+               if ((err = snd_ctl_add(tsc2101->card, 
+                               snd_ctl_new1(&tsc2101_control[i], 
+                               tsc2101->card))) < 0) {
+                       return err;
+               }
+       }
+       return 0;
+}
diff --git a/sound/arm/omap/omap-alsa-tsc2101-mixer.h b/sound/arm/omap/omap-alsa-tsc2101-mixer.h
new file mode 100644 (file)
index 0000000..f68f33d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2101-mixer.c
+ * 
+ * Alsa Driver for TSC2101 codec for OMAP platform boards.
+ *
+ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and 
+ *                  Everett Coleman II <gcc80x86@fuzzyneural.net>
+ *
+ * Based on the ideas in omap-aic23.c and sa11xx-uda1341.c
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2006-03-01   Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
+ *             Can switch between headset and loudspeaker playback, 
+ *             mute and unmute dgc, set dgc volume. Record source switch,
+ *             keyclick, buzzer and headset volume and handset volume control 
+ *             are still missing.
+ */
+
+#ifndef OMAPALSATSC2101MIXER_H_
+#define OMAPALSATSC2101MIXER_H_
+
+#include <asm/hardware/tsc2101.h>
+#include <../drivers/ssi/omap-tsc2101.h>
+#include "omap-alsa-dma.h"
+
+/* tsc2101 DAC gain control volume specific  */
+#define OUTPUT_VOLUME_MIN              0x7F    // 1111111 = -63.5 DB
+#define OUTPUT_VOLUME_MAX              0x32    // 110010
+#define OUTPUT_VOLUME_RANGE            (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
+
+/* use input vol of 75 for 0dB gain */
+#define INPUT_VOLUME_MIN               0x0
+#define INPUT_VOLUME_MAX               0x7D
+#define INPUT_VOLUME_RANGE             (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+
+#define PLAYBACK_TARGET_COUNT          0x02
+#define PLAYBACK_TARGET_LOUDSPEAKER    0x00
+#define PLAYBACK_TARGET_HEADPHONE      0x01
+
+/* following are used for register 03h Mixer PGA control bits D7-D5 for selecting record source */
+#define REC_SRC_TARGET_COUNT           0x08
+#define REC_SRC_SINGLE_ENDED_MICIN_HED MPC_MICSEL(0)   // oss code referred to MIXER_LINE
+#define REC_SRC_SINGLE_ENDED_MICIN_HND MPC_MICSEL(1)   // oss code referred to MIXER_MIC
+#define REC_SRC_SINGLE_ENDED_AUX1      MPC_MICSEL(2)
+#define REC_SRC_SINGLE_ENDED_AUX2      MPC_MICSEL(3)
+#define REC_SRC_MICIN_HED_AND_AUX1     MPC_MICSEL(4)
+#define REC_SRC_MICIN_HED_AND_AUX2     MPC_MICSEL(5)
+#define REC_SRC_MICIN_HND_AND_AUX1     MPC_MICSEL(6)
+#define REC_SRC_MICIN_HND_AND_AUX2     MPC_MICSEL(7)
+
+#define DEFAULT_OUTPUT_VOLUME          90      // default output volume to dac dgc
+#define DEFAULT_INPUT_VOLUME           20      // default record volume
+
+#define TSC2101_AUDIO_CODEC_REGISTERS_PAGE2     (2)
+
+#endif /*OMAPALSATSC2101MIXER_H_*/
diff --git a/sound/arm/omap/omap-alsa-tsc2101.c b/sound/arm/omap/omap-alsa-tsc2101.c
new file mode 100644 (file)
index 0000000..a0096cb
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * arch/arm/mach-omap1/omap-alsa-tsc2101.c
+ * 
+ * Alsa codec Driver for TSC2101 chip for OMAP platform boards. 
+ * Code obtained from oss omap drivers
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *     Written by Nishanth Menon and Sriram Kannan
+ *     
+ * Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ *     Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
+ * 
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * 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/delay.h>
+#include <linux/soundcard.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <asm/arch/mcbsp.h>
+
+#include <linux/slab.h>
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+#include <asm/mach-types.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/clock.h>
+
+#include <asm/hardware/tsc2101.h>
+#include <../drivers/ssi/omap-tsc2101.h>
+
+#include <asm/arch/omap-alsa.h>
+#include "omap-alsa-tsc2101.h"
+
+static struct clk *tsc2101_mclk = 0;
+
+//#define DUMP_TSC2101_AUDIO_REGISTERS
+#undef DUMP_TSC2101_AUDIO_REGISTERS
+
+/*
+ * Hardware capabilities 
+ */
+
+/*
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+       7350, 8000, 8018, 8727,
+       8820, 9600, 11025, 12000,
+       14700, 16000, 22050, 24000,
+       29400, 32000, 44100, 48000,
+};
+
+static snd_pcm_hw_constraint_list_t tsc2101_hw_constraints_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list = rates,
+       .mask = 0,
+};
+
+static const struct tsc2101_samplerate_reg_info
+    rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+       /* Div 6 */
+       {7350, 7, 1},
+       {8000, 7, 0},
+       /* Div 5.5 */
+       {8018, 6, 1},
+       {8727, 6, 0},
+       /* Div 5 */
+       {8820, 5, 1},
+       {9600, 5, 0},   
+       /* Div 4 */
+       {11025, 4, 1},
+       {12000, 4, 0},
+       /* Div 3 */
+       {14700, 3, 1},
+       {16000, 3, 0},
+       /* Div 2 */
+       {22050, 2, 1},
+       {24000, 2, 0},
+       /* Div 1.5 */
+       {29400, 1, 1},
+       {32000, 1, 0},
+       /* Div 1 */
+       {44100, 0, 1},
+       {48000, 0, 0},          
+};
+
+static snd_pcm_hardware_t tsc2101_snd_omap_alsa_playback = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),      
+       .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+       .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 |
+                 SNDRV_PCM_RATE_KNOT),
+       .rate_min = 7350,
+       .rate_max = 48000,
+       .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,
+};
+
+static snd_pcm_hardware_t tsc2101_snd_omap_alsa_capture = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+       .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 |
+                 SNDRV_PCM_RATE_KNOT),
+       .rate_min = 7350,
+       .rate_max = 48000,
+       .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,
+};
+
+/* 
+ * Simplified write for tsc Audio
+ */
+inline void tsc2101_audio_write(u8 address, u16 data)
+{
+       omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data);
+}
+
+/* 
+ * Simplified read for tsc  Audio
+ */
+inline u16 tsc2101_audio_read(u8 address)
+{
+       return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address));
+}
+
+#ifdef DUMP_TSC2101_AUDIO_REGISTERS
+void dump_tsc2101_audio_reg(void) {
+       printk("TSC2101_AUDIO_CTRL_1 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_1));
+       printk("TSC2101_HEADSET_GAIN_CTRL = 0x%04x\n",  tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL));
+       printk("TSC2101_DAC_GAIN_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL));
+       printk("TSC2101_MIXER_PGA_CTRL = 0x%04x\n",     tsc2101_audio_read(TSC2101_MIXER_PGA_CTRL));
+       printk("TSC2101_AUDIO_CTRL_2 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_2));
+       printk("TSC2101_CODEC_POWER_CTRL = 0x%04x\n",   tsc2101_audio_read(TSC2101_CODEC_POWER_CTRL));
+       printk("TSC2101_AUDIO_CTRL_3 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_3));
+       printk("TSC2101_LCH_BASS_BOOST_N0 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N0));
+       printk("TSC2101_LCH_BASS_BOOST_N1 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N1));
+       printk("TSC2101_LCH_BASS_BOOST_N2 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N2));
+       printk("TSC2101_LCH_BASS_BOOST_N3 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N3));
+       printk("TSC2101_LCH_BASS_BOOST_N4 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N4));
+       printk("TSC2101_LCH_BASS_BOOST_N5 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N5));
+       printk("TSC2101_LCH_BASS_BOOST_D1 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D1));
+       printk("TSC2101_LCH_BASS_BOOST_D2 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D2));
+       printk("TSC2101_LCH_BASS_BOOST_D4 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D4));
+       printk("TSC2101_LCH_BASS_BOOST_D5 = 0x%04x\n",  tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D5));
+       
+       printk("TSC2101_RCH_BASS_BOOST_N0 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N0));
+       printk("TSC2101_RCH_BASS_BOOST_N1 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N1));
+       printk("TSC2101_RCH_BASS_BOOST_N2 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N2));
+       printk("TSC2101_RCH_BASS_BOOST_N3 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N3));
+       printk("TSC2101_RCH_BASS_BOOST_N4 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N4));
+       printk("TSC2101_RCH_BASS_BOOST_N5 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N5));
+       printk("TSC2101_RCH_BASS_BOOST_D1 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D1));
+       printk("TSC2101_RCH_BASS_BOOST_D2 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D2));
+       printk("TSC2101_RCH_BASS_BOOST_D4 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D4));
+       printk("TSC2101_RCH_BASS_BOOST_D5 = 0x%04x\n",  tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D5));
+                                       
+       printk("TSC2101_PLL_PROG_1 = 0x%04x\n", tsc2101_audio_read(TSC2101_PLL_PROG_1));
+       printk("TSC2101_PLL_PROG_1 = 0x%04x\n", tsc2101_audio_read(TSC2101_PLL_PROG_2));
+       printk("TSC2101_AUDIO_CTRL_4 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_4));
+       printk("TSC2101_HANDSET_GAIN_CTRL = 0x%04x\n",  tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL));
+       printk("TSC2101_BUZZER_GAIN_CTRL = 0x%04x\n",   tsc2101_audio_read(TSC2101_BUZZER_GAIN_CTRL));
+       printk("TSC2101_AUDIO_CTRL_5 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_5));
+       printk("TSC2101_AUDIO_CTRL_6 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_6));
+       printk("TSC2101_AUDIO_CTRL_7 = 0x%04x\n",       tsc2101_audio_read(TSC2101_AUDIO_CTRL_7));
+       printk("TSC2101_GPIO_CTRL = 0x%04x\n",  tsc2101_audio_read(TSC2101_GPIO_CTRL));
+       printk("TSC2101_AGC_CTRL = 0x%04x\n",   tsc2101_audio_read(TSC2101_AGC_CTRL));
+       printk("TSC2101_POWERDOWN_STS = 0x%04x\n",      tsc2101_audio_read(TSC2101_POWERDOWN_STS));
+       printk("TSC2101_MIC_AGC_CONTROL = 0x%04x\n",    tsc2101_audio_read(TSC2101_MIC_AGC_CONTROL));
+       printk("TSC2101_CELL_AGC_CONTROL = 0x%04x\n",   tsc2101_audio_read(TSC2101_CELL_AGC_CONTROL));
+}
+#endif
+
+/*
+ * ALSA operations according to board file
+ */
+
+/*
+ * Sample rate changing
+ */
+void tsc2101_set_samplerate(long sample_rate)
+{
+       u8 count = 0;
+       u16 data = 0;
+       int clkgdv = 0;
+
+       u16 srgr1, srgr2;
+       /* wait for any frame to complete */
+       udelay(125);
+       ADEBUG();
+
+       sample_rate     = sample_rate;
+       /* Search for the right sample rate */
+       while ((rate_reg_info[count].sample_rate != sample_rate) &&
+              (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+               count++;
+       }
+       if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+               printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+                      (int) sample_rate);
+               return;         // -EPERM;
+       }
+
+       /* Set AC1 */
+       data    = tsc2101_audio_read(TSC2101_AUDIO_CTRL_1);
+       /* Clear prev settings */
+       data    &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
+       data    |= AC1_DACFS(rate_reg_info[count].divisor) | 
+                       AC1_ADCFS(rate_reg_info[count].divisor);
+       tsc2101_audio_write(TSC2101_AUDIO_CTRL_1, data);
+
+       /* Set the AC3 */
+       data    = tsc2101_audio_read(TSC2101_AUDIO_CTRL_3);
+       /*Clear prev settings */
+       data    &= ~(AC3_REFFS | AC3_SLVMS);
+       data    |= (rate_reg_info[count].fs_44kHz) ? AC3_REFFS : 0;
+#ifdef TSC_MASTER
+       data    |= AC3_SLVMS;
+#endif                         /* #ifdef TSC_MASTER */
+       tsc2101_audio_write(TSC2101_AUDIO_CTRL_3, data);
+
+       /* program the PLLs */
+       if (rate_reg_info[count].fs_44kHz) {
+               /* 44.1 khz - 12 MHz Mclk */
+               tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
+                               PLL1_PVAL(1) | PLL1_I_VAL(7));  /* PVAL 1; I_VAL 7 */
+               tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490));    /* D_VAL 5264 */
+       } else {
+               /* 48 khz - 12 Mhz Mclk */
+               tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
+                              PLL1_PVAL(1) | PLL1_I_VAL(8));   /* PVAL 1; I_VAL 8 */
+               tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780));     /* D_VAL 1920 */
+       }
+
+       /* Set the sample rate */
+#ifndef TSC_MASTER
+       clkgdv  = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+       if (clkgdv)
+               srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+       else
+               return (1);
+
+       /* Stereo Mode */
+       srgr2 = (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+#else
+       srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+       srgr2 = ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+#endif                         /* end of #ifdef TSC_MASTER */
+       OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR2, srgr2);
+       OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR1, srgr1);
+}
+
+void tsc2101_configure(void)
+{
+}
+
+/*
+ *  Omap MCBSP clock and Power Management configuration
+ *  
+ *  Here we have some functions that allows clock to be enabled and
+ *   disabled only when needed. Besides doing clock configuration 
+ *   it allows turn on/turn off audio when necessary. 
+ */
+/*
+ * Do clock framework mclk search
+ */
+void tsc2101_clock_setup(void)
+{
+       tsc2101_mclk = clk_get(0, "mclk");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and turn codec audio on
+ */
+int tsc2101_clock_on(void) 
+{
+       int     curUseCount;
+       uint    curRate;
+       int     err;
+
+       curUseCount     = clk_get_usecount(tsc2101_mclk);
+       DPRINTK("clock use count = %d\n", curUseCount);
+       if (curUseCount > 0) {
+               // MCLK is already in use
+               printk(KERN_WARNING
+                      "MCLK already in use at %d Hz. We change it to %d Hz\n",
+                      (uint) clk_get_rate(tsc2101_mclk),
+                      CODEC_CLOCK);
+       }
+       curRate = (uint)clk_get_rate(tsc2101_mclk);
+       DPRINTK("old clock rate = %d\n", curRate);
+       if (curRate != CODEC_CLOCK) {
+               err     = clk_set_rate(tsc2101_mclk, CODEC_CLOCK);
+               if (err) {
+                       printk(KERN_WARNING
+                              "Cannot set MCLK clock rate for TSC2101 CODEC, error code = %d\n", err);
+                       //return -ECANCELED;
+               }
+       }
+       else
+       {
+               printk(KERN_INFO
+                      "omap_alsa_tsc2101_clock_on(), no need to change rate, no need to change clock rate, rate already %d Hz.\n",
+                      CODEC_CLOCK);
+       }
+       err             = clk_enable(tsc2101_mclk);
+       curRate         = (uint)clk_get_rate(tsc2101_mclk);
+       curUseCount     = clk_get_usecount(tsc2101_mclk);
+       DPRINTK("MCLK = %d [%d], usecount = %d, clk_enable retval = %d\n",
+              curRate, 
+              CODEC_CLOCK,
+              curUseCount,
+              err);
+
+       // Now turn the audio on
+       omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS,
+                       TSC2101_CODEC_POWER_CTRL,
+                       0x0000);        
+       return 0;       
+}
+
+/*
+ * Do some sanity check, turn clock off and then turn
+ *  codec audio off
+ */
+int tsc2101_clock_off(void) 
+{
+       int curUseCount;
+       int curRate;
+
+       curUseCount     = clk_get_usecount(tsc2101_mclk);
+       DPRINTK("clock use count = %d\n", curUseCount);
+       if  (curUseCount > 0) {
+               curRate = clk_get_rate(tsc2101_mclk);
+               DPRINTK("clock rate = %d\n", curRate);
+               if (curRate != CODEC_CLOCK) {
+                       printk(KERN_WARNING
+                              "MCLK for audio should be %d Hz. But is %d Hz\n",
+                              (uint) clk_get_rate(tsc2101_mclk),
+                              CODEC_CLOCK);
+               }
+               clk_disable(tsc2101_mclk);
+               DPRINTK("clock disabled\n");
+       }
+       tsc2101_audio_write(TSC2101_CODEC_POWER_CTRL,
+                           ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+       DPRINTK("audio codec off\n");
+#ifdef DUMP_TSC2101_AUDIO_REGISTERS
+       printk("tsc2101_clock_off()\n");
+       dump_tsc2101_audio_reg();
+#endif 
+       return 0;       
+}
+
+int tsc2101_get_default_samplerate(void)
+{
+       return DEFAULT_SAMPLE_RATE;
+}
+
+static int __init snd_omap_alsa_tsc2101_probe(struct platform_device *pdev)
+{
+       int     ret;
+       struct  omap_alsa_codec_config *codec_cfg;
+       
+       codec_cfg = pdev->dev.platform_data;
+       if (codec_cfg != NULL) {
+               codec_cfg->hw_constraints_rates = &tsc2101_hw_constraints_rates;
+               codec_cfg->snd_omap_alsa_playback  = &tsc2101_snd_omap_alsa_playback;
+               codec_cfg->snd_omap_alsa_capture  = &tsc2101_snd_omap_alsa_capture;
+               codec_cfg->codec_configure_dev  = tsc2101_configure;
+               codec_cfg->codec_set_samplerate = tsc2101_set_samplerate;
+               codec_cfg->codec_clock_setup    = tsc2101_clock_setup;
+               codec_cfg->codec_clock_on       = tsc2101_clock_on;
+               codec_cfg->codec_clock_off      = tsc2101_clock_off;
+               codec_cfg->get_default_samplerate = tsc2101_get_default_samplerate;
+               ret     = snd_omap_alsa_post_probe(pdev, codec_cfg);
+       }
+       else
+               ret = -ENODEV;
+       return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+       .probe          = snd_omap_alsa_tsc2101_probe,
+       .remove         = snd_omap_alsa_remove,
+       .suspend        = snd_omap_alsa_suspend,
+       .resume         = snd_omap_alsa_resume,
+       .driver = {
+               .name = "omap_alsa_mcbsp",
+       },
+};
+
+static int __init omap_alsa_tsc2101_init(void)
+{
+       int err;
+       
+       ADEBUG();
+       err = platform_driver_register(&omap_alsa_driver);
+
+       return err;
+}
+
+static void __exit omap_alsa_tsc2101_exit(void)
+{
+       ADEBUG();
+       platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_tsc2101_init);
+module_exit(omap_alsa_tsc2101_exit);
diff --git a/sound/arm/omap/omap-alsa-tsc2101.h b/sound/arm/omap/omap-alsa-tsc2101.h
new file mode 100644 (file)
index 0000000..803d215
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * arch/arc/mach-omap1/omap-alsa-tsc2101.h
+ * 
+ * Alsa Driver for TSC2101 codec for OMAP platform boards.
+ *
+ * Based on former omap-aic23.h and tsc2101 OSS drivers.
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *     Written by Nishanth Menon and Sriram Kannan
+ *
+ * Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ *          Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
+ * 
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * 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 OMAP_ALSA_TSC2101_H_
+#define OMAP_ALSA_TSC2101_H_
+
+#include <linux/types.h>
+
+/* Define to set the tsc as the master w.r.t McBSP */
+#define TSC_MASTER
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED  16
+
+/*
+ * AUDIO related MACROS
+ */
+#ifndef DEFAULT_BITPERSAMPLE
+#define DEFAULT_BITPERSAMPLE           16
+#endif
+
+#define DEFAULT_SAMPLE_RATE            44100
+#define CODEC_CLOCK                    12000000
+#define AUDIO_MCBSP                    OMAP_MCBSP1
+
+#define PAGE2_AUDIO_CODEC_REGISTERS    (2)
+
+struct tsc2101_samplerate_reg_info {
+       u16 sample_rate;
+       u8 divisor;
+       u8 fs_44kHz;    /* if 0 48 khz, if 1 44.1 khz fsref */
+};
+
+/*
+ * Defines codec specific functions pointers that can be used from the 
+ * common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
+ */
+inline void tsc2101_configure(void);
+void tsc2101_set_samplerate(long rate);
+void tsc2101_clock_setup(void);
+int tsc2101_clock_on(void);
+int tsc2101_clock_off(void);
+int tsc2101_get_default_samplerate(void);
+
+#endif /*OMAP_ALSA_TSC2101_H_*/
diff --git a/sound/arm/omap/omap-alsa.c b/sound/arm/omap/omap-alsa.c
new file mode 100644 (file)
index 0000000..328003b
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * sound/arm/omap-alsa.c
+ * 
+ * Alsa Driver for OMAP
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ *            {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * Based on sa11xx-uda1341.c, 
+ * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2005-07-29   INdT Kernel Team - Alsa driver for omap osk. Creation of new 
+ *                                 file omap-aic23.c
+ * 
+ * 2005-12-18   Dirk Behme       - Added L/R Channel Interchange fix as proposed 
+ *                                 by Ajaya Babu
+ *
+ */
+
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+#include <sound/driver.h>
+#include <sound/core.h>
+
+#include <asm/arch/omap-alsa.h>
+#include "omap-alsa-dma.h"
+
+MODULE_AUTHOR("Mika Laitio, Daniel Petrini, David Cohen, Anderson Briglia - INdT");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OMAP driver for ALSA");
+MODULE_ALIAS("omap_alsa_mcbsp.1");
+
+static char *id        = NULL; 
+static struct snd_card_omap_codec      *alsa_codec             = NULL;
+static struct omap_alsa_codec_config   *alsa_codec_config      = NULL;
+
+/*
+ * HW interface start and stop helper functions
+ */
+static int audio_ifc_start(void)
+{
+       omap_mcbsp_start(AUDIO_MCBSP);
+       return 0;
+}
+
+static int audio_ifc_stop(void)
+{
+       omap_mcbsp_stop(AUDIO_MCBSP);
+       return 0;
+}
+
+static void omap_alsa_audio_init(struct snd_card_omap_codec *omap_alsa)
+{
+       /* Setup DMA stuff */
+       omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa omap out";
+       omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
+           SNDRV_PCM_STREAM_PLAYBACK;
+       omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
+           OMAP_DMA_MCBSP1_TX;
+       omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
+           audio_ifc_start;
+       omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
+           audio_ifc_stop;
+
+       omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa omap in";
+       omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
+           SNDRV_PCM_STREAM_CAPTURE;
+       omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
+           OMAP_DMA_MCBSP1_RX;
+       omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
+           audio_ifc_start;
+       omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
+           audio_ifc_stop;
+}
+
+/* 
+ * DMA functions 
+ * Depends on omap-alsa-dma.c functions and (omap) dma.c
+ * 
+ */
+static int audio_dma_request(struct audio_stream *s,
+                            void (*callback) (void *))
+{
+       int err;
+       ADEBUG();
+
+       err = omap_request_alsa_sound_dma(s->dma_dev, s->id, s, &s->lch);
+       if (err < 0)
+               printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dma_dev);
+       return err;
+}
+
+static int audio_dma_free(struct audio_stream *s)
+{
+       int err = 0;
+       ADEBUG();
+
+       err = omap_free_alsa_sound_dma(s, &s->lch);
+       if (err < 0)
+               printk(KERN_ERR "Unable to free audio dma channels!\n");
+       return err;
+}
+
+/*
+ *  This function should calculate the current position of the dma in the
+ *  buffer. It will help alsa middle layer to continue update the buffer.
+ *  Its correctness is crucial for good functioning.
+ */
+static u_int audio_get_dma_pos(struct audio_stream *s)
+{
+       snd_pcm_substream_t *substream = s->stream;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       unsigned int offset;
+       unsigned long flags;
+       dma_addr_t count;
+       ADEBUG();
+
+       /* this must be called w/ interrupts locked as requested in dma.c */
+       spin_lock_irqsave(&s->dma_lock, flags);
+
+       /* For the current period let's see where we are */
+       count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]);
+
+       spin_unlock_irqrestore(&s->dma_lock, flags);
+
+       /* Now, the position related to the end of that period */
+       offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count);
+
+       if (offset >= runtime->buffer_size)
+               offset = 0;
+
+       return offset;
+}
+
+/*
+ * this stops the dma and clears the dma ptrs
+ */
+static void audio_stop_dma(struct audio_stream *s)
+{
+       unsigned long flags;
+       ADEBUG();
+
+       spin_lock_irqsave(&s->dma_lock, flags);
+       s->active = 0;
+       s->period = 0;
+       s->periods = 0;
+
+       /* this stops the dma channel and clears the buffer ptrs */
+       omap_stop_alsa_sound_dma(s);
+
+       omap_clear_alsa_sound_dma(s);
+
+       spin_unlock_irqrestore(&s->dma_lock, flags);
+}
+
+/*
+ *  Main dma routine, requests dma according where you are in main alsa buffer
+ */
+static void audio_process_dma(struct audio_stream *s)
+{
+       snd_pcm_substream_t *substream = s->stream;
+       snd_pcm_runtime_t *runtime;
+       unsigned int dma_size;
+       unsigned int offset;
+       int ret;
+#ifdef CONFIG_MACH_OMAP_H6300
+       unsigned long flags;
+#endif
+       
+       ADEBUG();
+       runtime = substream->runtime;
+       if (s->active) {
+               dma_size = frames_to_bytes(runtime, runtime->period_size);
+               offset = dma_size * s->period;
+               snd_assert(dma_size <= DMA_BUF_SIZE,);
+#ifdef CONFIG_MACH_OMAP_H6300
+               spin_lock_irqsave(&s->dma_lock, flags);
+               omap_stop_alsa_sound_dma(s);
+               spin_unlock_irqrestore(&s->dma_lock, flags);
+#endif
+               ret = omap_start_alsa_sound_dma(s,
+                                        (dma_addr_t) runtime->dma_area +
+                                        offset, dma_size);
+               if (ret) {
+                       printk(KERN_ERR
+                              "audio_process_dma: cannot queue DMA buffer (%i)\n",
+                              ret);
+                       return;
+               }
+
+               s->period++;
+               s->period %= runtime->periods;
+               s->periods++;
+               s->offset = offset;
+       }
+}
+
+/* 
+ *  This is called when dma IRQ occurs at the end of each transmited block
+ */
+void callback_omap_alsa_sound_dma(void *data)
+{
+       struct audio_stream *s = data;
+       
+       ADEBUG();
+       /* 
+        * If we are getting a callback for an active stream then we inform
+        * the PCM middle layer we've finished a period
+        */
+       if (s->active)
+               snd_pcm_period_elapsed(s->stream);
+
+       spin_lock(&s->dma_lock);
+       if (s->periods > 0) 
+               s->periods--;
+       
+       audio_process_dma(s);
+       spin_unlock(&s->dma_lock);
+}
+
+/* 
+ * Alsa section
+ * PCM settings and callbacks
+ */
+static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       struct snd_card_omap_codec *chip =
+           snd_pcm_substream_chip(substream);
+       int stream_id = substream->pstr->stream;
+       struct audio_stream *s = &chip->s[stream_id];
+       int err = 0;
+       
+       ADEBUG();
+       /* note local interrupts are already disabled in the midlevel code */
+       spin_lock(&s->dma_lock);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               /* requested stream startup */
+               s->active = 1;
+               audio_process_dma(s);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               /* requested stream shutdown */
+               audio_stop_dma(s);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       spin_unlock(&s->dma_lock);
+       
+       return err;
+}
+
+static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream)
+{
+       struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct audio_stream *s = &chip->s[substream->pstr->stream];
+       
+       ADEBUG();
+       /* set requested samplerate */
+       alsa_codec_config->codec_set_samplerate(runtime->rate);
+       chip->samplerate = runtime->rate;
+
+       s->period = 0;
+       s->periods = 0;
+
+       return 0;
+}
+
+static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t *substream)
+{
+       struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+
+       ADEBUG();       
+       return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
+}
+
+static int snd_card_omap_alsa_open(snd_pcm_substream_t * substream)
+{
+       struct snd_card_omap_codec *chip =
+           snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int stream_id = substream->pstr->stream;
+       int err;
+       
+       ADEBUG();
+       chip->s[stream_id].stream = substream;
+       alsa_codec_config->codec_clock_on();
+       if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) 
+               runtime->hw = *(alsa_codec_config->snd_omap_alsa_playback);
+       else 
+               runtime->hw = *(alsa_codec_config->snd_omap_alsa_capture);
+       
+       if ((err = snd_pcm_hw_constraint_integer(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 
+               return err;
+       
+       if ((err = snd_pcm_hw_constraint_list(runtime,
+                                       0,
+                                       SNDRV_PCM_HW_PARAM_RATE,
+                                       alsa_codec_config->hw_constraints_rates)) < 0) 
+               return err;
+       
+       return 0;
+}
+
+static int snd_card_omap_alsa_close(snd_pcm_substream_t * substream)
+{
+       struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+       
+       ADEBUG();
+       alsa_codec_config->codec_clock_off();
+       chip->s[substream->pstr->stream].stream = NULL;
+       
+       return 0;
+}
+
+/* HW params & free */
+static int snd_omap_alsa_hw_params(snd_pcm_substream_t * substream,
+                                   snd_pcm_hw_params_t * hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int snd_omap_alsa_hw_free(snd_pcm_substream_t * substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* pcm operations */
+static snd_pcm_ops_t snd_card_omap_alsa_playback_ops = {
+       .open =         snd_card_omap_alsa_open,
+       .close =        snd_card_omap_alsa_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_omap_alsa_hw_params,
+       .hw_free =      snd_omap_alsa_hw_free,
+       .prepare =      snd_omap_alsa_prepare,
+       .trigger =      snd_omap_alsa_trigger,
+       .pointer =      snd_omap_alsa_pointer,
+};
+
+static snd_pcm_ops_t snd_card_omap_alsa_capture_ops = {
+       .open =         snd_card_omap_alsa_open,
+       .close =        snd_card_omap_alsa_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_omap_alsa_hw_params,
+       .hw_free =      snd_omap_alsa_hw_free,
+       .prepare =      snd_omap_alsa_prepare,
+       .trigger =      snd_omap_alsa_trigger,
+       .pointer =      snd_omap_alsa_pointer,
+};
+
+/*
+ *  Alsa init and exit section
+ *  
+ *  Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
+ */
+static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa, 
+                                       int device)
+{
+       snd_pcm_t *pcm;
+       int err;
+       
+       ADEBUG();
+       if ((err = snd_pcm_new(omap_alsa->card, "OMAP PCM", device, 1, 1, &pcm)) < 0)
+               return err;
+
+       /* sets up initial buffer with continuous allocation */
+       snd_pcm_lib_preallocate_pages_for_all(pcm,
+                                             SNDRV_DMA_TYPE_CONTINUOUS,
+                                             snd_dma_continuous_data
+                                             (GFP_KERNEL),
+                                             128 * 1024, 128 * 1024);
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_card_omap_alsa_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                       &snd_card_omap_alsa_capture_ops);
+       pcm->private_data = omap_alsa;
+       pcm->info_flags = 0;
+       strcpy(pcm->name, "omap alsa pcm");
+
+       omap_alsa_audio_init(omap_alsa);
+
+       /* setup DMA controller */
+       audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK],
+                         callback_omap_alsa_sound_dma);
+       audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE],
+                         callback_omap_alsa_sound_dma);
+
+       omap_alsa->pcm = pcm;
+
+       return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * Driver suspend/resume - calls alsa functions. Some hints from aaci.c
+ */
+int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_card_omap_codec *chip;
+       snd_card_t *card = platform_get_drvdata(pdev);
+       
+       if (card->power_state != SNDRV_CTL_POWER_D3hot) {
+               chip = card->private_data;
+               if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) {
+                       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+                       snd_pcm_suspend_all(chip->pcm);
+                       /* Mutes and turn clock off */
+                       alsa_codec_config->codec_clock_off();
+                       snd_omap_suspend_mixer();
+               }
+       }
+       return 0;
+}
+
+int snd_omap_alsa_resume(struct platform_device *pdev)
+{
+       struct snd_card_omap_codec *chip;
+       snd_card_t *card = platform_get_drvdata(pdev);
+
+       if (card->power_state != SNDRV_CTL_POWER_D0) {                          
+               chip = card->private_data;
+               if (chip->card->power_state != SNDRV_CTL_POWER_D0) {
+                       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+                       alsa_codec_config->codec_clock_on();
+                       snd_omap_resume_mixer();
+               }
+       }
+       return 0;
+}
+
+#endif /* CONFIG_PM */
+
+void snd_omap_alsa_free(snd_card_t * card)
+{
+       struct snd_card_omap_codec *chip = card->private_data;
+       ADEBUG();
+       
+       /*
+        * Turn off codec after it is done.
+        * Can't do it immediately, since it may still have
+        * buffered data.
+        */
+       schedule_timeout_interruptible(2);
+
+       omap_mcbsp_stop(AUDIO_MCBSP);
+       omap_mcbsp_free(AUDIO_MCBSP);
+
+       audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+       audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+}
+
+/* module init & exit */
+
+/* 
+ * Inits alsa soudcard structure.
+ * Called by the probe method in codec after function pointers has been set.
+ */
+int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config)
+{
+       int err = 0;
+       int def_rate;
+       snd_card_t *card;
+       
+       ADEBUG();
+       alsa_codec_config       = config;
+
+       alsa_codec_config->codec_clock_setup();
+       alsa_codec_config->codec_clock_on(); 
+
+       omap_mcbsp_request(AUDIO_MCBSP);
+       omap_mcbsp_stop(AUDIO_MCBSP);
+       omap_mcbsp_config(AUDIO_MCBSP, alsa_codec_config->mcbsp_regs_alsa);
+       omap_mcbsp_start(AUDIO_MCBSP);
+       
+       if (alsa_codec_config && alsa_codec_config->codec_configure_dev)
+               alsa_codec_config->codec_configure_dev();
+
+       alsa_codec_config->codec_clock_off();
+
+       /* register the soundcard */
+       card = snd_card_new(-1, id, THIS_MODULE, sizeof(alsa_codec));
+       if (card == NULL)
+               goto nodev1;
+
+       alsa_codec = kcalloc(1, sizeof(*alsa_codec), GFP_KERNEL);
+       if (alsa_codec == NULL)
+               goto nodev2;
+
+       card->private_data = (void *)alsa_codec;
+       card->private_free = snd_omap_alsa_free;
+
+       alsa_codec->card        = card;
+       def_rate                = alsa_codec_config->get_default_samplerate(); 
+       alsa_codec->samplerate  = def_rate;
+
+       spin_lock_init(&alsa_codec->s[0].dma_lock);
+       spin_lock_init(&alsa_codec->s[1].dma_lock);
+
+       /* mixer */
+       if ((err = snd_omap_mixer(alsa_codec)) < 0)
+               goto nodev3;
+
+       /* PCM */
+       if ((err = snd_card_omap_alsa_pcm(alsa_codec, 0)) < 0)
+               goto nodev3;
+
+       strcpy(card->driver, "OMAP_ALSA");
+       strcpy(card->shortname, alsa_codec_config->name);
+       sprintf(card->longname, alsa_codec_config->name);
+
+       snd_omap_init_mixer();
+       snd_card_set_dev(card, &pdev->dev);
+       
+       if ((err = snd_card_register(card)) == 0) {
+               printk(KERN_INFO "audio support initialized\n");
+               platform_set_drvdata(pdev, card);
+               return 0;
+       }
+       
+nodev3:
+       kfree(alsa_codec);      
+nodev2:        
+       snd_card_free(card);
+nodev1:
+       omap_mcbsp_stop(AUDIO_MCBSP);
+       omap_mcbsp_free(AUDIO_MCBSP);
+
+       return err;
+}
+
+int snd_omap_alsa_remove(struct platform_device *pdev)
+{
+       snd_card_t *card = platform_get_drvdata(pdev);
+       struct snd_card_omap_codec *chip = card->private_data;
+       
+       snd_card_free(card);
+
+       alsa_codec = NULL;
+       card->private_data = NULL;
+       kfree(chip);
+       
+       platform_set_drvdata(pdev, NULL);
+       
+       return 0;
+}