From 1dbd249045672d85c1ebf3482fa73012f5da7048 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E9=87=91=E6=B3=89?= Date: Mon, 1 Jul 2013 17:31:52 +0800 Subject: [PATCH] add es8323 codec for pcm modem --- drivers/misc/modem_sound.c | 72 +++++-- drivers/misc/modem_sound.h | 10 +- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/es8323_pcm.c | 363 ++++++++++++++++++++++++++++++++++ sound/soc/rk29/Kconfig | 7 + 6 files changed, 436 insertions(+), 19 deletions(-) create mode 100755 sound/soc/codecs/es8323_pcm.c diff --git a/drivers/misc/modem_sound.c b/drivers/misc/modem_sound.c index 41ff7c3e8e16..504b1fc089f4 100755 --- a/drivers/misc/modem_sound.c +++ b/drivers/misc/modem_sound.c @@ -23,9 +23,15 @@ #define DISABLE 0 static struct modem_sound_data *modem_sound; +int (*set_codec_for_pcm_modem)(int cmd) = NULL; /* Set the codec used only for PCM modem */ +void (*set_codec_spk)(int on) = NULL; #if defined(CONFIG_SND_RK_SOC_RK2928)|| defined(CONFIG_SND_RK29_SOC_RK610) extern void call_set_spk(int on); #endif +#ifdef CONFIG_SND_SOC_ES8323_PCM +extern int set_es8323(int cmd); +#endif + #define HP_MIC 0 #define MAIN_MIC 1 #if defined(CONFIG_MODEM_MIC_SWITCH) @@ -86,14 +92,14 @@ static ssize_t modem_sound_read(struct file *filp, char __user *ptr, size_t size static long modem_sound_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - long ret = 0; + int ret = 0, codec_pcm_cmd = -1, codec_cmd = -1, modem_spk_enable = -1; struct modem_sound_data *pdata = modem_sound; DBG("modem_sound_ioctl: cmd = %d arg = %ld\n",cmd, arg); ret = down_interruptible(&pdata->power_sem); if (ret < 0) { - printk("%s: down power_sem error ret = %ld\n", __func__, ret); + printk("%s: down power_sem error ret = %d\n", __func__, ret); return ret; } @@ -101,47 +107,70 @@ static long modem_sound_ioctl(struct file *filp, unsigned int cmd, unsigned long case IOCTL_MODEM_EAR_PHOEN: DBG("modem_sound_ioctl: MODEM_EAR_PHONE\n"); Modem_Sound_Mic_switch(MAIN_MIC); - call_set_spk(3); - modem_sound_spkctl(DISABLE); + codec_cmd = 3; + codec_pcm_cmd = RCV; + modem_spk_enable = DISABLE; break; case IOCTL_MODEM_SPK_PHONE: DBG("modem_sound_ioctl: MODEM_SPK_PHONE\n"); Modem_Sound_Mic_switch(MAIN_MIC); - call_set_spk(1); - modem_sound_spkctl(ENABLE); + codec_cmd = 1; + codec_pcm_cmd = SPK_PATH; + modem_spk_enable = ENABLE; break; case IOCTL_MODEM_HP_WITHMIC_PHONE: DBG("modem_sound_ioctl: MODEM_HP_WITHMIC_PHONE\n"); Modem_Sound_Mic_switch(HP_MIC); - call_set_spk(2); - modem_sound_spkctl(DISABLE); + codec_cmd = 2; + codec_pcm_cmd = HP_PATH; + modem_spk_enable = DISABLE; break; - case IOCTL_MODEM_BT_PHONE: - call_set_spk(3); - modem_sound_spkctl(DISABLE); + codec_cmd = 3; + codec_pcm_cmd = BT; + modem_spk_enable = DISABLE; DBG("modem_sound_ioctl: MODEM_BT_PHONE\n"); break; case IOCTL_MODEM_STOP_PHONE: DBG("modem_sound_ioctl: MODEM_STOP_PHONE\n"); Modem_Sound_Mic_release(); - call_set_spk(0); - modem_sound_spkctl(ENABLE); + codec_cmd = 0; + codec_pcm_cmd = OFF; + modem_spk_enable = ENABLE; break; case IOCTL_MODEM_HP_NOMIC_PHONE: DBG("modem_sound_ioctl: MODEM_HP_NOMIC_PHONE\n"); Modem_Sound_Mic_switch(MAIN_MIC); - call_set_spk(2); - modem_sound_spkctl(DISABLE); + codec_cmd = 2; + codec_pcm_cmd = HP_NO_MIC; + modem_spk_enable = DISABLE; break; - - default: printk("unknown ioctl cmd!\n"); ret = -EINVAL; break; } + if (set_codec_spk == NULL || set_codec_for_pcm_modem == NULL) + printk("modem_sound_ioctl(), %s %s\n", + set_codec_for_pcm_modem ? "" : "set_codec_for_pcm_modem is NULL", + set_codec_spk ? "" : "set_codec_spk is NULL"); + + if (ret >= 0) { + // close codec firstly for pop noise + if (codec_cmd == 0 && set_codec_spk) + set_codec_spk(codec_cmd); + + if (set_codec_for_pcm_modem) + set_codec_for_pcm_modem(codec_pcm_cmd); + + modem_sound_spkctl(modem_spk_enable); + + // open codec lastly for pop noise + if (codec_cmd != 0 && set_codec_spk) + set_codec_spk(codec_cmd); + } + up(&pdata->power_sem); return ret; @@ -175,7 +204,7 @@ static int modem_sound_probe(struct platform_device *pdev) struct modem_sound_data *pdata = pdev->dev.platform_data; if(!pdata) return -1; - + ret = misc_register(&modem_sound_dev); if (ret < 0){ printk("modem register err!\n"); @@ -188,6 +217,13 @@ static int modem_sound_probe(struct platform_device *pdev) modem_sound = pdata; printk("%s:modem sound initialized\n",__FUNCTION__); +#if defined(CONFIG_SND_RK_SOC_RK2928)|| defined(CONFIG_SND_RK29_SOC_RK610) + set_codec_spk = call_set_spk; +#endif +#ifdef CONFIG_SND_SOC_ES8323_PCM + set_codec_for_pcm_modem = set_es8323; +#endif + return ret; } diff --git a/drivers/misc/modem_sound.h b/drivers/misc/modem_sound.h index c3985661346c..35b1d0ea0a1d 100755 --- a/drivers/misc/modem_sound.h +++ b/drivers/misc/modem_sound.h @@ -17,7 +17,15 @@ #define IOCTL_SET_HP_WITHMIC_VALUME _IO(MODEM_SOUND, 0x13) #define IOCTL_SET_BT_VALUME _IO(MODEM_SOUND, 0x14) #define IOCTL_SET_HP_NOMIC_PHONE _IO(MODEM_SOUND, 0x15) - + +enum { + OFF, + RCV, + SPK_PATH, + HP_PATH, + HP_NO_MIC, + BT, +}; struct modem_sound_data { int spkctl_io; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 04e27b2582a0..5d7569b3f475 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -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 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 813ae0a47522..e6cd8add1147 100755 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -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 index 000000000000..c5f42d2489bc --- /dev/null +++ b/sound/soc/codecs/es8323_pcm.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "es8323.h" + +//#define ES8323_PROC +#ifdef ES8323_PROC +#include +#include +#include +#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 = ® + 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 diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 9a809966f77a..f6cf9f110cb1 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -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 -- 2.34.1