add es8323 codec for pcm modem
author陈金泉 <chenjq@rock-chips.com>
Mon, 1 Jul 2013 09:31:52 +0000 (17:31 +0800)
committer陈金泉 <chenjq@rock-chips.com>
Mon, 1 Jul 2013 09:31:52 +0000 (17:31 +0800)
drivers/misc/modem_sound.c
drivers/misc/modem_sound.h
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/es8323_pcm.c [new file with mode: 0755]
sound/soc/rk29/Kconfig

index 41ff7c3e8e1678243e53cd1079d87f8b35e788be..504b1fc089f40901765ee25ca9cbecfd92800627 100755 (executable)
 #define DISABLE            0\r
 \r
 static struct modem_sound_data *modem_sound;\r
+int (*set_codec_for_pcm_modem)(int cmd) = NULL; /* Set the codec used only for PCM modem */\r
+void (*set_codec_spk)(int on) = NULL;\r
 #if defined(CONFIG_SND_RK_SOC_RK2928)|| defined(CONFIG_SND_RK29_SOC_RK610)\r
 extern void call_set_spk(int on);\r
 #endif\r
+#ifdef CONFIG_SND_SOC_ES8323_PCM\r
+extern int set_es8323(int cmd);\r
+#endif\r
+\r
 #define HP_MIC 0\r
 #define MAIN_MIC 1\r
 #if defined(CONFIG_MODEM_MIC_SWITCH)\r
