📄 pcxhr.c
字号:
is_capture ? 'c' : 'p', chip->chip_idx, (void*)subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0); snd_assert(subs->runtime->dma_bytes < 0x200000); /* max buffer size is 2 MByte */ rmh.cmd[1] = subs->runtime->dma_bytes * 8; /* size in bits */ rmh.cmd[2] = subs->runtime->dma_addr >> 24; /* most significant byte */ rmh.cmd[2] |= 1<<19; /* this is a circular buffer */ rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD; /* least 3 significant bytes */ rmh.cmd_len = 4; err = pcxhr_send_msg(chip->mgr, &rmh); if (err) snd_printk(KERN_ERR "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err); return err;}#if 0static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, snd_pcm_uframes_t *sample_count){ struct pcxhr_rmh rmh; int err; pcxhr_t *chip = snd_pcm_substream_chip(stream->substream); pcxhr_init_rmh(&rmh, CMD_PIPE_SAMPLE_COUNT); pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture, 0, 0, 1<<stream->pipe->first_audio); err = pcxhr_send_msg(chip->mgr, &rmh); if (err == 0) { *sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24; *sample_count += (snd_pcm_uframes_t)rmh.stat[1]; } snd_printdd("PIPE_SAMPLE_COUNT = %lx\n", *sample_count); return err;}#endifstatic inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream, struct pcxhr_pipe **pipe){ if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN) { *pipe = stream->pipe; return 1; } return 0;}static void pcxhr_trigger_tasklet(unsigned long arg){ unsigned long flags; int i, j, err; struct pcxhr_pipe *pipe; struct snd_pcxhr *chip; struct pcxhr_mgr *mgr = (struct pcxhr_mgr*)(arg); int capture_mask = 0; int playback_mask = 0;#ifdef CONFIG_SND_DEBUG_DETECT struct timeval my_tv1, my_tv2; do_gettimeofday(&my_tv1);#endif down(&mgr->setup_mutex); /* check the pipes concerned and build pipe_array */ for (i = 0; i < mgr->num_cards; i++) { chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_capt; j++) { if (pcxhr_stream_scheduled_get_pipe(&chip->capture_stream[j], &pipe)) capture_mask |= (1 << pipe->first_audio); } for (j = 0; j < chip->nb_streams_play; j++) { if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) { playback_mask |= (1 << pipe->first_audio); break; /* add only once, as all playback streams of * one chip use the same pipe */ } } } if (capture_mask == 0 && playback_mask == 0) { up(&mgr->setup_mutex); snd_printk(KERN_ERR "pcxhr_trigger_tasklet : no pipes\n"); return; } snd_printdd("pcxhr_trigger_tasklet : playback_mask=%x capture_mask=%x\n", playback_mask, capture_mask); /* synchronous stop of all the pipes concerned */ err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0); if (err) { up(&mgr->setup_mutex); snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error stop pipes (P%x C%x)\n", playback_mask, capture_mask); return; } /* unfortunately the dsp lost format and buffer info with the stop pipe */ for (i = 0; i < mgr->num_cards; i++) { struct pcxhr_stream *stream; chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_capt; j++) { stream = &chip->capture_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) { err = pcxhr_set_format(stream); err = pcxhr_update_r_buffer(stream); } } for (j = 0; j < chip->nb_streams_play; j++) { stream = &chip->playback_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) { err = pcxhr_set_format(stream); err = pcxhr_update_r_buffer(stream); } } } /* start all the streams */ for (i = 0; i < mgr->num_cards; i++) { struct pcxhr_stream *stream; chip = mgr->chip[i]; for (j = 0; j < chip->nb_streams_capt; j++) { stream = &chip->capture_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) err = pcxhr_set_stream_state(stream); } for (j = 0; j < chip->nb_streams_play; j++) { stream = &chip->playback_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) err = pcxhr_set_stream_state(stream); } } /* synchronous start of all the pipes concerned */ err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1); if (err) { up(&mgr->setup_mutex); snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error start pipes (P%x C%x)\n", playback_mask, capture_mask); return; } /* put the streams into the running state now (increment pointer by interrupt) */ spin_lock_irqsave(&mgr->lock, flags); for ( i =0; i < mgr->num_cards; i++) { struct pcxhr_stream *stream; chip = mgr->chip[i]; for(j = 0; j < chip->nb_streams_capt; j++) { stream = &chip->capture_stream[j]; if(stream->status == PCXHR_STREAM_STATUS_STARTED) stream->status = PCXHR_STREAM_STATUS_RUNNING; } for (j = 0; j < chip->nb_streams_play; j++) { stream = &chip->playback_stream[j]; if (stream->status == PCXHR_STREAM_STATUS_STARTED) { /* playback will already have advanced ! */ stream->timer_period_frag += PCXHR_GRANULARITY; stream->status = PCXHR_STREAM_STATUS_RUNNING; } } } spin_unlock_irqrestore(&mgr->lock, flags); up(&mgr->setup_mutex);#ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", my_tv2.tv_usec - my_tv1.tv_usec, err);#endif}/* * trigger callback */static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd){ struct pcxhr_stream *stream; struct list_head *pos; struct snd_pcm_substream *s; int i; switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_printdd("SNDRV_PCM_TRIGGER_START\n"); i = 0; snd_pcm_group_for_each(pos, subs) { s = snd_pcm_group_substream_entry(pos); stream = s->runtime->private_data; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN; snd_pcm_trigger_done(s, subs); i++; } if (i==1) { snd_printdd("Only one Substream %c %d\n", stream->pipe->is_capture ? 'C' : 'P', stream->pipe->first_audio); if (pcxhr_set_format(stream)) return -EINVAL; if (pcxhr_update_r_buffer(stream)) return -EINVAL; if (pcxhr_set_stream_state(stream)) return -EINVAL; stream->status = PCXHR_STREAM_STATUS_RUNNING; } else { struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); tasklet_hi_schedule(&chip->mgr->trigger_taskq); } break; case SNDRV_PCM_TRIGGER_STOP: snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); snd_pcm_group_for_each(pos, subs) { s = snd_pcm_group_substream_entry(pos); stream = s->runtime->private_data; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP; if (pcxhr_set_stream_state(stream)) return -EINVAL; snd_pcm_trigger_done(s, subs); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* TODO */ default: return -EINVAL; } return 0;}static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start){ struct pcxhr_rmh rmh; int err; pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT); if (start) { mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID; /* last dsp time invalid */ rmh.cmd[0] |= PCXHR_GRANULARITY; } err = pcxhr_send_msg(mgr, &rmh); if (err < 0) snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n", err); return err;}/* * prepare callback for all pcms */static int pcxhr_prepare(struct snd_pcm_substream *subs){ struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); struct pcxhr_mgr *mgr = chip->mgr; /* struct pcxhr_stream *stream = (pcxhr_stream_t*)subs->runtime->private_data; */ int err = 0; snd_printdd("pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n", subs->runtime->period_size, subs->runtime->periods, subs->runtime->buffer_size); /* if(subs->runtime->period_size <= PCXHR_GRANULARITY) { snd_printk(KERN_ERR "pcxhr_prepare : error period_size too small (%x)\n", (unsigned int)subs->runtime->period_size); return -EINVAL; } */ down(&mgr->setup_mutex); do { /* if the stream was stopped before, format and buffer were reset */ /* if(stream->status == PCXHR_STREAM_STATUS_STOPPED) { err = pcxhr_set_format(stream); if(err) break; err = pcxhr_update_r_buffer(stream); if(err) break; } */ /* only the first stream can choose the sample rate */ /* the further opened streams will be limited to its frequency (see open) */ /* set the clock only once (first stream) */ if (mgr->sample_rate != subs->runtime->rate) { err = pcxhr_set_clock(mgr, subs->runtime->rate); if (err) break; if (mgr->sample_rate == 0) /* start the DSP-timer */ err = pcxhr_hardware_timer(mgr, 1); mgr->sample_rate = subs->runtime->rate; } } while(0); /* do only once (so we can use break instead of goto) */ up(&mgr->setup_mutex); return err;}/* * HW_PARAMS callback for all pcms */static int pcxhr_hw_params(struct snd_pcm_substream *subs, struct snd_pcm_hw_params *hw){ struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); struct pcxhr_mgr *mgr = chip->mgr; struct pcxhr_stream *stream = subs->runtime->private_data; snd_pcm_format_t format; int err; int channels; /* set up channels */ channels = params_channels(hw); /* set up format for the stream */ format = params_format(hw); down(&mgr->setup_mutex); stream->channels = channels; stream->format = format; /* set the format to the board */ /* err = pcxhr_set_format(stream); if(err) { up(&mgr->setup_mutex); return err; } */ /* allocate buffer */ err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw)); /* if (err > 0) { err = pcxhr_update_r_buffer(stream); } */ up(&mgr->setup_mutex); return err;}static int pcxhr_hw_free(struct snd_pcm_substream *subs){ snd_pcm_lib_free_pages(subs); return 0;}/* * CONFIGURATION SPACE for all pcms, mono pcm must update channels_max */static struct snd_pcm_hardware pcxhr_caps ={ .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | 0 /*SNDRV_PCM_INFO_PAUSE*/), .formats = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_FLOAT_LE ), .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, .rate_min = 8000, .rate_max = 192000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (32*1024), /* 1 byte == 1 frame U8 mono (PCXHR_GRANULARITY is frames!) */ .period_bytes_min = (2*PCXHR_GRANULARITY), .period_bytes_max = (16*1024), .periods_min = 2, .periods_max = (32*1024/PCXHR_GRANULARITY),};static int pcxhr_open(struct snd_pcm_substream *subs){ struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); struct pcxhr_mgr *mgr = chip->mgr; struct snd_pcm_runtime *runtime = subs->runtime; struct pcxhr_stream *stream; int is_capture; down(&mgr->setup_mutex); /* copy the struct snd_pcm_hardware struct */ runtime->hw = pcxhr_caps; if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) { snd_printdd("pcxhr_open playback chip%d subs%d\n", chip->chip_idx, subs->number); is_capture = 0; stream = &chip->playback_stream[subs->number]; } else { snd_printdd("pcxhr_open capture chip%d subs%d\n", chip->chip_idx, subs->number); is_capture = 1; if (mgr->mono_capture) runtime->hw.channels_max = 1; else runtime->hw.channels_min = 2; stream = &chip->capture_stream[subs->number]; } if (stream->status != PCXHR_STREAM_STATUS_FREE){ /* streams in use */ snd_printk(KERN_ERR "pcxhr_open chip%d subs%d in use\n", chip->chip_idx, subs->number); up(&mgr->setup_mutex); return -EBUSY; } /* if a sample rate is already used or fixed by external clock, * the stream cannot change */ if (mgr->sample_rate) runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; else { if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) { int external_rate; if (pcxhr_get_external_clock(mgr, mgr->use_clock_type, &external_rate) || external_rate == 0) { /* cannot detect the external clock rate */ up(&mgr->setup_mutex); return -EBUSY; } runtime->hw.rate_min = runtime->hw.rate_max = external_rate; } } stream->status = PCXHR_STREAM_STATUS_OPEN; stream->substream = subs; stream->channels = 0; /* not configured yet */ runtime->private_data = stream; snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); mgr->ref_count_rate++; up(&mgr->setup_mutex); return 0;}static int pcxhr_close(struct snd_pcm_substream *subs){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -