Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93bf5d87 authored by Markus Bollinger's avatar Markus Bollinger Committed by Takashi Iwai
Browse files

ALSA: pcxhr - add support for pcxhr stereo sound cards



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

Signed-off-by: default avatarMarkus Bollinger <bollinger@digigram.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 13d428af
Loading
Loading
Loading
Loading
+186 −105
Original line number Diff line number Diff line
@@ -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);
	}
+4 −1
Original line number Diff line number Diff line
@@ -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