@@ -86,14 +92,14 @@ static ssize_t modem_sound_read(struct file *filp, char __user *ptr, size_t size
 \r
 static long modem_sound_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\r
 {\r
-       long ret = 0;\r
+       int ret = 0, codec_pcm_cmd = -1, codec_cmd = -1, modem_spk_enable = -1;\r
        struct modem_sound_data *pdata = modem_sound;\r
 \r
        DBG("modem_sound_ioctl: cmd = %d arg = %ld\n",cmd, arg);\r
 \r
        ret = down_interruptible(&pdata->power_sem);\r
        if (ret < 0) {\r
-               printk("%s: down power_sem error ret = %ld\n", __func__, ret);\r
+               printk("%s: down power_sem error ret = %d\n", __func__, ret);\r
                return ret;\r
        }\r
 \r
@@ -101,47 +107,70 @@ static long modem_sound_ioctl(struct file *filp, unsigned int cmd, unsigned long
                case IOCTL_MODEM_EAR_PHOEN:\r
                        DBG("modem_sound_ioctl: MODEM_EAR_PHONE\n");\r
                        Modem_Sound_Mic_switch(MAIN_MIC);\r
-                       call_set_spk(3);\r
-                       modem_sound_spkctl(DISABLE);\r
+                       codec_cmd = 3;\r
+                       codec_pcm_cmd = RCV;\r
+                       modem_spk_enable = DISABLE;\r
                        break;\r
                case IOCTL_MODEM_SPK_PHONE:\r
                        DBG("modem_sound_ioctl: MODEM_SPK_PHONE\n");\r
                        Modem_Sound_Mic_switch(MAIN_MIC);\r
-                       call_set_spk(1);\r
-                       modem_sound_spkctl(ENABLE);\r
+                       codec_cmd = 1;\r
+                       codec_pcm_cmd = SPK_PATH;\r
+                       modem_spk_enable = ENABLE;\r
                        break;\r
                case IOCTL_MODEM_HP_WITHMIC_PHONE:\r
                        DBG("modem_sound_ioctl: MODEM_HP_WITHMIC_PHONE\n");\r
                        Modem_Sound_Mic_switch(HP_MIC);\r
-                       call_set_spk(2);\r
-                       modem_sound_spkctl(DISABLE);\r
+                       codec_cmd = 2;\r
+                       codec_pcm_cmd = HP_PATH;\r
+                       modem_spk_enable = DISABLE;\r
                        break;\r
-                       \r
                case IOCTL_MODEM_BT_PHONE:\r
-                       call_set_spk(3);\r
-                       modem_sound_spkctl(DISABLE);\r
+                       codec_cmd = 3;\r
+                       codec_pcm_cmd = BT;\r
+                       modem_spk_enable = DISABLE;\r
                        DBG("modem_sound_ioctl: MODEM_BT_PHONE\n");\r
                        break;\r
                case IOCTL_MODEM_STOP_PHONE:\r
                        DBG("modem_sound_ioctl: MODEM_STOP_PHONE\n");\r
                        Modem_Sound_Mic_release();\r
-                       call_set_spk(0);\r
-                       modem_sound_spkctl(ENABLE);\r
+                       codec_cmd = 0;\r
+                       codec_pcm_cmd = OFF;\r
+                       modem_spk_enable = ENABLE;\r
                        break;\r
                case IOCTL_MODEM_HP_NOMIC_PHONE:\r
                         DBG("modem_sound_ioctl: MODEM_HP_NOMIC_PHONE\n");\r
                        Modem_Sound_Mic_switch(MAIN_MIC);\r
-                        call_set_spk(2);\r
-                        modem_sound_spkctl(DISABLE);\r
+                       codec_cmd = 2;\r
+                       codec_pcm_cmd = HP_NO_MIC;\r
+                       modem_spk_enable = DISABLE;\r
                         break;\r
-\r
-\r
                default:\r
                        printk("unknown ioctl cmd!\n");\r
                        ret = -EINVAL;\r
                        break;\r
        }\r
 \r
+       if (set_codec_spk == NULL || set_codec_for_pcm_modem == NULL)\r
+               printk("modem_sound_ioctl(), %s %s\n",\r
+                       set_codec_for_pcm_modem ? "" : "set_codec_for_pcm_modem is NULL",\r
+                       set_codec_spk ? "" : "set_codec_spk is NULL");\r
+\r
+       if (ret >= 0) {\r
+               // close codec firstly for pop noise\r
+               if (codec_cmd == 0 && set_codec_spk)\r
+                       set_codec_spk(codec_cmd);\r
+\r
+               if (set_codec_for_pcm_modem)\r
+                       set_codec_for_pcm_modem(codec_pcm_cmd);\r
+\r
+               modem_sound_spkctl(modem_spk_enable);\r
+\r
+               // open codec lastly for pop noise\r
+               if (codec_cmd != 0 && set_codec_spk)\r
+                       set_codec_spk(codec_cmd);\r
+       }\r
+\r
        up(&pdata->power_sem);\r
 \r
        return ret;\r
@@ -175,7 +204,7 @@ static int modem_sound_probe(struct platform_device *pdev)
        struct modem_sound_data *pdata = pdev->dev.platform_data;\r
        if(!pdata)\r
                return -1;\r
-               \r
+\r
        ret = misc_register(&modem_sound_dev);\r
        if (ret < 0){\r
                printk("modem register err!\n");\r
@@ -188,6 +217,13 @@ static int modem_sound_probe(struct platform_device *pdev)
        modem_sound = pdata;\r
        printk("%s:modem sound initialized\n",__FUNCTION__);\r
 \r
+#if defined(CONFIG_SND_RK_SOC_RK2928)|| defined(CONFIG_SND_RK29_SOC_RK610)\r
+       set_codec_spk = call_set_spk;\r
+#endif\r
+#ifdef CONFIG_SND_SOC_ES8323_PCM\r
+       set_codec_for_pcm_modem = set_es8323;\r
+#endif\r
+\r
        return ret;\r
 }\r
 \r
index c3985661346c63d70fbe2aab0435d8fd5fd1f859..35b1d0ea0a1d79e12ab2326f398b7b7f7a5b44f5 100755 (executable)
 #define IOCTL_SET_HP_WITHMIC_VALUME                _IO(MODEM_SOUND, 0x13) \r
 #define IOCTL_SET_BT_VALUME                _IO(MODEM_SOUND, 0x14) \r
 #define IOCTL_SET_HP_NOMIC_PHONE            _IO(MODEM_SOUND, 0x15)\r
\r
+\r
+enum {\r
+       OFF,\r
+       RCV,\r
+       SPK_PATH,\r
+       HP_PATH,\r
+       HP_NO_MIC,\r
+       BT,\r
+};\r
 \r
 struct modem_sound_data {\r
        int spkctl_io;\r
index 04e27b2582a0375f1814536e81c631b42477c816..5d7569b3f47574adecd272be39000fe56d038750 100755 (executable)
@@ -33,6 +33,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_DA7210 if I2C
        select SND_SOC_DFBMCS320
        select SND_SOC_ES8323 if SND_SOC_I2C_AND_SPI
+       select SND_SOC_ES8323_PCM if SND_SOC_I2C_AND_SPI
        select SND_SOC_JZ4740_CODEC if SOC_JZ4740
        select SND_SOC_LM4857 if I2C
        select SND_SOC_MAX98088 if I2C
index 813ae0a4752280bbf5bf41738524b20709c2b79c..e6cd8add114700b9df8f6451e86a50869ba1e110 100755 (executable)
@@ -19,6 +19,7 @@ snd-soc-da7210-objs := da7210.o
 snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-es8323-objs := es8323.o
+snd-soc-es8323-pcm-objs := es8323_pcm.o
 snd-soc-l3-objs := l3.o
 snd-soc-max98088-objs := max98088.o
 snd-soc-max98095-objs := max98095.o
@@ -133,6 +134,7 @@ obj-$(CONFIG_SND_SOC_DA7210)        += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DFBMCS320)        += snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DMIC)     += snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_ES8323)   += snd-soc-es8323.o
+obj-$(CONFIG_SND_SOC_ES8323_PCM)       += snd-soc-es8323-pcm.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
diff --git a/sound/soc/codecs/es8323_pcm.c b/sound/soc/codecs/es8323_pcm.c
new file mode 100755 (executable)
index 0000000..c5f42d2
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * es8323.c -- es8323 ALSA SoC audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "es8323.h"
+
+//#define ES8323_PROC
+#ifdef ES8323_PROC
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#endif
+
+#if 1
+#define        DBG(x...)       printk(KERN_INFO x)
+#else
+#define        DBG(x...)
+#endif
+
+enum {
+       OFF,
+       RCV,
+       SPK_PATH,
+       HP_PATH,
+       HP_NO_MIC,
+       BT,
+};
+
+static struct i2c_client *i2c_client;
+int es8323_codec_state = OFF;
+
+static int codec_write(struct i2c_client *client, unsigned int reg,
+                             unsigned int value)
+{
+       u8 data[2];
+
+       data[0] = reg;
+       data[1] = value & 0x00ff;
+
+       //printk("%s: reg=0x%x value=0x%x\n",__func__,reg,value);
+       if (i2c_master_send(client, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static unsigned int codec_read(struct i2c_client *client,
+                                         unsigned int r)
+{
+       struct i2c_msg xfer[2];
+       u8 reg = r;
+       u16 data;
+       int ret;
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+       xfer[0].scl_rate = 100 * 1000;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+       xfer[1].scl_rate = 100 * 1000;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+               return 0;
+       }
+       //printk("%s: reg=0x%x value=0x%x\n",__func__,reg,(data >> 8) | ((data & 0xff) << 8));
+
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static int es8323_reg_init(struct i2c_client *client, bool main_mic)
+{
+       if (es8323_codec_state != OFF) {
+               if (main_mic) {
+                       codec_write(client, 0x0b,0x82);  //ADC INPUT=LIN2/RIN2 //82
+               } else {
+                       codec_write(client, 0x0b,0x02);  //ADC INPUT=LIN1/RIN1 //02
+               }
+
+               DBG("es8323_reg_init() change to %s\n",
+                       main_mic ? "main mic" : "headset mic");
+               return 0;
+       }
+
+       codec_write(client,  0x08, 0x00); //slave 0x00, master 0x80
+       codec_write(client,  0x02, 0xf3);
+       codec_write(client,  0x2b, 0x80); //use ADC LRCK, slave 0x80, master 0xc0
+       codec_write(client,  0x00, 0x36); //DACMCLK is the chip master clock source
+       codec_write(client,  0x01, 0x00); //all normal
+       codec_write(client,  0x03, 0x00); //all normal
+       codec_write(client,  0x04, 0x3c); //L/R DAC power up, L/R out1 enable
+       codec_write(client,  0x05, 0x00); //normal
+       codec_write(client,  0x06, 0x00); //normal
+       codec_write(client,  0x07, 0x7c);
+       codec_write(client,  0x09, 0x88); //MIC GAIN=24dB
+       codec_write(client,  0x0a, 0xf0); //L-R diff
+       if (main_mic) {
+               codec_write(client, 0x0b,0x82);  //ADC INPUT=LIN2/RIN2 //82
+       } else {
+               codec_write(client, 0x0b,0x02);  //ADC INPUT=LIN1/RIN1 //02
+       }
+       codec_write(client,  0x0c, 0x23); //PCM,16bit,2nd
+       codec_write(client,  0x0d, 0x02);
+       codec_write(client,  0x0f, 0xf0); //unmute ADC
+       codec_write(client,  0x10, 0x00);
+       codec_write(client,  0x11, 0x00);
+       codec_write(client,  0x12, 0x2a); //ALC off
+       codec_write(client,  0x13, 0xC0); //ALC
+       codec_write(client,  0x14, 0x05); //ALC
+       codec_write(client,  0x15, 0x06); //ALC
+       codec_write(client,  0x16, 0xb3); //ALC
+       codec_write(client,  0x17, 0x16); //PCM, 2nd,16bit
+       codec_write(client,  0x18, 0x02); // MCLK/256
+       codec_write(client,  0x19, 0x22);
+       codec_write(client,  0x1a, 0x00); //lout digital
+       codec_write(client,  0x1b, 0x00); //rout digital
+       codec_write(client,  0x26, 0x00);
+       codec_write(client,  0x27, 0xb8); //LD2LO to left mixer
+       codec_write(client,  0x28, 0x38);
+       codec_write(client,  0x29, 0x38);
+       codec_write(client,  0x2a, 0xb8); //RD2RO to right mixer
+       codec_write(client,  0x30, 0x19);
+       codec_write(client,  0x31, 0x19);
+       codec_write(client,  0x02, 0x00);
+
+       DBG("es8323_reg_init() set codec route with %s\n",
+               main_mic ? "main mic" : "headset mic");
+
+       return 0;
+}
+
+static int es8323_reset(struct i2c_client *client)
+{
+       es8323_codec_state = OFF;
+
+       codec_write(client, ES8323_CONTROL1, 0x80);
+       return codec_write(client, ES8323_CONTROL1, 0x00);
+}
+
+int set_es8323(int cmd)
+{
+       DBG("%s : set voice_call_path = %d\n", __func__,
+               cmd);
+
+       if (i2c_client == NULL) {
+               printk("%s : i2c_client is NULL!\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (cmd) {
+       case OFF:
+               es8323_reset(i2c_client);
+               break;
+       case HP_PATH:
+               es8323_reg_init(i2c_client, 0);
+               break;
+       case RCV:
+       case SPK_PATH:
+       case HP_NO_MIC:
+               es8323_reg_init(i2c_client, 1);
+               break;
+       case BT:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       es8323_codec_state = cmd;
+
+       return 0;
+}
+
+EXPORT_SYMBOL(set_es8323);
+
+static const struct i2c_device_id es8323_i2c_id[] = {
+       { "es8323-pcm", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
+
+static int es8323_proc_init(void);
+
+static int __devinit es8323_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       DBG("%s\n", __func__);
+
+       #ifdef ES8323_PROC      
+       es8323_proc_init();
+       #endif
+
+       i2c_client = i2c;
+       es8323_reset(i2c);
+
+       return 0;
+}
+
+static int __devexit es8323_i2c_remove(struct i2c_client *i2c)
+{
+       return 0;
+}
+
+struct i2c_driver es8323_i2c_driver = {
+       .driver = {
+               .name = "es8323-pcm",
+               .owner = THIS_MODULE,
+       },
+       .probe = es8323_i2c_probe,
+       .remove   = __devexit_p(es8323_i2c_remove),
+       .id_table = es8323_i2c_id,
+};
+
+static int __init es8323_modinit(void)
+{
+       return i2c_add_driver(&es8323_i2c_driver);
+}
+late_initcall(es8323_modinit);
+
+static void __exit es8323_modexit(void)
+{
+       i2c_del_driver(&es8323_i2c_driver);
+}
+module_exit(es8323_modexit);
+
+MODULE_DESCRIPTION("ASoC ES8323 driver");
+MODULE_AUTHOR("Jear");
+MODULE_LICENSE("GPL");
+
+
+#ifdef ES8323_PROC
+
+static ssize_t es8323_proc_write(struct file *file, const char __user *buffer,
+               unsigned long len, void *data)
+{
+       char *cookie_pot; 
+       char *p;
+       int reg;
+       int value;
+
+       cookie_pot = (char *)vmalloc( len );
+       if (!cookie_pot) 
+       {
+               return -ENOMEM;
+       } 
+       else 
+       {
+               if (copy_from_user( cookie_pot, buffer, len )) 
+                       return -EFAULT;
+       }
+
+       switch(cookie_pot[0])
+       {
+               case 'r':
+               case 'R':
+                       printk("Read reg debug\n");             
+                       if(cookie_pot[1] ==':')
+                       {
+                               strsep(&cookie_pot,":");
+                               while((p=strsep(&cookie_pot,",")))
+                               {
+                                       reg = simple_strtol(p,NULL,16);
+                                       value = codec_read(i2c_client,reg);
+                                       printk("codec_read:0x%04x = 0x%04x\n",reg,value);
+                               }
+                               printk("\n");
+                       }
+                       else
+                       {
+                               printk("Error Read reg debug.\n");
+                               printk("For example: echo r:22,23,24,25>es8323_ts\n");
+                       }
+                       break;
+               case 'w':
+               case 'W':
+                       printk("Write reg debug\n");            
+                       if(cookie_pot[1] ==':')
+                       {
+                               strsep(&cookie_pot,":");
+                               while((p=strsep(&cookie_pot,"=")))
+                               {
+                                       reg = simple_strtol(p,NULL,16);
+                                       p=strsep(&cookie_pot,",");
+                                       value = simple_strtol(p,NULL,16);
+                                       codec_write(i2c_client,reg,value);
+                                       printk("codec_write:0x%04x = 0x%04x\n",reg,value);
+                               }
+                               printk("\n");
+                       }
+                       else
+                       {
+                               printk("Error Write reg debug.\n");
+                               printk("For example: w:22=0,23=0,24=0,25=0>es8323_ts\n");
+                       }
+                       break;
+               case 'a':
+                       printk("Dump reg \n");          
+
+                       for(reg = 0; reg < 0x6e; reg+=2)
+                       {
+                               value = codec_read(i2c_client,reg);
+                               printk("codec_read:0x%04x = 0x%04x\n",reg,value);
+                       }
+
+                       break;          
+               default:
+                       printk("Help for es8323_ts .\n-->The Cmd list: \n");
+                       printk("-->'d&&D' Open or Off the debug\n");
+                       printk("-->'r&&R' Read reg debug,Example: echo 'r:22,23,24,25'>es8323_ts\n");
+                       printk("-->'w&&W' Write reg debug,Example: echo 'w:22=0,23=0,24=0,25=0'>es8323_ts\n");
+                       break;
+       }
+
+       return len;
+}
+
+static const struct file_operations es8323_proc_fops = {
+       .owner          = THIS_MODULE,
+};
+
+static int es8323_proc_init(void)
+{
+       struct proc_dir_entry *es8323_proc_entry;
+       es8323_proc_entry = create_proc_entry("driver/es8323_pcm_ts", 0777, NULL);
+       if(es8323_proc_entry != NULL)
+       {
+               es8323_proc_entry->write_proc = es8323_proc_write;
+               return 0;
+       }
+       else
+       {
+               printk("create proc error !\n");
+               return -1;
+       }
+}
+#endif
index 9a809966f77ad3c5616a7445449056953a3c6356..f6cf9f110cb1fbafbd6575aa4434da30b5827b19 100755 (executable)
@@ -95,6 +95,13 @@ config SND_RK29_SOC_ES8323
          Say Y if you want to add support for SoC audio on rockchip
          with the ES8323.
 
+config SND_SOC_ES8323_PCM
+       tristate "SoC I2S Audio support for rockchip - ES8323 for PCM modem"
+       depends on SND_RK29_SOC
+       help
+         Say Y if you want to add support for SoC audio on rockchip
+         with the ES8323 for PCM modem.
+
 config SND_RK29_SOC_WM8988
        tristate "SoC I2S Audio support for rockchip - WM8988"
        depends on SND_RK29_SOC