📄 hda_intel.c
字号:
hinfo->maxbps); 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", azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); azx_setup_periods(azx_dev); azx_setup_controller(chip, azx_dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; else azx_dev->fifo_size = 0; return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, azx_dev->format_val, substream);}static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; int err = 0; spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: azx_stream_start(chip, azx_dev); azx_dev->running = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: azx_stream_stop(chip, azx_dev); azx_dev->running = 0; break; default: err = -EINVAL; } spin_unlock(&chip->reg_lock); if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH || 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) ; } return err;}static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); unsigned int pos; if (chip->position_fix == POS_FIX_POSBUF || chip->position_fix == POS_FIX_AUTO) { /* use the position buffer */ pos = *azx_dev->posbuf; if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_intr == 1 && ! pos) { printk(KERN_WARNING "hda-intel: Invalid position buffer, " "using LPIB read method instead.\n"); chip->position_fix = POS_FIX_NONE; goto read_lpib; } } else { read_lpib: /* read LPIB */ pos = azx_sd_readl(azx_dev, SD_LPIB); if (chip->position_fix == POS_FIX_FIFO) pos += azx_dev->fifo_size; } if (pos >= azx_dev->bufsize) pos = 0; return bytes_to_frames(substream->runtime, pos);}static struct snd_pcm_ops azx_pcm_ops = { .open = azx_pcm_open, .close = azx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = azx_pcm_hw_params, .hw_free = azx_pcm_hw_free, .prepare = azx_pcm_prepare, .trigger = azx_pcm_trigger, .pointer = azx_pcm_pointer,};static void azx_pcm_free(struct snd_pcm *pcm){ kfree(pcm->private_data);}static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, struct hda_pcm *cpcm, int pcm_dev){ int err; struct snd_pcm *pcm; struct azx_pcm *apcm; snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL); snd_assert(cpcm->name, return -EINVAL); err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, cpcm->stream[0].substreams, cpcm->stream[1].substreams, &pcm); if (err < 0) return err; strcpy(pcm->name, cpcm->name); apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); if (apcm == NULL) return -ENOMEM; apcm->chip = chip; apcm->codec = codec; apcm->hinfo[0] = &cpcm->stream[0]; apcm->hinfo[1] = &cpcm->stream[1]; pcm->private_data = apcm; pcm->private_free = azx_pcm_free; if (cpcm->stream[0].substreams) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); if (cpcm->stream[1].substreams) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 1024 * 64, 1024 * 128); chip->pcm[pcm_dev] = pcm; chip->pcm_devs = pcm_dev + 1; return 0;}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) return err; /* create audio PCMs */ pcm_dev = 0; list_for_each(p, &chip->bus->codec_list) { codec = list_entry(p, struct hda_codec, 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"); return -EINVAL; } 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); for (c = 0; c < codec->num_pcms; c++) { 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"); return -EINVAL; } 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; pcm_dev++; } } return 0;}/* * mixer creation - all stuff is implemented in hda module */static int __devinit azx_mixer_create(struct azx *chip){ return snd_hda_build_controls(chip->bus);}/* * initialize SD streams */static int __devinit azx_init_stream(struct azx *chip){ int i; /* initialize each stream (aka device) * 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); struct azx_dev *azx_dev = &chip->azx_dev[i]; azx_dev->bdl = (u32 *)(chip->bdl.area + off); azx_dev->bdl_addr = chip->bdl.addr + off; azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ azx_dev->sd_int_sta_mask = 1 << i; /* stream tag: must be non-zero and unique */ azx_dev->index = i; azx_dev->stream_tag = i + 1; } return 0;}#ifdef CONFIG_PM/* * power management */static int azx_suspend(struct pci_dev *pci, pm_message_t state){ struct snd_card *card = pci_get_drvdata(pci); struct azx *chip = card->private_data; int i; 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); pci_disable_device(pci); pci_save_state(pci); return 0;}static int azx_resume(struct pci_dev *pci){ struct snd_card *card = pci_get_drvdata(pci); struct azx *chip = card->private_data; pci_restore_state(pci); pci_enable_device(pci); pci_set_master(pci); azx_init_chip(chip); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;}#endif /* CONFIG_PM *//* * destructor */static int azx_free(struct azx *chip){ 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); /* wait a little for interrupts to finish */ msleep(1); } if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); if (chip->remap_addr) iounmap(chip->remap_addr); if (chip->bdl.area) snd_dma_free_pages(&chip->bdl); if (chip->rb.area) snd_dma_free_pages(&chip->rb); if (chip->posbuf.area) snd_dma_free_pages(&chip->posbuf); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip->azx_dev); kfree(chip); return 0;}static int azx_dev_free(struct snd_device *device){ return azx_free(device->device_data);}/* * constructor */static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, int driver_type, struct azx **rchip){ struct azx *chip; int err = 0; static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (NULL == chip) { snd_printk(KERN_ERR SFX "cannot allocate chip\n"); 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; chip->driver_type = driver_type; chip->position_fix = position_fix; chip->single_cmd = single_cmd;#if BITS_PER_LONG != 64 /* Fix up base address on ULI M5461 */ if (chip->driver_type == AZX_DRIVER_ULI) { u16 tmp3; pci_read_config_word(pci, 0x40, &tmp3); pci_write_config_word(pci, 0x40, tmp3 | 0x10); pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0); }#endif if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { kfree(chip); pci_disable_device(pci); return err; } chip->addr = pci_resource_start(pci,0); chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0)); if (chip->remap_addr == NULL) { snd_printk(KERN_ERR SFX "ioremap error\n"); err = -ENXIO; goto errout; } if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ, "HDA Intel", (void*)chip)) { snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto errout; } chip->irq = pci->irq; pci_set_master(pci); synchronize_irq(chip->irq); switch (chip->driver_type) { case AZX_DRIVER_ULI: chip->playback_streams = ULI_NUM_PLAYBACK; chip->capture_streams = ULI_NUM_CAPTURE; chip->playback_index_offset = ULI_PLAYBACK_INDEX; chip->capture_index_offset = ULI_CAPTURE_INDEX; break; default: chip->playback_streams = ICH6_NUM_PLAYBACK; chip->capture_streams = ICH6_NUM_CAPTURE; chip->playback_index_offset = ICH6_PLAYBACK_INDEX; chip->capture_index_offset = ICH6_CAPTURE_INDEX; break; } chip->num_streams = chip->playback_streams + chip->capture_streams; 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) { 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) { 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) goto errout; /* initialize streams */ azx_init_stream(chip); /* initialize chip */ azx_init_chip(chip); chip->initialized = 1; /* codec detection */ if (! chip->codec_mask) { snd_printk(KERN_ERR SFX "no codecs found!\n"); err = -ENODEV; goto errout; } if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <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); *rchip = chip; return 0; errout: azx_free(chip); return err;}static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ struct snd_card *card; struct azx *chip; int err = 0; card = snd_card_new(index, id, THIS_MODULE, 0); if (NULL == card) { snd_printk(KERN_ERR SFX "Error creating card!\n"); return -ENOMEM; } if ((err = azx_create(card, pci, pci_id->driver_data, &chip)) < 0) { snd_card_free(card); return err; } card->private_data = chip; /* create codec instances */ if ((err = azx_codec_create(chip, model)) < 0) { snd_card_free(card); return err; } /* create PCM streams */ if ((err = azx_pcm_create(chip)) < 0) { snd_card_free(card); return err; } /* create mixer controls */ if ((err = azx_mixer_create(chip)) < 0) { snd_card_free(card); return err; } snd_card_set_dev(card, &pci->dev); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); return err;}static void __devexit azx_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}/* PCI IDs */static struct pci_device_id azx_ids[] __devinitdata = { { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */ { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */ { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */ { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ { 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, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA 026c */ { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA 0371 */ { 0, }};MODULE_DEVICE_TABLE(pci, azx_ids);/* pci_driver definition */static struct pci_driver driver = { .name = "HDA Intel", .id_table = azx_ids, .probe = azx_probe, .remove = __devexit_p(azx_remove),#ifdef CONFIG_PM .suspend = azx_suspend, .resume = azx_resume,#endif};static int __init alsa_card_azx_init(void){ return pci_register_driver(&driver);}static void __exit alsa_card_azx_exit(void){ pci_unregister_driver(&driver);}module_init(alsa_card_azx_init)module_exit(alsa_card_azx_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -