brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 8ca9dde Raw
452 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 */7 8#include "oxfw.h"9 10static int hw_rule_rate(struct snd_pcm_hw_params *params,11			struct snd_pcm_hw_rule *rule)12{13	u8 **formats = 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	struct snd_oxfw_stream_formation formation;22	int i, err;23 24	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {25		if (formats[i] == NULL)26			continue;27 28		err = snd_oxfw_stream_parse_format(formats[i], &formation);29		if (err < 0)30			continue;31		if (!snd_interval_test(c, formation.pcm))32			continue;33 34		t.min = min(t.min, formation.rate);35		t.max = max(t.max, formation.rate);36 37	}38	return snd_interval_refine(r, &t);39}40 41static int hw_rule_channels(struct snd_pcm_hw_params *params,42			    struct snd_pcm_hw_rule *rule)43{44	u8 **formats = rule->private;45	struct snd_interval *c =46		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);47	const struct snd_interval *r =48		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);49	struct snd_oxfw_stream_formation formation;50	int i, j, err;51	unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};52 53	count = 0;54	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {55		if (formats[i] == NULL)56			break;57 58		err = snd_oxfw_stream_parse_format(formats[i], &formation);59		if (err < 0)60			continue;61		if (!snd_interval_test(r, formation.rate))62			continue;63		if (list[count] == formation.pcm)64			continue;65 66		for (j = 0; j < ARRAY_SIZE(list); j++) {67			if (list[j] == formation.pcm)68				break;69		}70		if (j == ARRAY_SIZE(list)) {71			list[count] = formation.pcm;72			if (++count == ARRAY_SIZE(list))73				break;74		}75	}76 77	return snd_interval_list(c, count, list, 0);78}79 80static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)81{82	struct snd_oxfw_stream_formation formation;83	int i, err;84 85	hw->channels_min = UINT_MAX;86	hw->channels_max = 0;87 88	hw->rate_min = UINT_MAX;89	hw->rate_max = 0;90	hw->rates = 0;91 92	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {93		if (formats[i] == NULL)94			break;95 96		err = snd_oxfw_stream_parse_format(formats[i], &formation);97		if (err < 0)98			continue;99 100		hw->channels_min = min(hw->channels_min, formation.pcm);101		hw->channels_max = max(hw->channels_max, formation.pcm);102 103		hw->rate_min = min(hw->rate_min, formation.rate);104		hw->rate_max = max(hw->rate_max, formation.rate);105		hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);106	}107}108 109static int init_hw_params(struct snd_oxfw *oxfw,110			  struct snd_pcm_substream *substream)111{112	struct snd_pcm_runtime *runtime = substream->runtime;113	u8 **formats;114	struct amdtp_stream *stream;115	int err;116 117	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {118		runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;119		stream = &oxfw->tx_stream;120		formats = oxfw->tx_stream_formats;121	} else {122		runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;123		stream = &oxfw->rx_stream;124		formats = oxfw->rx_stream_formats;125	}126 127	limit_channels_and_rates(&runtime->hw, formats);128 129	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,130				  hw_rule_channels, formats,131				  SNDRV_PCM_HW_PARAM_RATE, -1);132	if (err < 0)133		goto end;134 135	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,136				  hw_rule_rate, formats,137				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);138	if (err < 0)139		goto end;140 141	err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);142end:143	return err;144}145 146static int limit_to_current_params(struct snd_pcm_substream *substream)147{148	struct snd_oxfw *oxfw = substream->private_data;149	struct snd_oxfw_stream_formation formation;150	enum avc_general_plug_dir dir;151	int err;152 153	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)154		dir = AVC_GENERAL_PLUG_DIR_OUT;155	else156		dir = AVC_GENERAL_PLUG_DIR_IN;157 158	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);159	if (err < 0)160		goto end;161 162	substream->runtime->hw.channels_min = formation.pcm;163	substream->runtime->hw.channels_max = formation.pcm;164	substream->runtime->hw.rate_min = formation.rate;165	substream->runtime->hw.rate_max = formation.rate;166end:167	return err;168}169 170static int pcm_open(struct snd_pcm_substream *substream)171{172	struct snd_oxfw *oxfw = substream->private_data;173	struct amdtp_domain *d = &oxfw->domain;174	int err;175 176	err = snd_oxfw_stream_lock_try(oxfw);177	if (err < 0)178		return err;179 180	err = init_hw_params(oxfw, substream);181	if (err < 0)182		goto err_locked;183 184	mutex_lock(&oxfw->mutex);185 186	// When source of clock is not internal or any stream is reserved for187	// transmission of PCM frames, the available sampling rate is limited188	// at current one.189	if (oxfw->substreams_count > 0 && d->events_per_period > 0) {190		unsigned int frames_per_period = d->events_per_period;191		unsigned int frames_per_buffer = d->events_per_buffer;192 193		err = limit_to_current_params(substream);194		if (err < 0) {195			mutex_unlock(&oxfw->mutex);196			goto err_locked;197		}198 199		if (frames_per_period > 0) {200			err = snd_pcm_hw_constraint_minmax(substream->runtime,201					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,202					frames_per_period, frames_per_period);203			if (err < 0) {204				mutex_unlock(&oxfw->mutex);205				goto err_locked;206			}207 208			err = snd_pcm_hw_constraint_minmax(substream->runtime,209					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,210					frames_per_buffer, frames_per_buffer);211			if (err < 0) {212				mutex_unlock(&oxfw->mutex);213				goto err_locked;214			}215		}216	}217 218	mutex_unlock(&oxfw->mutex);219 220	snd_pcm_set_sync(substream);221 222	return 0;223err_locked:224	snd_oxfw_stream_lock_release(oxfw);225	return err;226}227 228static int pcm_close(struct snd_pcm_substream *substream)229{230	struct snd_oxfw *oxfw = substream->private_data;231 232	snd_oxfw_stream_lock_release(oxfw);233	return 0;234}235 236static int pcm_capture_hw_params(struct snd_pcm_substream *substream,237				 struct snd_pcm_hw_params *hw_params)238{239	struct snd_oxfw *oxfw = substream->private_data;240	int err = 0;241 242	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {243		unsigned int rate = params_rate(hw_params);244		unsigned int channels = params_channels(hw_params);245		unsigned int frames_per_period = params_period_size(hw_params);246		unsigned int frames_per_buffer = params_buffer_size(hw_params);247 248		mutex_lock(&oxfw->mutex);249		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,250					rate, channels, frames_per_period,251					frames_per_buffer);252		if (err >= 0)253			++oxfw->substreams_count;254		mutex_unlock(&oxfw->mutex);255	}256 257	return err;258}259static int pcm_playback_hw_params(struct snd_pcm_substream *substream,260				  struct snd_pcm_hw_params *hw_params)261{262	struct snd_oxfw *oxfw = substream->private_data;263	int err = 0;264 265	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {266		unsigned int rate = params_rate(hw_params);267		unsigned int channels = params_channels(hw_params);268		unsigned int frames_per_period = params_period_size(hw_params);269		unsigned int frames_per_buffer = params_buffer_size(hw_params);270 271		mutex_lock(&oxfw->mutex);272		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,273					rate, channels, frames_per_period,274					frames_per_buffer);275		if (err >= 0)276			++oxfw->substreams_count;277		mutex_unlock(&oxfw->mutex);278	}279 280	return err;281}282 283static int pcm_capture_hw_free(struct snd_pcm_substream *substream)284{285	struct snd_oxfw *oxfw = substream->private_data;286 287	mutex_lock(&oxfw->mutex);288 289	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)290		--oxfw->substreams_count;291 292	snd_oxfw_stream_stop_duplex(oxfw);293 294	mutex_unlock(&oxfw->mutex);295 296	return 0;297}298static int pcm_playback_hw_free(struct snd_pcm_substream *substream)299{300	struct snd_oxfw *oxfw = substream->private_data;301 302	mutex_lock(&oxfw->mutex);303 304	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)305		--oxfw->substreams_count;306 307	snd_oxfw_stream_stop_duplex(oxfw);308 309	mutex_unlock(&oxfw->mutex);310 311	return 0;312}313 314static int pcm_capture_prepare(struct snd_pcm_substream *substream)315{316	struct snd_oxfw *oxfw = substream->private_data;317	int err;318 319	mutex_lock(&oxfw->mutex);320	err = snd_oxfw_stream_start_duplex(oxfw);321	mutex_unlock(&oxfw->mutex);322	if (err < 0)323		goto end;324 325	amdtp_stream_pcm_prepare(&oxfw->tx_stream);326end:327	return err;328}329static int pcm_playback_prepare(struct snd_pcm_substream *substream)330{331	struct snd_oxfw *oxfw = substream->private_data;332	int err;333 334	mutex_lock(&oxfw->mutex);335	err = snd_oxfw_stream_start_duplex(oxfw);336	mutex_unlock(&oxfw->mutex);337	if (err < 0)338		goto end;339 340	amdtp_stream_pcm_prepare(&oxfw->rx_stream);341end:342	return err;343}344 345static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)346{347	struct snd_oxfw *oxfw = substream->private_data;348	struct snd_pcm_substream *pcm;349 350	switch (cmd) {351	case SNDRV_PCM_TRIGGER_START:352		pcm = substream;353		break;354	case SNDRV_PCM_TRIGGER_STOP:355		pcm = NULL;356		break;357	default:358		return -EINVAL;359	}360	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);361	return 0;362}363static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)364{365	struct snd_oxfw *oxfw = substream->private_data;366	struct snd_pcm_substream *pcm;367 368	switch (cmd) {369	case SNDRV_PCM_TRIGGER_START:370		pcm = substream;371		break;372	case SNDRV_PCM_TRIGGER_STOP:373		pcm = NULL;374		break;375	default:376		return -EINVAL;377	}378	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);379	return 0;380}381 382static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)383{384	struct snd_oxfw *oxfw = sbstm->private_data;385 386	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);387}388static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)389{390	struct snd_oxfw *oxfw = sbstm->private_data;391 392	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);393}394 395static int pcm_capture_ack(struct snd_pcm_substream *substream)396{397	struct snd_oxfw *oxfw = substream->private_data;398 399	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);400}401 402static int pcm_playback_ack(struct snd_pcm_substream *substream)403{404	struct snd_oxfw *oxfw = substream->private_data;405 406	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);407}408 409int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)410{411	static const struct snd_pcm_ops capture_ops = {412		.open      = pcm_open,413		.close     = pcm_close,414		.hw_params = pcm_capture_hw_params,415		.hw_free   = pcm_capture_hw_free,416		.prepare   = pcm_capture_prepare,417		.trigger   = pcm_capture_trigger,418		.pointer   = pcm_capture_pointer,419		.ack       = pcm_capture_ack,420	};421	static const struct snd_pcm_ops playback_ops = {422		.open      = pcm_open,423		.close     = pcm_close,424		.hw_params = pcm_playback_hw_params,425		.hw_free   = pcm_playback_hw_free,426		.prepare   = pcm_playback_prepare,427		.trigger   = pcm_playback_trigger,428		.pointer   = pcm_playback_pointer,429		.ack       = pcm_playback_ack,430	};431	struct snd_pcm *pcm;432	unsigned int cap = 0;433	int err;434 435	if (oxfw->has_output)436		cap = 1;437 438	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);439	if (err < 0)440		return err;441 442	pcm->private_data = oxfw;443	pcm->nonatomic = true;444	strcpy(pcm->name, oxfw->card->shortname);445	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);446	if (cap > 0)447		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);448	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);449 450	return 0;451}452