brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 2cf2adb Raw
461 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * dice_pcm.c - a part of driver for DICE based devices4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>7 */8 9#include "dice.h"10 11static int dice_rate_constraint(struct snd_pcm_hw_params *params,12				struct snd_pcm_hw_rule *rule)13{14	struct snd_pcm_substream *substream = rule->private;15	struct snd_dice *dice = substream->private_data;16	unsigned int index = substream->pcm->device;17 18	const struct snd_interval *c =19		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);20	struct snd_interval *r =21		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);22	struct snd_interval rates = {23		.min = UINT_MAX, .max = 0, .integer = 124	};25	unsigned int *pcm_channels;26	enum snd_dice_rate_mode mode;27	unsigned int i, rate;28 29	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)30		pcm_channels = dice->tx_pcm_chs[index];31	else32		pcm_channels = dice->rx_pcm_chs[index];33 34	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {35		rate = snd_dice_rates[i];36		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)37			continue;38 39		if (!snd_interval_test(c, pcm_channels[mode]))40			continue;41 42		rates.min = min(rates.min, rate);43		rates.max = max(rates.max, rate);44	}45 46	return snd_interval_refine(r, &rates);47}48 49static int dice_channels_constraint(struct snd_pcm_hw_params *params,50				    struct snd_pcm_hw_rule *rule)51{52	struct snd_pcm_substream *substream = rule->private;53	struct snd_dice *dice = substream->private_data;54	unsigned int index = substream->pcm->device;55 56	const struct snd_interval *r =57		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);58	struct snd_interval *c =59		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);60	struct snd_interval channels = {61		.min = UINT_MAX, .max = 0, .integer = 162	};63	unsigned int *pcm_channels;64	enum snd_dice_rate_mode mode;65	unsigned int i, rate;66 67	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)68		pcm_channels = dice->tx_pcm_chs[index];69	else70		pcm_channels = dice->rx_pcm_chs[index];71 72	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {73		rate = snd_dice_rates[i];74		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)75			continue;76 77		if (!snd_interval_test(r, rate))78			continue;79 80		channels.min = min(channels.min, pcm_channels[mode]);81		channels.max = max(channels.max, pcm_channels[mode]);82	}83 84	return snd_interval_refine(c, &channels);85}86 87static int limit_channels_and_rates(struct snd_dice *dice,88				    struct snd_pcm_runtime *runtime,89				    enum amdtp_stream_direction dir,90				    unsigned int index)91{92	struct snd_pcm_hardware *hw = &runtime->hw;93	unsigned int *pcm_channels;94	unsigned int i;95 96	if (dir == AMDTP_IN_STREAM)97		pcm_channels = dice->tx_pcm_chs[index];98	else99		pcm_channels = dice->rx_pcm_chs[index];100 101	hw->channels_min = UINT_MAX;102	hw->channels_max = 0;103 104	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {105		enum snd_dice_rate_mode mode;106		unsigned int rate, channels;107 108		rate = snd_dice_rates[i];109		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)110			continue;111		hw->rates |= snd_pcm_rate_to_rate_bit(rate);112 113		channels = pcm_channels[mode];114		if (channels == 0)115			continue;116		hw->channels_min = min(hw->channels_min, channels);117		hw->channels_max = max(hw->channels_max, channels);118	}119 120	snd_pcm_limit_hw_rates(runtime);121 122	return 0;123}124 125static int init_hw_info(struct snd_dice *dice,126			struct snd_pcm_substream *substream)127{128	struct snd_pcm_runtime *runtime = substream->runtime;129	struct snd_pcm_hardware *hw = &runtime->hw;130	unsigned int index = substream->pcm->device;131	enum amdtp_stream_direction dir;132	struct amdtp_stream *stream;133	int err;134 135	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {136		hw->formats = AM824_IN_PCM_FORMAT_BITS;137		dir = AMDTP_IN_STREAM;138		stream = &dice->tx_stream[index];139	} else {140		hw->formats = AM824_OUT_PCM_FORMAT_BITS;141		dir = AMDTP_OUT_STREAM;142		stream = &dice->rx_stream[index];143	}144 145	err = limit_channels_and_rates(dice, substream->runtime, dir,146				       index);147	if (err < 0)148		return err;149 150	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,151				  dice_rate_constraint, substream,152				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);153	if (err < 0)154		return err;155	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,156				  dice_channels_constraint, substream,157				  SNDRV_PCM_HW_PARAM_RATE, -1);158	if (err < 0)159		return err;160 161	return amdtp_am824_add_pcm_hw_constraints(stream, runtime);162}163 164static int pcm_open(struct snd_pcm_substream *substream)165{166	struct snd_dice *dice = substream->private_data;167	struct amdtp_domain *d = &dice->domain;168	unsigned int source;169	bool internal;170	int err;171 172	err = snd_dice_stream_lock_try(dice);173	if (err < 0)174		return err;175 176	err = init_hw_info(dice, substream);177	if (err < 0)178		goto err_locked;179 180	err = snd_dice_transaction_get_clock_source(dice, &source);181	if (err < 0)182		goto err_locked;183	switch (source) {184	case CLOCK_SOURCE_AES1:185	case CLOCK_SOURCE_AES2:186	case CLOCK_SOURCE_AES3:187	case CLOCK_SOURCE_AES4:188	case CLOCK_SOURCE_AES_ANY:189	case CLOCK_SOURCE_ADAT:190	case CLOCK_SOURCE_TDIF:191	case CLOCK_SOURCE_WC:192		internal = false;193		break;194	default:195		internal = true;196		break;197	}198 199	mutex_lock(&dice->mutex);200 201	// When source of clock is not internal or any stream is reserved for202	// transmission of PCM frames, the available sampling rate is limited203	// at current one.204	if (!internal ||205	    (dice->substreams_counter > 0 && d->events_per_period > 0)) {206		unsigned int frames_per_period = d->events_per_period;207		unsigned int frames_per_buffer = d->events_per_buffer;208		unsigned int rate;209 210		err = snd_dice_transaction_get_rate(dice, &rate);211		if (err < 0) {212			mutex_unlock(&dice->mutex);213			goto err_locked;214		}215 216		substream->runtime->hw.rate_min = rate;217		substream->runtime->hw.rate_max = rate;218 219		if (frames_per_period > 0) {220			// For double_pcm_frame quirk.221			if (rate > 96000 && !dice->disable_double_pcm_frames) {222				frames_per_period *= 2;223				frames_per_buffer *= 2;224			}225 226			err = snd_pcm_hw_constraint_minmax(substream->runtime,227					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,228					frames_per_period, frames_per_period);229			if (err < 0) {230				mutex_unlock(&dice->mutex);231				goto err_locked;232			}233 234			err = snd_pcm_hw_constraint_minmax(substream->runtime,235					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,236					frames_per_buffer, frames_per_buffer);237			if (err < 0) {238				mutex_unlock(&dice->mutex);239				goto err_locked;240			}241		}242	}243 244	mutex_unlock(&dice->mutex);245 246	snd_pcm_set_sync(substream);247 248	return 0;249err_locked:250	snd_dice_stream_lock_release(dice);251	return err;252}253 254static int pcm_close(struct snd_pcm_substream *substream)255{256	struct snd_dice *dice = substream->private_data;257 258	snd_dice_stream_lock_release(dice);259 260	return 0;261}262 263static int pcm_hw_params(struct snd_pcm_substream *substream,264			 struct snd_pcm_hw_params *hw_params)265{266	struct snd_dice *dice = substream->private_data;267	int err = 0;268 269	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {270		unsigned int rate = params_rate(hw_params);271		unsigned int events_per_period = params_period_size(hw_params);272		unsigned int events_per_buffer = params_buffer_size(hw_params);273 274		mutex_lock(&dice->mutex);275		// For double_pcm_frame quirk.276		if (rate > 96000 && !dice->disable_double_pcm_frames) {277			events_per_period /= 2;278			events_per_buffer /= 2;279		}280		err = snd_dice_stream_reserve_duplex(dice, rate,281					events_per_period, events_per_buffer);282		if (err >= 0)283			++dice->substreams_counter;284		mutex_unlock(&dice->mutex);285	}286 287	return err;288}289 290static int pcm_hw_free(struct snd_pcm_substream *substream)291{292	struct snd_dice *dice = substream->private_data;293 294	mutex_lock(&dice->mutex);295 296	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)297		--dice->substreams_counter;298 299	snd_dice_stream_stop_duplex(dice);300 301	mutex_unlock(&dice->mutex);302 303	return 0;304}305 306static int capture_prepare(struct snd_pcm_substream *substream)307{308	struct snd_dice *dice = substream->private_data;309	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];310	int err;311 312	mutex_lock(&dice->mutex);313	err = snd_dice_stream_start_duplex(dice);314	mutex_unlock(&dice->mutex);315	if (err >= 0)316		amdtp_stream_pcm_prepare(stream);317 318	return 0;319}320static int playback_prepare(struct snd_pcm_substream *substream)321{322	struct snd_dice *dice = substream->private_data;323	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];324	int err;325 326	mutex_lock(&dice->mutex);327	err = snd_dice_stream_start_duplex(dice);328	mutex_unlock(&dice->mutex);329	if (err >= 0)330		amdtp_stream_pcm_prepare(stream);331 332	return err;333}334 335static int capture_trigger(struct snd_pcm_substream *substream, int cmd)336{337	struct snd_dice *dice = substream->private_data;338	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];339 340	switch (cmd) {341	case SNDRV_PCM_TRIGGER_START:342		amdtp_stream_pcm_trigger(stream, substream);343		break;344	case SNDRV_PCM_TRIGGER_STOP:345		amdtp_stream_pcm_trigger(stream, NULL);346		break;347	default:348		return -EINVAL;349	}350 351	return 0;352}353static int playback_trigger(struct snd_pcm_substream *substream, int cmd)354{355	struct snd_dice *dice = substream->private_data;356	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];357 358	switch (cmd) {359	case SNDRV_PCM_TRIGGER_START:360		amdtp_stream_pcm_trigger(stream, substream);361		break;362	case SNDRV_PCM_TRIGGER_STOP:363		amdtp_stream_pcm_trigger(stream, NULL);364		break;365	default:366		return -EINVAL;367	}368 369	return 0;370}371 372static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)373{374	struct snd_dice *dice = substream->private_data;375	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];376 377	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);378}379static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)380{381	struct snd_dice *dice = substream->private_data;382	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];383 384	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);385}386 387static int capture_ack(struct snd_pcm_substream *substream)388{389	struct snd_dice *dice = substream->private_data;390	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];391 392	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);393}394 395static int playback_ack(struct snd_pcm_substream *substream)396{397	struct snd_dice *dice = substream->private_data;398	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];399 400	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);401}402 403int snd_dice_create_pcm(struct snd_dice *dice)404{405	static const struct snd_pcm_ops capture_ops = {406		.open      = pcm_open,407		.close     = pcm_close,408		.hw_params = pcm_hw_params,409		.hw_free   = pcm_hw_free,410		.prepare   = capture_prepare,411		.trigger   = capture_trigger,412		.pointer   = capture_pointer,413		.ack       = capture_ack,414	};415	static const struct snd_pcm_ops playback_ops = {416		.open      = pcm_open,417		.close     = pcm_close,418		.hw_params = pcm_hw_params,419		.hw_free   = pcm_hw_free,420		.prepare   = playback_prepare,421		.trigger   = playback_trigger,422		.pointer   = playback_pointer,423		.ack       = playback_ack,424	};425	struct snd_pcm *pcm;426	unsigned int capture, playback;427	int i, j;428	int err;429 430	for (i = 0; i < MAX_STREAMS; i++) {431		capture = playback = 0;432		for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {433			if (dice->tx_pcm_chs[i][j] > 0)434				capture = 1;435			if (dice->rx_pcm_chs[i][j] > 0)436				playback = 1;437		}438 439		err = snd_pcm_new(dice->card, "DICE", i, playback, capture,440				  &pcm);441		if (err < 0)442			return err;443		pcm->private_data = dice;444		pcm->nonatomic = true;445		strcpy(pcm->name, dice->card->shortname);446 447		if (capture > 0)448			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,449					&capture_ops);450 451		if (playback > 0)452			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,453					&playback_ops);454 455		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,456					       NULL, 0, 0);457	}458 459	return 0;460}461