cregit-Linux how code gets into the kernel

Release 4.14 sound/pci/atiixp.c

Directory: sound/pci
/*
 *   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 <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.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/600}}");


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 bool 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 bool 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 const struct pci_device_id snd_atiixp_ids[] = {
	{ PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */
	{ PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */
	{ PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */
	{ PCI_VDEVICE(ATI, 0x4382), 0 }, /* SB600 */
	{ 0, }
};

MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);


static struct snd_pci_quirk atiixp_quirks[] = {
	SND_PCI_QUIRK(0x105b, 0x0c81, "Foxconn RC4107MA-RS2", 0),
	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; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7293.51%133.33%
Trond Myklebust33.90%133.33%
Takashi Iwai22.60%133.33%
Total77100.00%3100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela33295.40%266.67%
Takashi Iwai164.60%133.33%
Total348100.00%3100.00%

/* * 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; } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5690.32%150.00%
Takashi Iwai69.68%150.00%
Total62100.00%2100.00%

/* * AC97 interface */
static int snd_atiixp_acquire_codec(struct atiixp *chip) { int timeout = 1000; while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) { if (! timeout--) { dev_warn(chip->card->dev, "codec acquire timeout\n"); return -EBUSY; } udelay(1); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5083.33%133.33%
Takashi Iwai1016.67%266.67%
Total60100.00%3100.00%


static unsigned short snd_atiixp_codec_read(struct atiixp *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) dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg); return 0xffff; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela12592.59%250.00%
Takashi Iwai107.41%250.00%
Total135100.00%4100.00%


static void snd_atiixp_codec_write(struct atiixp *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); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7097.22%150.00%
Takashi Iwai22.78%150.00%
Total72100.00%2100.00%


static unsigned short snd_atiixp_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { struct atiixp *chip = ac97->private_data; return snd_atiixp_codec_read(chip, ac97->num, reg); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3389.19%375.00%
Takashi Iwai410.81%125.00%
Total37100.00%4100.00%


static void snd_atiixp_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct atiixp *chip = ac97->private_data; snd_atiixp_codec_write(chip, ac97->num, reg, val); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3790.24%266.67%
Takashi Iwai49.76%133.33%
Total41100.00%3100.00%

/* * reset AC link */
static int snd_atiixp_aclink_reset(struct atiixp *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); mdelay(1); atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); if (!--timeout) { dev_err(chip->card->dev, "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; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela14990.85%120.00%
Takashi Iwai159.15%480.00%
Total164100.00%5100.00%

#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp *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; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2793.10%266.67%
Takashi Iwai26.90%133.33%
Total29100.00%3100.00%

#endif /* * auto-detection of codecs * * the IXP chip can generate interrupts for the non-existing codecs. * NEW_FRAME interrupt is used to make sure that the interrupt is generated * even if all three codecs are connected. */ #define ALL_CODEC_NOT_READY \ (ATI_REG_ISR_CODEC0_NOT_READY |\ ATI_REG_ISR_CODEC1_NOT_READY |\ ATI_REG_ISR_CODEC2_NOT_READY) #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
static int ac97_probing_bugs(struct pci_dev *pci) { const struct snd_pci_quirk *q; q = snd_pci_quirk_lookup(pci, atiixp_quirks); if (q) { dev_dbg(&pci->dev, "atiixp quirk for %s. Forcing codec %d\n", snd_pci_quirk_name(q), q->value); return q->value; } /* this hardware doesn't need workarounds. Probe for codec */ return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Takashi Iwai3150.82%375.00%
Dan Carpenter3049.18%125.00%
Total61100.00%4100.00%


static int snd_atiixp_codec_detect(struct atiixp *chip) { int timeout; chip->codec_not_ready_bits = 0; if (ac97_codec == -1) ac97_codec = ac97_probing_bugs(chip->pci); if (ac97_codec >= 0) { chip->codec_not_ready_bits |= CODEC_CHECK_BITS ^ (1 << (ac97_codec + 10)); return 0; } atiixp_write(chip, IER, CODEC_CHECK_BITS); /* wait for the interrupts */ timeout = 50; while (timeout-- > 0) { mdelay(1); if (chip->codec_not_ready_bits) break; } atiixp_write(chip, IER, 0); /* disable irqs */ if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) { dev_err(chip->card->dev, "no codec detected!\n"); return -ENXIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8258.57%120.00%
Dan Carpenter4330.71%120.00%
Takashi Iwai1510.71%360.00%
Total140100.00%5100.00%

/* * enable DMA and irqs */
static int snd_atiixp_chip_start(struct atiixp *chip) { unsigned int reg; /* set up spdif, enable burst mode */ reg = atiixp_read(chip, CMD); reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT; reg |= ATI_REG_CMD_BURST_EN; atiixp_write(chip, CMD, reg); reg = atiixp_read(chip, SPDF_CMD); reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH); atiixp_write(chip, SPDF_CMD, reg); /* clear all interrupt source */ atiixp_write(chip, ISR, 0xffffffff); /* enable irqs */ atiixp_write(chip, IER, ATI_REG_IER_IO_STATUS_EN | ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN | ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela10098.04%266.67%
Takashi Iwai21.96%133.33%
Total102100.00%3100.00%

/* * disable DMA and IRQs */
static int snd_atiixp_chip_stop(struct atiixp *chip) { /* clear interrupt source */ atiixp_write(chip, ISR, atiixp_read(chip, ISR)); /* disable irqs */ atiixp_write(chip, IER, 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3794.87%150.00%
Takashi Iwai25.13%150.00%
Total39100.00%2100.00%

/* * PCM section */ /* * pointer callback simplly reads XXX_DMA_DT_CUR register as the current * position. when SG-buffer is implemented, the offset must be calculated * correctly... */
static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct atiixp_dma *dma = runtime->private_data; unsigned int curptr; int timeout = 1000; while (timeout--) { curptr = readl(chip->remap_addr + dma->ops->dt_cur); if (curptr < dma->buf_addr) continue; curptr -= dma->buf_addr; if (curptr >= dma->buf_bytes) continue; return bytes_to_frames(runtime, curptr); } dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n", readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela11787.97%360.00%
Takashi Iwai1612.03%240.00%
Total133100.00%5100.00%

/* * XRUN detected, and stop the PCM substream */
static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma) { if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); snd_pcm_stop_xrun(dma->substream); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3464.15%120.00%
Takashi Iwai1935.85%480.00%
Total53100.00%5100.00%

/* * the period ack. update the substream. */
static void snd_atiixp_update_dma(struct atiixp *chip, struct atiixp_dma *dma) { if (! dma->substream || ! dma->running) return; snd_pcm_period_elapsed(dma->substream); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3288.89%150.00%
Takashi Iwai411.11%150.00%
Total36100.00%2100.00%

/* set BUS_BUSY interrupt bit if any DMA is running */ /* call with spinlock held */
static void snd_atiixp_check_bus_busy(struct atiixp *chip) { unsigned int bus_busy; if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN)) bus_busy = ATI_REG_IER_SET_BUS_BUSY; else bus_busy = 0; atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5096.15%150.00%
Takashi Iwai23.85%150.00%
Total52100.00%2100.00%

/* common trigger callback * calling the lowlevel callbacks in it */
static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct atiixp *chip = snd_pcm_substream_chip(substream); struct atiixp_dma *dma = substream->runtime->private_data; int err = 0; if (snd_BUG_ON(!dma->ops->enable_transfer || !dma->ops->flush_dma)) return -EINVAL; spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: dma->ops->enable_transfer(chip, 1); dma->running = 1; dma->suspended = 0; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: dma->ops->enable_transfer(chip, 0); dma->running = 0; dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND; break; default: err = -EINVAL; break; } if (! err) { snd_atiixp_check_bus_busy(chip); if (cmd == SNDRV_PCM_TRIGGER_STOP) { dma->ops->flush_dma(chip); snd_atiixp_check_bus_busy(chip); } } spin_unlock(&chip->reg_lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela17086.29%250.00%
Takashi Iwai2713.71%250.00%
Total197100.00%4100.00%

/* * lowlevel callbacks for each DMA type * * every callback is supposed to be called in chip->reg_lock spinlock */ /* flush FIFO of analog OUT DMA */
static void atiixp_out_flush_dma(struct atiixp *chip) { atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela1890.00%150.00%
Takashi Iwai210.00%150.00%
Total20100.00%2100.00%

/* enable/disable analog OUT DMA */
static void atiixp_out_enable_dma(struct atiixp *chip, int on) { unsigned int data; data = atiixp_read(chip, CMD); if (on) { if (data & ATI_REG_CMD_OUT_DMA_EN) return; atiixp_out_flush_dma(chip); data |= ATI_REG_CMD_OUT_DMA_EN; } else data &= ~ATI_REG_CMD_OUT_DMA_EN; atiixp_write(chip, CMD, data); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6296.88%150.00%
Takashi Iwai23.12%150.00%
Total64100.00%2100.00%

/* start/stop transfer over OUT DMA */
static void atiixp_out_enable_transfer(struct atiixp *chip, int on) { atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN, on ? ATI_REG_CMD_SEND_EN : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2793.10%150.00%
Takashi Iwai26.90%150.00%
Total29100.00%2100.00%

/* enable/disable analog IN DMA */
static void atiixp_in_enable_dma(struct atiixp *chip, int on) { atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN, on ? ATI_REG_CMD_IN_DMA_EN : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2793.10%150.00%
Takashi Iwai26.90%150.00%
Total29100.00%2100.00%

/* start/stop analog IN DMA */
static void atiixp_in_enable_transfer(struct atiixp *chip, int on) { if (on) { unsigned int data = atiixp_read(chip, CMD); if (! (data & ATI_REG_CMD_RECEIVE_EN)) { data |= ATI_REG_CMD_RECEIVE_EN; #if 0 /* FIXME: this causes the endless loop */ /* wait until slot 3/4 are finished */ while ((atiixp_read(chip, COUNTER) & ATI_REG_COUNTER_SLOT) != 5) ; #endif atiixp_write(chip, CMD, data); } } else atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7397.33%150.00%
Takashi Iwai22.67%150.00%
Total75100.00%2100.00%

/* flush FIFO of analog IN DMA */
static void atiixp_in_flush_dma(struct atiixp *chip) { atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela1890.00%150.00%
Takashi Iwai210.00%150.00%
Total20100.00%2100.00%

/* enable/disable SPDIF OUT DMA */
static void atiixp_spdif_enable_dma(struct atiixp *chip, int on) { atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN, on ? ATI_REG_CMD_SPDF_DMA_EN : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2793.10%150.00%
Takashi Iwai26.90%150.00%
Total29100.00%2100.00%

/* start/stop SPDIF OUT DMA */
static void atiixp_spdif_enable_transfer(struct atiixp *chip, int on) { unsigned int data; data = atiixp_read(chip, CMD); if (on) data |= ATI_REG_CMD_SPDF_OUT_EN; else data &= ~ATI_REG_CMD_SPDF_OUT_EN; atiixp_write(chip, CMD, data); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4896.00%150.00%
Takashi Iwai24.00%150.00%
Total50100.00%2100.00%

/* flush FIFO of SPDIF OUT DMA */
static void atiixp_spdif_flush_dma(struct atiixp *chip) { int timeout; /* DMA off, transfer on */ atiixp_spdif_enable_dma(chip, 0); atiixp_spdif_enable_transfer(chip, 1); timeout = 100; do { if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED)) break; udelay(1); } while (timeout-- > 0); atiixp_spdif_enable_transfer(chip, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6997.18%150.00%
Takashi Iwai22.82%150.00%
Total71100.00%2100.00%

/* set up slots and formats for SPDIF OUT */
static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); spin_lock_irq(&chip->reg_lock); if (chip->spdif_over_aclink) { unsigned int data; /* enable slots 10/11 */ atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, ATI_REG_CMD_SPDF_CONFIG_01); data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK; data |= ATI_REG_OUT_DMA_SLOT_BIT(10) | ATI_REG_OUT_DMA_SLOT_BIT(11); data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; atiixp_write(chip, OUT_DMA_SLOT, data); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT, substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? ATI_REG_CMD_INTERLEAVE_OUT : 0); } else { atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0); } spin_unlock_irq(&chip->reg_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela14597.32%375.00%
Takashi Iwai42.68%125.00%
Total149100.00%4100.00%

/* set up slots and formats for analog OUT */
static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); unsigned int data; spin_lock_irq(&chip->reg_lock); data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK; switch (substream->runtime->channels) { case 8: data |= ATI_REG_OUT_DMA_SLOT_BIT(10) | ATI_REG_OUT_DMA_SLOT_BIT(11); /* fallthru */ case 6: data |= ATI_REG_OUT_DMA_SLOT_BIT(7) | ATI_REG_OUT_DMA_SLOT_BIT(8); /* fallthru */ case 4: data |= ATI_REG_OUT_DMA_SLOT_BIT(6) | ATI_REG_OUT_DMA_SLOT_BIT(9); /* fallthru */ default: data |= ATI_REG_OUT_DMA_SLOT_BIT(3) | ATI_REG_OUT_DMA_SLOT_BIT(4); break; } /* set output threshold */ data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; atiixp_write(chip, OUT_DMA_SLOT, data); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT, substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? ATI_REG_CMD_INTERLEAVE_OUT : 0); /* * enable 6 channel re-ordering bit if needed */ atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN, substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0); spin_unlock_irq(&chip->reg_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18397.86%266.67%
Takashi Iwai42.14%133.33%
Total187100.00%3100.00%

/* set up slots and formats for analog IN */
static int snd_atiixp_capture_prepare(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); spin_lock_irq(&chip->reg_lock); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN, substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? ATI_REG_CMD_INTERLEAVE_IN : 0); spin_unlock_irq(&chip->reg_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5793.44%266.67%
Takashi Iwai46.56%133.33%
Total61100.00%3100.00%

/* * hw_params - allocate the buffer and set up buffer descriptors */
static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct atiixp *chip = snd_pcm_substream_chip(substream); struct atiixp_dma *dma = substream->runtime->private_data; int err; err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; dma->buf_addr = substream->runtime->dma_addr; dma->buf_bytes = params_buffer_bytes(hw_params); err = atiixp_build_dma_packets(chip, dma, substream, params_periods(hw_params), params_period_bytes(hw_params)); if (err < 0) return err; if (dma->ac97_pcm_type >= 0) { struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type]; /* PCM is bound to AC97 codec(s) * set up the AC97 codecs */ if (dma->pcm_open_flag) { snd_ac97_pcm_close(pcm); dma->pcm_open_flag = 0; } err = snd_ac97_pcm_open(pcm, params_rate(hw_params), params_channels(hw_params), pcm->r[0].slots); if (err >= 0) dma->pcm_open_flag = 1; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18795.90%266.67%
Takashi Iwai84.10%133.33%
Total195100.00%3100.00%


static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); struct atiixp_dma *dma = substream->runtime->private_data; if (dma->pcm_open_flag) { struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type]; snd_ac97_pcm_close(pcm); dma->pcm_open_flag = 0; } atiixp_clear_dma_packets(chip, dma, substream); snd_pcm_lib_free_pages(substream); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7692.68%266.67%
Takashi Iwai67.32%133.33%
Total82100.00%3100.00%

/* * pcm hardware definition, identical for all DMA types */ static const struct snd_pcm_hardware snd_atiixp_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 256 * 1024, .period_bytes_min = 32, .period_bytes_max = 128 * 1024, .periods_min = 2, .periods_max = ATI_MAX_DESCRIPTORS, };
static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream, struct atiixp_dma *dma, int pcm_type) { struct atiixp *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) return -EINVAL; if (dma->opened) return -EBUSY; dma->substream = substream; runtime->hw = snd_atiixp_pcm_hw; dma->ac97_pcm_type = pcm_type; if (pcm_type >= 0) { runtime->hw.rates = chip->pcms[pcm_type]->rates; snd_pcm_limit_hw_rates(runtime); } else { /* direct SPDIF */ runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; } if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; runtime->private_data = dma; /* enable DMA bits */ spin_lock_irq(&chip->reg_lock); dma->ops->enable_dma(chip, 1); spin_unlock_irq(&chip->reg_lock); dma->opened = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela16485.86%466.67%
Takashi Iwai2714.14%233.33%
Total191100.00%6100.00%


static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream, struct atiixp_dma *dma) { struct atiixp *chip = snd_pcm_substream_chip(substream); /* disable DMA bits */ if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) return -EINVAL; spin_lock_irq(&chip->reg_lock); dma->ops->enable_dma(chip, 0); spin_unlock_irq(&chip->reg_lock); dma->substream = NULL; dma->opened = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6572.22%250.00%
Takashi Iwai2527.78%250.00%
Total90100.00%4100.00%

/* */
static int snd_atiixp_playback_open(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); int err; mutex_lock(&chip->open_mutex); err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0); mutex_unlock(&chip->open_mutex); if (err < 0) return err; substream->runtime->hw.channels_max = chip->max_channels; if (chip->max_channels > 2) /* channels must be even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9794.17%250.00%
Takashi Iwai43.88%125.00%
Ingo Molnar21.94%125.00%
Total103100.00%4100.00%


static int snd_atiixp_playback_close(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); int err; mutex_lock(&chip->open_mutex); err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]); mutex_unlock(&chip->open_mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5289.66%250.00%
Takashi Iwai46.90%125.00%
Ingo Molnar23.45%125.00%
Total58100.00%4100.00%


static int snd_atiixp_capture_open(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3389.19%266.67%
Takashi Iwai410.81%133.33%
Total37100.00%3100.00%


static int snd_atiixp_capture_close(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3188.57%150.00%
Takashi Iwai411.43%150.00%
Total35100.00%2100.00%


static int snd_atiixp_spdif_open(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); int err; mutex_lock(&chip->open_mutex); if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */ err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2); else err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1); mutex_unlock(&chip->open_mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8093.02%250.00%
Takashi Iwai44.65%125.00%
Ingo Molnar22.33%125.00%
Total86100.00%4100.00%


static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream) { struct atiixp *chip = snd_pcm_substream_chip(substream); int err; mutex_lock(&chip->open_mutex); if (chip->spdif_over_aclink) err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]); else err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]); mutex_unlock(&chip->open_mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7492.50%250.00%
Takashi Iwai45.00%125.00%
Ingo Molnar22.50%125.00%
Total80100.00%4100.00%

/* AC97 playback */ static const struct snd_pcm_ops snd_atiixp_playback_ops = { .open = snd_atiixp_playback_open, .close = snd_atiixp_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_atiixp_pcm_hw_params, .hw_free = snd_atiixp_pcm_hw_free, .prepare = snd_atiixp_playback_prepare, .trigger = snd_atiixp_pcm_trigger, .pointer = snd_atiixp_pcm_pointer, }; /* AC97 capture */ static const struct snd_pcm_ops snd_atiixp_capture_ops = { .open = snd_atiixp_capture_open, .close = snd_atiixp_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_atiixp_pcm_hw_params, .hw_free = snd_atiixp_pcm_hw_free, .prepare = snd_atiixp_capture_prepare, .trigger = snd_atiixp_pcm_trigger, .pointer = snd_atiixp_pcm_pointer, }; /* SPDIF playback */ static const struct snd_pcm_ops snd_atiixp_spdif_ops = { .open = snd_atiixp_spdif_open, .close = snd_atiixp_spdif_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_atiixp_pcm_hw_params, .hw_free = snd_atiixp_pcm_hw_free, .prepare = snd_atiixp_spdif_prepare, .trigger = snd_atiixp_pcm_trigger, .pointer = snd_atiixp_pcm_pointer, }; static const struct ac97_pcm atiixp_pcm_defs[] = { /* front PCM */ { .exclusive = 1, .r = { { .slots = (1 << AC97_SLOT_PCM_LEFT) | (1 << AC97_SLOT_PCM_RIGHT) | (1 << AC97_SLOT_PCM_CENTER) | (1 << AC97_SLOT_PCM_SLEFT) | (1 << AC97_SLOT_PCM_SRIGHT) | (1 << AC97_SLOT_LFE) } } }, /* PCM IN #1 */ { .stream = 1, .exclusive = 1, .r = { { .slots = (1 << AC97_SLOT_PCM_LEFT) | (1 << AC97_SLOT_PCM_RIGHT) } } }, /* S/PDIF OUT (optional) */ { .exclusive = 1, .spdif = 1, .r = { { .slots = (1 << AC97_SLOT_SPDIF_LEFT2) | (1 << AC97_SLOT_SPDIF_RIGHT2) } } }, }; static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = { .type = ATI_DMA_PLAYBACK, .llp_offset = ATI_REG_OUT_DMA_LINKPTR, .dt_cur = ATI_REG_OUT_DMA_DT_CUR, .enable_dma = atiixp_out_enable_dma, .enable_transfer = atiixp_out_enable_transfer, .flush_dma = atiixp_out_flush_dma, }; static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = { .type = ATI_DMA_CAPTURE, .llp_offset = ATI_REG_IN_DMA_LINKPTR, .dt_cur = ATI_REG_IN_DMA_DT_CUR, .enable_dma = atiixp_in_enable_dma, .enable_transfer = atiixp_in_enable_transfer, .flush_dma = atiixp_in_flush_dma, }; static const struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = { .type = ATI_DMA_SPDIF, .llp_offset = ATI_REG_SPDF_DMA_LINKPTR, .dt_cur = ATI_REG_SPDF_DMA_DT_CUR, .enable_dma = atiixp_spdif_enable_dma, .enable_transfer = atiixp_spdif_enable_transfer, .flush_dma = atiixp_spdif_flush_dma, };
static int snd_atiixp_pcm_new(struct atiixp *chip) { struct snd_pcm *pcm; struct snd_pcm_chmap *chmap; struct snd_ac97_bus *pbus = chip->ac97_bus; int err, i, num_pcms; /* initialize constants */ chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops; chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops; if (! chip->spdif_over_aclink) chip->dmas[ATI_DMA_SPDIF].ops = &snd_atiixp_spdif_dma_ops; /* assign AC97 pcm */ if (chip->spdif_over_aclink) num_pcms = 3; else num_pcms = 2; err = snd_ac97_pcm_assign(pbus, num_pcms, atiixp_pcm_defs); if (err < 0) return err; for (i = 0; i < num_pcms; i++) chip->pcms[i] = &pbus->pcms[i]; chip->max_channels = 2; if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) { if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_LFE)) chip->max_channels = 6; else chip->max_channels = 4; } /* PCM #0: analog I/O */ err = snd_pcm_new(chip->card, "ATI IXP AC97", ATI_PCMDEV_ANALOG, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->private_data = chip; strcpy(pcm->name, "ATI IXP AC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 128*1024); err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_alt_chmaps, chip->max_channels, 0, &chmap); if (err < 0) return err; chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; /* no SPDIF support on codec? */ if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates) return 0; /* FIXME: non-48k sample rate doesn't work on my test machine with AD1888 */ if (chip->pcms[ATI_PCM_SPDIF]) chip->pcms[ATI_PCM_SPDIF]->rates = SNDRV_PCM_RATE_48000; /* PCM #1: spdif playback */ err = snd_pcm_new(chip->card, "ATI IXP IEC958", ATI_PCMDEV_DIGITAL, 1, 0, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops); pcm->private_data = chip; if (chip->spdif_over_aclink) strcpy(pcm->name, "ATI IXP IEC958 (AC97)"); else strcpy(pcm->name, "ATI IXP IEC958 (Direct)"); chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 128*1024); /* pre-select AC97 SPDIF slots 10/11 */ for (i = 0; i < NUM_ATI_CODECS; i++) { if (chip->ac97[i]) snd_ac97_update_bits(chip->ac97[i], AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela48989.07%571.43%
Takashi Iwai6010.93%228.57%
Total549100.00%7100.00%

/* * interrupt handler */
static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) { struct atiixp *chip = dev_id; unsigned int status; status = atiixp_read(chip, ISR); if (! status) return IRQ_NONE; /* process audio DMA */ if (status & ATI_REG_ISR_OUT_XRUN) snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); else if (status & ATI_REG_ISR_OUT_STATUS) snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); if (status & ATI_REG_ISR_IN_XRUN) snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); else if (status & ATI_REG_ISR_IN_STATUS) snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); if (! chip->spdif_over_aclink) { if (status & ATI_REG_ISR_SPDF_XRUN) snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_SPDIF]); else if (status & ATI_REG_ISR_SPDF_STATUS) snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_SPDIF]); } /* for codec detection */ if (status & CODEC_CHECK_BITS) { unsigned int detected; detected = status & CODEC_CHECK_BITS; spin_lock(&chip->reg_lock); chip->codec_not_ready_bits |= detected; atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */ spin_unlock(&chip->reg_lock); } /* ack */ atiixp_write(chip, ISR, status); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela23299.15%375.00%
Takashi Iwai20.85%125.00%
Total234100.00%4100.00%

