📄 es1968.c
字号:
kfree(chunk); } } mutex_unlock(&chip->memory_mutex);}static void snd_es1968_free_dmabuf(struct es1968 *chip){ struct list_head *p; if (! chip->dma.area) return; snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci)); while ((p = chip->buf_list.next) != &chip->buf_list) { struct esm_memory *chunk = list_entry(p, struct esm_memory, list); list_del(p); kfree(chunk); }}static int __devinitsnd_es1968_init_dmabuf(struct es1968 *chip){ int err; struct esm_memory *chunk; chip->dma.dev.type = SNDRV_DMA_TYPE_DEV; chip->dma.dev.dev = snd_dma_pci_data(chip->pci); if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) { err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), chip->total_bufsize, &chip->dma); if (err < 0 || ! chip->dma.area) { snd_printk(KERN_ERR "es1968: can't allocate dma pages for size %d\n", chip->total_bufsize); return -ENOMEM; } if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) { snd_dma_free_pages(&chip->dma); snd_printk(KERN_ERR "es1968: DMA buffer beyond 256MB.\n"); return -ENOMEM; } } INIT_LIST_HEAD(&chip->buf_list); /* allocate an empty chunk */ chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); if (chunk == NULL) { snd_es1968_free_dmabuf(chip); return -ENOMEM; } memset(chip->dma.area, 0, ESM_MEM_ALIGN); chunk->buf = chip->dma; chunk->buf.area += ESM_MEM_ALIGN; chunk->buf.addr += ESM_MEM_ALIGN; chunk->buf.bytes -= ESM_MEM_ALIGN; chunk->empty = 1; list_add(&chunk->list, &chip->buf_list); return 0;}/* setup the dma_areas *//* buffer is extracted from the pre-allocated memory chunk */static int snd_es1968_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *chan = runtime->private_data; int size = params_buffer_bytes(hw_params); if (chan->memory) { if (chan->memory->buf.bytes >= size) { runtime->dma_bytes = size; return 0; } snd_es1968_free_memory(chip, chan->memory); } chan->memory = snd_es1968_new_memory(chip, size); if (chan->memory == NULL) { // snd_printd("cannot allocate dma buffer: size = %d\n", size); return -ENOMEM; } snd_pcm_set_runtime_buffer(substream, &chan->memory->buf); return 1; /* area was changed */}/* remove dma areas if allocated */static int snd_es1968_hw_free(struct snd_pcm_substream *substream){ struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *chan; if (runtime->private_data == NULL) return 0; chan = runtime->private_data; if (chan->memory) { snd_es1968_free_memory(chip, chan->memory); chan->memory = NULL; } return 0;}/* * allocate APU pair */static int snd_es1968_alloc_apu_pair(struct es1968 *chip, int type){ int apu; for (apu = 0; apu < NR_APUS; apu += 2) { if (chip->apu[apu] == ESM_APU_FREE && chip->apu[apu + 1] == ESM_APU_FREE) { chip->apu[apu] = chip->apu[apu + 1] = type; return apu; } } return -EBUSY;}/* * release APU pair */static void snd_es1968_free_apu_pair(struct es1968 *chip, int apu){ chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE;}/****************** * PCM open/close * ******************/static int snd_es1968_playback_open(struct snd_pcm_substream *substream){ struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *es; int apu1; /* search 2 APUs */ apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); if (apu1 < 0) return apu1; es = kzalloc(sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); return -ENOMEM; } es->apu[0] = apu1; es->apu[1] = apu1 + 1; es->apu_mode[0] = 0; es->apu_mode[1] = 0; es->running = 0; es->substream = substream; es->mode = ESM_MODE_PLAY; runtime->private_data = es; runtime->hw = snd_es1968_playback; runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = calc_available_memory_size(chip); spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); spin_unlock_irq(&chip->substream_lock); return 0;}static int snd_es1968_capture_open(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; int apu1, apu2; apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); if (apu1 < 0) return apu1; apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV); if (apu2 < 0) { snd_es1968_free_apu_pair(chip, apu1); return apu2; } es = kzalloc(sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); return -ENOMEM; } es->apu[0] = apu1; es->apu[1] = apu1 + 1; es->apu[2] = apu2; es->apu[3] = apu2 + 1; es->apu_mode[0] = 0; es->apu_mode[1] = 0; es->apu_mode[2] = 0; es->apu_mode[3] = 0; es->running = 0; es->substream = substream; es->mode = ESM_MODE_CAPTURE; /* get mixbuffer */ if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); kfree(es); return -ENOMEM; } memset(es->mixbuf->buf.area, 0, ESM_MIXBUF_SIZE); runtime->private_data = es; runtime->hw = snd_es1968_capture; runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */ snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES); spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); spin_unlock_irq(&chip->substream_lock); return 0;}static int snd_es1968_playback_close(struct snd_pcm_substream *substream){ struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; if (substream->runtime->private_data == NULL) return 0; es = substream->runtime->private_data; spin_lock_irq(&chip->substream_lock); list_del(&es->list); spin_unlock_irq(&chip->substream_lock); snd_es1968_free_apu_pair(chip, es->apu[0]); kfree(es); return 0;}static int snd_es1968_capture_close(struct snd_pcm_substream *substream){ struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; if (substream->runtime->private_data == NULL) return 0; es = substream->runtime->private_data; spin_lock_irq(&chip->substream_lock); list_del(&es->list); spin_unlock_irq(&chip->substream_lock); snd_es1968_free_memory(chip, es->mixbuf); snd_es1968_free_apu_pair(chip, es->apu[0]); snd_es1968_free_apu_pair(chip, es->apu[2]); kfree(es); return 0;}static struct snd_pcm_ops snd_es1968_playback_ops = { .open = snd_es1968_playback_open, .close = snd_es1968_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es1968_hw_params, .hw_free = snd_es1968_hw_free, .prepare = snd_es1968_pcm_prepare, .trigger = snd_es1968_pcm_trigger, .pointer = snd_es1968_pcm_pointer,};static struct snd_pcm_ops snd_es1968_capture_ops = { .open = snd_es1968_capture_open, .close = snd_es1968_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es1968_hw_params, .hw_free = snd_es1968_hw_free, .prepare = snd_es1968_pcm_prepare, .trigger = snd_es1968_pcm_trigger, .pointer = snd_es1968_pcm_pointer,};/* * measure clock */#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */static void __devinit es1968_measure_clock(struct es1968 *chip){ int i, apu; unsigned int pa, offset, t; struct esm_memory *memory; struct timeval start_time, stop_time; if (chip->clock == 0) chip->clock = 48000; /* default clock value */ /* search 2 APUs (although one apu is enough) */ if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) { snd_printk(KERN_ERR "Hmm, cannot find empty APU pair!?\n"); return; } if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) { snd_printk(KERN_ERR "cannot allocate dma buffer - using default clock %d\n", chip->clock); snd_es1968_free_apu_pair(chip, apu); return; } memset(memory->buf.area, 0, CLOCK_MEASURE_BUFSIZE); wave_set_register(chip, apu << 3, (memory->buf.addr - 0x10) & 0xfff8); pa = (unsigned int)((memory->buf.addr - chip->dma.addr) >> 1); pa |= 0x00400000; /* System RAM (Bit 22) */ /* initialize apu */ for (i = 0; i < 16; i++) apu_set_register(chip, apu, i, 0x0000); apu_set_register(chip, apu, 0, 0x400f); apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8); apu_set_register(chip, apu, 5, pa & 0xffff); apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff); apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2); apu_set_register(chip, apu, 8, 0x0000); apu_set_register(chip, apu, 9, 0xD000); apu_set_register(chip, apu, 10, 0x8F08); apu_set_register(chip, apu, 11, 0x0000); spin_lock_irq(&chip->reg_lock); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */ spin_unlock_irq(&chip->reg_lock); snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ chip->in_measurement = 1; chip->measure_apu = apu; spin_lock_irq(&chip->reg_lock); snd_es1968_bob_inc(chip, ESM_BOB_FREQ); __apu_set_register(chip, apu, 5, pa & 0xffff); snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); do_gettimeofday(&start_time); spin_unlock_irq(&chip->reg_lock); msleep(50); spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); do_gettimeofday(&stop_time); snd_es1968_trigger_apu(chip, apu, 0); /* stop */ snd_es1968_bob_dec(chip); chip->in_measurement = 0; spin_unlock_irq(&chip->reg_lock); /* check the current position */ offset -= (pa & 0xffff); offset &= 0xfffe; offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2); t = stop_time.tv_sec - start_time.tv_sec; t *= 1000000; if (stop_time.tv_usec < start_time.tv_usec) t -= start_time.tv_usec - stop_time.tv_usec; else t += stop_time.tv_usec - start_time.tv_usec; if (t == 0) { snd_printk(KERN_ERR "?? calculation error..\n"); } else { offset *= 1000; offset = (offset / t) * 1000 + ((offset % t) * 1000) / t; if (offset < 47500 || offset > 48500) { if (offset >= 40000 && offset <= 50000) chip->clock = (chip->clock * offset) / 48000; } printk(KERN_INFO "es1968: clocking to %d\n", chip->clock); } snd_es1968_free_memory(chip, memory); snd_es1968_free_apu_pair(chip, apu);}/* */static void snd_es1968_pcm_free(struct snd_pcm *pcm){ struct es1968 *esm = pcm->private_data; snd_es1968_free_dmabuf(esm); esm->pcm = NULL;}static int __devinitsnd_es1968_pcm(struct es1968 *chip, int device){ struct snd_pcm *pcm; int err; /* get DMA buffer */ if ((err = snd_es1968_init_dmabuf(chip)) < 0) return err; /* set PCMBAR */ wave_set_register(chip, 0x01FC, chip->dma.addr >> 12); wave_set_register(chip, 0x01FD, chip->dma.addr >> 12); wave_set_register(chip, 0x01FE, chip->dma.addr >> 12); wave_set_register(chip, 0x01FF, chip->dma.addr >> 12); if ((err = snd_pcm_new(chip->card, "ESS Maestro", device, chip->playback_streams, chip->capture_streams, &pcm)) < 0) return err; pcm->private_data = chip; pcm->private_free = snd_es1968_pcm_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops); pcm->info_flags = 0; strcpy(pcm->name, "ESS Maestro"); chip->pcm = pcm; return 0;}/* * update pointer */static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es){ unsigned int hwptr; unsigned int diff; struct snd_pcm_substream *subs = es->substream; if (subs == NULL || !es->running) return; hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; hwptr %= es->dma_size; diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size; es->hwptr = hwptr; es->count += diff; if (es->count > es->frag_size) { spin_unlock(&chip->substream_lock); snd_pcm_period_elapsed(subs); spin_lock(&chip->substream_lock); es->count %= es->frag_size; }}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -