static int lola_pcm_open(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_pcm *pcm = lola_get_pcm(substream); struct lola_stream *str = lola_get_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; mutex_lock(&chip->open_mutex); if (str->opened) { mutex_unlock(&chip->open_mutex); return -EBUSY; } str->substream = substream; str->master = NULL; str->opened = 1; runtime->hw = lola_pcm_hw; runtime->hw.channels_max = pcm->num_streams - str->index; if (chip->sample_rate) { /* sample rate is locked */ runtime->hw.rate_min = chip->sample_rate; runtime->hw.rate_max = chip->sample_rate; } else { runtime->hw.rate_min = chip->sample_rate_min; runtime->hw.rate_max = chip->sample_rate_max; } chip->ref_count_rate++; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, chip->granularity); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, chip->granularity); mutex_unlock(&chip->open_mutex); return 0; }
/* sync for FIFO ready/empty for all linked streams; * clear paused flag when FIFO gets ready again */ static int lola_sync_wait_for_fifo(struct lola *chip, struct snd_pcm_substream *substream, bool ready) { unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; unsigned long end_time = jiffies + msecs_to_jiffies(200); struct snd_pcm_substream *s; int pending = 0; while (time_before(jiffies, end_time)) { pending = 0; snd_pcm_group_for_each_entry(s, substream) { struct lola_stream *str; if (s->pcm->card != substream->pcm->card) continue; str = lola_get_stream(s); if (str->prepared && str->paused) { unsigned int reg; reg = lola_dsd_read(chip, str->dsd, STS); if ((reg & LOLA_DSD_STS_FIFORDY) != val) { pending = str->dsd + 1; break; } if (ready) str->paused = 0; } } if (!pending) return 0; msleep(1); } printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1); return -EIO; }
static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_stream *str = lola_get_stream(substream); unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB); if (pos >= str->bufsize) pos = 0; return bytes_to_frames(substream->runtime, pos); }
static int lola_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct lola_stream *str = lola_get_stream(substream); str->bufsize = 0; str->period_bytes = 0; str->format_verb = 0; return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); }
static int lola_pcm_hw_free(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_pcm *pcm = lola_get_pcm(substream); struct lola_stream *str = lola_get_stream(substream); mutex_lock(&chip->open_mutex); lola_stream_reset(chip, str); lola_cleanup_slave_streams(pcm, str); mutex_unlock(&chip->open_mutex); return snd_pcm_lib_free_pages(substream); }
static int lola_pcm_prepare(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_pcm *pcm = lola_get_pcm(substream); struct lola_stream *str = lola_get_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int bufsize, period_bytes, format_verb; int i, err; mutex_lock(&chip->open_mutex); lola_stream_reset(chip, str); lola_cleanup_slave_streams(pcm, str); if (str->index + runtime->channels > pcm->num_streams) { mutex_unlock(&chip->open_mutex); return -EINVAL; } for (i = 1; i < runtime->channels; i++) { str[i].master = str; str[i].opened = 1; } mutex_unlock(&chip->open_mutex); bufsize = snd_pcm_lib_buffer_bytes(substream); period_bytes = snd_pcm_lib_period_bytes(substream); format_verb = lola_get_format_verb(substream); str->bufsize = bufsize; str->period_bytes = period_bytes; str->format_verb = format_verb; err = lola_setup_periods(chip, pcm, substream, str); if (err < 0) return err; err = lola_set_sample_rate(chip, runtime->rate); if (err < 0) return err; chip->sample_rate = runtime->rate; /* sample rate gets locked */ err = lola_set_stream_config(chip, str, runtime->channels); if (err < 0) return err; err = lola_setup_controller(chip, pcm, str); if (err < 0) { lola_stream_reset(chip, str); return err; } return 0; }
static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_stream *str; struct snd_pcm_substream *s; unsigned int start; unsigned int tstamp; bool sync_streams; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = 0; break; default: return -EINVAL; } /* * sample correct synchronization is only needed starting several * streams. On stop or if only one stream do as quick as possible */ sync_streams = (start && snd_pcm_stream_linked(substream)); tstamp = lola_get_tstamp(chip, !sync_streams); spin_lock(&chip->reg_lock); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; str = lola_get_stream(s); if (start) lola_stream_start(chip, str, tstamp); else lola_stream_stop(chip, str, tstamp); str->running = start; str->paused = !start; snd_pcm_trigger_done(s, substream); } spin_unlock(&chip->reg_lock); return 0; }
static int lola_pcm_close(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_stream *str = lola_get_stream(substream); mutex_lock(&chip->open_mutex); if (str->substream == substream) { str->substream = NULL; str->opened = 0; } if (--chip->ref_count_rate == 0) { /* release sample rate */ chip->sample_rate = 0; } mutex_unlock(&chip->open_mutex); return 0; }
/* finish pause - prepare for a new resume */ static void lola_sync_pause(struct lola *chip, struct snd_pcm_substream *substream) { struct snd_pcm_substream *s; lola_sync_wait_for_fifo(chip, substream, false); snd_pcm_group_for_each_entry(s, substream) { struct lola_stream *str; if (s->pcm->card != substream->pcm->card) continue; str = lola_get_stream(s); if (str->paused && str->prepared) lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN | LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); } lola_sync_wait_for_fifo(chip, substream, true); }
static int lola_pcm_close(struct snd_pcm_substream *substream) { struct lola *chip = snd_pcm_substream_chip(substream); struct lola_stream *str = lola_get_stream(substream); mutex_l