/* * ac97 mixer section */ static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x103c, .subdevice = 0x006b, .name = "HP Pavilion ZV5030US", .type = AC97_TUNE_MUTE_LED }, { .subvendor = 0x103c, .subdevice = 0x308b, .name = "HP nx6125", .type = AC97_TUNE_MUTE_LED }, { .subvendor = 0x103c, .subdevice = 0x3091, .name = "unknown HP", .type = AC97_TUNE_MUTE_LED }, { } /* terminator */ };
static int snd_atiixp_mixer_new(struct atiixp *chip, int clock, const char *quirk_override) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; int i, err; int codec_count; static struct snd_ac97_bus_ops ops = { .write = snd_atiixp_ac97_write, .read = snd_atiixp_ac97_read, }; static unsigned int codec_skip[NUM_ATI_CODECS] = { ATI_REG_ISR_CODEC0_NOT_READY, ATI_REG_ISR_CODEC1_NOT_READY, ATI_REG_ISR_CODEC2_NOT_READY, }; if (snd_atiixp_codec_detect(chip) < 0) return -ENXIO; if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) return err; pbus->clock = clock; chip->ac97_bus = pbus; codec_count = 0; for (i = 0; i < NUM_ATI_CODECS; i++) { if (chip->codec_not_ready_bits & codec_skip[i]) continue; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.pci = chip->pci; ac97.num = i; ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; if (! chip->spdif_over_aclink) ac97.scaps |= AC97_SCAP_NO_SPDIF; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { chip->ac97[i] = NULL; /* to be sure */ dev_dbg(chip->card->dev, "codec %d not available for audio\n", i); continue; } codec_count++; } if (! codec_count) { dev_err(chip->card->dev, "no codec available\n"); return -ENODEV; } snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela27391.30%666.67%
Takashi Iwai268.70%333.33%
Total299100.00%9100.00%

