alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ASoC: Add National Semiconductor LM49352 Audio Codec support
@ 2011-03-07 12:03 Reddy, MR Swami
  2011-03-07 13:18 ` Mark Brown
  2011-03-16  7:38 ` [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support Reddy, MR Swami
  0 siblings, 2 replies; 7+ messages in thread
From: Reddy, MR Swami @ 2011-03-07 12:03 UTC (permalink / raw)
  To: alsa-devel@alsa-project.org; +Cc: Mark Brown, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 492 bytes --]

Hello,

Patch for adding National Semiconductor LM49352 Audio Codec (http://www.national.com/ds/LM/LM49352.pdf) support along with Samsung_6410 (i.e. SMDK6410) platform support for LM49352. 

[Tested this patch on SMDK6410 platform with Kernel- 2.6.24 and ALSA version is - 1.0.15].

Please review and let me know the comments/suggestion on this patch. 
And also let me know the forward-porting (to the latest ALSA version APIs) steps/process. Thanks in advance.


Thanks
Swami


[-- Attachment #2: lm49352_ALSA_Audio_driver_patch.diff --]
[-- Type: application/octet-stream, Size: 58113 bytes --]

diff -Naur sound.old/soc/codecs/Kconfig sound/soc/codecs/Kconfig
--- sound.old/soc/codecs/Kconfig        2011-03-04 19:11:38.000000000 +0530
+++ sound/soc/codecs/Kconfig        2010-03-27 11:33:10.000000000 +0530
@@ -2,6 +2,10 @@
         tristate
         depends on SND_SOC
 
+config SND_SOC_LM49352
+        tristate
+        depends on SND_SOC
+
 config SND_SOC_WM8731
         tristate
         depends on SND_SOC
diff -Naur sound.old/soc/codecs/lm49352.c sound/soc/codecs/lm49352.c
--- sound.old/soc/codecs/lm49352.c        1970-01-01 05:30:00.000000000 +0530
+++ sound/soc/codecs/lm49352.c        2011-03-04 19:16:44.000000000 +0530
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) <2011> National Semiconductor, Inc.
+ * 
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "lm49352.h"
+
+#define AUDIO_NAME "lm49352"
+#define LM49352_VERSION "0.1" 
+int lm49352_write1(struct snd_soc_codec *codec, unsigned int reg, unsigned int value);
+/*
+ * Debug
+ */
+
+
+static int attach_once=0;
+#ifdef LM49352_DEBUG
+#define dbg(format, arg...) \
+        printk(KERN_DEBUG AUDIO_NAME ": " format , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) \
+        printk(KERN_ERR AUDIO_NAME ": " format , ## arg)
+#define info(format, arg...) \
+        printk(KERN_INFO AUDIO_NAME ": " format , ## arg)
+#define warn(format, arg...) \
+        printk(KERN_WARNING AUDIO_NAME ": " format , ## arg)
+
+static const u16 lm49352_reg[] = {                                            /* Register cache for LM49352 driver. */
+        0x0121, 0x017e, 0x007d, 0x0014, /*R3*/
+        0x0121, 0x017e, 0x007d, 0x0194, /*R7*/
+        0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/
+        0x0182, 0x0082, 0x000a, 0x0024, /*R15*/
+        0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/
+        0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/
+        0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/
+        0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/
+        0x0000, 0x0000, 0x0031, 0x000b, /*R35*/
+        0x0039, 0x0000, 0x0010, 0x0032, /*R39*/
+        0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R47*/
+        0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R55*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R59*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R63*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R67*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R71*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R75*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R79*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R83*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R87*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R91*/
+        0x0000, 0x0000, 0x0000, 0x0000,  /*R95*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R99*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R103*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R107*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R111*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R115*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R119*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R123*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R127*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R131*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R135*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R139*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R143*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R147*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R151*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R155*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R159*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R163*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R167*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R171*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R175*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R179*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R183*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R187*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R191*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R193*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R197*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R201*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R205*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R209*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R213*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R217*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R221*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R225*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R229*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R233*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R237*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R241*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R245*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R249*/
+        0x0000, 0x0000, 0x0000, 0x0000, /*R253*/
+        0x0000, 0x0000,  /*R255*/
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name                                                                                                                                  //        
+//        lm49352_read_reg_cache- Reads specific register from reg cache.                                                                  //        
+//Synopsis                                                                                                                          //
+//        static inline unsigned int lm49352_read_reg_cache(struct snd_soc_codec *codec,unsigned int reg)                                  //
+//Arguments                                                                                                                          //
+//        codec- Pointer to the struct snd_soc_codec                                                                                  //
+//      reg- Specific register address                                                                                                    //
+//Decription                                                                                                                          //
+//        In this function a specific register value is read from reg cache and value is returned to the called function. 
+//                                                                                                                                  //                                                                                                                                                                                                                                                                                                                                                                               
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static inline unsigned int lm49352_read_reg_cache(struct snd_soc_codec *codec,unsigned int reg)
+{
+        dbg("in lm49352_read_reg_cache \n");
+        u16 *cache = codec->reg_cache;
+        BUG_ON(reg > ARRAY_SIZE(lm49352_reg));
+        dbg("in lm49352_read_reg_cache %x\n",cache[reg]);
+        return cache[reg];
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name 
+//     lm49352_write_reg_cache- Writes specific register value to reg cache.
+//Synopsis
+//        static inline void lm49352_write_reg_cache(struct snd_soc_codec *codec,unsigned int reg, unsigned int value) 
+//Arguments
+//      codec-Pointer to the struct snd_soc_codec 
+//      reg- Specific register address 
+//      value- The value that to be written to reg cache 
+//Decription 
+//        This function is called when a specific register is to be written with the given value. This function writes the value to reg cache.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline void lm49352_write_reg_cache(struct snd_soc_codec *codec,unsigned int reg, unsigned int value)
+{
+        u16 *cache = codec->reg_cache;
+        dbg("in lm49352_write_reg_cache \n");
+        dbg("value to be written is %x\n",value);
+        cache[reg] = value;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name 
+//    lm49352_write- Writes specific value to register and also to reg cache.
+//Synopsis 
+//        static int lm49352_write(struct snd_soc_codec *codec, unsigned int reg,unsigned int value) 
+//Arguments
+//      codec- Pointer to the struct snd_soc_codec 
+//      reg- Specific register address              
+//      value- The value that to be written to cache and also to given register
+//Decription 
+//        This function is called when a specific register is to be written with the given value. This function writes the value to the given register and also to reg cache. 
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_write(struct snd_soc_codec *codec, unsigned int reg,unsigned int value)
+{
+        u8 data[2];
+        int wrreg;
+        int wrvalue,tmp;
+        wrreg=reg;
+        wrvalue=value;
+        dbg("wrvalue  %x\n",wrvalue);
+        if (wrreg==DAC_MUTE)                                                  /* For mute operation. */
+        {
+                tmp=i2c_smbus_read_byte_data(codec->control_data, wrreg);     /* Value read from register.*/
+                if (wrvalue)
+                {
+                        dbg("wrvalue  %x\n",wrvalue);
+                        tmp|=LM49352_MUTE;
+                }
+        else
+        {
+                dbg("wrvalue  %x\n",wrvalue);
+                tmp&=~LM49352_MUTE;
+        }
+                wrvalue=tmp;
+        }
+        
+        i2c_smbus_write_byte_data(codec->control_data, wrreg, wrvalue);                  /* New value written to LM49352 register.*/
+        dbg("%s,%d : reg : 0x%x, value : 0x%x\n",__FUNCTION__,__LINE__,reg,value);
+        dbg("reg=%x value=%x \n",reg,value);
+        BUG_ON(reg > ARRAY_SIZE(lm49352_reg));
+
+        
+        value &= 0x1ff;
+        dbg("reg value is %x\n",value);
+
+        if (value == lm49352_read_reg_cache(codec, reg))
+                return 0;
+
+        data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+        data[1] = value & 0x00ff;
+        dbg("data[0]=%x data[1]=%x \n",data[0],data[1]);
+        lm49352_write_reg_cache(codec, reg, value);
+        if (codec->hw_write(codec->control_data, data, 2) == 2)
+        {
+                dbg("i2c write successful\n");
+                return 0;
+        }
+        else
+        {
+                dbg("i2c write failed\n");
+                return -EIO;
+        }
+
+
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name 
+//    lm49352_read- Reads specific register value from reg cache.
+//Synopsis 
+//        static inline unsigned int lm49352_read(struct snd_soc_codec *codec, unsigned int reg)                                          //
+//Arguments
+//      codec- Pointer to the struct snd_soc_codec
+//      reg- Specific register address
+//Decription 
+//        This function is called when a specific register value is to be read from reg cache.         
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static inline unsigned int lm49352_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+        return lm49352_read_reg_cache(codec, reg);        
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name       
+//     lm49352_out_vu- Sets the value of a single mixer control.        
+//Synopsis  
+//        static int lm49352_out_vu(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) 
+//Arguments 
+//      kcontrol- Pointer to the struct snd_kcontrol 
+//      ucontrol- Pointer to struct snd_ctl_elem_value
+//Decription 
+//     This function is called when a single mixer control value is to be set.
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_out_vu(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+        int reg = kcontrol->private_value & 0xff;
+        int ret;
+        u16 val;
+
+        lm49352_write_reg_cache(codec, reg, 0);           /* Resetting the reg cache value.*/
+        ret = snd_soc_put_volsw(kcontrol, ucontrol);     /* Callback to set the value of a single mixer control.*/
+        if (ret < 0)
+                return ret;
+        val = lm49352_read_reg_cache(codec, reg);         /* Reading the written new value.*/
+        return lm49352_write(codec, reg, val | 0x0100);   /* Oring the value with 0x100 and writing back to reg cavhe.*/
+}
+/* Macro for defining and adding single mixer controls. */
+#define SOC_LM49352_OUT_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
+{        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+        .tlv.p = (tlv_array), \
+        .info = snd_soc_info_volsw, \
+        .get = snd_soc_get_volsw, .put = lm49352_out_vu, \
+        .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static const struct snd_kcontrol_new lm49352_snd_controls[] = 
+{                                                                                                 /* Various controls of LM49352 Driver */
+        SOC_LM49352_OUT_SINGLE_R_TLV("Left DAC1 Playback Volume", LM49352_DIGITAL_ATTENUATION_DACL1,0, 0x3f, 0, dac_tlv),
+        SOC_LM49352_OUT_SINGLE_R_TLV("Right DAC1 Playback Volume", LM49352_DIGITAL_ATTENUATION_DACR1, 0, 0x3f, 0, dac_tlv),
+        SOC_LM49352_OUT_SINGLE_R_TLV("Headphone Playback Volume",ANALOG_MIXER_OUTPUT_OPTIONS,1,7,1,dac_tlv),
+        SOC_LM49352_OUT_SINGLE_R_TLV("Aux Playback Volume",ANALOG_MIXER_OUTPUT_OPTIONS ,4,1,0,dac_tlv),
+        SOC_LM49352_OUT_SINGLE_R_TLV("PC Speaker Playback Volume",ANALOG_MIXER_OUTPUT_OPTIONS ,6,3,0,dac_tlv),
+        SOC_SINGLE("DAC1 Deemphasis Switch", LM49352_DAC_CONTROL3, 2, 1, 0),
+        SOC_SINGLE("DAC1 Left Invert Switch", LM49352_DAC_CONTROL4,  0, 1, 0),
+        SOC_SINGLE("DAC1 Right Invert Switch", LM49352_DAC_CONTROL4, 1, 1, 0),
+        SOC_SINGLE("DAC ZC Switch", DAC_BASIC, 5, 1, 0),
+        SOC_SINGLE("DAC Mute Switch", DAC_MUTE, 2, 1, 0),           
+        SOC_SINGLE("ADCL Mute Switch", LM49352_ADC_CONTROL1, 2, 1, 0),
+        SOC_SINGLE("ADCR Mute Switch", LM49352_ADC_CONTROL1, 3, 1, 0),
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name         
+//   lm49352_add_controls- Adds the controls which are defined in lm49352_snd_controls struct.
+//Synopsis
+//      static int lm49352_add_controls(struct snd_soc_codec *codec)
+//Arguments 
+//        codec- Pointer to the struct snd_soc_codec 
+// 
+//Decription 
+//        This function is called when setting mixer controls and it adds the controls defined in lm49352_snd_controls.
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_add_controls(struct snd_soc_codec *codec)
+{
+        int err, i;
+
+        for (i = 0; i < ARRAY_SIZE(lm49352_snd_controls); i++) 
+         {
+           err = snd_ctl_add(codec->card,snd_soc_cnew(&lm49352_snd_controls[i],codec, NULL));      /* Adding the declared controls.*/
+           if (err < 0)
+              return err;
+         }
+        return 0;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name      
+//   lm49352_hw_params- Reads specific control information        
+//Synopsis 
+//   static int lm49352_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params) 
+//Arguments 
+//      substream- Pointer to the struct snd_pcm_substream 
+//      params- Pointer to struct snd_pcm_hw_params
+//Decription 
+//        This function is called while playback or record is done and it sets the DAC and ADC's clock divider value based on    
+//        substreams sample rates and also gives information about bit size.
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
+{
+        struct snd_soc_pcm_runtime *rtd = substream->private_data;
+        struct snd_soc_dai_link *dai = rtd->dai;
+        struct snd_soc_device *socdev = rtd->socdev;
+        struct snd_soc_codec *codec = socdev->codec;
+        dbg("\n in lm49352_hw_params \n");
+
+        switch (params_rate(params))                                           /* Setting the DAC and ADC clock dividers based on 
+                                                                                  substream sample rate.*/
+        {
+                case 8000:
+                        lm49352_write1(codec,DAC_CLOCK,0x17);
+                        lm49352_write1(codec,ADC_CLOCK,0x17);
+                        break;
+                case 11025:
+                        lm49352_write1(codec,DAC_CLOCK,0x10);
+                        lm49352_write1(codec,ADC_CLOCK,0x10);
+                        break;
+                case 16000:
+                        lm49352_write1(codec,DAC_CLOCK,0x0b);
+                        lm49352_write1(codec,ADC_CLOCK,0x0b);
+                        break;
+                case 22050:
+                        lm49352_write1(codec,DAC_CLOCK,0x08);
+                        lm49352_write1(codec,ADC_CLOCK,0x08);
+                        break;
+                case 32000:
+                        lm49352_write1(codec,DAC_CLOCK,0x05);
+                        lm49352_write1(codec,ADC_CLOCK,0x05);
+                        break;
+                case 44100:
+                        lm49352_write1(codec,DAC_CLOCK,0x03);
+                        lm49352_write1(codec,ADC_CLOCK,0x03);
+                        break;
+                case 48000:
+                        lm49352_write1(codec,DAC_CLOCK,0x03);
+                        lm49352_write1(codec,ADC_CLOCK,0x03);
+                        break;
+                case 96000:
+                        lm49352_write1(codec,DAC_CLOCK,0x01);
+                        break;
+                default:
+                        lm49352_write1(codec,DAC_CLOCK,0x01);
+                        lm49352_write1(codec,ADC_CLOCK,0x03);
+                        break;
+        }
+        /* bit size */
+        switch (params_format(params)) 
+        {
+                case SNDRV_PCM_FORMAT_S16_LE:
+                        dbg(KERN_CRIT "16 bit\n");
+                        break;
+                case SNDRV_PCM_FORMAT_S20_3LE:
+                        dbg(KERN_CRIT "20 bit\n");
+                        break;
+                case SNDRV_PCM_FORMAT_S24_LE:
+                        dbg(KERN_CRIT "24 bit\n");
+                        break;
+                case SNDRV_PCM_FORMAT_S32_LE:
+                        dbg(KERN_CRIT "32 bit\n");
+                        break;
+                default:
+                        return -EINVAL;
+        }
+
+        return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//    lm49352_write1- Reads specific control information        
+//Synopsis  
+//    int lm49352_write1(struct snd_soc_codec *codec, unsigned int reg,unsigned int value)
+//Arguments 
+//     codec- Pointer to the struct snd_soc_codec 
+//     reg- Specific register address
+//     value- The value that to be written to given register 
+//Decription 
+//        This function is called when a specific register is to be written with the given value.         
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ int lm49352_write1(struct snd_soc_codec *codec, unsigned int reg,unsigned int value)
+{
+        
+        dbg(" reg=%x value=%x \n",reg,value);
+        i2c_smbus_write_byte_data(codec->control_data, reg, value);                   /* I2C write call for LM49352 registers.*/
+
+}
+
+/* Formates supported by LM49352 driver. */
+#define LM49352_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_codec_dai lm49352_dai[] = {                                              /* LM49352 dai structure. */
+        {
+                .name = "LM49352",
+                .id = 0,
+                .playback = {
+                        .stream_name = "Playback",
+                        .channels_min = 2,
+                        .channels_max = 2,
+                        .rates = SNDRV_PCM_RATE_8000_192000,
+                        .formats = LM49352_FORMATS,
+                },
+                .capture = {
+                        .stream_name = "Capture",
+                        .channels_min = 2,
+                        .channels_max = 2,
+                        .rates = SNDRV_PCM_RATE_8000_192000,
+                        .formats = LM49352_FORMATS,
+                },
+                .ops = {
+                         .hw_params = lm49352_hw_params,
+                 },
+        },
+        
+};
+EXPORT_SYMBOL_GPL(lm49352_dai);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name      
+//    lm49352_init- Initializes LM49352 sound card.
+//Synopsis
+//    static int lm49352_init(struct snd_soc_device *socdev)
+//Arguments
+//    socdev- Pointer to the struct snd_soc_device
+// 
+//Decription
+//        This function initializes the LM49352 sound card and registers new PCM for it and also registers it as a new card in ALSA.
+//         This function also writes the default value to the Lm49352's registers.                                                  //         
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_init(struct snd_soc_device *socdev)
+{
+        struct snd_soc_codec *codec = socdev->codec;
+        int ret = 0;
+
+
+        codec->name = "LM49352";
+        codec->owner = THIS_MODULE;
+        codec->read = lm49352_read_reg_cache;
+        codec->write = lm49352_write;
+        codec->dai = lm49352_dai;
+        codec->num_dai = ARRAY_SIZE(lm49352_dai);
+        codec->reg_cache_size = ARRAY_SIZE(lm49352_reg);
+        codec->reg_cache = kmemdup(lm49352_reg, sizeof(lm49352_reg),
+                                   GFP_KERNEL);
+
+        if (codec->reg_cache == NULL)
+                return -ENOMEM;
+
+        /* register pcms */
+        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1);    /* Creating new PCM for LM49352. */
+        if (ret < 0) 
+        {
+                dbg(KERN_ERR "lm49352: failed to create pcms\n");
+                goto pcm_err;
+        }
+
+        /* Writing default values for LM49352 registers. */
+        lm49352_add_controls(codec);
+        lm49352_write1(codec,BASIC_SETUP_PMC_SETUP,0x13);
+        lm49352_write1(codec,BASIC_SETUP_PMC_CLOCK,0x02);
+        lm49352_write1(codec,PLL_CLK_SEL,0x00);
+        lm49352_write1(codec,ANALOG_MIXER_HEADPHONESL,0x02);
+        lm49352_write1(codec,ANALOG_MIXER_HEADPHONESR,0x01);
+        lm49352_write1(codec,ANALOG_MIXER_HP_SENSE,0x00);                              
+        lm49352_write1(codec,ADC_BASIC,0x033);                                           
+        lm49352_write1(codec,ADC_CLOCK,0x0b);                        
+        lm49352_write1(codec,DAC_BASIC,0x31);
+        lm49352_write1(codec,DAC_IP_SELECT,0x09);
+        lm49352_write1(codec,AUDIO_PORT1_BASIC,0x07);               
+        lm49352_write1(codec,PLL_M,0x00);
+        lm49352_write1(codec,PLL_N,0xec);
+        lm49352_write1(codec,PLL_N_MOD,0x14);
+        lm49352_write1(codec,PLL_P1,0x0d);
+        lm49352_write1(codec,ANALOG_MIXER_CLASSD,0x03);
+        lm49352_write1(codec,ANALOG_MIXER_AUX_OUT,0x23);
+        lm49352_write1(codec,ANALOG_MIXER_AUXL_LVL,0x2b);
+        lm49352_write1(codec,ADC_MIXER,0x0f);
+        lm49352_write1(codec,AUDIO_PORT1_IP,0x05);
+        lm49352_write1(codec,ADC_EFFECTS_HPF,0x04);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_ALC4,0x0a);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_ALC5,0x0a);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_ALC6,0x0a);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_ALC7,0x1f);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_L_LEVEL,0x33);
+        lm49352_write1(codec,ADC_EFFECTS_ADC_R_LEVEL,0x33);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_ALC1,0x02);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_ALC4,0x0a);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_ALC5,0x0a);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_ALC6,0x0a);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_ALC7,0x33);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_L_LEVEL,0x33);
+        lm49352_write1(codec,DAC_EFFECTS_DAC_R_LEVEL,0x33);        
+        lm49352_write1(codec,ANALOG_MIXER_MIC_LVL,0x0f);
+        lm49352_write1(codec,ANALOG_MIXER_ADC,0x0c);
+
+        ret = snd_soc_register_card(socdev);                            /* Registering new card instance for LM49352 sound card. */
+        if (ret < 0) 
+        {
+                dbg(KERN_ERR "lm49352: failed to register card\n");
+                goto card_err;
+        }
+        return ret;
+card_err:
+        snd_soc_free_pcms(socdev);
+        snd_soc_dapm_free(socdev);
+pcm_err:
+        kfree(codec->reg_cache);
+        return ret;
+}
+
+
+static struct snd_soc_device *lm49352_socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };                 /* Address range for Lm49352 I2C. */
+I2C_CLIENT_INSMOD;                                                          /* Defines I2C client address data. */                                                                
+static struct i2c_driver lm49352_i2c_driver;
+static struct i2c_client client_template;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//   lm49352_codec_probe- Probes the I2C for given address.        
+//Synopsis 
+//   static int lm49352_codec_probe(struct i2c_adapter *adap, int addr, int kind) 
+//Arguments 
+//   adap- Pointer to the struct i2c_adapter 
+//   addr- Default I2C address of LM49352(0x1a). 
+//   kind- Kind of I2C.
+//Decription
+//        This function is calles while doing I2C probe and it probes I2C for given address and also allocates memory for I2C and also attaches the client to I2C.         
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+        struct snd_soc_device *socdev = lm49352_socdev;
+        struct lm49352_setup_data *setup = socdev->codec_data;
+        struct snd_soc_codec *codec = socdev->codec;
+        struct i2c_client *i2c;
+        int ret;
+        dbg("in lm49352_codec_probe start\n");
+
+        if (addr != setup->i2c_address)
+                return -ENODEV;
+
+        client_template.adapter = adap;
+        client_template.addr = addr;
+        i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);    /* Allocating memory for LM49352 I2C data. */
+
+        if (i2c == NULL)
+        {
+                kfree(codec);
+                dbg("in lm49352_codec_probe  kmemdup failed\n");
+                return -ENOMEM;
+        }
+        i2c_set_clientdata(i2c, codec);
+        codec->control_data = i2c;
+        
+        ret = i2c_attach_client(i2c);                                  /* Attaching I2C client of LM49352. */
+        if (ret < 0)  
+        {
+                err("failed to attach codec at addr %x\n", addr);
+                goto err;
+        }
+        ret = lm49352_init(socdev);                                       /* Calling LM49352_ to initialise Lm49352. */
+        if (ret < 0) 
+        {
+                err("failed to initialise LM49352\n");
+                goto err;
+        }
+        dbg("in lm49352_codec_probe  end\n");
+        return ret;
+
+err:
+        kfree(codec);
+        kfree(i2c);
+        return ret;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//   lm49352_i2c_detach- Detaches or removes the created I2C instance.
+//Synopsis
+//   static int lm49352_i2c_detach(struct i2c_client *client)
+//Arguments
+//     client- Pointer to the struct i2c_client
+// 
+//Decription 
+//    This function removes or detaches the I2C instance.
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_i2c_detach(struct i2c_client *client)
+{
+        dbg("in lm49352_i2c_detach \n");
+        struct snd_soc_codec *codec = i2c_get_clientdata(client);
+        i2c_detach_client(client);                                         /* Detaching I2C client of LM49352. */
+        kfree(codec->reg_cache);
+        kfree(client);
+        return 0;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//   lm49352_i2c_attach- Attaches or creates I2C instance.        
+//Synopsis 
+//   static int lm49352_i2c_attach(struct i2c_adapter *adap)
+//Arguments
+//   adap- Pointer to the struct i2c_adapter 
+//Decription  
+//   This function attaches a new client to I2C.         
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_i2c_attach(struct i2c_adapter *adap)
+{
+        if(attach_once==0)
+        {        
+                attach_once++;
+                dbg("in lm49352_i2c_attach \n");
+                return i2c_probe(adap, &addr_data, lm49352_codec_probe);           /* Probes I2C for given address values. */
+        }
+        else 
+                return 0;
+}
+static struct i2c_driver lm49352_i2c_driver = {                /* I2C driver structure for LM49352. */
+        .driver = {
+                .name = "LM49352 I2C Codec",
+                .owner = THIS_MODULE,
+        },
+        .attach_adapter = lm49352_i2c_attach,
+        .detach_client =  lm49352_i2c_detach,
+        .command =        NULL,
+};
+
+static struct i2c_client client_template = {                    /* I2C client structure for LM49352. */
+        .name =   "LM49352",
+        .driver = &lm49352_i2c_driver,
+};
+#endif
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//  lm49352_probe- Probes LM49352 card.        
+//Synopsis
+//  static int lm49352_probe(struct platform_device *pdev)
+//Arguments
+//  pdev- Pointer to the struct platform_device
+// 
+//Decription 
+//        This function probes LM49352 and creates or allocates memory for codec structure and also for codec private data (lm49352) and adds I2C driver for LM49352.
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_probe(struct platform_device *pdev)
+{
+        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+        struct lm49352_setup_data *setup;
+        struct snd_soc_codec *codec;
+        int ret = 0;
+        dbg(" in lm49352_probe start \n");
+        info("LM49352 Audio Codec %s\n", LM49352_VERSION);
+
+        setup = socdev->codec_data;
+        codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);         /* Allocating memory for LM49352 codec data. */
+        if (codec == NULL)
+                return -ENOMEM;
+        dbg("in lm49352_probe lm49352 kzalloc success \n");
+        socdev->codec = codec;
+        mutex_init(&codec->mutex);
+        INIT_LIST_HEAD(&codec->dapm_widgets);
+        INIT_LIST_HEAD(&codec->dapm_paths);
+        lm49352_socdev = socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+        if (setup->i2c_address) 
+        {
+                normal_i2c[0] = setup->i2c_address;
+                dbg("i2c address is %x\n",setup->i2c_address);
+                codec->hw_write = (hw_write_t)i2c_master_send;                
+                ret = i2c_add_driver(&lm49352_i2c_driver);                    /* Adding I2C driver for LM49352. */
+                if (ret != 0)
+                        dbg(KERN_ERR "can't add i2c driver");
+        }
+#else
+                
+#endif
+        return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name                                                                                                                                  //        
+//        lm49352_remove- Removes Lm49352 driver structures.                                                                           //        
+//Synopsis                                                                                                                          //
+//        static int lm49352_remove(struct platform_device *pdev)                                                                          //
+//Arguments                                                                                                                          //
+//        pdev- Pointer to the struct platform_device                                                                                  //
+//                                                                                                                                        //
+//Decription                                                                                                                          //
+//        This function removes all the structures created while lm49352_probe.                                                          //         
+//                                                                                                                                  //                                                                                                                                                                                                                                                                                                                                                                               
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int lm49352_remove(struct platform_device *pdev)
+{
+        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+        struct snd_soc_codec *codec = socdev->codec;
+
+        snd_soc_free_pcms(socdev);
+        snd_soc_dapm_free(socdev);
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+        i2c_del_driver(&lm49352_i2c_driver);
+#endif
+        kfree(codec->private_data);
+        kfree(codec);
+
+        return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_lm49352 = {          /* Codec structure for LM49352. */
+        .probe =         lm49352_probe,
+        .remove =         lm49352_remove,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_lm49352);                      /* Exporting soc_codec_dev_lm49352 symbol. */
+ 
+MODULE_DESCRIPTION("ASoC LM49352 driver");
+MODULE_AUTHOR("M");
+MODULE_LICENSE("GPL");
diff -Naur sound.old/soc/codecs/lm49352.h sound/soc/codecs/lm49352.h
--- sound.old/soc/codecs/lm49352.h        1970-01-01 05:30:00.000000000 +0530
+++ sound/soc/codecs/lm49352.h        2011-03-04 19:16:58.000000000 +0530
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) <2011> National Semiconductor, Inc.
+ * 
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation.
+*/
+
+#ifndef _LM49352_H
+#define _LM49352_H
+
+
+/* LM49352 register space */
+#define BASIC_SETUP_PMC_SETUP                    0x00
+#define BASIC_SETUP_PMC_CLOCK                    0x01
+#define PLL_CLK_SEL                              0x03
+#define ANALOG_MIXER_HEADPHONESL                 0x11
+#define ANALOG_MIXER_HEADPHONESR                 0x12
+#define ANALOG_MIXER_OUTPUT_OPTIONS              0x14
+#define ANALOG_MIXER_ADC                         0x15
+#define ANALOG_MIXER_MIC_LVL                     0x16
+#define ANALOG_MIXER_HP_SENSE                    0x1B
+#define ADC_BASIC                                0x20
+#define ADC_CLOCK                                0x21
+#define DAC_BASIC                                0x30
+#define DAC_MUTE                                 0x30
+#define DAC_CLOCK                                0x31
+#define DAC_IP_SELECT                            0x44
+#define AUDIO_PORT1_BASIC                        0x50
+#define PLL_M                                    0x04
+#define PLL_N                                    0x05
+#define PLL_N_MOD                                0x06
+#define PLL_P1                                   0x07
+#define ANALOG_MIXER_CLASSD                      0x10
+#define ANALOG_MIXER_AUX_OUT                     0x13
+#define ANALOG_MIXER_ADC                         0x15
+#define ANALOG_MIXER_AUXL_LVL                    0x18
+#define ADC_MIXER                                0x23
+#define AUDIO_PORT1_IP                           0x42
+#define ADC_EFFECTS_HPF                          0x80
+#define ADC_EFFECTS_ADC_ALC4                     0x84
+#define ADC_EFFECTS_ADC_ALC5                     0x85
+#define ADC_EFFECTS_ADC_ALC6                     0x86
+#define ADC_EFFECTS_ADC_ALC7                     0x87
+#define ADC_EFFECTS_ADC_L_LEVEL                  0x89
+#define ADC_EFFECTS_ADC_R_LEVEL                  0x8A
+#define DAC_EFFECTS_DAC_ALC1                     0xA0
+#define DAC_EFFECTS_DAC_ALC4                     0xA3
+#define DAC_EFFECTS_DAC_ALC5                     0xA4
+#define DAC_EFFECTS_DAC_ALC6                     0xA5
+#define DAC_EFFECTS_DAC_ALC7                     0xA6
+#define DAC_EFFECTS_DAC_L_LEVEL                  0xA8
+#define DAC_EFFECTS_DAC_R_LEVEL                  0xA9
+#define SPREAD_SPECTRUM_RESET                    0xF0
+ 
+
+#define LM49352_DAC_CONTROL3                  0x71     
+#define LM49352_DAC_CONTROL4                  0xff     
+#define LM49352_DAC_CONTROL5                  0x30             
+#define LM49352_RESET                         0xf0   
+#define LM49352_ADC_CONTROL1                  0x20                    
+#define LM49352_MUTE                          0x0c
+#define LM49352_DIGITAL_ATTENUATION_DACL1     0xA8     
+#define LM49352_DIGITAL_ATTENUATION_DACR1     0xA9
+extern struct snd_soc_codec_device soc_codec_dev_lm49352;
+
+struct lm49352_setup_data {
+        unsigned short i2c_address;
+};
+
+#define LM49352_DAI_PAIFRX 0
+#define LM49352_DAI_PAIFTX 1
+
+
+extern struct snd_soc_codec_dai lm49352_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_lm49352;
+
+#endif
+
diff -Naur sound.old/soc/codecs/Makefile sound/soc/codecs/Makefile
--- sound.old/soc/codecs/Makefile        2011-03-04 19:12:02.000000000 +0530
+++ sound/soc/codecs/Makefile        2010-03-27 11:33:10.000000000 +0530
@@ -1,4 +1,5 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-lm49352-objs := lm49352.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
@@ -19,6 +20,7 @@
 snd-soc-wm9713-objs := wm9713.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)        += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_LM49352)        += snd-soc-lm49352.o
 obj-$(CONFIG_SND_SOC_WM8731)        += snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)        += snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)        += snd-soc-wm8753.o
