/* params used with PCXHR_DSP_ICR */ #define PCXHR_ICR_HI08_RREQ 0x01 #define PCXHR_ICR_HI08_TREQ 0x02 #define PCXHR_ICR_HI08_HDRQ 0x04 #define PCXHR_ICR_HI08_HF0 0x08 #define PCXHR_ICR_HI08_HF1 0x10 #define PCXHR_ICR_HI08_HLEND 0x20 #define PCXHR_ICR_HI08_INIT 0x80 /* params used with PCXHR_DSP_CVR */ #define PCXHR_CVR_HI08_HC 0x80 /* params used with PCXHR_DSP_ISR */ #define PCXHR_ISR_HI08_RXDF 0x01 #define PCXHR_ISR_HI08_TXDE 0x02 #define PCXHR_ISR_HI08_TRDY 0x04 #define PCXHR_ISR_HI08_ERR 0x08 #define PCXHR_ISR_HI08_CHK 0x10 #define PCXHR_ISR_HI08_HREQ 0x80
/* constants used for delay in msec */ #define PCXHR_WAIT_DEFAULT 2 #define PCXHR_WAIT_IT 25 #define PCXHR_WAIT_IT_EXTRA 65
/* * pcxhr_check_reg_bit - wait for the specified bit is set/reset on a register * @reg: register to check * @mask: bit mask * @bit: resultant bit to be checked * @time: time-out of loop in msec * * returns zero if a bit matches, or a negative error code.
*/ staticint pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsignedint reg, unsignedchar mask, unsignedchar bit, int time, unsignedchar* read)
{ int i = 0; unsignedlong end_time = jiffies + (time * HZ + 999) / 1000; do {
*read = PCXHR_INPB(mgr, reg); if ((*read & mask) == bit) { if (i > 100)
dev_dbg(&mgr->pci->dev, "ATTENTION! check_reg(%x) loopcount=%d\n",
reg, i); return 0;
}
i++;
} while (time_after_eq(end_time, jiffies));
dev_err(&mgr->pci->dev, "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
reg, mask, *read); return -EIO;
}
/* constants used with pcxhr_check_reg_bit() */ #define PCXHR_TIMEOUT_DSP 200
/* * load the xilinx image
*/ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, conststruct firmware *xilinx, int second)
{ unsignedint i; unsignedint chipsc; unsignedchar data; unsignedchar mask; constunsignedchar *image;
/* test first xilinx */
chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC); /* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */ /* this bit will always be 1; * no possibility to test presence of first xilinx
*/ if(second) { if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
dev_err(&mgr->pci->dev, "error loading first xilinx\n"); return -EINVAL;
} /* activate second xilinx */
chipsc |= PCXHR_CHIPSC_RESET_XILINX;
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
msleep( PCXHR_WAIT_DEFAULT ); /* wait 2 msec */
}
image = xilinx->data; for (i = 0; i < xilinx->size; i++, image++) {
data = *image;
mask = 0x80; while (mask) {
chipsc &= ~(PCXHR_CHIPSC_DATA_CLK |
PCXHR_CHIPSC_DATA_IN); if (data & mask)
chipsc |= PCXHR_CHIPSC_DATA_IN;
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
chipsc |= PCXHR_CHIPSC_DATA_CLK;
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
mask >>= 1;
} /* don't take too much time in this loop... */
cond_resched();
}
chipsc &= ~(PCXHR_CHIPSC_DATA_CLK | PCXHR_CHIPSC_DATA_IN);
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc); /* wait 2 msec (time to boot the xilinx before any access) */
msleep( PCXHR_WAIT_DEFAULT ); return 0;
}
/* * send an executable file to the DSP
*/ staticint pcxhr_download_dsp(struct pcxhr_mgr *mgr, conststruct firmware *dsp)
{ int err; unsignedint i; unsignedint len; constunsignedchar *data; unsignedchar dummy; /* check the length of boot image */ if (dsp->size <= 0) return -EINVAL; if (dsp->size % 3) return -EINVAL; if (snd_BUG_ON(!dsp->data)) return -EINVAL; /* transfert data buffer from PC to DSP */ for (i = 0; i < dsp->size; i += 3) {
data = dsp->data + i; if (i == 0) { /* test data header consistency */
len = (unsignedint)((data[0]<<16) +
(data[1]<<8) +
data[2]); if (len && (dsp->size != (len + 2) * 3)) return -EINVAL;
} /* wait DSP ready for new transfer */
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_TRDY,
PCXHR_ISR_HI08_TRDY,
PCXHR_TIMEOUT_DSP, &dummy); if (err) {
dev_err(&mgr->pci->dev, "dsp loading error at position %d\n", i); return err;
} /* send host data */
PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, data[0]);
PCXHR_OUTPB(mgr, PCXHR_DSP_TXM, data[1]);
PCXHR_OUTPB(mgr, PCXHR_DSP_TXL, data[2]);
/* don't take too much time in this loop... */
cond_resched();
} /* give some time to boot the DSP */
msleep(PCXHR_WAIT_DEFAULT); return 0;
}
/* * load the eeprom image
*/ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, conststruct firmware *eeprom)
{ int err; unsignedchar reg;
/* init value of the ICR register */
reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ; if (PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & PCXHR_MBOX0_BOOT_HERE) { /* no need to load the eeprom binary, * but init the HI08 interface
*/
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg | PCXHR_ICR_HI08_INIT);
msleep(PCXHR_WAIT_DEFAULT);
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
msleep(PCXHR_WAIT_DEFAULT);
dev_dbg(&mgr->pci->dev, "no need to load eeprom boot\n"); return 0;
}
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
err = pcxhr_download_dsp(mgr, eeprom); if (err) return err; /* wait for chk bit */ return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, ®);
}
/* * load the boot image
*/ int pcxhr_load_boot_binary(struct pcxhr_mgr *mgr, conststruct firmware *boot)
{ int err; unsignedint physaddr = mgr->hostport.addr; unsignedchar dummy;
/* send the hostport address to the DSP (only the upper 24 bit !) */ if (snd_BUG_ON(physaddr & 0xff)) return -EINVAL;
PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX1, (physaddr >> 8));
err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_BOOT, 0); if (err) return err; /* clear hf5 bit */
PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & ~PCXHR_MBOX0_HF5);
err = pcxhr_download_dsp(mgr, boot); if (err) return err; /* wait for hf5 bit */ return pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0, PCXHR_MBOX0_HF5,
PCXHR_MBOX0_HF5, PCXHR_TIMEOUT_DSP, &dummy);
}
/* * load the final dsp image
*/ int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, conststruct firmware *dsp)
{ int err; unsignedchar dummy;
err = pcxhr_send_it_dsp(mgr, PCXHR_IT_RESET_BOARD_FUNC, 0); if (err) return err;
err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_DSP, 0); if (err) return err;
err = pcxhr_download_dsp(mgr, dsp); if (err) return err; /* wait for chk bit */ return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_CHK,
PCXHR_ISR_HI08_CHK,
PCXHR_TIMEOUT_DSP, &dummy);
}
struct pcxhr_cmd_info {
u32 opcode; /* command word */
u16 st_length; /* status length */
u16 st_type; /* status type (RMH_SSIZE_XXX) */
};
/* RMH status type */ enum {
RMH_SSIZE_FIXED = 0, /* status size fix (st_length = 0..x) */
RMH_SSIZE_ARG = 1, /* status size given in the LSB byte */
RMH_SSIZE_MASK = 2, /* status size given in bitmask */
};
/* * pcxhr_send_msg - send a DSP message with spinlock * @rmh: the rmh record to send and receive * * returns 0 if successful, or a negative error code.
*/ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
{ int err;
staticinlineint pcxhr_pipes_running(struct pcxhr_mgr *mgr)
{ int start_mask = PCXHR_INPL(mgr, PCXHR_PLX_MBOX2); /* least segnificant 12 bits are the pipe states * for the playback audios * next 12 bits are the pipe states for the capture audios * (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
*/
start_mask &= 0xffffff;
dev_dbg(&mgr->pci->dev, "CMD_PIPE_STATE MBOX2=0x%06x\n", start_mask); return start_mask;
}
start_time = ktime_get(); #endif
audio_mask = (playback_mask |
(capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET)); /* current pipe state (playback + record) */
state = pcxhr_pipes_running(mgr);
dev_dbg(&mgr->pci->dev, "pcxhr_set_pipe_state %s (mask %x current %x)\n",
start ? "START" : "STOP", audio_mask, state); if (start) { /* start only pipes that are not yet started */
audio_mask &= ~state;
state = audio_mask; for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {
err = pcxhr_prepair_pipe_start(mgr, state, &state); if (err) return err; if (state == 0) break; /* success, all pipes prepaired */
mdelay(1); /* wait 1 millisecond and retry */
}
} else {
audio_mask &= state; /* stop only pipes that are started */
} if (audio_mask == 0) return 0;
err = pcxhr_toggle_pipes(mgr, audio_mask); if (err) return err;
i = 0; while (1) {
state = pcxhr_pipes_running(mgr); /* have all pipes the new state ? */ if ((state & audio_mask) == (start ? audio_mask : 0)) break; if (++i >= MAX_WAIT_FOR_DSP * 100) {
dev_err(&mgr->pci->dev, "error pipe start/stop\n"); return -EBUSY;
}
udelay(10); /* wait 10 microseconds */
} if (!start) {
err = pcxhr_stop_pipes(mgr, audio_mask); if (err) return err;
} #ifdef CONFIG_SND_DEBUG_VERBOSE
stop_time = ktime_get();
diff_time = ktime_sub(stop_time, start_time);
dev_dbg(&mgr->pci->dev, "***SET PIPE STATE*** TIME = %ld (err = %x)\n",
(long)(ktime_to_ns(diff_time)), err); #endif return 0;
}
int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsignedint mask, unsignedint value, int *changed)
{ struct pcxhr_rmh rmh; int err;
pcxhr_init_rmh(prmh, CMD_ASYNC);
prmh->cmd[0] |= 1; /* add SEL_ASYNC_EVENTS */ /* this is the only one extra long response command */
prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
err = pcxhr_send_msg(mgr, prmh); if (err)
dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n",
err);
i = 1; while (i < prmh->stat_len) { int nb_audio = ((prmh->stat[i] >> FIELD_SIZE) &
MASK_FIRST_FIELD); int nb_stream = ((prmh->stat[i] >> (2*FIELD_SIZE)) &
MASK_FIRST_FIELD); int pipe = prmh->stat[i] & MASK_FIRST_FIELD; int is_capture = prmh->stat[i] & 0x400000;
u32 err2;
if (prmh->stat[i] & 0x800000) { /* if BIT_END */
dev_dbg(&mgr->pci->dev, "TASKLET : End%sPipe %d\n",
is_capture ? "Record" : "Play",
pipe);
}
i++;
err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; if (err2)
pcxhr_handle_async_err(mgr, err2,
PCXHR_ERR_PIPE,
pipe, is_capture);
i += 2; for (j = 0; j < nb_stream; j++) {
err2 = prmh->stat[i] ?
prmh->stat[i] : prmh->stat[i+1]; if (err2)
pcxhr_handle_async_err(mgr, err2,
PCXHR_ERR_STREAM,
pipe,
is_capture);
i += 2;
} for (j = 0; j < nb_audio; j++) {
err2 = prmh->stat[i] ?
prmh->stat[i] : prmh->stat[i+1]; if (err2)
pcxhr_handle_async_err(mgr, err2,
PCXHR_ERR_AUDIO,
pipe,
is_capture);
i += 2;
}
}
}
}
/* get sample count for one stream */
pcxhr_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);
pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
stream->pipe->first_audio, 0, stream_mask); /* rmh.stat_len = 2; */ /* 2 resp data for each stream of the pipe */
err = pcxhr_send_msg(mgr, &rmh); if (err) return 0;
/* other irq's handled in the thread */ if (reg & PCXHR_IRQ_MASK) { if (reg & PCXHR_IRQ_ASYNC) { /* as we didn't request any async notifications, * some kind of xrun error will probably occurred
*/ /* better resynchronize all streams next interrupt : */
mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
}
mgr->src_it_dsp = reg;
wake_thread = true;
} #ifdef CONFIG_SND_DEBUG_VERBOSE if (reg & PCXHR_FATAL_DSP_ERR)
dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg); #endif
irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
{ struct pcxhr_mgr *mgr = dev_id; int i, j; struct snd_pcxhr *chip;
mutex_lock(&mgr->lock); if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) { /* is a 24 bit counter */ int dsp_time_new =
PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK; int dsp_time_diff = dsp_time_new - mgr->dsp_time_last;
if ((dsp_time_diff < 0) &&
(mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) { /* handle dsp counter wraparound without resync */ int tmp_diff = dsp_time_diff + PCXHR_DSP_TIME_MASK + 1;
dev_dbg(&mgr->pci->dev, "WARNING DSP timestamp old(%d) new(%d)",
mgr->dsp_time_last, dsp_time_new); if (tmp_diff > 0 && tmp_diff <= (2*mgr->granularity)) {
dev_dbg(&mgr->pci->dev, "-> timestamp wraparound OK: " "diff=%d\n", tmp_diff);
dsp_time_diff = tmp_diff;
} else {
dev_dbg(&mgr->pci->dev, "-> resynchronize all streams\n");
mgr->dsp_time_err++;
}
} #ifdef CONFIG_SND_DEBUG_VERBOSE if (dsp_time_diff == 0)
dev_dbg(&mgr->pci->dev, "ERROR DSP TIME NO DIFF time(%d)\n",
dsp_time_new); elseif (dsp_time_diff >= (2*mgr->granularity))
dev_dbg(&mgr->pci->dev, "ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
mgr->dsp_time_last,
dsp_time_new - mgr->dsp_time_last); elseif (dsp_time_diff % mgr->granularity)
dev_dbg(&mgr->pci->dev, "ERROR DSP TIME increased by %d\n",
dsp_time_diff); #endif
mgr->dsp_time_last = dsp_time_new;
for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_capt; j++)
pcxhr_update_timer_pos(mgr,
&chip->capture_stream[j],
dsp_time_diff);
} for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_play; j++)
pcxhr_update_timer_pos(mgr,
&chip->playback_stream[j],
dsp_time_diff);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.