#ifdef CONFIG_PM_SLEEP /* * power management */
static int snd_atiixp_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < NUM_ATI_PCMDEVS; i++) if (chip->pcmdevs[i]) { struct atiixp_dma *dma = &chip->dmas[i]; if (dma->substream && dma->running) dma->saved_curptr = readl(chip->remap_addr + dma->ops->dt_cur); snd_pcm_suspend_all(chip->pcmdevs[i]); } for (i = 0; i < NUM_ATI_CODECS; i++) snd_ac97_suspend(chip->ac97[i]); snd_atiixp_aclink_down(chip); snd_atiixp_chip_stop(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela12583.33%457.14%
Takashi Iwai2516.67%342.86%
Total150100.00%7100.00%


static int snd_atiixp_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); for (i = 0; i < NUM_ATI_CODECS; i++) snd_ac97_resume(chip->ac97[i]); for (i = 0; i < NUM_ATI_PCMDEVS; i++) if (chip->pcmdevs[i]) { struct atiixp_dma *dma = &chip->dmas[i]; if (dma->substream && dma->suspended) { dma->ops->enable_dma(chip, 1); dma->substream->ops->prepare(dma->substream); writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, chip->remap_addr + dma->ops->llp_offset); writel(dma->saved_curptr, chip->remap_addr + dma->ops->dt_cur); } } snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela15280.00%555.56%
Takashi Iwai3820.00%444.44%
Total190100.00%9100.00%