diff -Naur sound.old/soc/s3c/Kconfig sound/soc/s3c/Kconfig
--- sound.old/soc/s3c/Kconfig        2011-03-04 19:09:47.000000000 +0530
+++ sound/soc/s3c/Kconfig        2010-04-16 10:45:21.000000000 +0530
@@ -194,6 +194,15 @@
           Say Y here to make input stream as LINE-IN.
 endchoice
 
+config SND_S3C64XX_SOC_SMDK6410_LM49352
+        tristate "SoC I2S Audio support for SMDK6410 - LM49352"
+        depends on SND_S3C_SOC && (MACH_SMDK6410)
+        select SND_S3C6410_SOC_I2S
+        select SND_SOC_LM49352
+        help
+          Say Y if you want to add support for SoC audio on smdk6410
+          with the LM49352.
+
 config SND_S3C64XX_SOC_SMDK6400_WM8990
         tristate "SoC I2S Audio support for SMDK6400 - WM8990"
         depends on SND_S3C_SOC && MACH_SMDK6400
diff -Naur sound.old/soc/s3c/Makefile sound/soc/s3c/Makefile
--- sound.old/soc/s3c/Makefile        2011-03-04 19:12:20.000000000 +0530
+++ sound/soc/s3c/Makefile        2010-03-27 11:32:12.000000000 +0530
@@ -29,8 +29,10 @@
 snd-soc-smdk6400-wm9713-objs := smdk6400_wm9713.o
 snd-soc-smdk6400-wm8753-objs := smdk6400_wm8753.o
 snd-soc-smdk6400-wm8990-objs := smdk6400_wm8990.o
