/*
* Driver for Digigram pcxhr compatible soundcards
*
* low level interface with interrupt and message handling implementation
*
* Copyright (c) 2004 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <sound/core.h>
#include "pcxhr.h"
#include "pcxhr_mixer.h"
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
/* registers used on the PLX (port 1) */
#define PCXHR_PLX_OFFSET_MIN 0x40
#define PCXHR_PLX_MBOX0 0x40
#define PCXHR_PLX_MBOX1 0x44
#define PCXHR_PLX_MBOX2 0x48
#define PCXHR_PLX_MBOX3 0x4C
#define PCXHR_PLX_MBOX4 0x50
#define PCXHR_PLX_MBOX5 0x54
#define PCXHR_PLX_MBOX6 0x58
#define PCXHR_PLX_MBOX7 0x5C
#define PCXHR_PLX_L2PCIDB 0x64
#define PCXHR_PLX_IRQCS 0x68
#define PCXHR_PLX_CHIPSC 0x6C
/* registers used on the DSP (port 2) */
#define PCXHR_DSP_ICR 0x00
#define PCXHR_DSP_CVR 0x04
#define PCXHR_DSP_ISR 0x08
#define PCXHR_DSP_IVR 0x0C
#define PCXHR_DSP_RXH 0x14
#define PCXHR_DSP_TXH 0x14
#define PCXHR_DSP_RXM 0x18
#define PCXHR_DSP_TXM 0x18
#define PCXHR_DSP_RXL 0x1C
#define PCXHR_DSP_TXL 0x1C
#define PCXHR_DSP_RESET 0x20
#define PCXHR_DSP_OFFSET_MAX 0x20
/* access to the card */
#define PCXHR_PLX 1
#define PCXHR_DSP 2
#if (PCXHR_DSP_OFFSET_MAX > PCXHR_PLX_OFFSET_MIN)
#undef PCXHR_REG_TO_PORT(x)
#else
#define PCXHR_REG_TO_PORT(x) ((x)>PCXHR_DSP_OFFSET_MAX ? PCXHR_PLX : PCXHR_DSP)
#endif
#define PCXHR_INPB(mgr,x) inb((mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
#define PCXHR_INPL(mgr,x) inl((mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
#define PCXHR_OUTPB(mgr,x,data) outb((data), (mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
#define PCXHR_OUTPL(mgr,x,data) outl((data), (mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
/* attention : access the PCXHR_DSP_* registers with inb and outb only ! */
/* params used with PCXHR_PLX_MBOX0 */
#define PCXHR_MBOX0_HF5 (1 << 0)
#define PCXHR_MBOX0_HF4 (1 << 1)
#define PCXHR_MBOX0_BOOT_HERE (1 << 23)
/* params used with PCXHR_PLX_IRQCS */
#define PCXHR_IRQCS_ENABLE_PCIIRQ (1 << 8)
#define PCXHR_IRQCS_ENABLE_PCIDB (1 << 9)
#define PCXHR_IRQCS_ACTIVE_PCIDB (1 << 13)
/* params used with PCXHR_PLX_CHIPSC */
#define PCXHR_CHIPSC_INIT_VALUE 0x100D767E
#define PCXHR_CHIPSC_RESET_XILINX (1 << 16)
#define PCXHR_CHIPSC_GPI_USERI (1 << 17)
#define PCXHR_CHIPSC_DATA_CLK (1 << 24)
#define PCXHR_CHIPSC_DATA_IN (1 << 26)
/* 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.
*/
static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
unsigned char mask, unsigned char bit, int time,
unsigned char* read)
{
int i = 0;
unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
do {
*read = PCXHR_INPB(mgr, reg);
if ((*read & mask) == bit) {
if (i > 100)
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",
reg, mask, *read);
return -EIO;
}
/* constants used with pcxhr_check_reg_bit() */
#define PCXHR_TIMEOUT_DSP 200
#define PCXHR_MASK_EXTRA_INFO 0x0000FE
#define PCXHR_MASK_IT_HF0 0x000100
#define PCXHR_MASK_IT_HF1 0x000200
#define PCXHR_MASK_IT_NO_HF0_HF1 0x000400
#define PCXHR_MASK_IT_MANAGE_HF5 0x000800
#define PCXHR_MASK_IT_WAIT 0x010000
#define PCXHR_MASK_IT_WAIT_EXTRA 0x020000
#define PCXHR_IT_SEND_BYTE_XILINX (0x0000003C | PCXHR_MASK_IT_HF0)
#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)
#define PCXHR_IT_RESET_BOARD_FUNC (0x0000000C | PCXHR_MASK_IT_HF0 | \
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)
#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)
{
int