📄 hda_intel.c
字号:
static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev){ unsigned char val; int timeout;//printk(KERN_DEBUG"%s: %p tag=%d bufsize=%d fmt=0x%x\n", __FUNCTION__, azx_dev, (int)azx_dev->stream_tag, azx_dev->bufsize, (unsigned int)azx_dev->format_val); /* 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); /* reset stream */ 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) && --timeout) ; val &= ~SD_CTL_STREAM_RESET; azx_sd_writeb(azx_dev, SD_CTL, val); udelay(3); timeout = 300; /* waiting for hardware to report that the stream is out of reset */ while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && --timeout) ; /* program the stream_tag */ azx_sd_writel(azx_dev, SD_CTL, (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_CBL, azx_dev->bufsize); /* program the stream format */ /* this value needs to be the same as the one programmed */ azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val); /* program the stream LVI (last valid index) of the BDL */ azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1); /* program the BDL address */ /* lower BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); /* upper BDL address */ 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); /* 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); return 0;}/* * Codec initialization */static int __devinit azx_codec_create(azx_t *chip, const char *model){ struct hda_bus_template bus_temp; int c, codecs, err; memset(&bus_temp, 0, sizeof(bus_temp)); bus_temp.private_data = chip; bus_temp.modelname = model; bus_temp.pci = chip->pci; bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; bus_temp.ops.get_wallclock = azx_get_wallclock; bus_temp.ops.get_linkpos = azx_get_linkpos; if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0) return err; codecs = 0; for (c = 0; c < AZX_MAX_CODECS; c++) { if (chip->codec_mask & (1 << c)) { err = snd_hda_codec_new(chip->bus, c, NULL); if (err < 0) continue; codecs++; } } if (! codecs) { snd_printk(KERN_ERR SFX "no codecs initialized\n"); return -ENXIO; } return 0;}/* * PCM support *//* assign a stream for the PCM */static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream){ int dev, i, nums; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { dev = chip->playback_index_offset; nums = chip->playback_streams; } else { dev = chip->capture_index_offset; nums = chip->capture_streams; } for (i = 0; i < nums; i++, dev++) if (! chip->azx_dev[dev].opened) { chip->azx_dev[dev].opened = 1; return &chip->azx_dev[dev]; } return NULL;}/* release the assigned stream */static inline void azx_release_device(azx_dev_t *azx_dev){//printk(KERN_ERR"%s dev=%p\n", __FUNCTION__, azx_dev); azx_dev->opened = 0;}static snd_pcm_hardware_t azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE /*|*/ /*SNDRV_PCM_INFO_RESUME*/), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, .periods_min = 2, .periods_max = AZX_MAX_FRAG, .fifo_size = 0,};struct azx_pcm { azx_t *chip; struct hda_codec *codec; struct hda_pcm_stream *hinfo[2];};static int azx_pcm_open(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; azx_t *chip = apcm->chip; azx_dev_t *azx_dev; snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; int err; down(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream->stream); if (azx_dev == NULL) { up(&chip->open_mutex); return -EBUSY; } runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; runtime->hw.formats = hinfo->formats; runtime->hw.rates = hinfo->rates; snd_pcm_limit_hw_rates(runtime); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) { azx_release_device(azx_dev); up(&chip->open_mutex); return err; } spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = substream; azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); runtime->private_data = azx_dev; up(&chip->open_mutex); return 0;}static int azx_pcm_close(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); unsigned long flags;//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, substream); down(&chip->open_mutex); spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = NULL; azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); hinfo->ops.close(hinfo, apcm->codec, substream); up(&chip->open_mutex); return 0;}static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int azx_pcm_hw_free(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_dev_t *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, substream); /* reset BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0); azx_sd_writel(azx_dev, SD_CTL, 0); hinfo->ops.cleanup(hinfo, apcm->codec, substream); return snd_pcm_lib_free_pages(substream);}static int azx_pcm_prepare(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; snd_pcm_runtime_t *runtime = substream->runtime; azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); azx_dev->fragsize = snd_pcm_lib_period_bytes(substream); azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize; azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, 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", 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; azx_dev->last_pos = 0;// runtime->hw.fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE); return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, azx_dev->format_val, substream);}static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_dev_t *azx_dev = get_azx_dev(substream); azx_t *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(snd_pcm_substream_t *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); azx_t *chip = apcm->chip; azx_dev_t *azx_dev = get_azx_dev(substream); unsigned int pos; if (chip->position_fix == POS_FIX_POSBUF) { /* use the position buffer */ pos = *azx_dev->posbuf; } else { /* read LPIB */ pos = azx_sd_readl(azx_dev, SD_LPIB); if (chip->position_fix == POS_FIX_FIFO) pos += azx_dev->fifo_size;#if 0 /* disabled temprarily, auto-correction doesn't work well... */ else if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_updating) { /* check the validity of DMA position */ unsigned int diff = 0; azx_dev->last_pos += azx_dev->fragsize; if (azx_dev->last_pos > pos) diff = azx_dev->last_pos - pos; if (azx_dev->last_pos >= azx_dev->bufsize) { if (pos < azx_dev->fragsize) diff = 0; azx_dev->last_pos = 0; } if (diff > 0 && diff <= azx_dev->fifo_size) pos += azx_dev->fifo_size; else { snd_printdd(KERN_INFO "hda_intel: DMA position fix %d, switching to posbuf\n", diff); chip->position_fix = POS_FIX_POSBUF; pos = *azx_dev->posbuf; } azx_dev->period_updating = 0; }#else else if (chip->position_fix == POS_FIX_AUTO) pos += azx_dev->fifo_size;#endif } if (pos >= azx_dev->bufsize) pos = 0; return bytes_to_frames(substream->runtime, pos);}static snd_pcm_ops_t 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(snd_pcm_t *pcm){//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, pcm); kfree(pcm->private_data);}static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec, struct hda_pcm *cpcm, int pcm_dev){ int err; snd_pcm_t *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); cpcm->pcm = pcm; chip->pcm[pcm_dev] = pcm; chip->pcm_devs = pcm_dev + 1; return 0;}static int __devinit azx_pcm_create(azx_t *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++; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -