rk2928phonepad:

add support rk2928 codec for phonepad
This commit is contained in:
邱建斌 2012-10-19 10:48:56 +08:00
parent a38ec710e8
commit 0f47bc20b9
7 changed files with 403 additions and 17 deletions

View File

@ -46,9 +46,13 @@
#include <linux/sensor-dev.h>
#include <linux/mfd/tps65910.h>
#include <linux/regulator/rk29-pwm-regulator.h>
#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

View File

@ -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;

View File

@ -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"

View File

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

227
drivers/misc/modem_sound.c Executable file
View File

@ -0,0 +1,227 @@
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <asm/types.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#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");

14
drivers/misc/modem_sound.h Executable file
View File

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

View File

@ -43,8 +43,12 @@
#include <linux/clk.h>
#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: