📄 riptide.c
字号:
SET_AIACK(cif->hwport); SET_AIE(cif->hwport); SET_AIACK(cif->hwport); cif->is_reset = 1; if (chip) { for (i = 0; i < 4; i++) chip->firmware.ret.retwords[i] = firmware.ret.retwords[i]; } return 0;}static struct snd_pcm_hardware snd_riptide_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (64 * 1024), .period_bytes_min = PAGE_SIZE >> 1, .period_bytes_max = PAGE_SIZE << 8, .periods_min = 2, .periods_max = 64, .fifo_size = 0,};static struct snd_pcm_hardware snd_riptide_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (64 * 1024), .period_bytes_min = PAGE_SIZE >> 1, .period_bytes_max = PAGE_SIZE << 3, .periods_min = 2, .periods_max = 64, .fifo_size = 0,};static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data = get_pcmhwdev(substream); struct cmdif *cif = chip->cif; union cmdret rptr = CMDRET_ZERO; snd_pcm_uframes_t ret; SEND_GPOS(cif, 0, data->id, &rptr); if (data->size && runtime->period_size) { snd_printdd ("pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n", data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size, bytes_to_frames(runtime, rptr.retlongs[1]), bytes_to_frames(runtime, rptr.retlongs[1]) % runtime->period_size); if (rptr.retlongs[1] > data->pointer) ret = bytes_to_frames(runtime, rptr.retlongs[1] % data->size); else ret = bytes_to_frames(runtime, data->pointer % data->size); } else { snd_printdd("stream not started or strange parms (%d %ld)\n", data->size, runtime->period_size); ret = bytes_to_frames(runtime, 0); } return ret;}static int snd_riptide_trigger(struct snd_pcm_substream *substream, int cmd){ int i, j; struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); struct cmdif *cif = chip->cif; union cmdret rptr = CMDRET_ZERO; spin_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: if (!(data->state & ST_PLAY)) { SEND_SSTR(cif, data->id, data->sgdlist.addr); SET_AIE(cif->hwport); data->state = ST_PLAY; if (data->mixer != 0xff) setmixer(cif, data->mixer, 0x7fff, 0x7fff); chip->openstreams++; data->oldpos = 0; data->pointer = 0; } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: if (data->mixer != 0xff) setmixer(cif, data->mixer, 0, 0); setmixer(cif, data->mixer, 0, 0); SEND_KSTR(cif, data->id); data->state = ST_STOP; chip->openstreams--; j = 0; do { i = rptr.retlongs[1]; SEND_GPOS(cif, 0, data->id, &rptr); udelay(1); } while (i != rptr.retlongs[1] && j++ < MAX_WRITE_RETRY); if (j >= MAX_WRITE_RETRY) snd_printk(KERN_ERR "Riptide: Could not stop stream!"); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (!(data->state & ST_PAUSE)) { SEND_PSTR(cif, data->id); data->state |= ST_PAUSE; chip->openstreams--; } break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (data->state & ST_PAUSE) { SEND_SSTR(cif, data->id, data->sgdlist.addr); data->state &= ~ST_PAUSE; chip->openstreams++; } break; default: spin_unlock(&chip->lock); return -EINVAL; } spin_unlock(&chip->lock); return 0;}static int snd_riptide_prepare(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); struct pcmhw *data = get_pcmhwdev(substream); struct cmdif *cif = chip->cif; unsigned char *lbuspath = NULL; unsigned int rate, channels; int err = 0; snd_pcm_format_t format; snd_assert(cif && data, return -EINVAL); snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id, runtime->channels, runtime->format, runtime->rate); spin_lock_irq(&chip->lock); channels = runtime->channels; format = runtime->format; rate = runtime->rate; switch (channels) { case 1: if (rate == 48000 && format == SNDRV_PCM_FORMAT_S16_LE) lbuspath = data->paths.noconv; else lbuspath = data->paths.mono; break; case 2: if (rate == 48000 && format == SNDRV_PCM_FORMAT_S16_LE) lbuspath = data->paths.noconv; else lbuspath = data->paths.stereo; break; } snd_printdd("use sgdlist at 0x%p and buffer at 0x%p\n", data->sgdlist.area, sgbuf); if (data->sgdlist.area && sgbuf) { unsigned int i, j, size, pages, f, pt, period; struct sgd *c, *p = NULL; size = frames_to_bytes(runtime, runtime->buffer_size); period = frames_to_bytes(runtime, runtime->period_size); f = PAGE_SIZE; while ((size + (f >> 1) - 1) <= (f << 7) && (f << 1) > period) f = f >> 1; pages = (size + f - 1) / f; data->size = size; data->pages = pages; snd_printdd ("create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n", size, pages, f, period); pt = 0; j = 0; for (i = 0; i < pages; i++) { c = &data->sgdbuf[i]; if (p) p->dwNextLink = cpu_to_le32(data->sgdlist.addr + (i * sizeof(struct sgd))); c->dwNextLink = cpu_to_le32(data->sgdlist.addr); c->dwSegPtrPhys = cpu_to_le32(sgbuf->table[j].addr + pt); pt = (pt + f) % PAGE_SIZE; if (pt == 0) j++; c->dwSegLen = cpu_to_le32(f); c->dwStat_Ctl = cpu_to_le32(IEOB_ENABLE | IEOS_ENABLE | IEOC_ENABLE); p = c; size -= f; } data->sgdbuf[i].dwSegLen = cpu_to_le32(size); } if (lbuspath && lbuspath != data->lbuspath) { if (data->lbuspath) freelbuspath(cif, data->source, data->lbuspath); alloclbuspath(cif, data->source, lbuspath, &data->mixer, data->intdec); data->lbuspath = lbuspath; data->rate = 0; } if (data->rate != rate || data->format != format || data->channels != channels) { data->rate = rate; data->format = format; data->channels = channels; if (setsampleformat (cif, data->mixer, data->id, channels, format) || setsamplerate(cif, data->intdec, rate)) err = -EIO; } spin_unlock_irq(&chip->lock); return err;}static intsnd_riptide_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); struct snd_dma_buffer *sgdlist = &data->sgdlist; int err; snd_printdd("hw params id %d (sgdlist: 0x%p 0x%lx %d)\n", data->id, sgdlist->area, (unsigned long)sgdlist->addr, (int)sgdlist->bytes); if (sgdlist->area) snd_dma_free_pages(sgdlist); if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), sizeof(struct sgd) * (DESC_MAX_MASK + 1), sgdlist)) < 0) { snd_printk(KERN_ERR "Riptide: failed to alloc %d dma bytes\n", (int)sizeof(struct sgd) * (DESC_MAX_MASK + 1)); return err; } data->sgdbuf = (struct sgd *)sgdlist->area; return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_riptide_hw_free(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); struct cmdif *cif = chip->cif; if (cif && data) { if (data->lbuspath) freelbuspath(cif, data->source, data->lbuspath); data->lbuspath = NULL; data->source = 0xff; data->intdec[0] = 0xff; data->intdec[1] = 0xff; if (data->sgdlist.area) { snd_dma_free_pages(&data->sgdlist); data->sgdlist.area = NULL; } } return snd_pcm_lib_free_pages(substream);}static int snd_riptide_playback_open(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data; int index = substream->number; chip->playback_substream[index] = substream; runtime->hw = snd_riptide_playback; data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL); data->paths = lbus_play_paths[index]; data->id = play_ids[index]; data->source = play_sources[index]; data->intdec[0] = 0xff; data->intdec[1] = 0xff; data->state = ST_STOP; runtime->private_data = data; return snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);}static int snd_riptide_capture_open(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data; chip->capture_substream = substream; runtime->hw = snd_riptide_capture; data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL); data->paths = lbus_rec_path; data->id = PADC; data->source = ACLNK2PADC; data->intdec[0] = 0xff; data->intdec[1] = 0xff; data->state = ST_STOP; runtime->private_data = data; return snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);}static int snd_riptide_playback_close(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); int index = substream->number; substream->runtime->private_data = NULL; chip->playback_substream[index] = NULL; kfree(data); return 0;}static int snd_riptide_capture_close(struct snd_pcm_substream *substream){ struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); substream->runtime->private_data = NULL; chip->capture_substream = NULL; kfree(data); return 0;}static struct snd_pcm_ops snd_riptide_playback_ops = { .open = snd_riptide_playback_open, .close = snd_riptide_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_riptide_hw_params, .hw_free = snd_riptide_hw_free, .prepare = snd_riptide_prepare, .page = snd_pcm_sgbuf_ops_page, .trigger = snd_riptide_trigger, .pointer = snd_riptide_pointer,};static struct snd_pcm_ops snd_riptide_capture_ops = { .open = snd_riptide_capture_open, .close = snd_riptide_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_riptide_hw_params, .hw_free = snd_riptide_hw_free, .prepare = snd_riptide_prepare, .page = snd_pcm_sgbuf_ops_page, .trigger = snd_riptide_trigger, .pointer = snd_riptide_pointer,};static int __devinitsnd_riptide_pcm(struct snd_riptide *chip, int device, struct snd_pcm **rpcm){ struct snd_pcm *pcm; int err; if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "RIPTIDE", device, PLAYBACK_SUBSTREAMS, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_riptide_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_riptide_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; strcpy(pcm->name, "RIPTIDE"); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 64 * 1024, 128 * 1024); if (rpcm) *rpcm = pcm; return 0;}static irqreturn_tsnd_riptide_interrupt(int irq, void *dev_id){ struct snd_riptide *chip = dev_id; struct cmdif *cif = chip->cif; if (cif) { chip->received_irqs++; if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) || IS_EOCIRQ(cif->hwport)) { chip->handled_irqs++; tasklet_hi_schedule(&chip->riptide_tq); } if (chip->rmidi && IS_MPUIRQ(cif->hwport)) { chip->handled_irqs++; snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); } SET_AIACK(cif->hwport); } return IRQ_HANDLED;}static voidsnd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct snd_riptide *chip = ac97->private_data; struct cmdif *cif = chip->cif; union cmdret rptr = CMDRET_ZERO; int i = 0; snd_assert(cif, return);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -