brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · 360ebf3 Raw
379 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * bebob_pcm.c - a part of driver for BeBoB based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7 8#include "./bebob.h"9 10static int11hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)12{13	struct snd_bebob_stream_formation *formations = rule->private;14	struct snd_interval *r =15		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);16	const struct snd_interval *c =17		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);18	struct snd_interval t = {19		.min = UINT_MAX, .max = 0, .integer = 120	};21	unsigned int i;22 23	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {24		/* entry is invalid */25		if (formations[i].pcm == 0)26			continue;27 28		if (!snd_interval_test(c, formations[i].pcm))29			continue;30 31		t.min = min(t.min, snd_bebob_rate_table[i]);32		t.max = max(t.max, snd_bebob_rate_table[i]);33 34	}35	return snd_interval_refine(r, &t);36}37 38static int39hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)40{41	struct snd_bebob_stream_formation *formations = rule->private;42	struct snd_interval *c =43		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);44	const struct snd_interval *r =45		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);46	struct snd_interval t = {47		.min = UINT_MAX, .max = 0, .integer = 148	};49 50	unsigned int i;51 52	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {53		/* entry is invalid */54		if (formations[i].pcm == 0)55			continue;56 57		if (!snd_interval_test(r, snd_bebob_rate_table[i]))58			continue;59 60		t.min = min(t.min, formations[i].pcm);61		t.max = max(t.max, formations[i].pcm);62	}63 64	return snd_interval_refine(c, &t);65}66 67static void68limit_channels_and_rates(struct snd_pcm_hardware *hw,69			 struct snd_bebob_stream_formation *formations)70{71	unsigned int i;72 73	hw->channels_min = UINT_MAX;74	hw->channels_max = 0;75 76	hw->rate_min = UINT_MAX;77	hw->rate_max = 0;78	hw->rates = 0;79 80	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {81		/* entry has no PCM channels */82		if (formations[i].pcm == 0)83			continue;84 85		hw->channels_min = min(hw->channels_min, formations[i].pcm);86		hw->channels_max = max(hw->channels_max, formations[i].pcm);87 88		hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);89		hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);90		hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);91	}92}93 94static int95pcm_init_hw_params(struct snd_bebob *bebob,96		   struct snd_pcm_substream *substream)97{98	struct snd_pcm_runtime *runtime = substream->runtime;99	struct amdtp_stream *s;100	struct snd_bebob_stream_formation *formations;101	int err;102 103	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {104		runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;105		s = &bebob->tx_stream;106		formations = bebob->tx_stream_formations;107	} else {108		runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;109		s = &bebob->rx_stream;110		formations = bebob->rx_stream_formations;111	}112 113	limit_channels_and_rates(&runtime->hw, formations);114 115	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,116				  hw_rule_channels, formations,117				  SNDRV_PCM_HW_PARAM_RATE, -1);118	if (err < 0)119		goto end;120 121	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,122				  hw_rule_rate, formations,123				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);124	if (err < 0)125		goto end;126 127	err = amdtp_am824_add_pcm_hw_constraints(s, runtime);128end:129	return err;130}131 132static int pcm_open(struct snd_pcm_substream *substream)133{134	struct snd_bebob *bebob = substream->private_data;135	const struct snd_bebob_rate_spec *spec = bebob->spec->rate;136	struct amdtp_domain *d = &bebob->domain;137	enum snd_bebob_clock_type src;138	int err;139 140	err = snd_bebob_stream_lock_try(bebob);141	if (err < 0)142		return err;143 144	err = pcm_init_hw_params(bebob, substream);145	if (err < 0)146		goto err_locked;147 148	err = snd_bebob_stream_get_clock_src(bebob, &src);149	if (err < 0)150		goto err_locked;151 152	mutex_lock(&bebob->mutex);153 154	// When source of clock is not internal or any stream is reserved for155	// transmission of PCM frames, the available sampling rate is limited156	// at current one.157	if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||158	    (bebob->substreams_counter > 0 && d->events_per_period > 0)) {159		unsigned int frames_per_period = d->events_per_period;160		unsigned int frames_per_buffer = d->events_per_buffer;161		unsigned int sampling_rate;162 163		err = spec->get(bebob, &sampling_rate);164		if (err < 0) {165			mutex_unlock(&bebob->mutex);166			dev_err(&bebob->unit->device,167				"fail to get sampling rate: %d\n", err);168			goto err_locked;169		}170 171		substream->runtime->hw.rate_min = sampling_rate;172		substream->runtime->hw.rate_max = sampling_rate;173 174		if (frames_per_period > 0) {175			err = snd_pcm_hw_constraint_minmax(substream->runtime,176					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,177					frames_per_period, frames_per_period);178			if (err < 0) {179				mutex_unlock(&bebob->mutex);180				goto err_locked;181			}182 183			err = snd_pcm_hw_constraint_minmax(substream->runtime,184					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,185					frames_per_buffer, frames_per_buffer);186			if (err < 0) {187				mutex_unlock(&bebob->mutex);188				goto err_locked;189			}190		}191	}192 193	mutex_unlock(&bebob->mutex);194 195	snd_pcm_set_sync(substream);196 197	return 0;198err_locked:199	snd_bebob_stream_lock_release(bebob);200	return err;201}202 203static int204pcm_close(struct snd_pcm_substream *substream)205{206	struct snd_bebob *bebob = substream->private_data;207	snd_bebob_stream_lock_release(bebob);208	return 0;209}210 211static int pcm_hw_params(struct snd_pcm_substream *substream,212			 struct snd_pcm_hw_params *hw_params)213{214	struct snd_bebob *bebob = substream->private_data;215	int err = 0;216 217	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {218		unsigned int rate = params_rate(hw_params);219		unsigned int frames_per_period = params_period_size(hw_params);220		unsigned int frames_per_buffer = params_buffer_size(hw_params);221 222		mutex_lock(&bebob->mutex);223		err = snd_bebob_stream_reserve_duplex(bebob, rate,224					frames_per_period, frames_per_buffer);225		if (err >= 0)226			++bebob->substreams_counter;227		mutex_unlock(&bebob->mutex);228	}229 230	return err;231}232 233static int pcm_hw_free(struct snd_pcm_substream *substream)234{235	struct snd_bebob *bebob = substream->private_data;236 237	mutex_lock(&bebob->mutex);238 239	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)240		bebob->substreams_counter--;241 242	snd_bebob_stream_stop_duplex(bebob);243 244	mutex_unlock(&bebob->mutex);245 246	return 0;247}248 249static int250pcm_capture_prepare(struct snd_pcm_substream *substream)251{252	struct snd_bebob *bebob = substream->private_data;253	int err;254 255	err = snd_bebob_stream_start_duplex(bebob);256	if (err >= 0)257		amdtp_stream_pcm_prepare(&bebob->tx_stream);258 259	return err;260}261static int262pcm_playback_prepare(struct snd_pcm_substream *substream)263{264	struct snd_bebob *bebob = substream->private_data;265	int err;266 267	err = snd_bebob_stream_start_duplex(bebob);268	if (err >= 0)269		amdtp_stream_pcm_prepare(&bebob->rx_stream);270 271	return err;272}273 274static int275pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)276{277	struct snd_bebob *bebob = substream->private_data;278 279	switch (cmd) {280	case SNDRV_PCM_TRIGGER_START:281		amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);282		break;283	case SNDRV_PCM_TRIGGER_STOP:284		amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);285		break;286	default:287		return -EINVAL;288	}289 290	return 0;291}292static int293pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)294{295	struct snd_bebob *bebob = substream->private_data;296 297	switch (cmd) {298	case SNDRV_PCM_TRIGGER_START:299		amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);300		break;301	case SNDRV_PCM_TRIGGER_STOP:302		amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);303		break;304	default:305		return -EINVAL;306	}307 308	return 0;309}310 311static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)312{313	struct snd_bebob *bebob = sbstrm->private_data;314 315	return amdtp_domain_stream_pcm_pointer(&bebob->domain,316					       &bebob->tx_stream);317}318static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)319{320	struct snd_bebob *bebob = sbstrm->private_data;321 322	return amdtp_domain_stream_pcm_pointer(&bebob->domain,323					       &bebob->rx_stream);324}325 326static int pcm_capture_ack(struct snd_pcm_substream *substream)327{328	struct snd_bebob *bebob = substream->private_data;329 330	return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);331}332 333static int pcm_playback_ack(struct snd_pcm_substream *substream)334{335	struct snd_bebob *bebob = substream->private_data;336 337	return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);338}339 340int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)341{342	static const struct snd_pcm_ops capture_ops = {343		.open		= pcm_open,344		.close		= pcm_close,345		.hw_params	= pcm_hw_params,346		.hw_free	= pcm_hw_free,347		.prepare	= pcm_capture_prepare,348		.trigger	= pcm_capture_trigger,349		.pointer	= pcm_capture_pointer,350		.ack		= pcm_capture_ack,351	};352	static const struct snd_pcm_ops playback_ops = {353		.open		= pcm_open,354		.close		= pcm_close,355		.hw_params	= pcm_hw_params,356		.hw_free	= pcm_hw_free,357		.prepare	= pcm_playback_prepare,358		.trigger	= pcm_playback_trigger,359		.pointer	= pcm_playback_pointer,360		.ack		= pcm_playback_ack,361	};362	struct snd_pcm *pcm;363	int err;364 365	err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);366	if (err < 0)367		goto end;368 369	pcm->private_data = bebob;370	pcm->nonatomic = true;371	snprintf(pcm->name, sizeof(pcm->name),372		 "%s PCM", bebob->card->shortname);373	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);374	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);375	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);376end:377	return err;378}379