⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 atiixp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *   ALSA driver for ATI IXP 150/200/250/300 AC97 controllers * *	Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * *   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 <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/info.h>#include <sound/ac97_codec.h>#include <sound/initval.h>MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");MODULE_DESCRIPTION("ATI IXP AC97 controller");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}");static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */static int ac97_clock = 48000;static char *ac97_quirk;static int spdif_aclink = 1;static int ac97_codec = -1;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for ATI IXP controller.");module_param(ac97_clock, int, 0444);MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");module_param(ac97_quirk, charp, 0444);MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");module_param(ac97_codec, int, 0444);MODULE_PARM_DESC(ac97_codec, "Specify codec instead of probing.");module_param(spdif_aclink, bool, 0444);MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");/* just for backward compatibility */static int enable;module_param(enable, bool, 0444);/* */#define ATI_REG_ISR			0x00	/* interrupt source */#define  ATI_REG_ISR_IN_XRUN		(1U<<0)#define  ATI_REG_ISR_IN_STATUS		(1U<<1)#define  ATI_REG_ISR_OUT_XRUN		(1U<<2)#define  ATI_REG_ISR_OUT_STATUS		(1U<<3)#define  ATI_REG_ISR_SPDF_XRUN		(1U<<4)#define  ATI_REG_ISR_SPDF_STATUS	(1U<<5)#define  ATI_REG_ISR_PHYS_INTR		(1U<<8)#define  ATI_REG_ISR_PHYS_MISMATCH	(1U<<9)#define  ATI_REG_ISR_CODEC0_NOT_READY	(1U<<10)#define  ATI_REG_ISR_CODEC1_NOT_READY	(1U<<11)#define  ATI_REG_ISR_CODEC2_NOT_READY	(1U<<12)#define  ATI_REG_ISR_NEW_FRAME		(1U<<13)#define ATI_REG_IER			0x04	/* interrupt enable */#define  ATI_REG_IER_IN_XRUN_EN		(1U<<0)#define  ATI_REG_IER_IO_STATUS_EN	(1U<<1)#define  ATI_REG_IER_OUT_XRUN_EN	(1U<<2)#define  ATI_REG_IER_OUT_XRUN_COND	(1U<<3)#define  ATI_REG_IER_SPDF_XRUN_EN	(1U<<4)#define  ATI_REG_IER_SPDF_STATUS_EN	(1U<<5)#define  ATI_REG_IER_PHYS_INTR_EN	(1U<<8)#define  ATI_REG_IER_PHYS_MISMATCH_EN	(1U<<9)#define  ATI_REG_IER_CODEC0_INTR_EN	(1U<<10)#define  ATI_REG_IER_CODEC1_INTR_EN	(1U<<11)#define  ATI_REG_IER_CODEC2_INTR_EN	(1U<<12)#define  ATI_REG_IER_NEW_FRAME_EN	(1U<<13)	/* (RO */#define  ATI_REG_IER_SET_BUS_BUSY	(1U<<14)	/* (WO) audio is running */#define ATI_REG_CMD			0x08	/* command */#define  ATI_REG_CMD_POWERDOWN		(1U<<0)#define  ATI_REG_CMD_RECEIVE_EN		(1U<<1)#define  ATI_REG_CMD_SEND_EN		(1U<<2)#define  ATI_REG_CMD_STATUS_MEM		(1U<<3)#define  ATI_REG_CMD_SPDF_OUT_EN	(1U<<4)#define  ATI_REG_CMD_SPDF_STATUS_MEM	(1U<<5)#define  ATI_REG_CMD_SPDF_THRESHOLD	(3U<<6)#define  ATI_REG_CMD_SPDF_THRESHOLD_SHIFT	6#define  ATI_REG_CMD_IN_DMA_EN		(1U<<8)#define  ATI_REG_CMD_OUT_DMA_EN		(1U<<9)#define  ATI_REG_CMD_SPDF_DMA_EN	(1U<<10)#define  ATI_REG_CMD_SPDF_OUT_STOPPED	(1U<<11)#define  ATI_REG_CMD_SPDF_CONFIG_MASK	(7U<<12)#define   ATI_REG_CMD_SPDF_CONFIG_34	(1U<<12)#define   ATI_REG_CMD_SPDF_CONFIG_78	(2U<<12)#define   ATI_REG_CMD_SPDF_CONFIG_69	(3U<<12)#define   ATI_REG_CMD_SPDF_CONFIG_01	(4U<<12)#define  ATI_REG_CMD_INTERLEAVE_SPDF	(1U<<16)#define  ATI_REG_CMD_AUDIO_PRESENT	(1U<<20)#define  ATI_REG_CMD_INTERLEAVE_IN	(1U<<21)#define  ATI_REG_CMD_INTERLEAVE_OUT	(1U<<22)#define  ATI_REG_CMD_LOOPBACK_EN	(1U<<23)#define  ATI_REG_CMD_PACKED_DIS		(1U<<24)#define  ATI_REG_CMD_BURST_EN		(1U<<25)#define  ATI_REG_CMD_PANIC_EN		(1U<<26)#define  ATI_REG_CMD_MODEM_PRESENT	(1U<<27)#define  ATI_REG_CMD_ACLINK_ACTIVE	(1U<<28)#define  ATI_REG_CMD_AC_SOFT_RESET	(1U<<29)#define  ATI_REG_CMD_AC_SYNC		(1U<<30)#define  ATI_REG_CMD_AC_RESET		(1U<<31)#define ATI_REG_PHYS_OUT_ADDR		0x0c#define  ATI_REG_PHYS_OUT_CODEC_MASK	(3U<<0)#define  ATI_REG_PHYS_OUT_RW		(1U<<2)#define  ATI_REG_PHYS_OUT_ADDR_EN	(1U<<8)#define  ATI_REG_PHYS_OUT_ADDR_SHIFT	9#define  ATI_REG_PHYS_OUT_DATA_SHIFT	16#define ATI_REG_PHYS_IN_ADDR		0x10#define  ATI_REG_PHYS_IN_READ_FLAG	(1U<<8)#define  ATI_REG_PHYS_IN_ADDR_SHIFT	9#define  ATI_REG_PHYS_IN_DATA_SHIFT	16#define ATI_REG_SLOTREQ			0x14#define ATI_REG_COUNTER			0x18#define  ATI_REG_COUNTER_SLOT		(3U<<0)	/* slot # */#define  ATI_REG_COUNTER_BITCLOCK	(31U<<8)#define ATI_REG_IN_FIFO_THRESHOLD	0x1c#define ATI_REG_IN_DMA_LINKPTR		0x20#define ATI_REG_IN_DMA_DT_START		0x24	/* RO */#define ATI_REG_IN_DMA_DT_NEXT		0x28	/* RO */#define ATI_REG_IN_DMA_DT_CUR		0x2c	/* RO */#define ATI_REG_IN_DMA_DT_SIZE		0x30#define ATI_REG_OUT_DMA_SLOT		0x34#define  ATI_REG_OUT_DMA_SLOT_BIT(x)	(1U << ((x) - 3))#define  ATI_REG_OUT_DMA_SLOT_MASK	0x1ff#define  ATI_REG_OUT_DMA_THRESHOLD_MASK	0xf800#define  ATI_REG_OUT_DMA_THRESHOLD_SHIFT	11#define ATI_REG_OUT_DMA_LINKPTR		0x38#define ATI_REG_OUT_DMA_DT_START	0x3c	/* RO */#define ATI_REG_OUT_DMA_DT_NEXT		0x40	/* RO */#define ATI_REG_OUT_DMA_DT_CUR		0x44	/* RO */#define ATI_REG_OUT_DMA_DT_SIZE		0x48#define ATI_REG_SPDF_CMD		0x4c#define  ATI_REG_SPDF_CMD_LFSR		(1U<<4)#define  ATI_REG_SPDF_CMD_SINGLE_CH	(1U<<5)#define  ATI_REG_SPDF_CMD_LFSR_ACC	(0xff<<8)	/* RO */#define ATI_REG_SPDF_DMA_LINKPTR	0x50#define ATI_REG_SPDF_DMA_DT_START	0x54	/* RO */#define ATI_REG_SPDF_DMA_DT_NEXT	0x58	/* RO */#define ATI_REG_SPDF_DMA_DT_CUR		0x5c	/* RO */#define ATI_REG_SPDF_DMA_DT_SIZE	0x60#define ATI_REG_MODEM_MIRROR		0x7c#define ATI_REG_AUDIO_MIRROR		0x80#define ATI_REG_6CH_REORDER		0x84	/* reorder slots for 6ch */#define  ATI_REG_6CH_REORDER_EN		(1U<<0)	/* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */#define ATI_REG_FIFO_FLUSH		0x88#define  ATI_REG_FIFO_OUT_FLUSH		(1U<<0)#define  ATI_REG_FIFO_IN_FLUSH		(1U<<1)/* LINKPTR */#define  ATI_REG_LINKPTR_EN		(1U<<0)/* [INT|OUT|SPDIF]_DMA_DT_SIZE */#define  ATI_REG_DMA_DT_SIZE		(0xffffU<<0)#define  ATI_REG_DMA_FIFO_USED		(0x1fU<<16)#define  ATI_REG_DMA_FIFO_FREE		(0x1fU<<21)#define  ATI_REG_DMA_STATE		(7U<<26)#define ATI_MAX_DESCRIPTORS	256	/* max number of descriptor packets */struct atiixp;/* * DMA packate descriptor */struct atiixp_dma_desc {	u32 addr;	/* DMA buffer address */	u16 status;	/* status bits */	u16 size;	/* size of the packet in dwords */	u32 next;	/* address of the next packet descriptor */};/* * stream enum */enum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, ATI_DMA_SPDIF, NUM_ATI_DMAS }; /* DMAs */enum { ATI_PCM_OUT, ATI_PCM_IN, ATI_PCM_SPDIF, NUM_ATI_PCMS }; /* AC97 pcm slots */enum { ATI_PCMDEV_ANALOG, ATI_PCMDEV_DIGITAL, NUM_ATI_PCMDEVS }; /* pcm devices */#define NUM_ATI_CODECS	3/* * constants and callbacks for each DMA type */struct atiixp_dma_ops {	int type;			/* ATI_DMA_XXX */	unsigned int llp_offset;	/* LINKPTR offset */	unsigned int dt_cur;		/* DT_CUR offset */	/* called from open callback */	void (*enable_dma)(struct atiixp *chip, int on);	/* called from trigger (START/STOP) */	void (*enable_transfer)(struct atiixp *chip, int on); 	/* called from trigger (STOP only) */	void (*flush_dma)(struct atiixp *chip);};/* * DMA stream */struct atiixp_dma {	const struct atiixp_dma_ops *ops;	struct snd_dma_buffer desc_buf;	struct snd_pcm_substream *substream;	/* assigned PCM substream */	unsigned int buf_addr, buf_bytes;	/* DMA buffer address, bytes */	unsigned int period_bytes, periods;	int opened;	int running;	int suspended;	int pcm_open_flag;	int ac97_pcm_type;	/* index # of ac97_pcm to access, -1 = not used */	unsigned int saved_curptr;};/* * ATI IXP chip */struct atiixp {	struct snd_card *card;	struct pci_dev *pci;	unsigned long addr;	void __iomem *remap_addr;	int irq;		struct snd_ac97_bus *ac97_bus;	struct snd_ac97 *ac97[NUM_ATI_CODECS];	spinlock_t reg_lock;	struct atiixp_dma dmas[NUM_ATI_DMAS];	struct ac97_pcm *pcms[NUM_ATI_PCMS];	struct snd_pcm *pcmdevs[NUM_ATI_PCMDEVS];	int max_channels;		/* max. channels for PCM out */	unsigned int codec_not_ready_bits;	/* for codec detection */	int spdif_over_aclink;		/* passed from the module option */	struct mutex open_mutex;	/* playback open mutex */};/* */static struct pci_device_id snd_atiixp_ids[] = {	{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */	{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */	{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);static struct snd_pci_quirk atiixp_quirks[] __devinitdata = {	SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0),	{ } /* terminator */};/* * lowlevel functions *//* * update the bits of the given register. * return 1 if the bits changed. */static int snd_atiixp_update_bits(struct atiixp *chip, unsigned int reg,				 unsigned int mask, unsigned int value){	void __iomem *addr = chip->remap_addr + reg;	unsigned int data, old_data;	old_data = data = readl(addr);	data &= ~mask;	data |= value;	if (old_data == data)		return 0;	writel(data, addr);	return 1;}/* * macros for easy use */#define atiixp_write(chip,reg,value) \	writel(value, chip->remap_addr + ATI_REG_##reg)#define atiixp_read(chip,reg) \	readl(chip->remap_addr + ATI_REG_##reg)#define atiixp_update(chip,reg,mask,val) \	snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val)/* * handling DMA packets * * we allocate a linear buffer for the DMA, and split it to  each packet. * in a future version, a scatter-gather buffer should be implemented. */#define ATI_DESC_LIST_SIZE \	PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(struct atiixp_dma_desc))/* * build packets ring for the given buffer size. * * IXP handles the buffer descriptors, which are connected as a linked * list.  although we can change the list dynamically, in this version, * a static RING of buffer descriptors is used. * * the ring is built in this function, and is set up to the hardware.  */static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,				    struct snd_pcm_substream *substream,				    unsigned int periods,				    unsigned int period_bytes){	unsigned int i;	u32 addr, desc_addr;	unsigned long flags;	if (periods > ATI_MAX_DESCRIPTORS)		return -ENOMEM;	if (dma->desc_buf.area == NULL) {		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,					snd_dma_pci_data(chip->pci),					ATI_DESC_LIST_SIZE,					&dma->desc_buf) < 0)			return -ENOMEM;		dma->period_bytes = dma->periods = 0; /* clear */	}	if (dma->periods == periods && dma->period_bytes == period_bytes)		return 0;	/* reset DMA before changing the descriptor table */	spin_lock_irqsave(&chip->reg_lock, flags);	writel(0, chip->remap_addr + dma->ops->llp_offset);	dma->ops->enable_dma(chip, 0);	dma->ops->enable_dma(chip, 1);	spin_unlock_irqrestore(&chip->reg_lock, flags);	/* fill the entries */	addr = (u32)substream->runtime->dma_addr;	desc_addr = (u32)dma->desc_buf.addr;	for (i = 0; i < periods; i++) {		struct atiixp_dma_desc *desc;		desc = &((struct atiixp_dma_desc *)dma->desc_buf.area)[i];		desc->addr = cpu_to_le32(addr);		desc->status = 0;		desc->size = period_bytes >> 2; /* in dwords */		desc_addr += sizeof(struct atiixp_dma_desc);		if (i == periods - 1)			desc->next = cpu_to_le32((u32)dma->desc_buf.addr);		else			desc->next = cpu_to_le32(desc_addr);		addr += period_bytes;	}	writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,	       chip->remap_addr + dma->ops->llp_offset);	dma->period_bytes = period_bytes;	dma->periods = periods;	return 0;}/* * remove the ring buffer and release it if assigned */static void atiixp_clear_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,				     struct snd_pcm_substream *substream){	if (dma->desc_buf.area) {		writel(0, chip->remap_addr + dma->ops->llp_offset);		snd_dma_free_pages(&dma->desc_buf);		dma->desc_buf.area = NULL;	}}/* * AC97 interface */static int snd_atiixp_acquire_codec(struct atiixp *chip){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -