WM9714的耳机跟喇叭通道切换由寄存器软件控制,跟之前电视里面插入耳机让功放直接拉低关掉外音的方式有点差别。通过设置WM9714的0X1C寄存器的值来切换,其中设置耳机时为0X00A0,设置喇叭时为0X1200。
电路中有外部上拉固定EINT11为高电平,通过插入耳机将这个外中断EINT11拉低来触发中断,再进行WM9714的寄存器控制。首先在WM9713.C中wm9713_soc_probe()中,添加初始化通道设置和注册中断。
#define HdPhone_EintPin IRQ_EINT(11)
#define HdPhone_GpioPin S3C64XX_GPN(11)
#define OUTPUT_VALUE_HP (0x00a0)
#define OUTPUT_VALUE_SPK (0x1200)
static int wm9713_soc_probe(struct platform_device *pdev)
{
......
s3c_gpio_cfgpin(HdPhone_GpioPin, S3C_GPIO_SFN(2)); //配置GPIO11为外中断
s3c_gpio_setpull(HdPhone_GpioPin, S3C_GPIO_PULL_NONE); //不上拉也不下拉,硬件默认拉高
set_irq_type(HdPhone_EintPin, IRQ_TYPE_EDGE_BOTH); //插入跟拔出耳机都会触发中断
if(readl(S3C64XX_GPNDAT)&(1<<11)) //如果上电默认是喇叭,设置通道;否则设置耳机
{
ac97_write(codec, AC97_REC_GAIN, OUTPUT_VALUE_SPK);
}
else
{
ac97_write(codec, AC97_REC_GAIN, OUTPUT_VALUE_HP);
}
ret = request_irq(HdPhone_EintPin, s3c6400_9713HdSp_irq, //给EINT11注册中断和中断处理函数,参数包括中断方式,
IRQF_DISABLED, "HdphonSpeak", NULL); //中断名
if(ret<0) //如果注册不成功,执行GOTO
{
printk(KERN_ERR "request_irq(s3c6400_9713HdSp_irq) failed !!!/n");
goto irq_err;
}
.........
irq_err:
free_irq(HdPhone_EintPin,NULL); //释放中断
.........
}
中断handler如下:
static irqreturn_t s3c6400_9713HdSp_irq(int irq, void *dev_id)
{
struct snd_soc_codec *codec = wm9713_dai->codec; //获取系统的codec
writel(readl(S3C64XX_EINT0MASK)|(1<<11),S3C64XX_EINT0MASK); //disable EINT0 Interrupt before deal it
writel(1<<11,S3C64XX_EINT0PEND); //disable Interrupt
if(readl(S3C64XX_GPNDAT)&(1<<11)) //如果是喇叭,否则是耳机
{
ac97_write(codec, AC97_REC_GAIN, OUTPUT_VALUE_SPK);
}
else
{
ac97_write(codec, AC97_REC_GAIN, OUTPUT_VALUE_HP);
}
writel(readl(S3C64XX_EINT0MASK)&(~(1<<11)),S3C64XX_EINT0MASK); //Enable Interrupt after deal it
return IRQ_HANDLED;
}
以上即OK,在实际中还发现一个问题,插拔耳机切通道一直正常,但是第一次开机和仅仅用喇叭播放时喇叭不出声,也就是说一直需要耳机触发后喇叭跟耳机才会出声?通过测试波形,发现有耳机输入波形跟切换正常;但是功放输入会没有波形,估计还是切换通道的问题,查找后原因在于播放声音时,Entered s3c6400_ac97_hifi_prepare()中,判断SNDRV_PCM_STREAM_PLAYBACK会将0X1C寄存器写成0XAA,把喇叭所需的0X1200覆盖,屏蔽这句即可。可通过在s3c6400_ac97_trigger()中,设置打印语句:
for (temp =0x00;temp<=0x7e;temp+=0x02)
s3c6400_ac97_read(0,temp);
将CMD=1开始播放音频时9714的寄存器值全部读出,作出判断依据。
==================WIN CE的做法===============================================
初始化执行中:
Irq = IRQ_EINT11; //中断GPIO
DWORD m_dwSysintrHPSense = NULL; //耳机切换标志
HANDLE m_hHPSenseInterrupt = NULL; //耳机切换中断句柄
HANDLE m_hHPSenseInterruptThread; //耳机切换中断线程句柄
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(DWORD), &m_dwSysintrHPSense, sizeof(DWORD), NULL)) //GPIO作中断控制注册
{
WAV_ERR((_T("[WAV:ERR] InitInterruptThread() : HP sense IOCTL_HAL_REQUEST_SYSINTR Failed/n/r")));
m_dwSysintrHPSense = SYSINTR_UNDEFINED;
return FALSE;
}
m_hHPSenseInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL); //初始化中断句柄
if (m_hHPSenseInterrupt == NULL)
{
WAV_ERR((_T("[WAV:ERR] InitInterruptThread() : HP sense CreateEvent() Failed /n/r")));
return(FALSE);
}
if (!InterruptInitialize(m_dwSysintrHPSense, m_hHPSenseInterrupt, NULL, 0)) //绑定中断句柄和标志
{
WAV_ERR((_T("[WAV:ERR] InitInterruptThread() : HP sense InterruptInitialize() Failed /n/r")));
return FALSE;
}
m_hHPSenseInterruptThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE)CallInterruptThreadHPSense, this, 0, NULL); //创建中断线程
if (m_hHPSenseInterruptThread == NULL) //如果没有创建成功线程,返回。
{
WAV_ERR((_T("[WAV:ERR] InitInterruptThread() : HP sense CreateThread() Failed /n/r")));
return FALSE;
}
void CallInterruptThreadHPSense(HardwareContext *pHWContext)
{
pHWContext->InterruptThreadHPSense(); //通过硬件上下文调用中断处理函数
}
中断处理函数:
void HardwareContext::InterruptThreadHPSense()
{
......
if(!(g_pGPIOReg->GPNDAT & (1<<11))) //如果插入耳机
{
WriteCodecRegister(WM9713_OUTPUT_MUX, OUTPUT_VALUE_HP);
RETAILMSG(AC97_HP_DEBUG,(_T("[WAV:] HeadPhone OUTPUT/n/r")));
}
while(TRUE)
{
WaitForSingleObject(m_hHPSenseInterrupt, INFINITE); //线程等待中断发生
g_pGPIOReg->EINT0MASK |= (0x1<<11); // Mask EINT9, Clear Interrupt Pending
g_pGPIOReg->EINT0PEND = (0x1<<11); // Clear pending EINT9
if(g_pGPIOReg->GPNDAT & (1<<11))
{
WriteCodecRegister(WM9713_OUTPUT_MUX, OUTPUT_VALUE_SPK);
RETAILMSG(AC97_HP_DEBUG,(_T("[WAV:] SPEAK OUTPUT/n/r")));
}
else
{
WriteCodecRegister(WM9713_OUTPUT_MUX, OUTPUT_VALUE_HP);
RETAILMSG(AC97_HP_DEBUG,(_T("[WAV:] HeadPhone OUTPUT/n/r")));
}
InterruptDone(m_dwSysintrHPSense);
g_pGPIOReg->EINT0MASK &= ~(0x1<<11); // Unmask EINT9, Enable Interrupt
}
......
}
关于WINCE的这种做法,还需要做个说明:针对在线程循环中的WaitForSingleObject,如果事件是主动设置的事件,需要SetEvent函数来使能事件;但对于中断这类处理,由于绑定了中断跟事件,只要中断被动发生,事件就变成有信号,所以无需SetEvent函数。
阅读(3020) | 评论(0) | 转发(0) |