From: 邱建斌 Date: Fri, 19 Oct 2012 02:48:56 +0000 (+0800) Subject: rk2928phonepad: X-Git-Tag: firefly_0821_release~8366 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=0f47bc20b9c49af5e3deaba0c58df3768e8ee302;p=firefly-linux-kernel-4.4.55.git rk2928phonepad: add support rk2928 codec for phonepad --- diff --git a/arch/arm/mach-rk2928/board-rk2928-phonepad.c b/arch/arm/mach-rk2928/board-rk2928-phonepad.c index 36905f67c6ca..66dc02bead5e 100755 --- a/arch/arm/mach-rk2928/board-rk2928-phonepad.c +++ b/arch/arm/mach-rk2928/board-rk2928-phonepad.c @@ -46,9 +46,13 @@ #include #include #include +#if defined(CONFIG_MODEM_SOUND) +#include "../../../drivers/misc/modem_sound.h" +#endif #if defined(CONFIG_HDMI_RK30) #include "../../../drivers/video/rockchip/hdmi/rk_hdmi.h" #endif +#include "../../../drivers/headset_observe/rk_headset.h" #if defined(CONFIG_SPIM_RK29) #include "../../../drivers/spi/rk29_spim.h" @@ -500,7 +504,7 @@ static struct rk30_adc_battery_platform_data rk30_adc_battery_platdata = { .batt_low_pin = INVALID_GPIO, .charge_set_pin = INVALID_GPIO, .charge_ok_pin = RK2928_PIN1_PA0, - .dc_det_level = GPIO_LOW, // + .dc_det_level = GPIO_HIGH, // .charge_ok_level = GPIO_HIGH, }; @@ -567,6 +571,22 @@ struct platform_device pwm_regulator_device[1] = { }, }; #endif + +#if defined(CONFIG_MODEM_SOUND) + +struct modem_sound_data modem_sound_info = { + .spkctl_io = RK2928_PIN3_PD4, + .spkctl_active = GPIO_HIGH, +}; + +struct platform_device modem_sound_device = { + .name = "modem_sound", + .id = -1, + .dev = { + .platform_data = &modem_sound_info, + } + }; +#endif /************************************************************************************************** * SDMMC devices, include the module of SD,MMC,and sdio.noted by xbw at 2012-03-05 **************************************************************************************************/ @@ -736,6 +756,53 @@ struct platform_device rk29_device_sc6610 = { } }; #endif +#if defined (CONFIG_RK_HEADSET_DET) || defined (CONFIG_RK_HEADSET_IRQ_HOOK_ADC_DET) +static int rk_headset_io_init(int gpio, char *iomux_name, int iomux_mode) +{ + int ret; + ret = gpio_request(gpio, "headset_io"); + if(ret) + return ret; + + rk30_mux_api_set(iomux_name, iomux_mode); + gpio_pull_updown(gpio, PullDisable); + gpio_direction_input(gpio); + mdelay(50); + return 0; +}; + +static int rk_hook_io_init(int gpio, char *iomux_name, int iomux_mode) +{ + int ret; + ret = gpio_request(gpio, "hook_io"); + if(ret) + return ret; + + rk30_mux_api_set(iomux_name, iomux_mode); + gpio_pull_updown(gpio, PullDisable); + gpio_direction_input(gpio); + mdelay(50); + return 0; +}; + +struct rk_headset_pdata rk_headset_info = { + .Headset_gpio = RK2928_PIN1_PB4, + .Hook_gpio = RK2928_PIN0_PD1, + .headset_in_type = HEADSET_IN_HIGH, + .hook_key_code = KEY_MEDIA, + .headset_gpio_info = {GPIO1B4_SPI_CSN1_NAME, GPIO1B_GPIO1B4}, + .headset_io_init = rk_headset_io_init, + .hook_gpio_info = {GPIO0D1_UART2_CTSN_NAME, GPIO0D_GPIO0D1}, + .hook_io_init = rk_hook_io_init, +}; +struct platform_device rk_device_headset = { + .name = "rk_headsetdet", + .id = 0, + .dev = { + .platform_data = &rk_headset_info, + } +}; +#endif #ifdef CONFIG_SND_SOC_RK2928 static struct resource resources_acodec[] = { { @@ -782,6 +849,12 @@ static struct platform_device *devices[] __initdata = { &rk29_device_sc6610, #endif +#if defined (CONFIG_RK_HEADSET_DET) || defined (CONFIG_RK_HEADSET_IRQ_HOOK_ADC_DET) + &rk_device_headset, +#endif +#if defined (CONFIG_MODEM_SOUND) + &modem_sound_device, +#endif }; //i2c #ifdef CONFIG_I2C0_RK30 diff --git a/drivers/headset_observe/rk_headset.c b/drivers/headset_observe/rk_headset.c index e9f39d805ed6..3365da599cfa 100755 --- a/drivers/headset_observe/rk_headset.c +++ b/drivers/headset_observe/rk_headset.c @@ -61,7 +61,9 @@ #define enable 1 #define disable 0 - +#ifdef CONFIG_SND_RK_SOC_RK2928 +extern void rk2928_codec_set_spk(bool on); +#endif #ifdef CONFIG_SND_SOC_WM8994 extern int wm8994_set_status(void); #endif @@ -92,6 +94,16 @@ int Headset_isMic(void) } EXPORT_SYMBOL_GPL(Headset_isMic); +int Headset_status(void) +{ + if(headset_info->cur_headset_status == BIT_HEADSET_NO_MIC || + headset_info->cur_headset_status == BIT_HEADSET ) + return HEADSET_IN; + else + return HEADSET_OUT; +} +EXPORT_SYMBOL_GPL(Headset_status); + static irqreturn_t headset_interrupt(int irq, void *dev_id) { DBG("---headset_interrupt---\n"); @@ -210,6 +222,9 @@ static void headsetobserve_work(struct work_struct *work) disable_irq(headset_info->irq[HOOK]); } headset_info->cur_headset_status = ~(BIT_HEADSET|BIT_HEADSET_NO_MIC); + #ifdef CONFIG_SND_RK_SOC_RK2928 + rk2928_codec_set_spk(HEADSET_OUT); + #endif headset_change_irqtype(HEADSET,IRQF_TRIGGER_RISING);// rk28_send_wakeup_key(); switch_set_state(&headset_info->sdev, headset_info->cur_headset_status); @@ -237,7 +252,10 @@ static void headsetobserve_work(struct work_struct *work) DBG("disable headset_hook irq\n"); headset_info->isHook_irq = disable; disable_irq(headset_info->irq[HOOK]); - } + } + #ifdef CONFIG_SND_RK_SOC_RK2928 + rk2928_codec_set_spk(HEADSET_OUT); + #endif headset_info->cur_headset_status = ~(BIT_HEADSET|BIT_HEADSET_NO_MIC); headset_change_irqtype(HEADSET,IRQF_TRIGGER_FALLING);// rk28_send_wakeup_key(); @@ -378,7 +396,9 @@ static void headset_timer_callback(unsigned long arg) headset_info->cur_headset_status = BIT_HEADSET; printk("headset->isMic = %d\n",headset->isMic); } - + #ifdef CONFIG_SND_RK_SOC_RK2928 + rk2928_codec_set_spk(HEADSET_IN); + #endif rk28_send_wakeup_key(); switch_set_state(&headset_info->sdev, headset_info->cur_headset_status); DBG("headset_info->cur_headset_status = %d\n",headset_info->cur_headset_status); @@ -501,11 +521,10 @@ static int rockchip_headsetobserve_probe(struct platform_device *pdev) //------------------------------------------------------------------ if (pdata->Headset_gpio) { - ret = gpio_request(pdata->Headset_gpio, NULL); + ret = pdata->headset_io_init(pdata->Headset_gpio, pdata->headset_gpio_info.iomux_name, pdata->headset_gpio_info.iomux_mode); if (ret) - goto failed_free_dev; - gpio_pull_updown(pdata->Headset_gpio, PullDisable); - gpio_direction_input(pdata->Headset_gpio); + goto failed_free; + headset->irq[HEADSET] = gpio_to_irq(pdata->Headset_gpio); if(pdata->headset_in_type == HEADSET_IN_HIGH) @@ -521,11 +540,9 @@ static int rockchip_headsetobserve_probe(struct platform_device *pdev) goto failed_free_dev; //------------------------------------------------------------------ if (pdata->Hook_gpio) { - ret = gpio_request(pdata->Hook_gpio , NULL); + ret = pdata->hook_io_init(pdata->Hook_gpio, pdata->hook_gpio_info.iomux_name, pdata->hook_gpio_info.iomux_mode); if (ret) - goto failed_free_dev; - gpio_pull_updown(pdata->Hook_gpio, PullDisable); - gpio_direction_input(pdata->Hook_gpio); + goto failed_free; headset->irq[HOOK] = gpio_to_irq(pdata->Hook_gpio); headset->irq_type[HOOK] = IRQF_TRIGGER_FALLING; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 964597fd1c49..5e2990cfdd90 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -539,6 +539,10 @@ config RK29_SC8800 config TDSC8800 bool "TDSC8800 modem control driver" default n +config MODEM_SOUND + bool "modem sound control driver" + default n + source "drivers/misc/c2port/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index ea32927a092e..7d02af44d9e8 100755 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-y += inv_mpu/ obj-$(CONFIG_TDSC8800) += tdsc8800.o obj-$(CONFIG_RK29_SC8800) += sc8800.o obj-y += rk2928_callpad_misc/ +obj-$(CONFIG_MODEM_SOUND) += modem_sound.o diff --git a/drivers/misc/modem_sound.c b/drivers/misc/modem_sound.c new file mode 100755 index 000000000000..1f402b30cbf9 --- /dev/null +++ b/drivers/misc/modem_sound.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modem_sound.h" +#if 1 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif +#define MODEM_EARPHOEN 0 //ÌýͲµç»° +#define MODEM_HANDFREE 1 //ÃâÌá +#define MODEM_HPPHONE 2 //¶ú»úµç»° +#define MODEM_BTPHONE 3 //À¶ÑÀµç»° +#define MODEM_STOP_PHONE 4 //ֹͣͨ»° + +#define ENABLE 1 +#define DISABLE 0 + +static struct modem_sound_data *modem_sound; +#ifdef CONFIG_SND_RK_SOC_RK2928 +extern void call_set_spk(bool on); +#endif +int modem_sound_spkctl(int status) +{ + if(status == ENABLE) + gpio_direction_output(modem_sound->spkctl_io,GPIO_HIGH);//modem_sound->spkctl_io? GPIO_HIGH:GPIO_LOW); + else + gpio_direction_output(modem_sound->spkctl_io,GPIO_LOW); //modem_sound->spkctl_io? GPIO_LOW:GPIO_HIGH); + + return 0; +} + +static void modem_sound_delay_power_downup(struct work_struct *work) +{ + struct modem_sound_data *pdata = container_of(work, struct modem_sound_data, work); + if (pdata == NULL) { + printk("%s: pdata = NULL\n", __func__); + return; + } + + down(&pdata->power_sem); + up(&pdata->power_sem); +} + +static int modem_sound_open(struct inode *inode, struct file *filp) +{ + DBG("modem_sound_open\n"); + + return 0; +} + +static ssize_t modem_sound_read(struct file *filp, char __user *ptr, size_t size, loff_t *pos) +{ + if (ptr == NULL) + printk("%s: user space address is NULL\n", __func__); + return sizeof(int); +} + +static long modem_sound_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + 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); + return ret; + } + + switch (cmd){ + case MODEM_EARPHOEN: + DBG("modem_sound_ioctl: MODEM_EAR_PHONE\n"); + call_set_spk(0); + modem_sound_spkctl(DISABLE); + break; + case MODEM_HANDFREE: + DBG("modem_sound_ioctl: MODEM_SPK_PHONE\n"); + call_set_spk(0); + modem_sound_spkctl(ENABLE); + break; + case MODEM_HPPHONE: + DBG("modem_sound_ioctl: MODEM_HP_PHONE\n"); + call_set_spk(0); + modem_sound_spkctl(DISABLE); + break; + + case MODEM_BTPHONE: + call_set_spk(0); + modem_sound_spkctl(DISABLE); + DBG("modem_sound_ioctl: MODEM_BT_PHONE\n"); + break; + case MODEM_STOP_PHONE: + DBG("modem_sound_ioctl: MODEM_STOP_PHONE\n"); + call_set_spk(1); + break; + + default: + printk("unknown ioctl cmd!\n"); + up(&pdata->power_sem); + ret = -EINVAL; + break; + } + + up(&pdata->power_sem); + + return ret; +} + +static int modem_sound_release(struct inode *inode, struct file *filp) +{ + DBG("modem_sound_release\n"); + + return 0; +} + +static struct file_operations modem_sound_fops = { + .owner = THIS_MODULE, + .open = modem_sound_open, + .read = modem_sound_read, + .unlocked_ioctl = modem_sound_ioctl, + .release = modem_sound_release, +}; + +static struct miscdevice modem_sound_dev = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = "modem_sound", + .fops = &modem_sound_fops, +}; + +static int modem_sound_probe(struct platform_device *pdev) +{ + int ret = 0; + 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"); + return ret; + } + + sema_init(&pdata->power_sem,1); + pdata->wq = create_freezable_workqueue("modem_sound"); + INIT_WORK(&pdata->work, modem_sound_delay_power_downup); + modem_sound = pdata; + printk("%s:modem sound initialized\n",__FUNCTION__); + + return ret; +} + +static int modem_sound_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct modem_sound_data *pdata = pdev->dev.platform_data; + + if(!pdata) { + printk("%s: pdata = NULL ...... \n", __func__); + return -1; + } + printk("%s\n",__FUNCTION__); + return 0; +} + +static int modem_sound_resume(struct platform_device *pdev) +{ + struct modem_sound_data *pdata = pdev->dev.platform_data; + + if(!pdata) { + printk("%s: pdata = NULL ...... \n", __func__); + return -1; + } + printk("%s\n",__FUNCTION__); + return 0; +} + +static int modem_sound_remove(struct platform_device *pdev) +{ + struct modem_sound_data *pdata = pdev->dev.platform_data; + if(!pdata) + return -1; + + misc_deregister(&modem_sound_dev); + + return 0; +} + +static struct platform_driver modem_sound_driver = { + .probe = modem_sound_probe, + .remove = modem_sound_remove, + .suspend = modem_sound_suspend, + .resume = modem_sound_resume, + .driver = { + .name = "modem_sound", + .owner = THIS_MODULE, + }, +}; + +static int __init modem_sound_init(void) +{ + return platform_driver_register(&modem_sound_driver); +} + +static void __exit modem_sound_exit(void) +{ + platform_driver_unregister(&modem_sound_driver); +} + +module_init(modem_sound_init); +module_exit(modem_sound_exit); +MODULE_DESCRIPTION ("modem sound driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/modem_sound.h b/drivers/misc/modem_sound.h new file mode 100755 index 000000000000..e78314ee9ecf --- /dev/null +++ b/drivers/misc/modem_sound.h @@ -0,0 +1,14 @@ + +#ifndef __MODEM_SOUND_H__ +#define __MODEM_SOUND_H__ + +struct modem_sound_data { + int spkctl_io; + int spkctl_active; + int codec_flag; + struct semaphore power_sem; + struct workqueue_struct *wq; + struct work_struct work; +}; + +#endif diff --git a/sound/soc/codecs/rk2928_codec.c b/sound/soc/codecs/rk2928_codec.c index dc77d77416b5..adbebd2c1d5e 100755 --- a/sound/soc/codecs/rk2928_codec.c +++ b/sound/soc/codecs/rk2928_codec.c @@ -43,8 +43,12 @@ #include #include "rk2928_codec.h" +#define HP_OUT 0 +#define HP_IN 1 + static struct rk2928_codec_data { struct device *dev; + struct snd_soc_codec *codec; int regbase; int regbase_phy; int regsize_phy; @@ -52,6 +56,8 @@ static struct rk2928_codec_data { int mute; int hdmi_enable; int spkctl; + int call_enable; + int headset_status; } rk2928_data; static const struct snd_soc_dapm_widget rk2928_dapm_widgets[] = { @@ -135,6 +141,44 @@ void codec_set_spk(bool on) rk2928_write(NULL, CODEC_REG_DAC_MUTE, v_MUTE_DAC(0)); } } +#ifdef CONFIG_MODEM_SOUND +void call_set_spk(bool on) +{ + if(on == 0) { + printk("%s speaker is disabled\n", __FUNCTION__); + rk2928_data.call_enable = 1; + rk2928_write(NULL, CODEC_REG_DAC_MUTE, v_MUTE_DAC(1)); + } + else { + printk("%s speaker is enabled\n", __FUNCTION__); + rk2928_data.call_enable = 0; + rk2928_write(NULL, CODEC_REG_DAC_MUTE, v_MUTE_DAC(0)); + } +} +#endif +#ifdef CONFIG_RK_HEADSET_DET +//for headset +void rk2928_codec_set_spk(bool on) +{ + struct snd_soc_codec *codec = rk2928_data.codec; + + printk("%s: headset %s %s PA bias_level=%d\n",__FUNCTION__,on?"in":"out",on?"disable":"enable",codec->dapm.bias_level); + if(on) { + rk2928_data.headset_status = HP_IN; + if(rk2928_data.spkctl != INVALID_GPIO) + gpio_direction_output(rk2928_data.spkctl, GPIO_LOW); + } + else { + rk2928_data.headset_status = HP_OUT; + if(codec->dapm.bias_level == SND_SOC_BIAS_STANDBY + || codec->dapm.bias_level == SND_SOC_BIAS_OFF){ + return; + } + if(rk2928_data.spkctl != INVALID_GPIO) + gpio_direction_output(rk2928_data.spkctl, GPIO_HIGH); + } +} +#endif static int rk2928_audio_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, @@ -158,7 +202,10 @@ static int rk2928_audio_trigger(struct snd_pcm_substream *substream, int cmd, int err = 0; int data, pd_adc; DBG("%s cmd 0x%x", __FUNCTION__, cmd); - +#ifdef CONFIG_MODEM_SOUND + if(rk2928_data.call_enable) + return err; +#endif switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -212,7 +259,7 @@ static int rk2928_audio_trigger(struct snd_pcm_substream *substream, int cmd, rk2928_write(codec, CODEC_REG_DAC_MUTE, v_MUTE_DAC(0)); } rk2928_data.mute = 0; - if(rk2928_data.spkctl != INVALID_GPIO) { + if(rk2928_data.spkctl != INVALID_GPIO && rk2928_data.headset_status == HP_OUT) { gpio_direction_output(rk2928_data.spkctl, GPIO_HIGH); } } @@ -251,7 +298,7 @@ static int rk2928_audio_startup(struct snd_pcm_substream *substream, static int rk2928_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - DBG("%s level %d", __FUNCTION__, level); + DBG("%s level %d\n", __FUNCTION__, level); if(codec == NULL) return -1; @@ -326,7 +373,7 @@ static int rk2928_probe(struct snd_soc_codec *codec) else { rk2928_data.spkctl = res->start; } - + if(rk2928_data.spkctl != INVALID_GPIO) { ret = gpio_request(rk2928_data.spkctl, NULL); if (ret != 0) { @@ -335,7 +382,7 @@ static int rk2928_probe(struct snd_soc_codec *codec) else gpio_direction_output(rk2928_data.spkctl, GPIO_LOW); } - + // Select SDI input from internal audio codec writel(0x04000400, RK2928_GRF_BASE + GRF_SOC_CON0); @@ -352,6 +399,9 @@ static int rk2928_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, rk2926_audio_map, ARRAY_SIZE(rk2926_audio_map)); } + rk2928_data.call_enable = 0; + rk2928_data.headset_status = HP_OUT; + rk2928_data.codec=codec; return 0; err1: