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

📄 atiixp_modem.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *   ALSA driver for ATI IXP 150/200/250 AC97 modem 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 MC97 controller");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250}}");static int index = -2; /* Exclude the first card */static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */static int ac97_clock = 48000;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).");/* just for backward compatibility */static int enable;module_param(enable, bool, 0444);/* */#define ATI_REG_ISR			0x00	/* interrupt source */#define  ATI_REG_ISR_MODEM_IN_XRUN	(1U<<0)#define  ATI_REG_ISR_MODEM_IN_STATUS	(1U<<1)#define  ATI_REG_ISR_MODEM_OUT1_XRUN	(1U<<2)#define  ATI_REG_ISR_MODEM_OUT1_STATUS	(1U<<3)#define  ATI_REG_ISR_MODEM_OUT2_XRUN	(1U<<4)#define  ATI_REG_ISR_MODEM_OUT2_STATUS	(1U<<5)#define  ATI_REG_ISR_MODEM_OUT3_XRUN	(1U<<6)#define  ATI_REG_ISR_MODEM_OUT3_STATUS	(1U<<7)#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_ISR_MODEM_GPIO_DATA	(1U<<14)#define ATI_REG_IER			0x04	/* interrupt enable */#define  ATI_REG_IER_MODEM_IN_XRUN_EN	(1U<<0)#define  ATI_REG_IER_MODEM_STATUS_EN	(1U<<1)#define  ATI_REG_IER_MODEM_OUT1_XRUN_EN	(1U<<2)#define  ATI_REG_IER_MODEM_OUT2_XRUN_EN	(1U<<4)#define  ATI_REG_IER_MODEM_OUT3_XRUN_EN	(1U<<6)#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_MODEM_GPIO_DATA_EN	(1U<<14)	/* (WO) modem is running */#define  ATI_REG_IER_MODEM_SET_BUS_BUSY	(1U<<15)#define ATI_REG_CMD			0x08	/* command */#define  ATI_REG_CMD_POWERDOWN	(1U<<0)#define  ATI_REG_CMD_MODEM_RECEIVE_EN	(1U<<1)	/* modem only */#define  ATI_REG_CMD_MODEM_SEND1_EN	(1U<<2)	/* modem only */#define  ATI_REG_CMD_MODEM_SEND2_EN	(1U<<3)	/* modem only */#define  ATI_REG_CMD_MODEM_SEND3_EN	(1U<<4)	/* modem only */#define  ATI_REG_CMD_MODEM_STATUS_MEM	(1U<<5)	/* modem only */#define  ATI_REG_CMD_MODEM_IN_DMA_EN	(1U<<8)	/* modem only */#define  ATI_REG_CMD_MODEM_OUT_DMA1_EN	(1U<<9)	/* modem only */#define  ATI_REG_CMD_MODEM_OUT_DMA2_EN	(1U<<10)	/* modem only */#define  ATI_REG_CMD_MODEM_OUT_DMA3_EN	(1U<<11)	/* modem only */#define  ATI_REG_CMD_AUDIO_PRESENT	(1U<<20)#define  ATI_REG_CMD_MODEM_GPIO_THRU_DMA	(1U<<22)	/* modem only */#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_MODEM_IN_DMA_LINKPTR	0x20#define ATI_REG_MODEM_IN_DMA_DT_START	0x24	/* RO */#define ATI_REG_MODEM_IN_DMA_DT_NEXT	0x28	/* RO */#define ATI_REG_MODEM_IN_DMA_DT_CUR	0x2c	/* RO */#define ATI_REG_MODEM_IN_DMA_DT_SIZE	0x30#define ATI_REG_MODEM_OUT_FIFO		0x34	/* output threshold */#define  ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK	(0xf<<16)#define  ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT	16#define ATI_REG_MODEM_OUT_DMA1_LINKPTR	0x38#define ATI_REG_MODEM_OUT_DMA2_LINKPTR	0x3c#define ATI_REG_MODEM_OUT_DMA3_LINKPTR	0x40#define ATI_REG_MODEM_OUT_DMA1_DT_START	0x44#define ATI_REG_MODEM_OUT_DMA1_DT_NEXT	0x48#define ATI_REG_MODEM_OUT_DMA1_DT_CUR	0x4c#define ATI_REG_MODEM_OUT_DMA2_DT_START	0x50#define ATI_REG_MODEM_OUT_DMA2_DT_NEXT	0x54#define ATI_REG_MODEM_OUT_DMA2_DT_CUR	0x58#define ATI_REG_MODEM_OUT_DMA3_DT_START	0x5c#define ATI_REG_MODEM_OUT_DMA3_DT_NEXT	0x60#define ATI_REG_MODEM_OUT_DMA3_DT_CUR	0x64#define ATI_REG_MODEM_OUT_DMA12_DT_SIZE	0x68#define ATI_REG_MODEM_OUT_DMA3_DT_SIZE	0x6c#define ATI_REG_MODEM_OUT_FIFO_USED     0x70#define ATI_REG_MODEM_OUT_GPIO		0x74#define  ATI_REG_MODEM_OUT_GPIO_EN	   1#define  ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT 5#define ATI_REG_MODEM_IN_GPIO		0x78#define ATI_REG_MODEM_MIRROR		0x7c#define ATI_REG_AUDIO_MIRROR		0x80#define ATI_REG_MODEM_FIFO_FLUSH	0x88#define  ATI_REG_MODEM_FIFO_OUT1_FLUSH	(1U<<0)#define  ATI_REG_MODEM_FIFO_OUT2_FLUSH	(1U<<1)#define  ATI_REG_MODEM_FIFO_OUT3_FLUSH	(1U<<2)#define  ATI_REG_MODEM_FIFO_IN_FLUSH	(1U<<3)/* LINKPTR */#define  ATI_REG_LINKPTR_EN		(1U<<0)#define ATI_MAX_DESCRIPTORS	256	/* max number of descriptor packets */struct atiixp_modem;/* * 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, NUM_ATI_DMAS }; /* DMAs */enum { ATI_PCM_OUT, ATI_PCM_IN, NUM_ATI_PCMS }; /* AC97 pcm slots */enum { ATI_PCMDEV_ANALOG, 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_modem *chip, int on);	/* called from trigger (START/STOP) */	void (*enable_transfer)(struct atiixp_modem *chip, int on); 	/* called from trigger (STOP only) */	void (*flush_dma)(struct atiixp_modem *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 pcm_open_flag;	int ac97_pcm_type;	/* index # of ac97_pcm to access, -1 = not used */};/* * ATI IXP chip */struct atiixp_modem {	struct snd_card *card;	struct pci_dev *pci;	struct resource *res;		/* memory i/o */	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, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */	{ 0x1002, 0x4378, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);/* * lowlevel functions *//* * update the bits of the given register. * return 1 if the bits changed. */static int snd_atiixp_update_bits(struct atiixp_modem *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_modem *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_modem *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_modem *chip){	int timeout = 1000;	while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {		if (! timeout--) {			snd_printk(KERN_WARNING "atiixp-modem: codec acquire timeout\n");			return -EBUSY;		}		udelay(1);	}	return 0;}static unsigned short snd_atiixp_codec_read(struct atiixp_modem *chip,					    unsigned short codec,					    unsigned short reg){	unsigned int data;	int timeout;	if (snd_atiixp_acquire_codec(chip) < 0)		return 0xffff;	data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |		ATI_REG_PHYS_OUT_ADDR_EN |		ATI_REG_PHYS_OUT_RW |		codec;	atiixp_write(chip, PHYS_OUT_ADDR, data);	if (snd_atiixp_acquire_codec(chip) < 0)		return 0xffff;	timeout = 1000;	do {		data = atiixp_read(chip, PHYS_IN_ADDR);		if (data & ATI_REG_PHYS_IN_READ_FLAG)			return data >> ATI_REG_PHYS_IN_DATA_SHIFT;		udelay(1);	} while (--timeout);	/* time out may happen during reset */	if (reg < 0x7c)		snd_printk(KERN_WARNING "atiixp-modem: codec read timeout (reg %x)\n", reg);	return 0xffff;}static void snd_atiixp_codec_write(struct atiixp_modem *chip,				   unsigned short codec,				   unsigned short reg, unsigned short val){	unsigned int data;    	if (snd_atiixp_acquire_codec(chip) < 0)		return;	data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) |		((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |		ATI_REG_PHYS_OUT_ADDR_EN | codec;

⌨️ 快捷键说明

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