ALSA: pcxhr - add support for pcxhr stereo sound cards

- Add support for pcxhr stereo cards
- do some clean up

Signed-off-by: Markus Bollinger <bollinger@digigram.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Markus Bollinger 2008-11-25 12:21:05 +01:00 committed by Takashi Iwai
parent 13d428afc0
commit 93bf5d8753
2 changed files with 190 additions and 106 deletions

View file

@ -132,13 +132,15 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
*read = PCXHR_INPB(mgr, reg);
if ((*read & mask) == bit) {
if (i > 100)
snd_printdd("ATTENTION! check_reg(%x) loopcount=%d\n",
snd_printdd("ATTENTION! check_reg(%x) "
"loopcount=%d\n",
reg, i);
return 0;
}
i++;
} while (time_after_eq(end_time, jiffies));
snd_printk(KERN_ERR "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=0x%x\n",
snd_printk(KERN_ERR
"pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
reg, mask, *read);
return -EIO;
}
@ -159,18 +161,22 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
#define PCXHR_IT_TEST_XILINX (0x0000003C | PCXHR_MASK_IT_HF1 | \
PCXHR_MASK_IT_MANAGE_HF5)
#define PCXHR_IT_DOWNLOAD_BOOT (0x0000000C | PCXHR_MASK_IT_HF1 | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_RESET_BOARD_FUNC (0x0000000C | PCXHR_MASK_IT_HF0 | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT_EXTRA)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT_EXTRA)
#define PCXHR_IT_DOWNLOAD_DSP (0x0000000C | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_DEBUG (0x0000005A | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_SEMAPHORE (0x0000005C | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_MESSAGE (0x00000074 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_CHK (0x00000076 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_UPDATE_RBUFFER (0x00000078 | PCXHR_MASK_IT_NO_HF0_HF1)
static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atomic)
static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
unsigned int itdsp, int atomic)
{
int err;
unsigned char reg;
@ -178,17 +184,21 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
/* clear hf5 bit */
PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & ~PCXHR_MBOX0_HF5);
PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) &
~PCXHR_MBOX0_HF5);
}
if ((itdsp & PCXHR_MASK_IT_NO_HF0_HF1) == 0) {
reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
reg = (PCXHR_ICR_HI08_RREQ |
PCXHR_ICR_HI08_TREQ |
PCXHR_ICR_HI08_HDRQ);
if (itdsp & PCXHR_MASK_IT_HF0)
reg |= PCXHR_ICR_HI08_HF0;
if (itdsp & PCXHR_MASK_IT_HF1)
reg |= PCXHR_ICR_HI08_HF1;
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
}
reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) | PCXHR_CVR_HI08_HC);
reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) |
PCXHR_CVR_HI08_HC);
PCXHR_OUTPB(mgr, PCXHR_DSP_CVR, reg);
if (itdsp & PCXHR_MASK_IT_WAIT) {
if (atomic)
@ -211,10 +221,14 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
}
if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
/* wait for hf5 bit */
err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0, PCXHR_MBOX0_HF5,
PCXHR_MBOX0_HF5, PCXHR_TIMEOUT_DSP, &reg);
err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0,
PCXHR_MBOX0_HF5,
PCXHR_MBOX0_HF5,
PCXHR_TIMEOUT_DSP,
&reg);
if (err) {
snd_printk(KERN_ERR "pcxhr_send_it_dsp : TIMEOUT HF5\n");
snd_printk(KERN_ERR
"pcxhr_send_it_dsp : TIMEOUT HF5\n");
return err;
}
}
@ -263,7 +277,8 @@ void pcxhr_enable_dsp(struct pcxhr_mgr *mgr)
/*
* load the xilinx image
*/
int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilinx, int second)
int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr,
const struct firmware *xilinx, int second)
{
unsigned int i;
unsigned int chipsc;
@ -274,7 +289,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
/* 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 */
/* this bit will always be 1;
* no possibility to test presence of first xilinx
*/
if(second) {
if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
snd_printk(KERN_ERR "error loading first xilinx\n");
@ -290,7 +307,8 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
data = *image;
mask = 0x80;
while (mask) {
chipsc &= ~(PCXHR_CHIPSC_DATA_CLK | PCXHR_CHIPSC_DATA_IN);
chipsc &= ~(PCXHR_CHIPSC_DATA_CLK |
PCXHR_CHIPSC_DATA_IN);
if (data & mask)
chipsc |= PCXHR_CHIPSC_DATA_IN;
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
@ -330,15 +348,20 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
data = dsp->data + i;
if (i == 0) {
/* test data header consistency */
len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]);
if (len && dsp->size != (len + 2) * 3)
len = (unsigned int)((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);
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_TRDY,
PCXHR_ISR_HI08_TRDY,
PCXHR_TIMEOUT_DSP, &dummy);
if (err) {
snd_printk(KERN_ERR "dsp loading error at position %d\n", i);
snd_printk(KERN_ERR
"dsp loading error at position %d\n", i);
return err;
}
/* send host data */
@ -357,7 +380,8 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
/*
* load the eeprom image
*/
int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eeprom)
int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr,
const struct firmware *eeprom)
{
int err;
unsigned char reg;
@ -365,7 +389,9 @@ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eepro
/* 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 */
/* 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);
@ -429,8 +455,10 @@ int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, const struct firmware *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);
return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_CHK,
PCXHR_ISR_HI08_CHK,
PCXHR_TIMEOUT_DSP, &dummy);
}
@ -443,8 +471,8 @@ struct pcxhr_cmd_info {
/* 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 (used with st_length = 1) */
RMH_SSIZE_MASK = 2, /* status size given in bitmask (used with st_length = 1) */
RMH_SSIZE_ARG = 1, /* status size given in the LSB byte */
RMH_SSIZE_MASK = 2, /* status size given in bitmask */
};
/*
@ -474,7 +502,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[CMD_UPDATE_R_BUFFERS] = { 0x840000, 0, RMH_SSIZE_FIXED },
[CMD_FORMAT_STREAM_OUT] = { 0x860000, 0, RMH_SSIZE_FIXED },
[CMD_FORMAT_STREAM_IN] = { 0x870000, 0, RMH_SSIZE_FIXED },
[CMD_STREAM_SAMPLE_COUNT] = { 0x902000, 2, RMH_SSIZE_FIXED }, /* stat_len = nb_streams * 2 */
[CMD_STREAM_SAMPLE_COUNT] = { 0x902000, 2, RMH_SSIZE_FIXED },
[CMD_AUDIO_LEVEL_ADJUST] = { 0xc22000, 0, RMH_SSIZE_FIXED },
};
@ -524,10 +552,13 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
for (i = 0; i < rmh->stat_len; i++) {
/* wait for receiver full */
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_RXDF,
PCXHR_ISR_HI08_RXDF,
PCXHR_TIMEOUT_DSP, &reg);
if (err) {
snd_printk(KERN_ERR "ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )\n",
snd_printk(KERN_ERR "ERROR RMH stat: "
"ISR:RXDF=1 (ISR = %x; i=%d )\n",
reg, i);
return err;
}
@ -537,10 +568,10 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
/* need to update rmh->stat_len on the fly ?? */
if (i==0) {
if (!i) {
if (rmh->dsp_stat != RMH_SSIZE_FIXED) {
if (rmh->dsp_stat == RMH_SSIZE_ARG) {
rmh->stat_len = (u16)(data & 0x0000ff) + 1;
rmh->stat_len = (data & 0x0000ff) + 1;
data &= 0xffff00;
} else {
/* rmh->dsp_stat == RMH_SSIZE_MASK */
@ -562,7 +593,8 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
rmh->stat[i] = data;
}
if (rmh->stat_len > max_stat_len) {
snd_printdd("PCXHR : rmh->stat_len=%x too big\n", rmh->stat_len);
snd_printdd("PCXHR : rmh->stat_len=%x too big\n",
rmh->stat_len);
rmh->stat_len = max_stat_len;
}
return 0;
@ -605,7 +637,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data &= 0xff7fff; /* MASK_1_WORD_COMMAND */
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
snd_printdd("MSG cmd[0]=%x (%s)\n",
data, cmd_names[rmh->cmd_idx]);
#endif
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
@ -619,8 +652,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
if (rmh->cmd_len > 1) {
/* send length */
data = rmh->cmd_len - 1;
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &reg);
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_TRDY,
PCXHR_ISR_HI08_TRDY,
PCXHR_TIMEOUT_DSP, &reg);
if (err)
return err;
PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
@ -653,8 +688,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
/* test status ISR */
if (reg & PCXHR_ISR_HI08_ERR) {
/* ERROR, wait for receiver full */
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
PCXHR_ISR_HI08_RXDF,
PCXHR_ISR_HI08_RXDF,
PCXHR_TIMEOUT_DSP, &reg);
if (err) {
snd_printk(KERN_ERR "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
return err;
@ -663,7 +700,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n", rmh->cmd_idx, data);
snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n",
rmh->cmd_idx, data);
err = -EINVAL;
} else {
/* read the response data */
@ -732,8 +770,9 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
static inline int 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
/* 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;
@ -744,7 +783,8 @@ static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
#define PCXHR_PIPE_STATE_CAPTURE_OFFSET 12
#define MAX_WAIT_FOR_DSP 20
static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *retry)
static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr,
int audio_mask, int *retry)
{
struct pcxhr_rmh rmh;
int err;
@ -766,11 +806,14 @@ static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
"error pipe start (CMD_CAN_START_PIPE) err=%x!\n",
"error pipe start "
"(CMD_CAN_START_PIPE) err=%x!\n",
err);
return err;
}
/* if the pipe couldn't be prepaired for start, retry it later */
/* if the pipe couldn't be prepaired for start,
* retry it later
*/
if (rmh.stat[0] == 0)
*retry |= (1<<audio);
}
@ -801,8 +844,8 @@ static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
"error pipe stop (CMD_STOP_PIPE) err=%x!\n",
err);
"error pipe stop "
"(CMD_STOP_PIPE) err=%x!\n", err);
return err;
}
}
@ -822,15 +865,16 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
if (audio_mask & 1) {
pcxhr_init_rmh(&rmh, CMD_CONF_PIPE);
if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET)
pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0, 1 << audio);
pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0,
1 << audio);
else
pcxhr_set_pipe_cmd_params(&rmh, 1, 0, 0,
1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
"error pipe start (CMD_CONF_PIPE) err=%x!\n",
err);
"error pipe start "
"(CMD_CONF_PIPE) err=%x!\n", err);
return err;
}
}
@ -841,7 +885,9 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR "error pipe start (CMD_SEND_IRQA) err=%x!\n", err );
snd_printk(KERN_ERR
"error pipe start (CMD_SEND_IRQA) err=%x!\n",
err);
return err;
}
return 0;
@ -849,7 +895,8 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_mask, int start)
int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
int capture_mask, int start)
{
int state, i, err;
int audio_mask;
@ -858,21 +905,23 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
struct timeval my_tv1, my_tv2;
do_gettimeofday(&my_tv1);
#endif
audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
audio_mask = (playback_mask |
(capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
/* current pipe state (playback + record) */
state = pcxhr_pipes_running(mgr);
snd_printdd("pcxhr_set_pipe_state %s (mask %x current %x)\n",
start ? "START" : "STOP", audio_mask, state);
if (start) {
audio_mask &= ~state; /* start only pipes that are not yet started */
/* 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 for start */
mdelay(1); /* otherwise wait 1 millisecond and retry */
break; /* success, all pipes prepaired */
mdelay(1); /* wait 1 millisecond and retry */
}
} else {
audio_mask &= state; /* stop only pipes that are started */
@ -891,7 +940,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
if ((state & audio_mask) == (start ? audio_mask : 0))
break;
if (++i >= MAX_WAIT_FOR_DSP * 100) {
snd_printk(KERN_ERR "error pipe start/stop (ED_NO_RESPONSE_AT_IRQA)\n");
snd_printk(KERN_ERR "error pipe start/stop\n");
return -EBUSY;
}
udelay(10); /* wait 10 microseconds */
@ -918,7 +967,8 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
spin_lock_irqsave(&mgr->msg_lock, flags);
if ((mgr->io_num_reg_cont & mask) == value) {
snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n", mask, value);
snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n",
mask, value);
if (changed)
*changed = 0;
spin_unlock_irqrestore(&mgr->msg_lock, flags);
@ -971,7 +1021,8 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
err = ((err >> 12) & 0xfff);
if (!err)
return 0;
snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n", err_src_name[err_src],
snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
err_src_name[err_src],
is_capture ? "Record" : "Play", pipe, err);
if (err == 0xe01)
mgr->async_err_stream_xrun++;
@ -996,6 +1047,13 @@ void pcxhr_msg_tasklet(unsigned long arg)
snd_printdd("TASKLET : PCXHR_IRQ_TIME_CODE event occured\n");
if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
snd_printdd("TASKLET : PCXHR_IRQ_NOTIFY event occured\n");
if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
/* clear events FREQ_CHANGE and TIME_CODE */
pcxhr_init_rmh(prmh, CMD_TEST_IT);
err = pcxhr_send_msg(mgr, prmh);
snd_printdd("CMD_TEST_IT : err=%x, stat=%x\n",
err, prmh->stat[0]);
}
if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
snd_printdd("TASKLET : PCXHR_IRQ_ASYNC event occured\n");
@ -1005,18 +1063,22 @@ void pcxhr_msg_tasklet(unsigned long arg)
prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
err = pcxhr_send_msg(mgr, prmh);
if (err)
snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n", err);
snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%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 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 */
snd_printdd("TASKLET : End%sPipe %d\n",
is_capture ? "Record" : "Play", pipe);
is_capture ? "Record" : "Play",
pipe);
}
i++;
err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
@ -1072,18 +1134,21 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
hw_sample_count += (u_int64_t)rmh.stat[1];
snd_printdd("stream %c%d : abs samples real(%ld) timer(%ld)\n",
stream->pipe->is_capture ? 'C':'P', stream->substream->number,
stream->pipe->is_capture ? 'C' : 'P',
stream->substream->number,
(long unsigned int)hw_sample_count,
(long unsigned int)(stream->timer_abs_periods +
stream->timer_period_frag + PCXHR_GRANULARITY));
stream->timer_period_frag +
mgr->granularity));
return hw_sample_count;
}
static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
struct pcxhr_stream *stream, int samples_to_add)
struct pcxhr_stream *stream,
int samples_to_add)
{
if (stream->substream && (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
if (stream->substream &&
(stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
u_int64_t new_sample_count;
int elapsed = 0;
int hardware_read = 0;
@ -1092,20 +1157,22 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
if (samples_to_add < 0) {
stream->timer_is_synced = 0;
/* add default if no hardware_read possible */
samples_to_add = PCXHR_GRANULARITY;
samples_to_add = mgr->granularity;
}
if (!stream->timer_is_synced) {
if (stream->timer_abs_periods != 0 ||
stream->timer_period_frag + PCXHR_GRANULARITY >=
runtime->period_size) {
new_sample_count = pcxhr_stream_read_position(mgr, stream);
if ((stream->timer_abs_periods != 0) ||
((stream->timer_period_frag + samples_to_add) >=
runtime->period_size)) {
new_sample_count =
pcxhr_stream_read_position(mgr, stream);
hardware_read = 1;
if (new_sample_count >= PCXHR_GRANULARITY_MIN) {
/* sub security offset because of jitter and
* finer granularity of dsp time (MBOX4)
if (new_sample_count >= mgr->granularity) {
/* sub security offset because of
* jitter and finer granularity of
* dsp time (MBOX4)
*/
new_sample_count -= PCXHR_GRANULARITY_MIN;
new_sample_count -= mgr->granularity;
stream->timer_is_synced = 1;
}
}
@ -1128,12 +1195,15 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
stream->timer_buf_periods = 0;
stream->timer_abs_periods = new_elapse_pos;
}
if (new_sample_count >= stream->timer_abs_periods)
stream->timer_period_frag = (u_int32_t)(new_sample_count -
if (new_sample_count >= stream->timer_abs_periods) {
stream->timer_period_frag =
(u_int32_t)(new_sample_count -
stream->timer_abs_periods);
else
snd_printk(KERN_ERR "ERROR new_sample_count too small ??? %lx\n",
} else {
snd_printk(KERN_ERR
"ERROR new_sample_count too small ??? %ld\n",
(long unsigned int)new_sample_count);
}
if (elapsed) {
spin_unlock(&mgr->lock);
@ -1143,7 +1213,6 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
}
}
irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
{
struct pcxhr_mgr *mgr = dev_id;
@ -1156,7 +1225,8 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
spin_unlock(&mgr->lock);
return IRQ_NONE; /* this device did not cause the interrupt */
/* this device did not cause the interrupt */
return IRQ_NONE;
}
/* clear interrupt */
@ -1167,10 +1237,12 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
if (reg & PCXHR_IRQ_TIMER) {
int timer_toggle = reg & 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_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) {
if ((dsp_time_diff < 0) &&
(mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) {
snd_printdd("ERROR DSP TIME old(%d) new(%d) -> "
"resynchronize all streams\n",
mgr->dsp_time_last, dsp_time_new);
@ -1178,40 +1250,49 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (dsp_time_diff == 0)
snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n",
dsp_time_new);
else if (dsp_time_diff >= (2*mgr->granularity))
snd_printdd("ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
mgr->dsp_time_last, dsp_time_new - mgr->dsp_time_last);
mgr->dsp_time_last,
dsp_time_new - mgr->dsp_time_last);
else if (dsp_time_diff % mgr->granularity)
snd_printdd("ERROR DSP TIME increased by %d\n",
dsp_time_diff);
#endif
mgr->dsp_time_last = dsp_time_new;
if (timer_toggle == mgr->timer_toggle)
if (timer_toggle == mgr->timer_toggle) {
snd_printdd("ERROR TIMER TOGGLE\n");
mgr->dsp_time_err++;
}
mgr->timer_toggle = timer_toggle;
reg &= ~PCXHR_IRQ_TIMER;
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],
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],
pcxhr_update_timer_pos(mgr,
&chip->playback_stream[j],
dsp_time_diff);
}
}
/* other irq's handled in the tasklet */
if (reg & PCXHR_IRQ_MASK) {
/* as we didn't request any notifications, some kind of xrun error
* will probably occured
if (reg & PCXHR_IRQ_ASYNC) {
/* as we didn't request any async notifications,
* some kind of xrun error will probably occured
*/
/* better resynchronize all streams next interrupt : */
mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
}
mgr->src_it_dsp = reg;
tasklet_hi_schedule(&mgr->msg_taskq);
}

View file

@ -65,7 +65,7 @@ enum {
CMD_RESYNC_AUDIO_INPUTS, /* cmd_len = 1 stat_len = 0 */
CMD_GET_DSP_RESOURCES, /* cmd_len = 1 stat_len = 4 */
CMD_SET_TIMER_INTERRUPT, /* cmd_len = 1 stat_len = 0 */
CMD_RES_PIPE, /* cmd_len = 2 stat_len = 0 */
CMD_RES_PIPE, /* cmd_len >=2 stat_len = 0 */
CMD_FREE_PIPE, /* cmd_len = 1 stat_len = 0 */
CMD_CONF_PIPE, /* cmd_len = 2 stat_len = 0 */
CMD_STOP_PIPE, /* cmd_len = 1 stat_len = 0 */
@ -96,6 +96,8 @@ void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd);
void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh* rmh, int capture, unsigned int param1,
unsigned int param2, unsigned int param3);
#define DSP_EXT_CMD_SET(x) (x->dsp_version > 0x012800)
/*
send the rmh
*/
@ -110,6 +112,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
#define IO_NUM_REG_STATUS 5
#define IO_NUM_REG_CUER 10
#define IO_NUM_UER_CHIP_REG 11
#define IO_NUM_REG_CONFIG_SRC 12
#define IO_NUM_REG_OUT_ANA_LEVEL 20
#define IO_NUM_REG_IN_ANA_LEVEL 21