static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (channels->min > 2) { static const struct snd_interval single_rates = { .min = 1, .max = 48000, }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); return snd_interval_refine(rate, &single_rates); } return 0; } static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (rate->min > 48000) { static const struct snd_interval double_rate_channels = { .min = 2, .max = 2, }; struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); return snd_interval_refine(channels, &double_rate_channels); } return 0; } /** * snd_ac97_pcm_double_rate_rules - set double rate constraints * @runtime: the runtime of the ac97 front playback pcm * * Installs the hardware constraint rules to prevent using double rates and * more than two channels at the same time. * * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) { int err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, double_rate_hw_constraint_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, double_rate_hw_constraint_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); return err; } EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
static int snd_pcm_period_size_rule(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) { snd_interval_t *periodsize; snd_interval_t *channels; snd_interval_t newperiodsize; int refine = 0; DEBUG_PRINT(("ALSA Core : >>> snd_pcm_period_size_rule \n")); periodsize = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); newperiodsize = *periodsize; channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); DEBUG_PRINT(("ALSA Core : snd_pcm_period_size_rule Period size min=%d PS max =%d, channel min=%d channel max =%d PCMP_MAX_SAMPLES =%d \n",periodsize->min,newperiodsize.max,channels->max, channels->min,PCMP_MAX_SAMPLES)); if((periodsize->max * channels->min) > PCMP_MAX_SAMPLES) { newperiodsize.max = PCMP_MAX_SAMPLES / channels->min; refine = 1; } if((periodsize->min * channels->min) > PCMP_MAX_SAMPLES) { newperiodsize.min = PCMP_MAX_SAMPLES / channels->min; refine = 1; } if(refine) { DEBUG_PRINT(("snd_pcm_period_size_rule: refining (%d,%d) to (%d,%d)\n",periodsize->min,periodsize->max,newperiodsize.min,newperiodsize.max)); return snd_interval_refine(periodsize, &newperiodsize); } DEBUG_PRINT(("ALSA Core : <<< snd_pcm_period_size_rule \n")); return 0; }
/* x = a * k / b */ static int rule_mulkdiv(snd_pcm_hw_params_t *params, int x, int a, int k, int b) { snd_interval_t t; snd_interval_mulkdiv(hw_param_interval(params, a), k, hw_param_interval(params, b), &t); return snd_interval_refine(hw_param_interval(params, x), &t); }
static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (channels->min > 2) { static const struct snd_interval single_rates = { .min = 1, .max = 48000, }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); return snd_interval_refine(rate, &single_rates); } return 0; } static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (rate->min > 48000) { static const struct snd_interval double_rate_channels = { .min = 2, .max = 2, }; struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); return snd_interval_refine(channels, &double_rate_channels); } return 0; } int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) { int err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, double_rate_hw_constraint_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, double_rate_hw_constraint_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); return err; } EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) { struct snd_interval t; t.empty = 0; t.min = t.max = val; t.openmin = t.openmax = 0; t.integer = 1; return snd_interval_refine(i, &t); }
static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) { snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { snd_interval_t t = { .min = 1, .max = 1 }; return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); } return 0; }
static int rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_hardware *hw = rule->private; struct snd_interval t; t.min = hw->channels_min; t.max = hw->channels_max; t.openmin = t.openmax = 0; t.integer = 0; return snd_interval_refine(hw_param_interval(params, rule->var), &t); }
static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { int changed; if (hw_is_mask(var)) { struct snd_mask *m = hw_param_mask(params, var); if (val == 0 && dir < 0) { changed = -EINVAL; snd_mask_none(m); } else { if (dir > 0) val++; else if (dir < 0) val--; changed = snd_mask_refine_set( hw_param_mask(params, var), val); } } else if (hw_is_interval(var)) { struct snd_interval *i = hw_param_interval(params, var); if (val == 0 && dir < 0) { changed = -EINVAL; snd_interval_none(i); } else if (dir == 0) changed = snd_interval_refine_set(i, val); else { struct snd_interval t; t.openmin = 1; t.openmax = 1; t.empty = 0; t.integer = 0; if (dir < 0) { t.min = val - 1; t.max = val; } else { t.min = val; t.max = val+1; } changed = snd_interval_refine(i, &t); } } else { return -EINVAL; } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed; }
static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *buffer_size = hw_param_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct omap_mcbsp_data *mcbsp_data = rule->private; struct snd_interval frames; int size; snd_interval_any(&frames); size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id); frames.min = size / channels->min; frames.integer = 1; return snd_interval_refine(buffer_size, &frames); }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; struct snd_bebob_stream_formation *formations; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } limit_channels_and_rates(&runtime->hw, formations); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, formations, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) { hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ hw->periods_max = UINT_MAX; hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ /* Just to prevent from allocating much pages. */ hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, mode; for (i = 0; i < ARRAY_SIZE(freq_table); i++) { mode = get_multiplier_mode_with_index(i); if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, freq_table[i]); t.max = max(t.max, freq_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, mode; for (i = 0; i < ARRAY_SIZE(freq_table); i++) { mode = get_multiplier_mode_with_index(i); if (!snd_interval_test(r, freq_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels) { unsigned int i, mode; hw->channels_min = UINT_MAX; hw->channels_max = 0; for (i = 0; i < ARRAY_SIZE(freq_table); i++) { mode = get_multiplier_mode_with_index(i); if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, pcm_channels[mode]); hw->channels_max = max(hw->channels_max, pcm_channels[mode]); } }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, amdtp_rate_table[i]); t.max = max(t.max, amdtp_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(r, amdtp_rate_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { unsigned int rate, channels; int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, channels); hw->channels_max = max(hw->channels_max, channels); rate = amdtp_rate_table[i]; hw->rates |= snd_pcm_rate_to_rate_bit(rate); hw->rate_min = min(hw->rate_min, rate); hw->rate_max = max(hw->rate_max, rate); } } static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; const unsigned int *pcm_channels; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; pcm_channels = ff->spec->pcm_capture_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->rx_stream; pcm_channels = ff->spec->pcm_playback_channels; } limit_channels_and_rates(&runtime->hw, pcm_channels); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_ff_add_pcm_hw_constraints(s, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; unsigned int rate; enum snd_ff_clock_src src; int i, err; err = snd_ff_stream_lock_try(ff); if (err < 0) return err; err = pcm_init_hw_params(ff, substream); if (err < 0) goto release_lock; err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } /* * The unit is configured at sampling frequency which packet * streaming engine can't support. */ if (i >= CIP_SFC_COUNT) { err = -EIO; goto release_lock; } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { if (amdtp_stream_pcm_running(&ff->rx_stream) || amdtp_stream_pcm_running(&ff->tx_stream)) { rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } } snd_pcm_set_sync(substream); return 0; release_lock: snd_ff_stream_lock_release(ff); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; snd_ff_stream_lock_release(ff); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->tx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->rx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) { static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = ff; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
static int firewave_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static const struct snd_interval all_channels = { .min = 6, .max = 6 }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* 32/44.1 kHz work only with all six channels */ if (snd_interval_max(rate) < 48000) return snd_interval_refine(channels, &all_channels); return 0; } static int firewave_constraints(struct snd_pcm_runtime *runtime) { static unsigned int channels_list[] = { 2, 6 }; static struct snd_pcm_hw_constraint_list channels_list_constraint = { .count = 2, .list = channels_list, }; int err; runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000; runtime->hw.channels_max = 6; err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &channels_list_constraint); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, firewave_rate_constraint, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, firewave_channels_constraint, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; return 0; } static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) { runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; return 0; } static int fwspk_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, .periods_min = 1, .periods_max = UINT_MAX, }; struct fwspk *fwspk = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; runtime->hw = hardware; err = fwspk->device_info->pcm_constraints(runtime); if (err < 0) return err; err = snd_pcm_limit_hw_rates(runtime); if (err < 0) return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; return 0; } static int fwspk_close(struct snd_pcm_substream *substream) { return 0; } static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } } static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) { u8 *buf; int err; buf = kmalloc(8, GFP_KERNEL); if (!buf) return -ENOMEM; buf[0] = 0x00; /* AV/C, CONTROL */ buf[1] = 0xff; /* unit */ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ buf[3] = 0x00; /* plug 0 */ buf[4] = 0x90; /* format: audio */ buf[5] = 0x00 | sfc; /* AM824, frequency */ buf[6] = 0xff; /* SYT (not used) */ buf[7] = 0xff; err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); if (err < 0) goto error; if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { dev_err(&fwspk->unit->device, "failed to set sample rate\n"); err = -EIO; goto error; } err = 0; error: kfree(buf); return err; } static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) goto error; amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) goto err_buffer; return 0; err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: return err; } static int fwspk_hw_free(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int fwspk_prepare(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); if (!fwspk->stream_running) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; err = amdtp_out_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) goto err_connection; fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); amdtp_out_stream_pcm_prepare(&fwspk->stream); return 0; err_connection: cmp_connection_break(&fwspk->connection); err_mutex: mutex_unlock(&fwspk->mutex); return err; } static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) { struct fwspk *fwspk = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { case SNDRV_PCM_TRIGGER_START: pcm = substream; break; case SNDRV_PCM_TRIGGER_STOP: pcm = NULL; break; default: return -EINVAL; } amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; return amdtp_out_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) { static struct snd_pcm_ops ops = { .open = fwspk_open, .close = fwspk_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = fwspk_hw_params, .hw_free = fwspk_hw_free, .prepare = fwspk_prepare, .trigger = fwspk_trigger, .pointer = fwspk_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); if (err < 0) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; fwspk->pcm->ops = &ops; return 0; } enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, CTL_MAX = 0x03, CTL_CURRENT = 0x10, }; static int fwspk_mute_command(struct fwspk *fwspk, bool *value, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(11, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ buf[5] = 0x10; /* control attribute: current */ buf[6] = 0x02; /* selector length */ buf[7] = 0x00; /* audio channel number */ buf[8] = 0x01; /* control selector: mute */ buf[9] = 0x01; /* control data length */ if (action == CTL_READ) buf[10] = 0xff; else buf[10] = *value ? 0x70 : 0x60; err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); if (err < 0) goto error; if (err < 11) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "mute command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = buf[10] == 0x70; err = 0; error: kfree(buf); return err; }
static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_substream *substream = rule->private; struct snd_dice *dice = substream->private_data; const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, rate, mode, *pcm_channels; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) pcm_channels = dice->tx_channels; else pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; rates.min = min(rates.min, rate); rates.max = max(rates.max, rate); } return snd_interval_refine(r, &rates); } static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_substream *substream = rule->private; struct snd_dice *dice = substream->private_data; const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i, rate, mode, *pcm_channels; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) pcm_channels = dice->tx_channels; else pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(r, rate)) continue; channels.min = min(channels.min, pcm_channels[mode]); channels.max = max(channels.max, pcm_channels[mode]); } return snd_interval_refine(c, &channels); } static void limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, unsigned int *pcm_channels) { struct snd_pcm_hardware *hw = &runtime->hw; unsigned int i, rate, mode; hw->channels_min = UINT_MAX; hw->channels_max = 0; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; hw->rates |= snd_pcm_rate_to_rate_bit(rate); if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, pcm_channels[mode]); hw->channels_max = max(hw->channels_max, pcm_channels[mode]); } snd_pcm_limit_hw_rates(runtime); } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) { hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ hw->periods_max = UINT_MAX; hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ /* Just to prevent from allocating much pages. */ hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; } static int init_hw_info(struct snd_dice *dice, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; unsigned int *pcm_channels; int err; hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_BLOCK_TRANSFER; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AMDTP_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; pcm_channels = dice->tx_channels; } else { hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; pcm_channels = dice->rx_channels; } limit_channels_and_rates(dice, runtime, pcm_channels); limit_period_and_buffer(hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, dice_rate_constraint, substream, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, dice_channels_constraint, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; unsigned int source, rate; bool internal; int err; err = snd_dice_stream_lock_try(dice); if (err < 0) goto end; err = init_hw_info(dice, substream); if (err < 0) goto err_locked; err = snd_dice_transaction_get_clock_source(dice, &source); if (err < 0) goto err_locked; switch (source) { case CLOCK_SOURCE_AES1: case CLOCK_SOURCE_AES2: case CLOCK_SOURCE_AES3: case CLOCK_SOURCE_AES4: case CLOCK_SOURCE_AES_ANY: case CLOCK_SOURCE_ADAT: case CLOCK_SOURCE_TDIF: case CLOCK_SOURCE_WC: internal = false; break; default: internal = true; break; } /* * When source of clock is not internal or any PCM streams are running, * available sampling rate is limited at current sampling rate. */ if (!internal || amdtp_stream_pcm_running(&dice->tx_stream) || amdtp_stream_pcm_running(&dice->rx_stream)) { err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_dice_stream_lock_release(dice); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; snd_dice_stream_lock_release(dice); return 0; } static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } amdtp_stream_set_pcm_format(&dice->tx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&dice->tx_stream); return 0; } static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&dice->rx_stream); return err; } static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dice->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dice->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; return amdtp_stream_pcm_pointer(&dice->tx_stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; return amdtp_stream_pcm_pointer(&dice->rx_stream); } int snd_dice_create_pcm(struct snd_dice *dice) { static struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = capture_hw_params, .hw_free = capture_hw_free, .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; static struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = playback_hw_params, .hw_free = playback_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; unsigned int i, capture, playback; int err; capture = playback = 0; for (i = 0; i < 3; i++) { if (dice->tx_channels[i] > 0) capture = 1; if (dice->rx_channels[i] > 0) playback = 1; } err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); if (playback > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(c, snd_dg00x_stream_pcm_channels[i])) continue; t.min = min(t.min, snd_dg00x_stream_rates[i]); t.max = max(t.max, snd_dg00x_stream_rates[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(r, snd_dg00x_stream_rates[i])) continue; t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); } return snd_interval_refine(c, &t); } static int pcm_init_hw_params(struct snd_dg00x *dg00x, struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 44100, .rate_max = 96000, .channels_min = 10, .channels_max = 18, .period_bytes_min = 4 * 18, .period_bytes_max = 4 * 18 * 2048, .buffer_bytes_max = 4 * 18 * 2048 * 2, .periods_min = 2, .periods_max = UINT_MAX, }; struct amdtp_stream *s; int err; substream->runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->tx_stream; } else { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; s = &dg00x->rx_stream; } err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; enum snd_dg00x_clock clock; bool detect; unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) goto end; err = pcm_init_hw_params(dg00x, substream); if (err < 0) goto err_locked; /* Check current clock source. */ err = snd_dg00x_stream_get_clock(dg00x, &clock); if (err < 0) goto err_locked; if (clock != SND_DG00X_CLOCK_INTERNAL) { err = snd_dg00x_stream_check_external_clock(dg00x, &detect); if (err < 0) goto err_locked; if (!detect) { err = -EBUSY; goto err_locked; } } if ((clock != SND_DG00X_CLOCK_INTERNAL) || amdtp_stream_pcm_running(&dg00x->rx_stream) || amdtp_stream_pcm_running(&dg00x->tx_stream)) { err = snd_dg00x_stream_get_external_rate(dg00x, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_dg00x_stream_lock_release(dg00x); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; snd_dg00x_stream_lock_release(dg00x); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dg00x->tx_stream); mutex_unlock(&dg00x->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) { amdtp_stream_pcm_prepare(&dg00x->rx_stream); amdtp_dot_reset(&dg00x->rx_stream); } mutex_unlock(&dg00x->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, struct snd_bebob *bebob, struct snd_bebob_stream_formation *formations) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule, struct snd_bebob *bebob, struct snd_bebob_stream_formation *formations) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static inline int hw_rule_capture_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_rate(params, rule, bebob, bebob->tx_stream_formations); } static inline int hw_rule_playback_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_rate(params, rule, bebob, bebob->rx_stream_formations); } static inline int hw_rule_capture_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_channels(params, rule, bebob, bebob->tx_stream_formations); } static inline int hw_rule_playback_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob *bebob = rule->private; return hw_rule_channels(params, rule, bebob, bebob->rx_stream_formations); } static void prepare_channels(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); } return; } static void prepare_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } return; }
/* * When the bit clock is input, limit the maximum rate according to the * Serial Clock Ratio Considerations section from the SSC documentation: * * The Transmitter and the Receiver can be programmed to operate * with the clock signals provided on either the TK or RK pins. * This allows the SSC to support many slave-mode data transfers. * In this case, the maximum clock speed allowed on the RK pin is: * - Peripheral clock divided by 2 if Receiver Frame Synchro is input * - Peripheral clock divided by 3 if Receiver Frame Synchro is output * In addition, the maximum clock speed allowed on the TK pin is: * - Peripheral clock divided by 6 if Transmit Frame Synchro is input * - Peripheral clock divided by 2 if Transmit Frame Synchro is output * * When the bit clock is output, limit the rate according to the * SSC divider restrictions. */ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct atmel_ssc_info *ssc_p = rule->private; struct ssc_device *ssc = ssc_p->ssc; struct snd_interval *i = hw_param_interval(params, rule->var); struct snd_interval t; struct snd_ratnum r = { .den_min = 1, .den_max = 4095, .den_step = 1, }; unsigned int num = 0, den = 0; int frame_size; int mck_div = 2; int ret; frame_size = snd_soc_params_to_frame_size(params); if (frame_size < 0) return frame_size; switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) && ssc->clk_from_rk_pin) /* Receiver Frame Synchro (i.e. capture) * is output (format is _CFS) and the RK pin * is used for input (format is _CBM_). */ mck_div = 3; break; case SND_SOC_DAIFMT_CBM_CFM: if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) && !ssc->clk_from_rk_pin) /* Transmit Frame Synchro (i.e. playback) * is input (format is _CFM) and the TK pin * is used for input (format _CBM_ but not * using the RK pin). */ mck_div = 6; break; } switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: r.num = ssc_p->mck_rate / mck_div / frame_size; ret = snd_interval_ratnum(i, 1, &r, &num, &den); if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { params->rate_num = num; params->rate_den = den; } break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFM: t.min = 8000; t.max = ssc_p->mck_rate / mck_div / frame_size; t.openmin = t.openmax = 0; t.integer = 0; ret = snd_interval_refine(i, &t); break; default: ret = -EINVAL; break; } return ret; } /*-------------------------------------------------------------------------*\ * DAI functions \*-------------------------------------------------------------------------*/ /* * Startup. Only that one substream allowed in each direction. */ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; int ret; pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n", ssc_readl(ssc_p->ssc->regs, SR)); /* Enable PMC peripheral clock for this SSC */ pr_debug("atmel_ssc_dai: Starting clock\n"); clk_enable(ssc_p->ssc->clk); ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); /* Reset the SSC unless initialized to keep it in a clean state */ if (!ssc_p->initialized) ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dir = 0; dir_mask = SSC_DIR_MASK_PLAYBACK; } else { dir = 1; dir_mask = SSC_DIR_MASK_CAPTURE; } ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, atmel_ssc_hw_rule_rate, ssc_p, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret < 0) { dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); return ret; } dma_params = &ssc_dma_params[pdev->id][dir]; dma_params->ssc = ssc_p->ssc; dma_params->substream = substream; ssc_p->dma_params[dir] = dma_params; snd_soc_dai_set_dma_data(dai, substream, dma_params); spin_lock_irq(&ssc_p->lock); if (ssc_p->dir_mask & dir_mask) { spin_unlock_irq(&ssc_p->lock); return -EBUSY; } ssc_p->dir_mask |= dir_mask; spin_unlock_irq(&ssc_p->lock); return 0; } /* * Shutdown. Clear DMA parameters and shutdown the SSC if there * are no other substreams open. */ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; if (dma_params != NULL) { dma_params->ssc = NULL; dma_params->substream = NULL; ssc_p->dma_params[dir] = NULL; } dir_mask = 1 << dir; spin_lock_irq(&ssc_p->lock); ssc_p->dir_mask &= ~dir_mask; if (!ssc_p->dir_mask) { if (ssc_p->initialized) { free_irq(ssc_p->ssc->irq, ssc_p); ssc_p->initialized = 0; } /* Reset the SSC */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); /* Clear the SSC dividers */ ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; } spin_unlock_irq(&ssc_p->lock); /* Shutdown the SSC clock. */ pr_debug("atmel_ssc_dai: Stopping clock\n"); clk_disable(ssc_p->ssc->clk); } /* * Record the DAI format for use in hw_params(). */ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct platform_device *pdev = to_platform_device(cpu_dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; ssc_p->daifmt = fmt; return 0; } /* * Record SSC clock dividers for use in hw_params(). */ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { struct platform_device *pdev = to_platform_device(cpu_dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; switch (div_id) { case ATMEL_SSC_CMR_DIV: /* * The same master clock divider is used for both * transmit and receive, so if a value has already * been set, it must match this value. */ if (ssc_p->dir_mask != (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) ssc_p->cmr_div = div; else if (ssc_p->cmr_div == 0) ssc_p->cmr_div = div; else if (div != ssc_p->cmr_div) return -EBUSY; break; case ATMEL_SSC_TCMR_PERIOD: ssc_p->tcmr_period = div; break; case ATMEL_SSC_RCMR_PERIOD: ssc_p->rcmr_period = div; break; default: return -EINVAL; } return 0; } /* * Configure the SSC. */ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); int id = pdev->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; int ret; int fslen, fslen_ext; /* * Currently, there is only one set of dma params for * each direction. If more are added, this code will * have to be changed to select the proper set. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; channels = params_channels(params); /* * Determine sample size in bits and the PDC increment. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: bits = 8; dma_params->pdc_xfer_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: bits = 16; dma_params->pdc_xfer_size = 2; break; case SNDRV_PCM_FORMAT_S24_LE: bits = 24; dma_params->pdc_xfer_size = 4; break; case SNDRV_PCM_FORMAT_S32_LE: bits = 32; dma_params->pdc_xfer_size = 4; break; default: printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format"); return -EINVAL; } /* * Compute SSC register settings. */ switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: /* * I2S format, SSC provides BCLK and LRC clocks. * * The SSC transmit and receive clocks are generated * from the MCK divider, and the BCLK signal * is output on the SSC TK line. */ if (bits > 16 && !ssc->pdata->has_fslen_ext) { dev_err(dai->dev, "sample size %d is too large for SSC device\n", bits); return -EINVAL; } fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(RFMR_FSLEN, fslen) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(TFMR_FSLEN, fslen) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: /* I2S format, CODEC supplies BCLK and LRC clocks. */ rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, 0) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS: /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */ if (bits > 16 && !ssc->pdata->has_fslen_ext) { dev_err(dai->dev, "sample size %d is too large for SSC device\n", bits); return -EINVAL; } fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(RFMR_FSLEN, fslen) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(TFMR_FSLEN, fslen) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: /* * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. * * The SSC transmit and receive clocks are generated from the * MCK divider, and the BCLK signal is output * on the SSC TK line. */ rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, 1) | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: /* * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks. * * Data is transferred on first BCLK after LRC pulse rising * edge.If stereo, the right channel data is contiguous with * the left channel data. */ rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, 0) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; default: printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", ssc_p->daifmt); return -EINVAL; } pr_debug("atmel_ssc_hw_params: " "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr); if (!ssc_p->initialized) { if (!ssc_p->ssc->pdata->use_dma) { ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); } ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, ssc_p->name, ssc_p); if (ret < 0) { printk(KERN_WARNING "atmel_ssc_dai: request_irq failure\n"); pr_debug("Atmel_ssc_dai: Stoping clock\n"); clk_disable(ssc_p->ssc->clk); return ret; } ssc_p->initialized = 1; } /* set SSC clock mode register */ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div); /* set receive clock mode and format */ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr); ssc_writel(ssc_p->ssc->regs, RFMR, rfmr); /* set transmit clock mode and format */ ssc_writel(ssc_p->ssc->regs, TCMR, tcmr); ssc_writel(ssc_p->ssc->regs, TFMR, tfmr); pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n"); return 0; } static int atmel_ssc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error); pr_debug("%s enabled SSC_SR=0x%08x\n", dir ? "receive" : "transmit", ssc_readl(ssc_p->ssc->regs, SR)); return 0; } static int atmel_ssc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); break; default: ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); break; } return 0; } #ifdef CONFIG_PM static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; struct platform_device *pdev = to_platform_device(cpu_dai->dev); if (!cpu_dai->active) return 0; ssc_p = &ssc_info[pdev->id]; /* Save the status register before disabling transmit and receive */ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS)); /* Save the current interrupt mask, then disable unmasked interrupts */ ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR); ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr); ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR); ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR); ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR); ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR); ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR); return 0; } static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; struct platform_device *pdev = to_platform_device(cpu_dai->dev); u32 cr; if (!cpu_dai->active) return 0; ssc_p = &ssc_info[pdev->id]; /* restore SSC register settings */ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr); ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr); ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr); ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr); /* re-enable interrupts */ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr); /* Re-enable receive and transmit as appropriate */ cr = 0; cr |= (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0; cr |= (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0; ssc_writel(ssc_p->ssc->regs, CR, cr); return 0; } #else /* CONFIG_PM */ # define atmel_ssc_suspend NULL # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, .shutdown = atmel_ssc_shutdown, .prepare = atmel_ssc_prepare, .trigger = atmel_ssc_trigger, .hw_params = atmel_ssc_hw_params, .set_fmt = atmel_ssc_set_dai_fmt, .set_clkdiv = atmel_ssc_set_dai_clkdiv, }; static struct snd_soc_dai_driver atmel_ssc_dai = { .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops,
static int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { struct snd_interval t = { .min = 1, .max = 1 }; return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); } return 0; } static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream) { unsigned long flags; struct snd_sb *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int mixreg, rate, size, count; unsigned char format; unsigned char stereo = runtime->channels > 1; int dma; rate = runtime->rate; switch (chip->hardware) { case SB_HW_JAZZ16: if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) { if (chip->mode & SB_MODE_CAPTURE_16) return -EBUSY; else chip->mode |= SB_MODE_PLAYBACK_16; } chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; break; case SB_HW_PRO: if (runtime->channels > 1) { if (snd_BUG_ON(rate != SB8_RATE(11025) && rate != SB8_RATE(22050))) return -EINVAL; chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; break; } /* fallthru */ case SB_HW_201: if (rate > 23000) { chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; break; } /* fallthru */ case SB_HW_20: chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; break; case SB_HW_10: chip->playback_format = SB_DSP_OUTPUT; break; default: return -EINVAL; } if (chip->mode & SB_MODE_PLAYBACK_16) { format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT; dma = chip->dma16; } else { format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT; chip->mode |= SB_MODE_PLAYBACK_8; dma = chip->dma8; } size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); spin_lock_irqsave(&chip->reg_lock, flags); snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); if (chip->hardware == SB_HW_JAZZ16) snd_sbdsp_command(chip, format); else if (stereo) { /* set playback stereo mode */ spin_lock(&chip->mixer_lock); mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); spin_unlock(&chip->mixer_lock); /* Soundblaster hardware programming reference guide, 3-23 */ snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); runtime->dma_area[0] = 0x80; snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE); /* force interrupt */ snd_sbdsp_command(chip, SB_DSP_OUTPUT); snd_sbdsp_command(chip, 0); snd_sbdsp_command(chip, 0); } snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); if (stereo) { snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); spin_lock(&chip->mixer_lock); /* save output filter status and turn it off */ mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); spin_unlock(&chip->mixer_lock); /* just use force_mode16 for temporary storate... */ chip->force_mode16 = mixreg; } else { snd_sbdsp_command(chip, 256 - runtime->rate_den); } if (chip->playback_format != SB_DSP_OUTPUT) { if (chip->mode & SB_MODE_PLAYBACK_16) count /= 2; count--; snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); snd_sbdsp_command(chip, count & 0xff); snd_sbdsp_command(chip, count >> 8); } spin_unlock_irqrestore(&chip->reg_lock, flags); snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); return 0; }