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

📄 atiixp.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *   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 <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_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};static int spdif_aclink[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};static int boot_devs;module_param_array(index, int, boot_devs, 0444);MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");module_param_array(id, charp, boot_devs, 0444);MODULE_PARM_DESC(id, "ID string for ATI IXP controller.");module_param_array(enable, bool, boot_devs, 0444);MODULE_PARM_DESC(enable, "Enable audio part of ATI IXP controller.");module_param_array(ac97_clock, int, boot_devs, 0444);MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");module_param_array(spdif_aclink, bool, boot_devs, 0444);MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");/* */#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 *//* */typedef struct snd_atiixp atiixp_t;typedef struct snd_atiixp_dma atiixp_dma_t;typedef struct snd_atiixp_dma_ops atiixp_dma_ops_t;/* * DMA packate descriptor */typedef 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 */} atiixp_dma_desc_t;/* * 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 snd_atiixp_dma_ops {	int type;			/* ATI_DMA_XXX */	unsigned int llp_offset;	/* LINKPTR offset */	unsigned int dt_cur;		/* DT_CUR offset */	void (*enable_dma)(atiixp_t *chip, int on);	/* called from open callback */	void (*enable_transfer)(atiixp_t *chip, int on); /* called from trigger (START/STOP) */	void (*flush_dma)(atiixp_t *chip);		/* called from trigger (STOP only) */};/* * DMA stream */struct snd_atiixp_dma {	const atiixp_dma_ops_t *ops;	struct snd_dma_buffer desc_buf;	snd_pcm_substream_t *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 snd_atiixp {	snd_card_t *card;	struct pci_dev *pci;	unsigned long addr;	void __iomem *remap_addr;	int irq;		ac97_bus_t *ac97_bus;	ac97_t *ac97[NUM_ATI_CODECS];	spinlock_t reg_lock;	spinlock_t ac97_lock;	atiixp_dma_t dmas[NUM_ATI_DMAS];	struct ac97_pcm *pcms[NUM_ATI_PCMS];	snd_pcm_t *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 semaphore 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);/* * lowlevel functions *//* * update the bits of the given register. * return 1 if the bits changed. */static int snd_atiixp_update_bits(atiixp_t *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)/* delay for one tick */#define do_delay() do { \	set_current_state(TASK_UNINTERRUPTIBLE); \	schedule_timeout(1); \} while (0)/* * 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(atiixp_dma_desc_t))/* * 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(atiixp_t *chip, atiixp_dma_t *dma,				   snd_pcm_substream_t *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++) {		atiixp_dma_desc_t *desc = &((atiixp_dma_desc_t *)dma->desc_buf.area)[i];		desc->addr = cpu_to_le32(addr);		desc->status = 0;		desc->size = period_bytes >> 2; /* in dwords */		desc_addr += sizeof(atiixp_dma_desc_t);		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(atiixp_t *chip, atiixp_dma_t *dma, snd_pcm_substream_t *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(atiixp_t *chip){	int timeout = 1000;	while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {		if (! timeout--) {			snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");			return -EBUSY;		}		udelay(1);	}	return 0;}static unsigned short snd_atiixp_codec_read(atiixp_t *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: codec read timeout (reg %x)\n", reg);	return 0xffff;}static void snd_atiixp_codec_write(atiixp_t *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;	atiixp_write(chip, PHYS_OUT_ADDR, data);}static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg){	atiixp_t *chip = ac97->private_data;	unsigned short data;	spin_lock(&chip->ac97_lock);	data = snd_atiixp_codec_read(chip, ac97->num, reg);	spin_unlock(&chip->ac97_lock);	return data;    }static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){	atiixp_t *chip = ac97->private_data;	spin_lock(&chip->ac97_lock);	snd_atiixp_codec_write(chip, ac97->num, reg, val);	spin_unlock(&chip->ac97_lock);}/* * reset AC link */static int snd_atiixp_aclink_reset(atiixp_t *chip){	int timeout;	/* reset powerdoewn */	if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0))		udelay(10);	/* perform a software reset */	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET);	atiixp_read(chip, CMD);	udelay(10);	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0);    	timeout = 10;	while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) {		/* do a hard reset */		atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,			      ATI_REG_CMD_AC_SYNC);		atiixp_read(chip, CMD);		do_delay();		atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);		if (--timeout) {			snd_printk(KERN_ERR "atiixp: codec reset timeout\n");			break;		}	}	/* deassert RESET and assert SYNC to make sure */	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,		      ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET);	return 0;}#ifdef CONFIG_PMstatic int snd_atiixp_aclink_down(atiixp_t *chip){	// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */	//	return -EBUSY;	atiixp_update(chip, CMD,		     ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET,		     ATI_REG_CMD_POWERDOWN);	return 0;}#endif

⌨️ 快捷键说明

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