| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * oxfw_pcm.c - a part of driver for OXFW970/971 based devices | 
 | 3 |  * | 
 | 4 |  * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | 
 | 5 |  * Licensed under the terms of the GNU General Public License, version 2. | 
 | 6 |  */ | 
 | 7 |  | 
 | 8 | #include "oxfw.h" | 
 | 9 |  | 
 | 10 | static 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 = 1 | 
 | 20 | 	}; | 
 | 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 |  | 
 | 41 | static 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 |  | 
 | 80 | static 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 |  | 
 | 109 | static 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); | 
 | 142 | end: | 
 | 143 | 	return err; | 
 | 144 | } | 
 | 145 |  | 
 | 146 | static 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 | 	else | 
 | 156 | 		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; | 
 | 166 | end: | 
 | 167 | 	return err; | 
 | 168 | } | 
 | 169 |  | 
 | 170 | static int pcm_open(struct snd_pcm_substream *substream) | 
 | 171 | { | 
 | 172 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 173 | 	int err; | 
 | 174 |  | 
 | 175 | 	err = snd_oxfw_stream_lock_try(oxfw); | 
 | 176 | 	if (err < 0) | 
 | 177 | 		goto end; | 
 | 178 |  | 
 | 179 | 	err = init_hw_params(oxfw, substream); | 
 | 180 | 	if (err < 0) | 
 | 181 | 		goto err_locked; | 
 | 182 |  | 
 | 183 | 	/* | 
 | 184 | 	 * When any PCM streams are already running, the available sampling | 
 | 185 | 	 * rate is limited at current value. | 
 | 186 | 	 */ | 
 | 187 | 	if (amdtp_stream_pcm_running(&oxfw->tx_stream) || | 
 | 188 | 	    amdtp_stream_pcm_running(&oxfw->rx_stream)) { | 
 | 189 | 		err = limit_to_current_params(substream); | 
 | 190 | 		if (err < 0) | 
 | 191 | 			goto end; | 
 | 192 | 	} | 
 | 193 |  | 
 | 194 | 	snd_pcm_set_sync(substream); | 
 | 195 | end: | 
 | 196 | 	return err; | 
 | 197 | err_locked: | 
 | 198 | 	snd_oxfw_stream_lock_release(oxfw); | 
 | 199 | 	return err; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static int pcm_close(struct snd_pcm_substream *substream) | 
 | 203 | { | 
 | 204 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 205 |  | 
 | 206 | 	snd_oxfw_stream_lock_release(oxfw); | 
 | 207 | 	return 0; | 
 | 208 | } | 
 | 209 |  | 
 | 210 | static int pcm_capture_hw_params(struct snd_pcm_substream *substream, | 
 | 211 | 				 struct snd_pcm_hw_params *hw_params) | 
 | 212 | { | 
 | 213 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 214 | 	int err; | 
 | 215 |  | 
 | 216 | 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream, | 
 | 217 | 					       params_buffer_bytes(hw_params)); | 
 | 218 | 	if (err < 0) | 
 | 219 | 		return err; | 
 | 220 |  | 
 | 221 | 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { | 
 | 222 | 		mutex_lock(&oxfw->mutex); | 
 | 223 | 		oxfw->capture_substreams++; | 
 | 224 | 		mutex_unlock(&oxfw->mutex); | 
 | 225 | 	} | 
 | 226 |  | 
 | 227 | 	return 0; | 
 | 228 | } | 
 | 229 | static int pcm_playback_hw_params(struct snd_pcm_substream *substream, | 
 | 230 | 				  struct snd_pcm_hw_params *hw_params) | 
 | 231 | { | 
 | 232 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 233 | 	int err; | 
 | 234 |  | 
 | 235 | 	err = snd_pcm_lib_alloc_vmalloc_buffer(substream, | 
 | 236 | 					       params_buffer_bytes(hw_params)); | 
 | 237 | 	if (err < 0) | 
 | 238 | 		return err; | 
 | 239 |  | 
 | 240 | 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { | 
 | 241 | 		mutex_lock(&oxfw->mutex); | 
 | 242 | 		oxfw->playback_substreams++; | 
 | 243 | 		mutex_unlock(&oxfw->mutex); | 
 | 244 | 	} | 
 | 245 |  | 
 | 246 | 	return 0; | 
 | 247 | } | 
 | 248 |  | 
 | 249 | static int pcm_capture_hw_free(struct snd_pcm_substream *substream) | 
 | 250 | { | 
 | 251 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 252 |  | 
 | 253 | 	mutex_lock(&oxfw->mutex); | 
 | 254 |  | 
 | 255 | 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) | 
 | 256 | 		oxfw->capture_substreams--; | 
 | 257 |  | 
 | 258 | 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); | 
 | 259 |  | 
 | 260 | 	mutex_unlock(&oxfw->mutex); | 
 | 261 |  | 
 | 262 | 	return snd_pcm_lib_free_vmalloc_buffer(substream); | 
 | 263 | } | 
 | 264 | static int pcm_playback_hw_free(struct snd_pcm_substream *substream) | 
 | 265 | { | 
 | 266 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 267 |  | 
 | 268 | 	mutex_lock(&oxfw->mutex); | 
 | 269 |  | 
 | 270 | 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) | 
 | 271 | 		oxfw->playback_substreams--; | 
 | 272 |  | 
 | 273 | 	snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); | 
 | 274 |  | 
 | 275 | 	mutex_unlock(&oxfw->mutex); | 
 | 276 |  | 
 | 277 | 	return snd_pcm_lib_free_vmalloc_buffer(substream); | 
 | 278 | } | 
 | 279 |  | 
 | 280 | static int pcm_capture_prepare(struct snd_pcm_substream *substream) | 
 | 281 | { | 
 | 282 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 283 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 284 | 	int err; | 
 | 285 |  | 
 | 286 | 	mutex_lock(&oxfw->mutex); | 
 | 287 | 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, | 
 | 288 | 					    runtime->rate, runtime->channels); | 
 | 289 | 	mutex_unlock(&oxfw->mutex); | 
 | 290 | 	if (err < 0) | 
 | 291 | 		goto end; | 
 | 292 |  | 
 | 293 | 	amdtp_stream_pcm_prepare(&oxfw->tx_stream); | 
 | 294 | end: | 
 | 295 | 	return err; | 
 | 296 | } | 
 | 297 | static int pcm_playback_prepare(struct snd_pcm_substream *substream) | 
 | 298 | { | 
 | 299 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 300 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 301 | 	int err; | 
 | 302 |  | 
 | 303 | 	mutex_lock(&oxfw->mutex); | 
 | 304 | 	err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, | 
 | 305 | 					    runtime->rate, runtime->channels); | 
 | 306 | 	mutex_unlock(&oxfw->mutex); | 
 | 307 | 	if (err < 0) | 
 | 308 | 		goto end; | 
 | 309 |  | 
 | 310 | 	amdtp_stream_pcm_prepare(&oxfw->rx_stream); | 
 | 311 | end: | 
 | 312 | 	return err; | 
 | 313 | } | 
 | 314 |  | 
 | 315 | static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) | 
 | 316 | { | 
 | 317 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 318 | 	struct snd_pcm_substream *pcm; | 
 | 319 |  | 
 | 320 | 	switch (cmd) { | 
 | 321 | 	case SNDRV_PCM_TRIGGER_START: | 
 | 322 | 		pcm = substream; | 
 | 323 | 		break; | 
 | 324 | 	case SNDRV_PCM_TRIGGER_STOP: | 
 | 325 | 		pcm = NULL; | 
 | 326 | 		break; | 
 | 327 | 	default: | 
 | 328 | 		return -EINVAL; | 
 | 329 | 	} | 
 | 330 | 	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm); | 
 | 331 | 	return 0; | 
 | 332 | } | 
 | 333 | static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) | 
 | 334 | { | 
 | 335 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 336 | 	struct snd_pcm_substream *pcm; | 
 | 337 |  | 
 | 338 | 	switch (cmd) { | 
 | 339 | 	case SNDRV_PCM_TRIGGER_START: | 
 | 340 | 		pcm = substream; | 
 | 341 | 		break; | 
 | 342 | 	case SNDRV_PCM_TRIGGER_STOP: | 
 | 343 | 		pcm = NULL; | 
 | 344 | 		break; | 
 | 345 | 	default: | 
 | 346 | 		return -EINVAL; | 
 | 347 | 	} | 
 | 348 | 	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); | 
 | 349 | 	return 0; | 
 | 350 | } | 
 | 351 |  | 
 | 352 | static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) | 
 | 353 | { | 
 | 354 | 	struct snd_oxfw *oxfw = sbstm->private_data; | 
 | 355 |  | 
 | 356 | 	return amdtp_stream_pcm_pointer(&oxfw->tx_stream); | 
 | 357 | } | 
 | 358 | static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) | 
 | 359 | { | 
 | 360 | 	struct snd_oxfw *oxfw = sbstm->private_data; | 
 | 361 |  | 
 | 362 | 	return amdtp_stream_pcm_pointer(&oxfw->rx_stream); | 
 | 363 | } | 
 | 364 |  | 
 | 365 | static int pcm_capture_ack(struct snd_pcm_substream *substream) | 
 | 366 | { | 
 | 367 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 368 |  | 
 | 369 | 	return amdtp_stream_pcm_ack(&oxfw->tx_stream); | 
 | 370 | } | 
 | 371 |  | 
 | 372 | static int pcm_playback_ack(struct snd_pcm_substream *substream) | 
 | 373 | { | 
 | 374 | 	struct snd_oxfw *oxfw = substream->private_data; | 
 | 375 |  | 
 | 376 | 	return amdtp_stream_pcm_ack(&oxfw->rx_stream); | 
 | 377 | } | 
 | 378 |  | 
 | 379 | int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) | 
 | 380 | { | 
 | 381 | 	static const struct snd_pcm_ops capture_ops = { | 
 | 382 | 		.open      = pcm_open, | 
 | 383 | 		.close     = pcm_close, | 
 | 384 | 		.ioctl     = snd_pcm_lib_ioctl, | 
 | 385 | 		.hw_params = pcm_capture_hw_params, | 
 | 386 | 		.hw_free   = pcm_capture_hw_free, | 
 | 387 | 		.prepare   = pcm_capture_prepare, | 
 | 388 | 		.trigger   = pcm_capture_trigger, | 
 | 389 | 		.pointer   = pcm_capture_pointer, | 
 | 390 | 		.ack       = pcm_capture_ack, | 
 | 391 | 		.page      = snd_pcm_lib_get_vmalloc_page, | 
 | 392 | 	}; | 
 | 393 | 	static const struct snd_pcm_ops playback_ops = { | 
 | 394 | 		.open      = pcm_open, | 
 | 395 | 		.close     = pcm_close, | 
 | 396 | 		.ioctl     = snd_pcm_lib_ioctl, | 
 | 397 | 		.hw_params = pcm_playback_hw_params, | 
 | 398 | 		.hw_free   = pcm_playback_hw_free, | 
 | 399 | 		.prepare   = pcm_playback_prepare, | 
 | 400 | 		.trigger   = pcm_playback_trigger, | 
 | 401 | 		.pointer   = pcm_playback_pointer, | 
 | 402 | 		.ack       = pcm_playback_ack, | 
 | 403 | 		.page      = snd_pcm_lib_get_vmalloc_page, | 
 | 404 | 	}; | 
 | 405 | 	struct snd_pcm *pcm; | 
 | 406 | 	unsigned int cap = 0; | 
 | 407 | 	int err; | 
 | 408 |  | 
 | 409 | 	if (oxfw->has_output) | 
 | 410 | 		cap = 1; | 
 | 411 |  | 
 | 412 | 	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm); | 
 | 413 | 	if (err < 0) | 
 | 414 | 		return err; | 
 | 415 |  | 
 | 416 | 	pcm->private_data = oxfw; | 
 | 417 | 	strcpy(pcm->name, oxfw->card->shortname); | 
 | 418 | 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); | 
 | 419 | 	if (cap > 0) | 
 | 420 | 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); | 
 | 421 |  | 
 | 422 | 	return 0; | 
 | 423 | } |