📄 vx_pcm.c
字号:
return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ }/* * vx_alloc_pipe - allocate a pipe and initialize the pipe instance * @capture: 0 = playback, 1 = capture operation * @audioid: the audio id to be assigned * @num_audio: number of audio channels * @pipep: the returned pipe instance * * return 0 on success, or a negative error code. */static int vx_alloc_pipe(vx_core_t *chip, int capture, int audioid, int num_audio, vx_pipe_t **pipep){ int err; vx_pipe_t *pipe; struct vx_rmh rmh; int data_mode; *pipep = NULL; vx_init_rmh(&rmh, CMD_RES_PIPE); vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio);#if 0 // NYI if (underrun_skip_sound) rmh.Cmd[0] |= BIT_SKIP_SOUND;#endif // NYI data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; if (! capture && data_mode) rmh.Cmd[0] |= BIT_DATA_MODE; err = vx_send_msg(chip, &rmh); if (err < 0) return err; /* initialize the pipe record */ pipe = kcalloc(1, sizeof(*pipe), GFP_KERNEL); if (! pipe) { /* release the pipe */ vx_init_rmh(&rmh, CMD_FREE_PIPE); vx_set_pipe_cmd_params(&rmh, capture, audioid, 0); vx_send_msg(chip, &rmh); return -ENOMEM; } /* the pipe index should be identical with the audio index */ pipe->number = audioid; pipe->is_capture = capture; pipe->channels = num_audio; pipe->differed_type = 0; pipe->pcx_time = 0; pipe->data_mode = data_mode; *pipep = pipe; return 0;}/* * vx_free_pipe - release a pipe * @pipe: pipe to be released */static int vx_free_pipe(vx_core_t *chip, vx_pipe_t *pipe){ struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_FREE_PIPE); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); vx_send_msg(chip, &rmh); kfree(pipe); return 0;}/* * vx_start_stream - start the stream * * called from trigger callback only */static int vx_start_stream(vx_core_t *chip, vx_pipe_t *pipe){ struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_START_ONE_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); vx_set_differed_time(chip, &rmh, pipe); return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ }/* * vx_stop_stream - stop the stream * * called from trigger callback only */static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe){ struct vx_rmh rmh; vx_init_rmh(&rmh, CMD_STOP_STREAM); vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ }/* * playback hw information */static snd_pcm_hardware_t vx_pcm_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_RESUME), .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 126, .period_bytes_max = (128*1024), .periods_min = 2, .periods_max = VX_MAX_PERIODS, .fifo_size = 126,};static void vx_pcm_delayed_start(unsigned long arg);/* * vx_pcm_playback_open - open callback for playback */static int vx_pcm_playback_open(snd_pcm_substream_t *subs){ snd_pcm_runtime_t *runtime = subs->runtime; vx_core_t *chip = snd_pcm_substream_chip(subs); vx_pipe_t *pipe = NULL; unsigned int audio; int err; if (chip->chip_status & VX_STAT_IS_STALE) return -EBUSY; audio = subs->pcm->device * 2; snd_assert(audio < chip->audio_outs, return -EINVAL); /* playback pipe may have been already allocated for monitoring */ pipe = chip->playback_pipes[audio]; if (! pipe) { /* not allocated yet */ err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */ if (err < 0) return err; chip->playback_pipes[audio] = pipe; } /* open for playback */ pipe->references++; pipe->substream = subs; tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); chip->playback_pipes[audio] = pipe; runtime->hw = vx_pcm_playback_hw; runtime->hw.period_bytes_min = chip->ibl.size; runtime->private_data = pipe; /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ 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); return 0;}/* * vx_pcm_playback_close - close callback for playback */static int vx_pcm_playback_close(snd_pcm_substream_t *subs){ vx_core_t *chip = snd_pcm_substream_chip(subs); vx_pipe_t *pipe; if (! subs->runtime->private_data) return -EINVAL; pipe = subs->runtime->private_data; if (--pipe->references == 0) { chip->playback_pipes[pipe->number] = NULL; vx_free_pipe(chip, pipe); } return 0;}/* * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe * @pipe: the pipe to notify * * NB: call with a certain lock. */static int vx_notify_end_of_buffer(vx_core_t *chip, vx_pipe_t *pipe){ int err; struct vx_rmh rmh; /* use a temporary rmh here */ /* Toggle Dsp Host Interface into Message mode */ vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); vx_init_rmh(&rmh, CMD_NOTIFY_END_OF_BUFFER); vx_set_stream_cmd_params(&rmh, 0, pipe->number); err = vx_send_msg_nolock(chip, &rmh); if (err < 0) return err; /* Toggle Dsp Host Interface back to sound transfer mode */ vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); return 0;}/* * vx_pcm_playback_transfer_chunk - transfer a single chunk * @subs: substream * @pipe: the pipe to transfer * @size: chunk size in bytes * * transfer a single buffer chunk. EOB notificaton is added after that. * called from the interrupt handler, too. * * return 0 if ok. */static int vx_pcm_playback_transfer_chunk(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe, int size){ int space, err = 0; space = vx_query_hbuffer_size(chip, pipe); if (space < 0) { /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); snd_printd("error hbuffer\n"); return space; } if (space < size) { vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); snd_printd("no enough hbuffer space %d\n", space); return -EIO; /* XRUN */ } /* we don't need irqsave here, because this function * is called from either trigger callback or irq handler */ spin_lock(&chip->lock); vx_pseudo_dma_write(chip, runtime, pipe, size); err = vx_notify_end_of_buffer(chip, pipe); /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); spin_unlock(&chip->lock); return err;}/* * update the position of the given pipe. * pipe->position is updated and wrapped within the buffer size. * pipe->transferred is updated, too, but the size is not wrapped, * so that the caller can check the total transferred size later * (to call snd_pcm_period_elapsed). */static int vx_update_pipe_position(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe){ struct vx_rmh rmh; int err, update; u64 count; vx_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT); vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); err = vx_send_msg(chip, &rmh); if (err < 0) return err; count = ((u64)(rmh.Stat[0] & 0xfffff) << 24) | (u64)rmh.Stat[1]; update = (int)(count - pipe->cur_count); pipe->cur_count = count; pipe->position += update; if (pipe->position >= (int)runtime->buffer_size) pipe->position %= runtime->buffer_size; pipe->transferred += update; return 0;}/* * transfer the pending playback buffer data to DSP * called from interrupt handler */static void vx_pcm_playback_transfer(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe, int nchunks){ int i, err; snd_pcm_runtime_t *runtime = subs->runtime; if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) return; for (i = 0; i < nchunks; i++) { if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe, chip->ibl.size)) < 0) return; }}/* * update the playback position and call snd_pcm_period_elapsed() if necessary * called from interrupt handler */static void vx_pcm_playback_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe){ int err; snd_pcm_runtime_t *runtime = subs->runtime; if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) { if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0) return; if (pipe->transferred >= (int)runtime->period_size) { pipe->transferred %= runtime->period_size; snd_pcm_period_elapsed(subs); } }}/* * start the stream and pipe. * this function is called from tasklet, which is invoked by the trigger * START callback. */static void vx_pcm_delayed_start(unsigned long arg){ snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg; vx_core_t *chip = subs->pcm->private_data; vx_pipe_t *pipe = subs->runtime->private_data; int err; /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ if ((err = vx_start_stream(chip, pipe)) < 0) { snd_printk(KERN_ERR "vx: cannot start stream\n"); return; } if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { snd_printk(KERN_ERR "vx: cannot start pipe\n"); return; } /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/}/* * vx_pcm_playback_trigger - trigger callback for playback */static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd){ vx_core_t *chip = snd_pcm_substream_chip(subs); vx_pipe_t *pipe = subs->runtime->private_data; int err; if (chip->chip_status & VX_STAT_IS_STALE) return -EBUSY; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: if (! pipe->is_capture) vx_pcm_playback_transfer(chip, subs, pipe, 2); /* FIXME: * we trigger the pipe using tasklet, so that the interrupts are * issued surely after the trigger is completed. */ tasklet_hi_schedule(&pipe->start_tq); chip->pcm_running++; pipe->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: vx_toggle_pipe(chip, pipe, 0); vx_stop_pipe(chip, pipe); vx_stop_stream(chip, pipe); chip->pcm_running--; pipe->running = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0) return err; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) return err; break; default: return -EINVAL; } return 0;}/* * vx_pcm_playback_pointer - pointer callback for playback */static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs){ snd_pcm_runtime_t *runtime = subs->runtime; vx_pipe_t *pipe = runtime->private_data; return pipe->position;}/* * vx_pcm_hw_params - hw_params callback for playback and capture */static int vx_pcm_hw_params(snd_pcm_substream_t *subs, snd_pcm_hw_params_t *hw_params){ return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params));}/* * vx_pcm_hw_free - hw_free callback for playback and capture */static int vx_pcm_hw_free(snd_pcm_substream_t *subs){ return snd_pcm_free_vmalloc_buffer(subs);}/* * vx_pcm_prepare - prepare callback for playback and capture */static int vx_pcm_prepare(snd_pcm_substream_t *subs){ vx_core_t *chip = snd_pcm_substream_chip(subs); snd_pcm_runtime_t *runtime = subs->runtime; vx_pipe_t *pipe = runtime->private_data; int err, data_mode; // int max_size, nchunks;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -