6.7 中断处理
6.7.1 azx_interrupt
之前已经提到过,中断由azx_interrupt来处理。
irqreturn_t azx_interrupt(int irq, void *dev_id)
{
struct azx *chip = dev_id;
struct hdac_bus *bus = azx_bus(chip);
u32 status;
bool active, handled = false;
int repeat = 0; /* count for avoiding endless loop */
#ifdef CONFIG_PM
if (azx_has_pm_runtime(chip))
if (!pm_runtime_active(chip->card->dev))
return IRQ_NONE;
#endif
spin_lock(&bus->reg_lock);
if (chip->disabled)
goto unlock;
do {
status = azx_readl(chip, INTSTS);
if (status == 0 || status == 0xffffffff)
break;
handled = true;
active = false;
// 处理流中断,stream_update为回调函数
if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
active = true;
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
/*
* Clearing the interrupt status here ensures that no
* interrupt gets masked after the RIRB wp is read in
* snd_hdac_bus_update_rirb. This avoids a possible
* race condition where codec response in RIRB may
* remain unserviced by IRQ, eventually falling back
* to polling mode in azx_rirb_get_response.
*/
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
active = true;
if (status & RIRB_INT_RESPONSE) {
if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
udelay(80);
snd_hdac_bus_update_rirb(bus);
}
}
} while (active && ++repeat < 10);
unlock:
spin_unlock(&bus->reg_lock);
return IRQ_RETVAL(handled);
}
再来看一个寄存器:
* INTSTS(Interrupt Status Register)中断状态寄存器,32位。
第31位,Global Interrupt Status (GIS),全局中断状态,只要有中断,它就会被置1。
第30位,Controller Interrupt Status (CIS),控制器中断状态,用来标记RIRB对应的中断,代码里看起来,并没有被用到。
其它位用来标记流的中断,根据流的数量来决定要使用的位数。
int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
void (*ack)(struct hdac_bus *,
struct hdac_stream *))
{
struct hdac_stream *azx_dev;
u8 sd_status;
int handled = 0;
// 遍历所有的流,检查是否有中断发生
list_for_each_entry(azx_dev, &bus->stream_list, list) {
if (status & azx_dev->sd_int_sta_mask) {
// 读取流的状态信息
sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
// 将状态复原
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
handled |= 1 << azx_dev->index;
// 只处理都周期完成的中断
if (!azx_dev->substream || !azx_dev->running ||
!(sd_status & SD_INT_COMPLETE))
continue;
// 调用回调函数
if (ack)
ack(bus, azx_dev);
}
}
return handled;
}
以上函数的目的确认是否有流发生了中断,然后进行回调。
static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
{
struct azx *chip = bus_to_azx(bus);
struct azx_dev *azx_dev = stream_to_azx_dev(s);
/* check whether this IRQ is really acceptable */
// 在设置了position_check的情况下,会去先检查位置是否有效
if (!chip->ops->position_check ||
chip->ops->position_check(chip, azx_dev)) {
spin_unlock(&bus->reg_lock);
snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
spin_lock(&bus->reg_lock);
}
}
默认设置的position_check是azx_position_check,来看看它的实