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函數。