+snd-soc-smdk6410-lm49352-objs := smdk6410_lm49352.o
 snd-soc-smdk6410-wm8753-objs := smdk6400_wm8753.o
 snd-soc-smdk6410-wm8990-objs := smdk6410_wm8990.o
+snd-soc-smdk6410-lm49352-pcmif-objs := smdk6410_lm49352_pcmif.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2416_WM8753) += snd-soc-smdk2416-wm8753.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
@@ -43,5 +45,7 @@
 
 # Temporary for 6410
 obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_WM9713) += snd-soc-smdk6400-wm9713.o
+obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_LM49352) += snd-soc-smdk6410-lm49352.o
 obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_WM8753) += snd-soc-smdk6400-wm8753.o
 obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_WM8990) += snd-soc-smdk6410-wm8990.o
+obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_LM49352_PCMIF) += snd-soc-smdk6410-lm49352-pcmif.o
diff -Naur sound.old/soc/s3c/smdk6410_lm49352.c sound/soc/s3c/smdk6410_lm49352.c
--- sound.old/soc/s3c/smdk6410_lm49352.c        1970-01-01 05:30:00.000000000 +0530
+++ sound/soc/s3c/smdk6410_lm49352.c        2011-03-04 19:16:18.000000000 +0530
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) <2011> National Semiconductor, Inc.
+ * 
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/regs-iis.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+#include <asm/arch/audio.h>
+#include <asm/io.h>
+#include <asm/arch/spi-gpio.h>
+#include <asm/arch/regs-s3c6410-clock.h>
+
+#include "../codecs/lm49352.h"
+#include "s3c-pcm.h"
+#include "s3c-i2s.h"
+
+#ifdef CONFIG_SND_DEBUG
+#define s3cdbg(x...) printk(x)
+#else
+#define s3cdbg(x...)
+#endif
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name        
+//    smdk6410_hw_params- Sets the hardware parameters of SMDk6410
+//Synopsis  
+//    static int smdk6410_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
+//Arguments 
+//    substream- Pointer to the struct snd_pcm_substream
+//      params- Pointer to struct snd_pcm_hw_params 
+//Decription 
+//        Called by ALSA to set the hardware information of SMDk6410. In this function PLL of samsung board is set based on         //
+//      substreams sample rates and prescalar of SMDk6410 is also set based on sample rates and bit length of substream.  
+// 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int smdk6410_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
+{
+        struct snd_soc_pcm_runtime *rtd = substream->private_data;
+        struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+        unsigned int pll_out = 0; /*bclk = 0; */
+        int ret = 0;
+        unsigned int prescaler;
+
+        s3cdbg("Entered %s, rate = %d\n", __FUNCTION__, params_rate(params));
+        
+        /*PCLK & SCLK gating enable*/
+        writel(readl(S3C_PCLK_GATE)|S3C_CLKCON_PCLK_IIS2, S3C_PCLK_GATE);
+        writel(readl(S3C_SCLK_GATE)|S3C_CLKCON_SCLK_AUDIO0, S3C_SCLK_GATE);
+        s3cdbg("Entered %s, S3C_PCLK_GATE = %x\n",__FUNCTION__,readl(S3C_PCLK_GATE));
+        s3cdbg("Entered %s, S3C_SCLK_GATE = %x\n",__FUNCTION__,readl(S3C_SCLK_GATE));
+        /*Clear I2S prescaler value [13:8] and disable prescaler*/
+        /* set prescaler division for sample rate */
+        ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, 0);
+        if (ret < 0)
+                return ret;
+        
+        s3cdbg("%s: %d , params = %d\n", __FUNCTION__, __LINE__, params_rate(params));
+
+        switch (params_rate(params)) 
+        {
+                case 8000:
+                case 16000:
+                case 32000:
+                case 64100:
+                        writel(50332, S3C_EPLL_CON1);
+                        writel((1<<31)|(32<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
+                        s3cdbg("Entered %s, S3C_EPLL_CON0 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON0));
+                        s3cdbg("Entered %s, S3C_EPLL_CON1 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON1));
+                        break;
+                case 11025:
+                case 22050:
+                case 44100:
+                case 88200:
+                        /* K=10398, M=45, P=1, S=3 -- Fout=67.738 */
+                        writel(10398, S3C_EPLL_CON1);
+                        writel((1<<31)|(45<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
+                        s3cdbg("Entered %s, S3C_EPLL_CON0 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON0));
+                        s3cdbg("Entered %s, S3C_EPLL_CON1 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON1));
+                        break;
+                case 48000:
+                case 96000:
+                        /* K=9961, M=49, P=1, S=3 -- Fin=12, Fout=73.728; r=1536 */
+                        writel(9961, S3C_EPLL_CON1);
+                        writel((1<<31)|(49<<16)|(1<<8)|(3<<0) ,S3C_EPLL_CON0);
+                        s3cdbg("Entered %s, S3C_EPLL_CON0 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON0));
+                        s3cdbg("Entered %s, S3C_EPLL_CON1 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON1));
+                        break;
+                default:
+                        writel(0, S3C_EPLL_CON1);
+                        writel((1<<31)|(128<<16)|(25<<8)|(0<<0) ,S3C_EPLL_CON0);
+                        s3cdbg("Entered %s, S3C_EPLL_CON0 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON0));
+                        s3cdbg("Entered %s, S3C_EPLL_CON1 = %x\n",__FUNCTION__,readl(S3C_EPLL_CON1));
+                        break;
+        }
+
+        while(!(__raw_readl(S3C_EPLL_CON0)&(1<<30)));
+
+        /* MUXepll : FOUTepll */
+        writel(readl(S3C_CLK_SRC)|S3C_CLKSRC_EPLL_CLKSEL, S3C_CLK_SRC);
+        s3cdbg("Entered %s, S3C_CLK_SRC = %x\n",__FUNCTION__,readl(S3C_CLK_SRC));
+        /* AUDIO2 sel : FOUTepll */
+        writel((readl(S3C_CLK_SRC2)&~(0x7<<0))|(0<<0), S3C_CLK_SRC2);
+        s3cdbg("Entered %s, S3C_CLK_SRC2 = %x\n",__FUNCTION__,readl(S3C_CLK_SRC2));
+        /* CLK_DIV2 setting */
+        writel(0x0,S3C_CLK_DIV2);
+        s3cdbg("Entered %s, S3C_CLK_DIV2 = %x\n",__FUNCTION__,readl(S3C_CLK_DIV2));
+        switch (params_rate(params)) 
+        {
+                case 8000:
+                        pll_out = 2048000;
+                        prescaler = 8;
+                        break;
+                case 11025:
+                        pll_out = 2822400;
+                        prescaler = 8; 
+                        break;
+                case 16000:
+                        pll_out = 4096000;
+                        prescaler = 4; 
+                        break;
+                case 22050:
+                        pll_out = 5644800;
+                        prescaler = 4; 
+                        break;
+                case 32000:
+                        pll_out = 8192000;
+                        prescaler = 2; 
+                        break;
+                case 44100:
+                        /* Fout=73.728 */
+                        pll_out = 11289600;
+                        prescaler = 2;
+                        break;
+                case 48000:
+                        /* Fout=67.738 */
+                        pll_out = 12288000;
+                        prescaler = 2; 
+                        break;
+                case 88200:
+                        pll_out = 22579200;
+                        prescaler = 1; 
+                        break;
+                case 96000:
+                        pll_out = 24576000;
+                        prescaler = 1;
+                        break;
+                default:
+                        prescaler = 4;
+                        pll_out = 12288000;
+                        break;
+        }
+
+        /* set MCLK division for sample rate */
+        switch (params_format(params)) 
+        {
+                case SNDRV_PCM_FORMAT_S8:
+                case SNDRV_PCM_FORMAT_S16_LE:
+                        prescaler *= 3;
+                        break;
+                case SNDRV_PCM_FORMAT_S24_3LE:
+                        prescaler *= 2;
+                        break;
+                case SNDRV_PCM_FORMAT_S24_LE:
+                        prescaler *= 2;
+                        break;
+                default:
+                        return -EINVAL;
+        }
+
+        prescaler = prescaler - 1; 
+
+        /* set cpu DAI configuration */
+        ret = cpu_dai->dai_ops.set_fmt(cpu_dai,SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+        if (ret < 0)
+                return ret;
+        ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,S3C_IIS0MOD_256FS);                                          
+        if (ret < 0)
+                return ret;
+
+        /* set prescaler division for sample rate */
+        ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,(prescaler << 0x8));
+        if (ret < 0)
+                return ret;
+
+        return 0;
+}
+
+/*
+ * LM49352 DAI operations.
+ */
+static struct snd_soc_ops smdk6410_ops = {
+        .hw_params = smdk6410_hw_params,
+};
+
+static struct snd_soc_dai_link smdk6410_dai[] = {
+{
+        .name = "LM49352",
+        .stream_name = "LM49352 Playback",
+        .cpu_dai = &s3c_i2s_v40_dai,                    
+        .codec_dai = &lm49352_dai[LM49352_DAI_PAIFRX],
+        .ops = &smdk6410_ops,
+},
+};
+
+static struct snd_soc_machine smdk6410 = {
+        .name = "smdk6410",
+        .dai_link = smdk6410_dai,
+        .num_links = ARRAY_SIZE(smdk6410_dai),
+};
+
+static struct lm49352_setup_data smdk6410_lm49352_setup = {
+        .i2c_address = 0x1a,                                         
+}; 
+
+static struct snd_soc_device smdk6410_snd_devdata = {
+        .machine = &smdk6410,
+        .platform = &s3c24xx_soc_platform,
+        .codec_dev = &soc_codec_dev_lm49352,
+        .codec_data = &smdk6410_lm49352_setup,
+};
+
+static struct platform_device *smdk6410_snd_device;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name
+//        smdk6410_init- Innitialization of SMDk6410.
+//Synopsis 
+//        static int __init smdk6410_init(void)
+//Decription
+//        Called when the driver is inserted and in this function a platform device is allocated and added. The gpio pins of  I2S are enabled and pulled up.
+//                            
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int __init smdk6410_init(void)
+{
+        int ret;
+        /* Set up GPIOs to enable I2SV40 pin */
+        s3c_gpio_cfgpin(S3C_GPC4, S3C_GPC4_I2S_V40_DO0);
+        s3c_gpio_cfgpin(S3C_GPC5, S3C_GPC5_I2S_V40_DO1);
+        s3c_gpio_cfgpin(S3C_GPC7, S3C_GPC7_I2S_V40_DO2);
+        s3c_gpio_cfgpin(S3C_GPH6, S3C_GPH6_I2S_V40_BCLK);
+        s3c_gpio_cfgpin(S3C_GPH7, S3C_GPH7_I2S_V40_CDCLK);
+        s3c_gpio_cfgpin(S3C_GPH8, S3C_GPH8_I2S_V40_LRCLK);
+        s3c_gpio_cfgpin(S3C_GPH9, S3C_GPH9_I2S_V40_DI);
+
+        /* pull-up-enable, pull-down-disable*/
+        s3c_gpio_pullup(S3C_GPH9, 0x02);
+        s3c_gpio_pullup(S3C_GPH8, 0x02);
+        s3c_gpio_pullup(S3C_GPH7, 0x02);
+        s3c_gpio_pullup(S3C_GPH6, 0x02);
+        s3c_gpio_pullup(S3C_GPC4, 0x02);
+        s3c_gpio_pullup(S3C_GPC5, 0x02);
+        s3c_gpio_pullup(S3C_GPC7, 0x02);
+
+        smdk6410_snd_device = platform_device_alloc("soc-audio", -1);            /* Create a platform device object which can have other objects attached to it, and which will                                                                                     have attached objects freed when it is released. */          
+        if (!smdk6410_snd_device)
+                return -ENOMEM;
+
+        platform_set_drvdata(smdk6410_snd_device, &smdk6410_snd_devdata);        /* Registering platform device.*/
+        smdk6410_snd_devdata.dev = &smdk6410_snd_device->dev;
+        ret = platform_device_add(smdk6410_snd_device);                          /* Add a platform device to device hierarchy.*/ 
+
+        if (ret)
+                platform_device_put(smdk6410_snd_device);                        /* Free platform device.*/
+        
+        return ret;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//Name   
+//        smdk6410_exit- Unregistering platform device.
+//Synopsis
+//        static void __exit smdk6410_exit(void) 
+//Decription 
+//        Called whenever the platform device is needed to be removed(unregistering platform device).                                   //         
+//          
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static void __exit smdk6410_exit(void)
+{
+        platform_device_unregister(smdk6410_snd_device);
+}
+
+module_init(smdk6410_init);        /* Called whenever insmod is called. */
+module_exit(smdk6410_exit);        /* Called whenever rmmod is called. */
+  
+/* Module information */
+MODULE_AUTHOR("M");
+MODULE_DESCRIPTION("ALSA SoC SMDK6410 LM49352");
+MODULE_LICENSE("GPL");

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

end of thread, other threads:[~2011-03-16 14:35 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-07 12:03 [PATCH] ASoC: Add National Semiconductor LM49352 Audio Codec support Reddy, MR Swami
2011-03-07 13:18 ` Mark Brown
2011-03-08  4:31   ` Reddy, MR Swami
2011-03-16  7:38 ` [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support Reddy, MR Swami
2011-03-16 10:08   ` Mark Brown
2011-03-16 13:38     ` Reddy, MR Swami
2011-03-16 14:35       ` Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).