irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) { struct mixart_mgr *mgr = dev_id; u32 it_reg; it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); if( !(it_reg & MIXART_OIDI) ) { /* this device did not cause the interrupt */ return IRQ_NONE; } /* mask all interrupts */ writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET)); /* outdoorbell register clear */ it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); /* clear interrupt */ writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); return IRQ_WAKE_THREAD; }
irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) { struct mixart_mgr *mgr = dev_id; int err; struct mixart_msg resp; u32 msg; u32 it_reg; spin_lock(&mgr->lock); it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); if( !(it_reg & MIXART_OIDI) ) { /* this device did not cause the interrupt */ spin_unlock(&mgr->lock); return IRQ_NONE; } /* mask all interrupts */ writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET)); /* outdoorbell register clear */ it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); /* clear interrupt */ writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); /* process interrupt */ while (retrieve_msg_frame(mgr, &msg)) { switch (msg & MSG_TYPE_MASK) { case MSG_TYPE_COMMAND: resp.message_id = 0; resp.data = mixart_msg_data; resp.size = sizeof(mixart_msg_data); err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK); if( err < 0 ) { dev_err(&mgr->pci->dev, "interrupt: error(%d) reading mf %x\n", err, msg); break; } if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) { int i; struct mixart_timer_notify *notify; notify = (struct mixart_timer_notify *)mixart_msg_data; for(i=0; i<notify->stream_count; i++) { u32 buffer_id = notify->streams[i].buffer_id; unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */ unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */ unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */ unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */ struct snd_mixart *chip = mgr->chip[chip_number]; struct mixart_stream *stream; if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) { dev_err(&mgr->pci->dev, "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n", buffer_id, notify->streams[i].sample_pos_low_part); break; } if (is_capture) stream = &chip->capture_stream[pcm_number]; else stream = &chip->playback_stream[pcm_number][sub_number]; if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) { struct snd_pcm_runtime *runtime = stream->substream->runtime; int elapsed = 0; u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32; sample_count |= notify->streams[i].sample_pos_low_part; while (1) { u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size; if (new_elapse_pos > sample_count) { break; /* while */ } else { elapsed = 1; stream->buf_periods++; if (stream->buf_periods >= runtime->periods) stream->buf_periods = 0; stream->abs_period_elapsed = new_elapse_pos; } } stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed ); if(elapsed) { spin_unlock(&mgr->lock); snd_pcm_period_elapsed(stream->substream); spin_lock(&mgr->lock); } } } break; } if(resp.message_id == MSG_SERVICES_REPORT_TRACES) { if(resp.size > 1) { #ifndef __BIG_ENDIAN /* Traces are text: the swapped msg_data has to be swapped back ! */ int i; for(i=0; i<(resp.size/4); i++) { (mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]); } #endif ((char*)mixart_msg_data)[resp.size - 1] = 0; dev_dbg(&mgr->pci->dev, "MIXART TRACE : %s\n", (char *)mixart_msg_data); } break; } dev_dbg(&mgr->pci->dev, "command %x not handled\n", resp.message_id); break; case MSG_TYPE_NOTIFY: if(msg & MSG_CANCEL_NOTIFY_MASK) { msg &= ~MSG_CANCEL_NOTIFY_MASK; dev_err(&mgr->pci->dev, "canceled notification %x !\n", msg); } /* no break, continue ! */ case MSG_TYPE_ANSWER: /* answer or notification to a message we are waiting for*/ spin_lock(&mgr->msg_lock); if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) { wake_up(&mgr->msg_sleep); mgr->pending_event = 0; } /* answer to a message we did't want to wait for */ else { mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg; mgr->msg_fifo_writeptr++; mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE; tasklet_schedule(&mgr->msg_taskq); } spin_unlock(&mgr->msg_lock); break; case MSG_TYPE_REQUEST: default: dev_dbg(&mgr->pci->dev, "interrupt received request %x\n", msg); /* TODO : are there things to do here ? */ break; } /* switch on msg type */ } /* while there are msgs */