static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); #define SND_ATIIXP_PM_OPS &snd_atiixp_pm #else #define SND_ATIIXP_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ /* * proc interface for register dump */
static void snd_atiixp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct atiixp *chip = entry->private_data; int i; for (i = 0; i < 256; i += 4) snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i)); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5490.00%266.67%
Takashi Iwai610.00%133.33%
Total60100.00%3100.00%


static void snd_atiixp_proc_init(struct atiixp *chip) { struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp", &entry)) snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3690.00%150.00%
Takashi Iwai410.00%150.00%
Total40100.00%2100.00%

/* * destructor */
static int snd_atiixp_free(struct atiixp *chip) { if (chip->irq < 0) goto __hw_end; snd_atiixp_chip_stop(chip); __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); iounmap(chip->remap_addr); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7397.33%480.00%
Takashi Iwai22.67%120.00%
Total75100.00%5100.00%


static int snd_atiixp_dev_free(struct snd_device *device) { struct atiixp *chip = device->device_data; return snd_atiixp_free(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2284.62%266.67%
Takashi Iwai415.38%133.33%
Total26100.00%3100.00%

/* * constructor for chip instance */
static int snd_atiixp_create(struct snd_card *card, struct pci_dev *pci, struct atiixp **r_chip) { static struct snd_device_ops ops = { .dev_free = snd_atiixp_dev_free, }; struct atiixp *chip; int err; if ((err = pci_enable_device(pci)) < 0) return err; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) { pci_disable_device(pci); return -ENOMEM; } spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; chip->pci = pci; chip->irq = -1; if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) { pci_disable_device(pci); kfree(chip); return err; } chip->addr = pci_resource_start(pci, 0); chip->remap_addr = pci_ioremap_bar(pci, 0); if (chip->remap_addr == NULL) { dev_err(card->dev, "AC'97 space ioremap problem\n"); snd_atiixp_free(chip); return -EIO; } if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); snd_atiixp_free(chip); return -EBUSY; } chip->irq = pci->irq; pci_set_master(pci); synchronize_irq(chip->irq); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_atiixp_free(chip); return err; } *r_chip = chip; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela27992.38%646.15%
Takashi Iwai206.62%430.77%
Thomas Gleixner10.33%17.69%
Ingo Molnar10.33%17.69%
Arjan van de Ven10.33%17.69%
Total302100.00%13100.00%


