Chinaunix首页 | 论坛 | 博客
  • 博客访问: 383145
  • 博文数量: 82
  • 博客积分: 1855
  • 博客等级: 上尉
  • 技术积分: 846
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-12 12:28
文章存档

2013年(3)

2012年(8)

2011年(71)

分类: LINUX

2013-02-28 14:07:55

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) |
给主人留下些什么吧!~~