/*
*
- * hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio.
+ * hda_intel.c - Implementation of primary alsa driver code base
+ * for Intel HD Audio.
*
* Copyright(c) 2004 Intel Corporation. All rights reserved.
*
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include "hda_codec.h"
-static int index = SNDRV_DEFAULT_IDX1;
-static char *id = SNDRV_DEFAULT_STR1;
-static char *model;
-static int position_fix;
-static int probe_mask = -1;
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int single_cmd;
static int enable_msi;
-module_param(index, int, 0444);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
-module_param(id, charp, 0444);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
-module_param(model, charp, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
-module_param(position_fix, int, 0444);
-MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
-module_param(probe_mask, int, 0444);
+module_param_array(position_fix, int, NULL, 0444);
+MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
+ "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param(single_cmd, bool, 0444);
-MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only).");
-module_param(enable_msi, int, 0);
+MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
+ "(for debugging only).");
+module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* power_save option is defined in hda_codec.c */
-/* just for backward compatibility */
-static int enable;
-module_param(enable, bool, 0444);
+/* reset the HD-audio controller in power save mode.
+ * this may give more power-saving, but will take longer time to
+ * wake up.
+ */
+static int power_save_controller = 1;
+module_param(power_save_controller, bool, 0644);
+MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
+#endif
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{ATI, SB600},"
"{ATI, RS600},"
"{ATI, RS690},"
+ "{ATI, RS780},"
+ "{ATI, R600},"
+ "{ATI, RV630},"
+ "{ATI, RV610},"
+ "{ATI, RV670},"
+ "{ATI, RV635},"
+ "{ATI, RV620},"
+ "{ATI, RV770},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
#define SFX "hda-intel: "
+
/*
* registers
*/
#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04 /* completion interrupt */
-#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE)
+#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
+ SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
-#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
-#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
-#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
+#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
+#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* GCTL unsolicited response enable bit */
#define ICH6_GCTL_UREN (1<<8)
*/
struct azx_dev {
- u32 *bdl; /* virtual address of the BDL */
- dma_addr_t bdl_addr; /* physical address of the BDL */
- u32 *posbuf; /* position buffer pointer */
+ u32 *bdl; /* virtual address of the BDL */
+ dma_addr_t bdl_addr; /* physical address of the BDL */
+ u32 *posbuf; /* position buffer pointer */
- unsigned int bufsize; /* size of the play buffer in bytes */
- unsigned int fragsize; /* size of each period in bytes */
- unsigned int frags; /* number for period in the play buffer */
- unsigned int fifo_size; /* FIFO size */
+ unsigned int bufsize; /* size of the play buffer in bytes */
+ unsigned int fragsize; /* size of each period in bytes */
+ unsigned int frags; /* number for period in the play buffer */
+ unsigned int fifo_size; /* FIFO size */
- void __iomem *sd_addr; /* stream descriptor pointer */
+ void __iomem *sd_addr; /* stream descriptor pointer */
- u32 sd_int_sta_mask; /* stream int status mask */
+ u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
- struct snd_pcm_substream *substream; /* assigned substream, set in PCM open */
- unsigned int format_val; /* format value to be set in the controller and the codec */
+ struct snd_pcm_substream *substream; /* assigned substream,
+ * set in PCM open
+ */
+ unsigned int format_val; /* format value to be set in the
+ * controller and the codec
+ */
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
/* for sanity check of position buffer */
/* flags */
int position_fix;
+ unsigned int running :1;
unsigned int initialized :1;
unsigned int single_cmd :1;
unsigned int polling_mode :1;
unsigned int msi :1;
+
+ /* for debugging */
+ unsigned int last_cmd; /* last issued command (to sync) */
};
/* driver types */
int err;
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
PAGE_SIZE, &chip->rb);
if (err < 0) {
snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
}
/* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
{
struct azx *chip = codec->bus->private_data;
unsigned int wp;
- u32 val;
-
- val = (u32)(codec->addr & 0x0f) << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= para;
/* add command to corb */
wp = azx_readb(chip, CORBWP);
azx_update_rirb(chip);
spin_unlock_irq(&chip->reg_lock);
}
- if (! chip->rirb.cmds)
+ if (!chip->rirb.cmds)
return chip->rirb.res; /* the last value */
- schedule_timeout_interruptible(1);
+ udelay(10);
+ cond_resched();
} while (time_after_eq(timeout, jiffies));
if (chip->msi) {
snd_printk(KERN_WARNING "hda_intel: No response from codec, "
- "disabling MSI...\n");
+ "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
free_irq(chip->irq, chip);
chip->irq = -1;
pci_disable_msi(chip->pci);
if (!chip->polling_mode) {
snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
- "switching to polling mode...\n");
+ "switching to polling mode: last cmd=0x%08x\n",
+ chip->last_cmd);
chip->polling_mode = 1;
goto again;
}
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
- "switching to single_cmd mode...\n");
+ "switching to single_cmd mode: last cmd=0x%08x\n",
+ chip->last_cmd);
chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0;
/* switch to single_cmd mode */
*/
/* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb,
- unsigned int para)
+static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
{
struct azx *chip = codec->bus->private_data;
- u32 val;
int timeout = 50;
- val = (u32)(codec->addr & 0x0f) << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= para;
-
while (timeout--) {
/* check ICB busy bit */
- if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
+ if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
/* Clear IRV valid bit */
- azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ ICH6_IRS_VALID);
azx_writel(chip, IC, val);
- azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ ICH6_IRS_BUSY);
return 0;
}
udelay(1);
}
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val);
+ snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
return -EIO;
}
return azx_readl(chip, IR);
udelay(1);
}
- snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS));
+ snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
return (unsigned int)-1;
}
unsigned int para)
{
struct azx *chip = codec->bus->private_data;
+ u32 val;
+
+ val = (u32)(codec->addr & 0x0f) << 28;
+ val |= (u32)direct << 27;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= para;
+ chip->last_cmd = val;
+
if (chip->single_cmd)
- return azx_single_send_cmd(codec, nid, direct, verb, para);
+ return azx_single_send_cmd(codec, val);
else
- return azx_corb_send_cmd(codec, nid, direct, verb, para);
+ return azx_corb_send_cmd(codec, val);
}
/* get a response */
return azx_rirb_get_response(codec);
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static void azx_power_notify(struct hda_codec *codec);
+#endif
/* reset codec link */
static int azx_reset(struct azx *chip)
{
int count;
+ /* clear STATESTS */
+ azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+
/* reset controller */
azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
/*
- * initialize the chip
+ * reset and start the controller registers
*/
static void azx_init_chip(struct azx *chip)
{
- unsigned char reg;
-
- /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
- * TCSEL == Traffic Class Select Register, which sets PCI express QOS
- * Ensuring these bits are 0 clears playback static on some HD Audio codecs
- */
- pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, ®);
- pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8);
+ if (chip->initialized)
+ return;
/* reset controller */
azx_reset(chip);
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+ chip->initialized = 1;
+}
+
+/*
+ * initialize the PCI registers
+ */
+/* update bits in a PCI register byte */
+static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
+ unsigned char mask, unsigned char val)
+{
+ unsigned char data;
+
+ pci_read_config_byte(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_byte(pci, reg, data);
+}
+
+static void azx_init_pci(struct azx *chip)
+{
+ /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio
+ * codecs
+ */
+ update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
+
switch (chip->driver_type) {
case AZX_DRIVER_ATI:
/* For ATI SB450 azalia HD audio, we need to enable snoop */
- pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- ®);
- pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- (reg & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP);
+ update_pci_byte(chip->pci,
+ ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
+ 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
break;
case AZX_DRIVER_NVIDIA:
/* For NVIDIA HDA, enable snoop */
- pci_read_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, ®);
- pci_write_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR,
- (reg & 0xf0) | NVIDIA_HDA_ENABLE_COHBITS);
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_TRANSREG_ADDR,
+ 0x0f, NVIDIA_HDA_ENABLE_COHBITS);
break;
}
}
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
- if (! chip->single_cmd && (status & RIRB_INT_RESPONSE))
+ if (!chip->single_cmd && (status & RIRB_INT_RESPONSE))
azx_update_rirb(chip);
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
int timeout;
/* make sure the run bit is zero for SD */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START);
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
+ ~SD_CTL_DMA_START);
/* reset stream */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET);
+ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
+ SD_CTL_STREAM_RESET);
udelay(3);
timeout = 300;
while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
/* program the stream_tag */
azx_sd_writel(azx_dev, SD_CTL,
- (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
(azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
/* program the length of samples in cyclic buffer */
azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
/* enable the position buffer */
- if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+ if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+ azx_writel(chip, DPLBASE,
+ (u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE);
/* set the interrupt enable bits in the descriptor control register */
- azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
+ azx_sd_writel(azx_dev, SD_CTL,
+ azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
return 0;
}
[AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */
};
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
+static int __devinit azx_codec_create(struct azx *chip, const char *model,
+ unsigned int codec_probe_mask)
{
struct hda_bus_template bus_temp;
int c, codecs, audio_codecs, err;
bus_temp.pci = chip->pci;
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ bus_temp.ops.pm_notify = azx_power_notify;
+#endif
- if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)
+ err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
+ if (err < 0)
return err;
codecs = audio_codecs = 0;
for (c = 0; c < AZX_MAX_CODECS; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
if (!audio_codecs) {
/* probe additional slots if no codec is found */
for (; c < azx_max_codecs[chip->driver_type]; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
continue;
nums = chip->capture_streams;
}
for (i = 0; i < nums; i++, dev++)
- if (! chip->azx_dev[dev].opened) {
+ if (!chip->azx_dev[dev].opened) {
chip->azx_dev[dev].opened = 1;
return &chip->azx_dev[dev];
}
}
static struct snd_pcm_hardware azx_pcm_hw = {
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
/* No full-resume yet implemented */
128);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
128);
- if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {
+ snd_hda_power_up(apcm->codec);
+ err = hinfo->ops.open(hinfo, apcm->codec, substream);
+ if (err < 0) {
azx_release_device(azx_dev);
+ snd_hda_power_down(apcm->codec);
mutex_unlock(&chip->open_mutex);
return err;
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
azx_release_device(azx_dev);
hinfo->ops.close(hinfo, apcm->codec, substream);
+ snd_hda_power_down(apcm->codec);
mutex_unlock(&chip->open_mutex);
return 0;
}
-static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
}
static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
runtime->channels,
runtime->format,
hinfo->maxbps);
- if (! azx_dev->format_val) {
- snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n",
+ if (!azx_dev->format_val) {
+ snd_printk(KERN_ERR SFX
+ "invalid format_val, rate=%d, ch=%d, format=%d\n",
runtime->rate, runtime->channels, runtime->format);
return -EINVAL;
}
- snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n",
+ snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, "
+ "format=0x%x\n",
azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
azx_setup_periods(azx_dev);
azx_setup_controller(chip, azx_dev);
cmd == SNDRV_PCM_TRIGGER_SUSPEND ||
cmd == SNDRV_PCM_TRIGGER_STOP) {
int timeout = 5000;
- while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout)
+ while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) &&
+ --timeout)
;
}
return err;
/* use the position buffer */
pos = le32_to_cpu(*azx_dev->posbuf);
if (chip->position_fix == POS_FIX_AUTO &&
- azx_dev->period_intr == 1 && ! pos) {
+ azx_dev->period_intr == 1 && !pos) {
printk(KERN_WARNING
"hda-intel: Invalid position buffer, "
"using LPIB read method instead.\n");
snd_assert(cpcm->name, return -EINVAL);
err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
- cpcm->stream[0].substreams, cpcm->stream[1].substreams,
+ cpcm->stream[0].substreams,
+ cpcm->stream[1].substreams,
&pcm);
if (err < 0)
return err;
static int __devinit azx_pcm_create(struct azx *chip)
{
- struct list_head *p;
struct hda_codec *codec;
int c, err;
int pcm_dev;
- if ((err = snd_hda_build_pcms(chip->bus)) < 0)
+ err = snd_hda_build_pcms(chip->bus);
+ if (err < 0)
return err;
/* create audio PCMs */
pcm_dev = 0;
- list_for_each(p, &chip->bus->codec_list) {
- codec = list_entry(p, struct hda_codec, list);
+ list_for_each_entry(codec, &chip->bus->codec_list, list) {
for (c = 0; c < codec->num_pcms; c++) {
if (codec->pcm_info[c].is_modem)
continue; /* create later */
if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {
- snd_printk(KERN_ERR SFX "Too many audio PCMs\n");
+ snd_printk(KERN_ERR SFX
+ "Too many audio PCMs\n");
return -EINVAL;
}
- err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+ err = create_codec_pcm(chip, codec,
+ &codec->pcm_info[c], pcm_dev);
if (err < 0)
return err;
pcm_dev++;
/* create modem PCMs */
pcm_dev = AZX_MAX_AUDIO_PCMS;
- list_for_each(p, &chip->bus->codec_list) {
- codec = list_entry(p, struct hda_codec, list);
+ list_for_each_entry(codec, &chip->bus->codec_list, list) {
for (c = 0; c < codec->num_pcms; c++) {
- if (! codec->pcm_info[c].is_modem)
+ if (!codec->pcm_info[c].is_modem)
continue; /* already created */
if (pcm_dev >= AZX_MAX_PCMS) {
- snd_printk(KERN_ERR SFX "Too many modem PCMs\n");
+ snd_printk(KERN_ERR SFX
+ "Too many modem PCMs\n");
return -EINVAL;
}
- err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+ err = create_codec_pcm(chip, codec,
+ &codec->pcm_info[c], pcm_dev);
if (err < 0)
return err;
chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM;
int i;
/* initialize each stream (aka device)
- * assign the starting bdl address to each stream (device) and initialize
+ * assign the starting bdl address to each stream (device)
+ * and initialize
*/
for (i = 0; i < chip->num_streams; i++) {
unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
}
+static void azx_stop_chip(struct azx *chip)
+{
+ if (!chip->initialized)
+ return;
+
+ /* disable interrupts */
+ azx_int_disable(chip);
+ azx_int_clear(chip);
+
+ /* disable CORB/RIRB */
+ azx_free_cmd_io(chip);
+
+ /* disable position buffer */
+ azx_writel(chip, DPLBASE, 0);
+ azx_writel(chip, DPUBASE, 0);
+
+ chip->initialized = 0;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+/* power-up/down the controller */
+static void azx_power_notify(struct hda_codec *codec)
+{
+ struct azx *chip = codec->bus->private_data;
+ struct hda_codec *c;
+ int power_on = 0;
+
+ list_for_each_entry(c, &codec->bus->codec_list, list) {
+ if (c->power_on) {
+ power_on = 1;
+ break;
+ }
+ }
+ if (power_on)
+ azx_init_chip(chip);
+ else if (chip->running && power_save_controller)
+ azx_stop_chip(chip);
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
#ifdef CONFIG_PM
/*
* power management
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
for (i = 0; i < chip->pcm_devs; i++)
snd_pcm_suspend_all(chip->pcm[i]);
- snd_hda_suspend(chip->bus, state);
- azx_free_cmd_io(chip);
+ if (chip->initialized)
+ snd_hda_suspend(chip->bus, state);
+ azx_stop_chip(chip);
if (chip->irq >= 0) {
synchronize_irq(chip->irq);
free_irq(chip->irq, chip);
chip->msi = 0;
if (azx_acquire_irq(chip, 1) < 0)
return -EIO;
- azx_init_chip(chip);
+ azx_init_pci(chip);
+
+ if (snd_hda_codecs_inuse(chip->bus))
+ azx_init_chip(chip);
+
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
{
if (chip->initialized) {
int i;
-
for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]);
-
- /* disable interrupts */
- azx_int_disable(chip);
- azx_int_clear(chip);
-
- /* disable CORB/RIRB */
- azx_free_cmd_io(chip);
-
- /* disable position buffer */
- azx_writel(chip, DPLBASE, 0);
- azx_writel(chip, DPUBASE, 0);
+ azx_stop_chip(chip);
}
if (chip->irq >= 0) {
*/
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
+ SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
{}
};
if (fix == POS_FIX_AUTO) {
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
- snd_printdd(KERN_INFO
+ printk(KERN_INFO
"hda_intel: position_fix set to %d "
"for device %04x:%04x\n",
q->value, q->subvendor, q->subdevice);
return fix;
}
+/*
+ * black-lists for probe_mask
+ */
+static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
+ /* Thinkpad often breaks the controller communication when accessing
+ * to the non-working (or non-existing) modem codec slot.
+ */
+ SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
+ SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
+ SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+ {}
+};
+
+static void __devinit check_probe_mask(struct azx *chip, int dev)
+{
+ const struct snd_pci_quirk *q;
+
+ if (probe_mask[dev] == -1) {
+ q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
+ if (q) {
+ printk(KERN_INFO
+ "hda_intel: probe_mask set to 0x%x "
+ "for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
+ probe_mask[dev] = q->value;
+ }
+ }
+}
+
+
/*
* constructor
*/
static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int driver_type,
+ int dev, int driver_type,
struct azx **rchip)
{
struct azx *chip;
chip->driver_type = driver_type;
chip->msi = enable_msi;
- chip->position_fix = check_position_fix(chip, position_fix);
+ chip->position_fix = check_position_fix(chip, position_fix[dev]);
+ check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
break;
}
chip->num_streams = chip->playback_streams + chip->capture_streams;
- chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL);
+ chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
+ GFP_KERNEL);
if (!chip->azx_dev) {
snd_printk(KERN_ERR "cannot malloc azx_dev\n");
goto errout;
}
/* allocate memory for the BDL for each stream */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- BDL_SIZE, &chip->bdl)) < 0) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ BDL_SIZE, &chip->bdl);
+ if (err < 0) {
snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
goto errout;
}
/* allocate memory for the position buffer */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- chip->num_streams * 8, &chip->posbuf)) < 0) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ chip->num_streams * 8, &chip->posbuf);
+ if (err < 0) {
snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
goto errout;
}
/* allocate CORB/RIRB */
- if (! chip->single_cmd)
- if ((err = azx_alloc_cmd_io(chip)) < 0)
+ if (!chip->single_cmd) {
+ err = azx_alloc_cmd_io(chip);
+ if (err < 0)
goto errout;
+ }
/* initialize streams */
azx_init_stream(chip);
/* initialize chip */
+ azx_init_pci(chip);
azx_init_chip(chip);
- chip->initialized = 1;
-
/* codec detection */
if (!chip->codec_mask) {
snd_printk(KERN_ERR SFX "no codecs found!\n");
goto errout;
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err <0) {
snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
goto errout;
}
strcpy(card->driver, "HDA-Intel");
strcpy(card->shortname, driver_short_names[chip->driver_type]);
- sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq);
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ card->shortname, chip->addr, chip->irq);
*rchip = chip;
return 0;
return err;
}
-static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static void power_down_all_codecs(struct azx *chip)
+{
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ /* The codecs were powered up in snd_hda_codec_new().
+ * Now all initialization done, so turn them down if possible
+ */
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &chip->bus->codec_list, list) {
+ snd_hda_power_down(codec);
+ }
+#endif
+}
+
+static int __devinit azx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
+ static int dev;
struct snd_card *card;
struct azx *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (!card) {
snd_printk(KERN_ERR SFX "Error creating card!\n");
return -ENOMEM;
}
- err = azx_create(card, pci, pci_id->driver_data, &chip);
+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0) {
snd_card_free(card);
return err;
card->private_data = chip;
/* create codec instances */
- if ((err = azx_codec_create(chip, model)) < 0) {
+ err = azx_codec_create(chip, model[dev], probe_mask[dev]);
+ if (err < 0) {
snd_card_free(card);
return err;
}
/* create PCM streams */
- if ((err = azx_pcm_create(chip)) < 0) {
+ err = azx_pcm_create(chip);
+ if (err < 0) {
snd_card_free(card);
return err;
}
/* create mixer controls */
- if ((err = azx_mixer_create(chip)) < 0) {
+ err = azx_mixer_create(chip);
+ if (err < 0) {
snd_card_free(card);
return err;
}
snd_card_set_dev(card, &pci->dev);
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_card_free(card);
return err;
}
pci_set_drvdata(pci, card);
+ chip->running = 1;
+ power_down_all_codecs(chip);
return err;
}
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
+ { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
+ { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
+ { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
+ { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
+ { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
+ { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
+ { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
+ { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+ { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+ { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+ { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+ { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+ { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+ { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+ { 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
+ { 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
+ { 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
+ { 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);