static int snd_atiixp_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct snd_card *card; struct atiixp *chip; int err; err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card); if (err < 0) return err; strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); strcpy(card->shortname, "ATI IXP"); if ((err = snd_atiixp_create(card, pci, &chip)) < 0) goto __error; card->private_data = chip; if ((err = snd_atiixp_aclink_reset(chip)) < 0) goto __error; chip->spdif_over_aclink = spdif_aclink; if ((err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk)) < 0) goto __error; if ((err = snd_atiixp_pcm_new(chip)) < 0) goto __error; snd_atiixp_proc_init(chip); snd_atiixp_chip_start(chip); snprintf(card->longname, sizeof(card->longname), "%s rev %x with %s at %#lx, irq %i", card->shortname, pci->revision, chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?", chip->addr, chip->irq); if ((err = snd_card_register(card)) < 0) goto __error; pci_set_drvdata(pci, card); return 0; __error: snd_card_free(card); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela23890.15%550.00%
Takashi Iwai249.09%440.00%
Auke-Jan H Kok20.76%110.00%
Total264100.00%10100.00%


static void snd_atiixp_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19100.00%2100.00%
Total19100.00%2100.00%

static struct pci_driver atiixp_driver = { .name = KBUILD_MODNAME, .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = snd_atiixp_remove, .driver = { .pm = SND_ATIIXP_PM_OPS, }, }; module_pci_driver(atiixp_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela669188.55%2838.89%
Takashi Iwai6328.36%2534.72%
Dan Carpenter1101.46%11.39%
Daniel T Chen330.44%22.78%
Matthew Garrett220.29%11.39%
Joe Perches210.28%22.78%
Ingo Molnar130.17%11.39%
Clemens Ladisch70.09%11.39%
Benoit Taine60.08%11.39%
Trond Myklebust60.08%11.39%
Julia Lawall60.08%22.78%
Rusty Russell20.03%11.39%
Auke-Jan H Kok20.03%11.39%
Arjan van de Ven10.01%11.39%
Thomas Gleixner10.01%11.39%
Arvind Yadav10.01%11.39%
Paul Gortmaker10.01%11.39%
Bhumika Goyal10.01%11.39%
Bill Pemberton0.00%00.00%
Total7556100.00%72100.00%
Directory: sound/pci
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.