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

* Re: [PATCH] ASoC: Add National Semiconductor LM49352 Audio Codec support
  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
  1 sibling, 1 reply; 7+ messages in thread
From: Mark Brown @ 2011-03-07 13:18 UTC (permalink / raw)
  To: Reddy, MR Swami; +Cc: alsa-devel@alsa-project.org, Liam Girdwood

On Mon, Mar 07, 2011 at 04:03:55AM -0800, Reddy, MR Swami wrote:

Please fix your MUA to word wrap within paragraphs at less than 80
columns.  I've reflowed your mail for legibility.  There are some
suggestions in Documentation/email-clients.txt.

> 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. 

You need to follow the standard kernel patch submission process, which
is documented in Documentation/SubmittingPatches.  In particular you
should:

 - Conform to the coding standards in CodingStandards (checkpatch.pl is 
   helpful for detecting obvious issues).  A good high level check is if
   your code should visually resemble the rest of the code base.
 - Submit your code against the current development kernel version.  The
   trees will be listed in MAINTAINERS, or -next is a good approximation.
 - Send your patch in-line rather than as an attachment.
 - Send your patch against the full Linux tree, not a subdirectory of
   it.

You should also split your code up into a series of independant patches
- in general each individual driver should be a single patch so your
machine and CODEC drivers should be split up.  If the machine driver is
just for a flying wire system you should remove it, otherwise please
also submit the arch/arm parts.

I've not done a detailed review of the code due to the above high level
issues.

> And also let me know the forward-porting (to the latest ALSA version APIs) steps/process. Thanks in advance.

You need to figure out a process that works for you.  In general
inspecting the kernel history is usually very useful for stuff like
this, or you could just start from scratch and copy over the active bits
of code.

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

* Re: [PATCH] ASoC: Add National Semiconductor LM49352 Audio Codec support
  2011-03-07 13:18 ` Mark Brown
@ 2011-03-08  4:31   ` Reddy, MR Swami
  0 siblings, 0 replies; 7+ messages in thread
From: Reddy, MR Swami @ 2011-03-08  4:31 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel@alsa-project.org, Liam Girdwood

Thank you very much for your feedback. I will update the patch as per the comments and submit the update patch for review.

Thanks
Swami

-----Original Message-----
From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] 
Sent: Monday, March 07, 2011 6:48 PM
To: Reddy, MR Swami
Cc: alsa-devel@alsa-project.org; Liam Girdwood
Subject: Re: [PATCH] ASoC: Add National Semiconductor LM49352 Audio Codec support

On Mon, Mar 07, 2011 at 04:03:55AM -0800, Reddy, MR Swami wrote:

Please fix your MUA to word wrap within paragraphs at less than 80 columns.  I've reflowed your mail for legibility.  There are some suggestions in Documentation/email-clients.txt.

> 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. 

You need to follow the standard kernel patch submission process, which is documented in Documentation/SubmittingPatches.  In particular you
should:

 - Conform to the coding standards in CodingStandards (checkpatch.pl is 
   helpful for detecting obvious issues).  A good high level check is if
   your code should visually resemble the rest of the code base.
 - Submit your code against the current development kernel version.  The
   trees will be listed in MAINTAINERS, or -next is a good approximation.
 - Send your patch in-line rather than as an attachment.
 - Send your patch against the full Linux tree, not a subdirectory of
   it.

You should also split your code up into a series of independant patches
- in general each individual driver should be a single patch so your machine and CODEC drivers should be split up.  If the machine driver is just for a flying wire system you should remove it, otherwise please also submit the arch/arm parts.

I've not done a detailed review of the code due to the above high level issues.

> And also let me know the forward-porting (to the latest ALSA version APIs) steps/process. Thanks in advance.

You need to figure out a process that works for you.  In general inspecting the kernel history is usually very useful for stuff like this, or you could just start from scratch and copy over the active bits of code.

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

