📄 sa11xx-uda1341.c
字号:
runtime = substream->runtime; while (s->active && s->periods < runtime->periods) { dma_size = frames_to_bytes(runtime, runtime->period_size); if (s->old_offset) { /* a little trick, we need resume from old position */ offset = frames_to_bytes(runtime, s->old_offset - 1); s->old_offset = 0; s->periods = 0; s->period = offset / dma_size; offset %= dma_size; dma_size = dma_size - offset; if (!dma_size) continue; /* special case */ } else { offset = dma_size * s->period; snd_assert(dma_size <= DMA_BUF_SIZE, ); }#ifdef HH_VERSION ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size); if (ret) return; //FIXME#else ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size); if (ret) { printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret); return; }#endif s->period++; s->period %= runtime->periods; s->periods++; }}#ifdef HH_VERSIONstatic void audio_dma_callback(void *data, int size)#elsestatic void audio_dma_callback(void *data)#endif{ audio_stream_t *s = data; /* * If we are getting a callback for an active stream then we inform * the PCM middle layer we've finished a period */ if (s->active) snd_pcm_period_elapsed(s->stream); spin_lock(&s->dma_lock); if (!s->tx_spin && s->periods > 0) s->periods--; audio_process_dma(s); spin_unlock(&s->dma_lock);}/* }}} *//* {{{ PCM setting *//* {{{ trigger & timer */static int snd_sa11xx_uda1341_trigger(snd_pcm_substream_t * substream, int cmd){ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); int stream_id = substream->pstr->stream; audio_stream_t *s = &chip->s[stream_id]; audio_stream_t *s1 = &chip->s[stream_id ^ 1]; int err = 0; /* note local interrupts are already disabled in the midlevel code */ spin_lock(&s->dma_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* now we need to make sure a record only stream has a clock */ if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) { /* we need to force fill the xmit DMA with zeros */ s1->tx_spin = 1; audio_process_dma(s1); } /* this case is when you were recording then you turn on a * playback stream so we stop (also clears it) the dma first, * clear the sync flag and then we let it turned on */ else { s->tx_spin = 0; } /* requested stream startup */ s->active = 1; audio_process_dma(s); break; case SNDRV_PCM_TRIGGER_STOP: /* requested stream shutdown */ audio_stop_dma(s); /* * now we need to make sure a record only stream has a clock * so if we're stopping a playback with an active capture * we need to turn the 0 fill dma on for the xmit side */ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) { /* we need to force fill the xmit DMA with zeros */ s->tx_spin = 1; audio_process_dma(s); } /* * we killed a capture only stream, so we should also kill * the zero fill transmit */ else { if (s1->tx_spin) { s1->tx_spin = 0; audio_stop_dma(s1); } } break; case SNDRV_PCM_TRIGGER_SUSPEND: s->active = 0;#ifdef HH_VERSION sa1100_dma_stop(s->dmach);#else //FIXME - DMA API#endif s->old_offset = audio_get_dma_pos(s) + 1;#ifdef HH_VERSION sa1100_dma_flush_all(s->dmach);#else //FIXME - DMA API#endif s->periods = 0; break; case SNDRV_PCM_TRIGGER_RESUME: s->active = 1; s->tx_spin = 0; audio_process_dma(s); if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) { s1->tx_spin = 1; audio_process_dma(s1); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH:#ifdef HH_VERSION sa1100_dma_stop(s->dmach);#else //FIXME - DMA API#endif s->active = 0; if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { if (s1->active) { s->tx_spin = 1; s->old_offset = audio_get_dma_pos(s) + 1;#ifdef HH_VERSION sa1100_dma_flush_all(s->dmach);#else //FIXME - DMA API#endif audio_process_dma(s); } } else { if (s1->tx_spin) { s1->tx_spin = 0;#ifdef HH_VERSION sa1100_dma_flush_all(s1->dmach);#else //FIXME - DMA API#endif } } break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: s->active = 1; if (s->old_offset) { s->tx_spin = 0; audio_process_dma(s); break; } if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) { s1->tx_spin = 1; audio_process_dma(s1); }#ifdef HH_VERSION sa1100_dma_resume(s->dmach);#else //FIXME - DMA API#endif break; default: err = -EINVAL; break; } spin_unlock(&s->dma_lock); return err;}static int snd_sa11xx_uda1341_prepare(snd_pcm_substream_t * substream){ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; audio_stream_t *s = &chip->s[substream->pstr->stream]; /* set requested samplerate */ sa11xx_uda1341_set_samplerate(chip, runtime->rate); /* set requestd format when available */ /* set FMT here !!! FIXME */ s->period = 0; s->periods = 0; return 0;}static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(snd_pcm_substream_t * substream){ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); return audio_get_dma_pos(&chip->s[substream->pstr->stream]);}/* }}} */static snd_pcm_hardware_t snd_sa11xx_uda1341_capture ={ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_KNOT), .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 64*1024, .period_bytes_min = 64, .period_bytes_max = DMA_BUF_SIZE, .periods_min = 2, .periods_max = 255, .fifo_size = 0,};static snd_pcm_hardware_t snd_sa11xx_uda1341_playback ={ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_KNOT), .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 64*1024, .period_bytes_min = 64, .period_bytes_max = DMA_BUF_SIZE, .periods_min = 2, .periods_max = 255, .fifo_size = 0,};static int snd_card_sa11xx_uda1341_open(snd_pcm_substream_t * substream){ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int stream_id = substream->pstr->stream; int err; chip->s[stream_id].stream = substream; if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = snd_sa11xx_uda1341_playback; else runtime->hw = snd_sa11xx_uda1341_capture; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) return err; return 0;}static int snd_card_sa11xx_uda1341_close(snd_pcm_substream_t * substream){ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream); chip->s[substream->pstr->stream].stream = NULL; return 0;}/* {{{ HW params & free */static int snd_sa11xx_uda1341_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 snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream){ return snd_pcm_lib_free_pages(substream);}/* }}} */static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = { .open = snd_card_sa11xx_uda1341_open, .close = snd_card_sa11xx_uda1341_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_sa11xx_uda1341_hw_params, .hw_free = snd_sa11xx_uda1341_hw_free, .prepare = snd_sa11xx_uda1341_prepare, .trigger = snd_sa11xx_uda1341_trigger, .pointer = snd_sa11xx_uda1341_pointer,};static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = { .open = snd_card_sa11xx_uda1341_open, .close = snd_card_sa11xx_uda1341_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_sa11xx_uda1341_hw_params, .hw_free = snd_sa11xx_uda1341_hw_free, .prepare = snd_sa11xx_uda1341_prepare, .trigger = snd_sa11xx_uda1341_trigger, .pointer = snd_sa11xx_uda1341_pointer,};static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device){ snd_pcm_t *pcm; int err; if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0) return err; /* * this sets up our initial buffers and sets the dma_type to isa. * isa works but I'm not sure why (or if) it's the right choice * this may be too large, trying it for now */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_ISA, snd_pcm_dma_flags(0), 64*1024, 64*1024); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops); pcm->private_data = sa11xx_uda1341; pcm->info_flags = 0; strcpy(pcm->name, "UDA1341 PCM"); sa11xx_uda1341_audio_init(sa11xx_uda1341); /* setup DMA controller */ audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback); audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback); sa11xx_uda1341->pcm = pcm; return 0;}/* }}} *//* {{{ module init & exit */#ifdef CONFIG_PMstatic int snd_sa11xx_uda1341_suspend(snd_card_t *card, unsigned int state){ sa11xx_uda1341_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->pcm);#ifdef HH_VERSION sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach); sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);#else //FIXME#endif l3_command(chip->uda1341, CMD_SUSPEND, NULL); sa11xx_uda1341_audio_shutdown(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0;}static int snd_sa11xx_uda1341_resume(snd_card_t *card, unsigned int state){ sa11xx_uda1341_t *chip = card->pm_private_data; sa11xx_uda1341_audio_init(chip); l3_command(chip->uda1341, CMD_RESUME, NULL);#ifdef HH_VERSION sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach); sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);#else //FIXME#endif snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;}#endif /* COMFIG_PM */void snd_sa11xx_uda1341_free(snd_card_t *card){ sa11xx_uda1341_t *chip = card->private_data; audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); sa11xx_uda1341 = NULL; card->private_data = NULL; kfree(chip);}static int __init sa11xx_uda1341_init(void){ int err; snd_card_t *card; if (!machine_is_h3xxx()) return -ENODEV; /* register the soundcard */ card = snd_card_new(-1, id, THIS_MODULE, sizeof(sa11xx_uda1341_t)); if (card == NULL) return -ENOMEM; sa11xx_uda1341 = kcalloc(1, sizeof(*sa11xx_uda1341), GFP_KERNEL); if (sa11xx_uda1341 == NULL) return -ENOMEM; spin_lock_init(&chip->s[0].dma_lock); spin_lock_init(&chip->s[1].dma_lock); card->private_data = (void *)sa11xx_uda1341; card->private_free = snd_sa11xx_uda1341_free; sa11xx_uda1341->card = card; sa11xx_uda1341->samplerate = AUDIO_RATE_DEFAULT; // mixer if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341))) goto nodev; // PCM if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0)) < 0) goto nodev; snd_card_set_dev_pm_callback(card, PM_SYS_DEV, snd_sa11xx_uda1341_suspend, snd_sa11_uda1341_resume, sa11xx_uda1341); strcpy(card->driver, "UDA1341"); strcpy(card->shortname, "H3600 UDA1341TS"); sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS"); if ((err = snd_card_register(card)) == 0) { printk( KERN_INFO "iPAQ audio support initialized\n" ); return 0; } nodev: snd_card_free(card); return err;}static void __exit sa11xx_uda1341_exit(void){ snd_card_free(sa11xx_uda1341->card);}module_init(sa11xx_uda1341_init);module_exit(sa11xx_uda1341_exit);/* }}} *//* * Local variables: * indent-tabs-mode: t * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -