/* * enable DMA and irqs */ static int snd_atiixp_chip_start(atiixp_t *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; }
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; }
static int snd_atiixp_chip_stop(struct atiixp_modem *chip) { /* */ atiixp_write(chip, ISR, atiixp_read(chip, ISR)); /* */ atiixp_write(chip, IER, 0); return 0; }
/* * disable DMA and IRQs */ static int snd_atiixp_chip_stop(atiixp_t *chip) { /* clear interrupt source */ atiixp_write(chip, ISR, atiixp_read(chip, ISR)); /* disable irqs */ atiixp_write(chip, IER, 0); return 0; }
/* set up slots and formats for SPDIF OUT */ static int snd_atiixp_spdif_prepare(snd_pcm_substream_t *substream) { atiixp_t *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; }
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; }
/* start/stop SPDIF OUT DMA */ static void atiixp_spdif_enable_transfer(atiixp_t *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); }
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 void atiixp_in_enable_transfer(struct atiixp_modem *chip, int on) { if (on) { unsigned int data = atiixp_read(chip, CMD); if (! (data & ATI_REG_CMD_MODEM_RECEIVE_EN)) { data |= ATI_REG_CMD_MODEM_RECEIVE_EN; atiixp_write(chip, CMD, data); } } else atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_RECEIVE_EN, 0); }
static int snd_atiixp_chip_start(struct atiixp_modem *chip) { unsigned int reg; /* */ reg = atiixp_read(chip, CMD); reg |= ATI_REG_CMD_BURST_EN; if(!(reg & ATI_REG_CMD_MODEM_PRESENT)) reg |= ATI_REG_CMD_MODEM_PRESENT; atiixp_write(chip, CMD, reg); /* */ atiixp_write(chip, ISR, 0xffffffff); /* */ atiixp_write(chip, IER, ATI_REG_IER_MODEM_STATUS_EN | ATI_REG_IER_MODEM_IN_XRUN_EN | ATI_REG_IER_MODEM_OUT1_XRUN_EN); return 0; }
static void snd_atiixp_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct atiixp_modem *chip = ac97->private_data; if (reg == AC97_GPIO_STATUS) { atiixp_write(chip, MODEM_OUT_GPIO, (val << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN); return; } snd_atiixp_codec_write(chip, ac97->num, reg, val); }
/* * enable DMA and irqs */ static int snd_atiixp_chip_start(struct atiixp_modem *chip) { unsigned int reg; /* set up spdif, enable burst mode */ reg = atiixp_read(chip, CMD); reg |= ATI_REG_CMD_BURST_EN; if(!(reg & ATI_REG_CMD_MODEM_PRESENT)) reg |= ATI_REG_CMD_MODEM_PRESENT; atiixp_write(chip, CMD, reg); /* clear all interrupt source */ atiixp_write(chip, ISR, 0xffffffff); /* enable irqs */ atiixp_write(chip, IER, ATI_REG_IER_MODEM_STATUS_EN | ATI_REG_IER_MODEM_IN_XRUN_EN | ATI_REG_IER_MODEM_OUT1_XRUN_EN); return 0; }
static int snd_atiixp_codec_detect(struct atiixp_modem *chip) { int timeout; chip->codec_not_ready_bits = 0; atiixp_write(chip, IER, CODEC_CHECK_BITS); /* */ timeout = 50; while (timeout-- > 0) { msleep(1); if (chip->codec_not_ready_bits) break; } atiixp_write(chip, IER, 0); /* */ if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) { snd_printk(KERN_ERR "atiixp-modem: no codec detected!\n"); return -ENXIO; } return 0; }
static int snd_atiixp_codec_detect(atiixp_t *chip) { int timeout; chip->codec_not_ready_bits = 0; atiixp_write(chip, IER, CODEC_CHECK_BITS); /* wait for the interrupts */ timeout = HZ / 10; while (timeout-- > 0) { do_delay(); 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) { snd_printk(KERN_ERR "atiixp: no codec detected!\n"); return -ENXIO; } return 0; }
static int snd_atiixp_codec_detect(struct atiixp_modem *chip) { int timeout; chip->codec_not_ready_bits = 0; atiixp_write(chip, IER, CODEC_CHECK_BITS); /* wait for the interrupts */ timeout = 50; while (timeout-- > 0) { msleep(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) { ; return -ENXIO; } return 0; }
/* enable/disable analog OUT DMA */ static void atiixp_out_enable_dma(atiixp_t *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); }
static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream) { struct atiixp_modem *chip = snd_pcm_substream_chip(substream); unsigned int data; spin_lock_irq(&chip->reg_lock); /* */ data = atiixp_read(chip, MODEM_OUT_FIFO); data &= ~ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK; data |= 0x04 << ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT; atiixp_write(chip, MODEM_OUT_FIFO, data); spin_unlock_irq(&chip->reg_lock); return 0; }
/* start/stop analog IN DMA */ static void atiixp_in_enable_transfer(atiixp_t *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); }
/* set up slots and formats for analog OUT */ static int snd_atiixp_playback_prepare(snd_pcm_substream_t *substream) { atiixp_t *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; }
/* * interrupt handler */ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { atiixp_t *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; }
static void atiixp_in_flush_dma(struct atiixp_modem *chip) { atiixp_write(chip, MODEM_FIFO_FLUSH, ATI_REG_MODEM_FIFO_IN_FLUSH); }
/* flush FIFO of analog IN DMA */ static void atiixp_in_flush_dma(atiixp_t *chip) { atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH); }
/* flush FIFO of analog OUT DMA */ static void atiixp_out_flush_dma(atiixp_t *chip) { atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH); }
static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream, struct atiixp_dma *dma, int pcm_type) { struct atiixp_modem *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; static struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; 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 ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) return err; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; runtime->private_data = dma; /* */ spin_lock_irq(&chip->reg_lock); dma->ops->enable_dma(chip, 1); spin_unlock_irq(&chip->reg_lock); dma->opened = 1; return 0; } static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream, struct atiixp_dma *dma) { struct atiixp_modem *chip = snd_pcm_substream_chip(substream); /* */ 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; } /* */ static int snd_atiixp_playback_open(struct snd_pcm_substream *substream) { struct atiixp_modem *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; return 0; } static int snd_atiixp_playback_close(struct snd_pcm_substream *substream) { struct atiixp_modem *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; } static int snd_atiixp_capture_open(struct snd_pcm_substream *substream) { struct atiixp_modem *chip = snd_pcm_substream_chip(substream); return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1); } static int snd_atiixp_capture_close(struct snd_pcm_substream *substream) { struct atiixp_modem *chip = snd_pcm_substream_chip(substream); return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]); } /* */ static 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, }; /* */ static 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, }; static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = { .type = ATI_DMA_PLAYBACK, .llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR, .dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR, .enable_dma = atiixp_out_enable_dma, .enable_transfer = atiixp_out_enable_transfer, .flush_dma = atiixp_out_flush_dma, }; static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = { .type = ATI_DMA_CAPTURE, .llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR, .dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR, .enable_dma = atiixp_in_enable_dma, .enable_transfer = atiixp_in_enable_transfer, .flush_dma = atiixp_in_flush_dma, }; static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip) { struct snd_pcm *pcm; int err; /* */ chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops; chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops; /* */ err = snd_pcm_new(chip->card, "ATI IXP MC97", 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->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; strcpy(pcm->name, "ATI IXP MC97"); 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); return 0; } /* */ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) { struct atiixp_modem *chip = dev_id; unsigned int status; status = atiixp_read(chip, ISR); if (! status) return IRQ_NONE; /* */ if (status & ATI_REG_ISR_MODEM_OUT1_XRUN) snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); else if (status & ATI_REG_ISR_MODEM_OUT1_STATUS) snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); if (status & ATI_REG_ISR_MODEM_IN_XRUN) snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); else if (status & ATI_REG_ISR_MODEM_IN_STATUS) snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); /* */ 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); /* */ spin_unlock(&chip->reg_lock); } /* */ atiixp_write(chip, ISR, status); return IRQ_HANDLED; } /* */ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) { 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_AUDIO | AC97_SCAP_POWER_SAVE; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { chip->ac97[i] = NULL; /* */ snd_printdd("atiixp-modem: codec %d not available for modem\n", i); continue; } codec_count++; } if (! codec_count) { snd_printk(KERN_ERR "atiixp-modem: no codec available\n"); return -ENODEV; } /* */ return 0; } #ifdef CONFIG_PM /* */ static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct atiixp_modem *chip = card->private_data; int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < NUM_ATI_PCMDEVS; i++) 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); pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, pci_choose_state(pci, state)); return 0; } static int snd_atiixp_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct atiixp_modem *chip = card->private_data; int i; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { printk(KERN_ERR "atiixp-modem: pci_enable_device failed, " "disabling device\n"); snd_card_disconnect(card); return -EIO; } pci_set_master(pci); snd_atiixp_aclink_reset(chip); snd_atiixp_chip_start(chip); for (i = 0; i < NUM_ATI_CODECS; i++) snd_ac97_resume(chip->ac97[i]); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } #endif /* */ #ifdef CONFIG_PROC_FS /* */ static void snd_atiixp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct atiixp_modem *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)); } static void __devinit snd_atiixp_proc_init(struct atiixp_modem *chip) { struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } #else #define snd_atiixp_proc_init(chip) #endif /* */ static int snd_atiixp_free(struct atiixp_modem *chip) { if (chip->irq < 0) goto __hw_end; snd_atiixp_chip_stop(chip); __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); if (chip->remap_addr) iounmap(chip->remap_addr); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0; } static int snd_atiixp_dev_free(struct snd_device *device) { struct atiixp_modem *chip = device->device_data; return snd_atiixp_free(chip); } /* */ static int __devinit snd_atiixp_create(struct snd_card *card, struct pci_dev *pci, struct atiixp_modem **r_chip) { static struct snd_device_ops ops = { .dev_free = snd_atiixp_dev_free, }; struct atiixp_modem *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 MC97")) < 0) { kfree(chip); pci_disable_device(pci); return err; } chip->addr = pci_resource_start(pci, 0); chip->remap_addr = pci_ioremap_bar(pci, 0); if (chip->remap_addr == NULL) { snd_printk(KERN_ERR "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)) { snd_printk(KERN_ERR "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; } snd_card_set_dev(card, &pci->dev); *r_chip = chip; return 0; } static int __devinit snd_atiixp_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct snd_card *card; struct atiixp_modem *chip; int err; err = snd_card_create(index, id, THIS_MODULE, 0, &card); if (err < 0) return err; strcpy(card->driver, "ATIIXP-MODEM"); strcpy(card->shortname, "ATI IXP Modem"); 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; if ((err = snd_atiixp_mixer_new(chip, ac97_clock)) < 0) goto __error; if ((err = snd_atiixp_pcm_new(chip)) < 0) goto __error; snd_atiixp_proc_init(chip); snd_atiixp_chip_start(chip); sprintf(card->longname, "%s rev %x at 0x%lx, irq %i", card->shortname, pci->revision, 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; } static void __devexit snd_atiixp_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); }