* [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support
  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-16  7:38 ` Reddy, MR Swami
  2011-03-16 10:08   ` Mark Brown
  1 sibling, 1 reply; 7+ messages in thread
From: Reddy, MR Swami @ 2011-03-16  7:38 UTC (permalink / raw)
  To: alsa-devel@alsa-project.org; +Cc: Mark Brown, Liam Girdwood

From: M R Swami Reddy <MR.Swami.Reddy@nsc.com>

Patch for adding National Semiconductor LM49352 codec support.

Signed-off-by: M R Swami Reddy <MR.Swami.Reddy@nsc.com>
---
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.

diff -upNr -X linux-2.6.24-lm49352/Documentation/dontdiff linux-2.6.24/sound/soc/codecs/Kconfig linux-2.6.24-lm49352/sound/soc/codecs/Kconfig
--- linux-2.6.24/sound/soc/codecs/Kconfig       2011-03-14 14:36:03.000000000 +0530
+++ linux-2.6.24-lm49352/sound/soc/codecs/Kconfig       2010-03-27 11:33:10.000000000 +0530
@@ -2,6 +2,10 @@ config SND_SOC_AC97_CODEC
        tristate
        depends on SND_SOC

+config SND_SOC_LM49352
+       tristate
+       depends on SND_SOC
+
 config SND_SOC_WM8731
        tristate
        depends on SND_SOC
diff -upNr -X linux-2.6.24-lm49352/Documentation/dontdiff linux-2.6.24/sound/soc/codecs/lm49352.c linux-2.6.24-lm49352/sound/soc/codecs/lm49352.c
--- linux-2.6.24/sound/soc/codecs/lm49352.c     1970-01-01 05:30:00.000000000 +0530
+++ linux-2.6.24-lm49352/sound/soc/codecs/lm49352.c     2011-03-14 13:03:00.000000000 +0530
@@ -0,0 +1,834 @@
+/*
+ * 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;
+
+#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)
+
+/* Register cache for LM49352 driver. */
+static const u16 lm49352_reg[] = {
+       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);
+
+       /* For mute operation. */
+       if (wrreg == DAC_MUTE) {
+               /* Value read from register.*/
+               tmp = i2c_smbus_read_byte_data(codec->control_data, wrreg);
+
+               if (wrvalue) {
+                       dbg("wrvalue  %x\n", wrvalue);
+                       tmp |= LM49352_MUTE;
+               } else {
+                       dbg("wrvalue  %x\n", wrvalue);
+                       tmp &= ~LM49352_MUTE;
+               }
+               wrvalue = tmp;
+       }
+
+       /* New value written to LM49352 register.*/
+       i2c_smbus_write_byte_data(codec->control_data, wrreg, wrvalue);
+       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,
+ * 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,
+ * 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;
+
+       /* Resetting the reg cache value. */
+       lm49352_write_reg_cache(codec, reg, 0);
+
+       /* Callback to set the value of a single mixer control. */
+       ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+       if (ret < 0)
+               return ret;
+
+       /* Reading the written new value. */
+       val = lm49352_read_reg_cache(codec, reg);
+       /* Oring the value with 0x100 and writing back to reg cavhe. */
+       return lm49352_write(codec, reg, val | 0x0100);
+}
+
+/* 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++) {
+               /* Adding the declared controls.*/
+               err = snd_ctl_add(codec->card,
+                                 snd_soc_cnew(&lm49352_snd_controls[i],
+                                              codec, NULL));
+               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");
+
+
+       /* Setting the DAC and ADC clock dividers based on substream
+          sample rate. */
+       switch (params_rate(params)) {
+       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 write call for LM49352 registers.*/
+       i2c_smbus_write_byte_data(codec->control_data, reg, value);
+}
+
+/* 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)
+
+/* LM49352 dai structure. */
+struct snd_soc_codec_dai lm49352_dai[] = {
+       {
+               .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 */
+       /* Creating new PCM for LM49352. */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       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);
+
+       /* Registering new card instance for LM49352 sound card. */
+       ret = snd_soc_register_card(socdev);
+       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)
+
+
+/* Address range for Lm49352 I2C. */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Defines I2C client address data. */
+I2C_CLIENT_INSMOD;
+
+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;
+
+       /* Allocating memory for LM49352 I2C data. */
+       i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+
+       if (i2c == NULL) {
+               kfree(codec);
+               dbg("in lm49352_codec_probe  kmemdup failed\n");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(i2c, codec);
+       codec->control_data = i2c;
+
+       /* Attaching I2C client of LM49352. */
+       ret = i2c_attach_client(i2c);
+       if (ret < 0) {
+               err("failed to attach codec at addr %x\n", addr);
+               goto err;
+       }
+
+       /* Calling LM49352_ to initialise Lm49352. */
+       ret = lm49352_init(socdev);
+       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);
+
+       /* Detaching I2C client of LM49352. */
+       i2c_detach_client(client);
+       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");
+               /* Probes I2C for given address values. */
+               return i2c_probe(adap, &addr_data, lm49352_codec_probe);
+       } else
+               return 0;
+}
+
+/* I2C driver structure for LM49352. */
+static struct i2c_driver lm49352_i2c_driver = {
+       .driver = {
+               .name = "LM49352 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .attach_adapter = lm49352_i2c_attach,
+       .detach_client =  lm49352_i2c_detach,
+       .command =      NULL,
+};
+
+/* I2C client structure for LM49352. */
+static struct i2c_client client_template = {
+       .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;
+       /* Allocating memory for LM49352 codec data. */
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       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;
+               /* Adding I2C driver for LM49352. */
+               ret = i2c_add_driver(&lm49352_i2c_driver);
+               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;
+}
+
+/* Codec structure for LM49352. */
+struct snd_soc_codec_device soc_codec_dev_lm49352 = {
+       .probe  = lm49352_probe,
+       .remove = lm49352_remove,
+};
+
+/* Exporting soc_codec_dev_lm49352 symbol. */
+EXPORT_SYMBOL_GPL(soc_codec_dev_lm49352);
+
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@nsc.com");
+MODULE_DESCRIPTION("ASoC LM49352 driver");
+MODULE_LICENSE("GPL");
diff -upNr -X linux-2.6.24-lm49352/Documentation/dontdiff linux-2.6.24/sound/soc/codecs/lm49352.h linux-2.6.24-lm49352/sound/soc/codecs/lm49352.h
--- linux-2.6.24/sound/soc/codecs/lm49352.h     1970-01-01 05:30:00.000000000 +0530
+++ linux-2.6.24-lm49352/sound/soc/codecs/lm49352.h     2011-03-14 13:03:17.000000000 +0530
@@ -0,0 +1,90 @@
+/*
+ * 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 -upNr -X linux-2.6.24-lm49352/Documentation/dontdiff linux-2.6.24/sound/soc/codecs/Makefile linux-2.6.24-lm49352/sound/soc/codecs/Makefile
--- linux-2.6.24/sound/soc/codecs/Makefile      2011-03-14 14:36:10.000000000 +0530
+++ linux-2.6.24-lm49352/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-wm9712-objs := wm9712.o
 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

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

* Re: [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support
  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
  0 siblings, 1 reply; 7+ messages in thread
From: Mark Brown @ 2011-03-16 10:08 UTC (permalink / raw)
  To: Reddy, MR Swami; +Cc: alsa-devel@alsa-project.org, Liam Girdwood

On Wed, Mar 16, 2011 at 12:38:57AM -0700, Reddy, MR Swami wrote:

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

As previously mentioned you need to submit code against current Linux
versions.  Linux 2.6.24 is over three years old and development of the
kernel has moved on substantially in that time.

> 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.

As previously mentioned this is pretty much up to you - you can apply
a range of techniques, either from starting from scratch or reviewing
the kernel changelogs for the core and other drivers and making gradual
changes to forward port.  Which approach works best for you is largely a
matter of personal preference.  If you have questions on specific things
then please feel free to ask.

I've had a brief glance at the driver and there are a number of obvious
coding style issues, please before resubmitting review the coding style
you are using and ensure that your driver is following a similar coding
style to the existing kernel code.  For example comments like this:

> +/***************************************************************************
> + * 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)

are not at all idiomatic for the kernel.

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

* Re: [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support
  2011-03-16 10:08   ` Mark Brown
@ 2011-03-16 13:38     ` Reddy, MR Swami
  2011-03-16 14:35       ` Mark Brown
  0 siblings, 1 reply; 7+ messages in thread
From: Reddy, MR Swami @ 2011-03-16 13:38 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel@alsa-project.org, Liam Girdwood

>From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] 

>As previously mentioned this is pretty much up to you - you can apply a range of techniques, either from starting from >scratch or reviewing the kernel changelogs for the core and other drivers and making gradual changes to forward port.  >Which approach works best for you is largely a matter of personal preference.  If you have questions on specific things >then please feel free to ask.

OK. I will look into the kernel changelogs and update the LM49352 driver to use the latest ALSA APIs and sync with latest kernel sources. If there are any other comments (ie apart from the coding standards), please let me know.


>I've had a brief glance at the driver and there are a number of obvious coding style issues, please before resubmitting >review the coding style you are using and ensure that your driver is following a similar coding style to the existing >kernel code.  For example comments like this:

I ran the scripts/chcekpatch.pl script for lm49352.c and lm49352.h file, there no warnings/errors. 
OK, I could remove the comments for all functions in the next patch submission.

Thanks
Swami

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

* Re: [PATCH] ASoC: Add National Semiconductor LM49352 Codec Support
  2011-03-16 13:38     ` Reddy, MR Swami
@ 2011-03-16 14:35       ` Mark Brown
  0 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2011-03-16 14:35 UTC (permalink / raw)
  To: Reddy, MR Swami; +Cc: alsa-devel@alsa-project.org, Liam Girdwood

On Wed, Mar 16, 2011 at 06:38:15AM -0700, Reddy, MR Swami wrote:
> >From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] 

Please fix your MUA to word wrap at less than 80 columns within
paragraphs.

> >As previously mentioned this is pretty much up to you - you can apply
> >a range of techniques, either from starting from >scratch or
> >reviewing the kernel changelogs for the core and other drivers and
> >making gradual changes to forward port.  >Which approach works best
> >for you is largely a matter of personal preference.  If you have
> >questions on specific things >then please feel free to ask.

> OK. I will look into the kernel changelogs and update the LM49352
> driver to use the latest ALSA APIs and sync with latest kernel
> sources. If there are any other comments (ie apart from the coding
> standards), please let me know.

Given the great age of the kernel you were developing against I didn't
really look at the code, there has been substantial change in the kernel
since then.

> >I've had a brief glance at the driver and there are a number of
> >obvious coding style issues, please before resubmitting >review the
> >coding style you are using and ensure that your driver is following a
> >similar coding style to the existing >kernel code.  For example
> >comments like this:

> I ran the scripts/chcekpatch.pl script for lm49352.c and lm49352.h
> file, there no warnings/errors.  OK, I could remove the comments for
> all functions in the next patch submission.

It's not about passing checkpatch, it's about making sure your code
looks like other similar code.  If you look at your code next to
another, similar driver and it's easy to tell which is which visually
there's an issue.  checkpatch is a useful tool but it's only part of
making sure your code is idiomatic for the kernel.

^ 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).