brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 81dfc6a Raw
477 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.4 *5 * @File	ctpcm.c6 *7 * @Brief8 * This file contains the definition of the pcm device functions.9 *10 * @Author	Liu Chun11 * @Date 	Apr 2 200812 */13 14#include "ctpcm.h"15#include "cttimer.h"16#include <linux/slab.h>17#include <sound/pcm.h>18 19/* Hardware descriptions for playback */20static const struct snd_pcm_hardware ct_pcm_playback_hw = {21	.info			= (SNDRV_PCM_INFO_MMAP |22				   SNDRV_PCM_INFO_INTERLEAVED |23				   SNDRV_PCM_INFO_BLOCK_TRANSFER |24				   SNDRV_PCM_INFO_MMAP_VALID |25				   SNDRV_PCM_INFO_PAUSE),26	.formats		= (SNDRV_PCM_FMTBIT_U8 |27				   SNDRV_PCM_FMTBIT_S16_LE |28				   SNDRV_PCM_FMTBIT_S24_3LE |29				   SNDRV_PCM_FMTBIT_S32_LE |30				   SNDRV_PCM_FMTBIT_FLOAT_LE),31	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |32				   SNDRV_PCM_RATE_8000_192000),33	.rate_min		= 8000,34	.rate_max		= 192000,35	.channels_min		= 1,36	.channels_max		= 2,37	.buffer_bytes_max	= (128*1024),38	.period_bytes_min	= (64),39	.period_bytes_max	= (128*1024),40	.periods_min		= 2,41	.periods_max		= 1024,42	.fifo_size		= 0,43};44 45static const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {46	.info			= (SNDRV_PCM_INFO_MMAP |47				   SNDRV_PCM_INFO_INTERLEAVED |48				   SNDRV_PCM_INFO_BLOCK_TRANSFER |49				   SNDRV_PCM_INFO_MMAP_VALID |50				   SNDRV_PCM_INFO_PAUSE),51	.formats		= SNDRV_PCM_FMTBIT_S16_LE,52	.rates			= (SNDRV_PCM_RATE_48000 |53				   SNDRV_PCM_RATE_44100 |54				   SNDRV_PCM_RATE_32000),55	.rate_min		= 32000,56	.rate_max		= 48000,57	.channels_min		= 2,58	.channels_max		= 2,59	.buffer_bytes_max	= (128*1024),60	.period_bytes_min	= (64),61	.period_bytes_max	= (128*1024),62	.periods_min		= 2,63	.periods_max		= 1024,64	.fifo_size		= 0,65};66 67/* Hardware descriptions for capture */68static const struct snd_pcm_hardware ct_pcm_capture_hw = {69	.info			= (SNDRV_PCM_INFO_MMAP |70				   SNDRV_PCM_INFO_INTERLEAVED |71				   SNDRV_PCM_INFO_BLOCK_TRANSFER |72				   SNDRV_PCM_INFO_PAUSE |73				   SNDRV_PCM_INFO_MMAP_VALID),74	.formats		= (SNDRV_PCM_FMTBIT_U8 |75				   SNDRV_PCM_FMTBIT_S16_LE |76				   SNDRV_PCM_FMTBIT_S24_3LE |77				   SNDRV_PCM_FMTBIT_S32_LE |78				   SNDRV_PCM_FMTBIT_FLOAT_LE),79	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |80				   SNDRV_PCM_RATE_8000_96000),81	.rate_min		= 8000,82	.rate_max		= 96000,83	.channels_min		= 1,84	.channels_max		= 2,85	.buffer_bytes_max	= (128*1024),86	.period_bytes_min	= (384),87	.period_bytes_max	= (64*1024),88	.periods_min		= 2,89	.periods_max		= 1024,90	.fifo_size		= 0,91};92 93static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)94{95	struct ct_atc_pcm *apcm = atc_pcm;96 97	if (!apcm->substream)98		return;99 100	snd_pcm_period_elapsed(apcm->substream);101}102 103static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)104{105	struct ct_atc_pcm *apcm = runtime->private_data;106	struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);107 108	atc->pcm_release_resources(atc, apcm);109	ct_timer_instance_free(apcm->timer);110	kfree(apcm);111	runtime->private_data = NULL;112}113 114/* pcm playback operations */115static int ct_pcm_playback_open(struct snd_pcm_substream *substream)116{117	struct ct_atc *atc = snd_pcm_substream_chip(substream);118	struct snd_pcm_runtime *runtime = substream->runtime;119	struct ct_atc_pcm *apcm;120	int err;121 122	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);123	if (!apcm)124		return -ENOMEM;125 126	apcm->substream = substream;127	apcm->interrupt = ct_atc_pcm_interrupt;128	if (IEC958 == substream->pcm->device) {129		runtime->hw = ct_spdif_passthru_playback_hw;130		atc->spdif_out_passthru(atc, 1);131	} else {132		runtime->hw = ct_pcm_playback_hw;133		if (FRONT == substream->pcm->device)134			runtime->hw.channels_max = 8;135	}136 137	err = snd_pcm_hw_constraint_integer(runtime,138					    SNDRV_PCM_HW_PARAM_PERIODS);139	if (err < 0)140		goto free_pcm;141 142	err = snd_pcm_hw_constraint_minmax(runtime,143					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,144					   1024, UINT_MAX);145	if (err < 0)146		goto free_pcm;147 148	apcm->timer = ct_timer_instance_new(atc->timer, apcm);149	if (!apcm->timer) {150		err = -ENOMEM;151		goto free_pcm;152	}153	runtime->private_data = apcm;154	runtime->private_free = ct_atc_pcm_free_substream;155 156	return 0;157 158free_pcm:159	kfree(apcm);160	return err;161}162 163static int ct_pcm_playback_close(struct snd_pcm_substream *substream)164{165	struct ct_atc *atc = snd_pcm_substream_chip(substream);166 167	/* TODO: Notify mixer inactive. */168	if (IEC958 == substream->pcm->device)169		atc->spdif_out_passthru(atc, 0);170 171	/* The ct_atc_pcm object will be freed by runtime->private_free */172 173	return 0;174}175 176static int ct_pcm_hw_params(struct snd_pcm_substream *substream,177				     struct snd_pcm_hw_params *hw_params)178{179	struct ct_atc *atc = snd_pcm_substream_chip(substream);180	struct ct_atc_pcm *apcm = substream->runtime->private_data;181 182	/* clear previous resources */183	atc->pcm_release_resources(atc, apcm);184	return 0;185}186 187static int ct_pcm_hw_free(struct snd_pcm_substream *substream)188{189	struct ct_atc *atc = snd_pcm_substream_chip(substream);190	struct ct_atc_pcm *apcm = substream->runtime->private_data;191 192	/* clear previous resources */193	atc->pcm_release_resources(atc, apcm);194	return 0;195}196 197 198static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)199{200	int err;201	struct ct_atc *atc = snd_pcm_substream_chip(substream);202	struct snd_pcm_runtime *runtime = substream->runtime;203	struct ct_atc_pcm *apcm = runtime->private_data;204 205	if (IEC958 == substream->pcm->device)206		err = atc->spdif_passthru_playback_prepare(atc, apcm);207	else208		err = atc->pcm_playback_prepare(atc, apcm);209 210	if (err < 0) {211		dev_err(atc->card->dev,212			"Preparing pcm playback failed!!!\n");213		return err;214	}215 216	return 0;217}218 219static int220ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)221{222	struct ct_atc *atc = snd_pcm_substream_chip(substream);223	struct snd_pcm_runtime *runtime = substream->runtime;224	struct ct_atc_pcm *apcm = runtime->private_data;225 226	switch (cmd) {227	case SNDRV_PCM_TRIGGER_START:228	case SNDRV_PCM_TRIGGER_RESUME:229	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:230		atc->pcm_playback_start(atc, apcm);231		break;232	case SNDRV_PCM_TRIGGER_STOP:233	case SNDRV_PCM_TRIGGER_SUSPEND:234	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:235		atc->pcm_playback_stop(atc, apcm);236		break;237	default:238		break;239	}240 241	return 0;242}243 244static snd_pcm_uframes_t245ct_pcm_playback_pointer(struct snd_pcm_substream *substream)246{247	unsigned long position;248	struct ct_atc *atc = snd_pcm_substream_chip(substream);249	struct snd_pcm_runtime *runtime = substream->runtime;250	struct ct_atc_pcm *apcm = runtime->private_data;251 252	/* Read out playback position */253	position = atc->pcm_playback_position(atc, apcm);254	position = bytes_to_frames(runtime, position);255	if (position >= runtime->buffer_size)256		position = 0;257	return position;258}259 260/* pcm capture operations */261static int ct_pcm_capture_open(struct snd_pcm_substream *substream)262{263	struct ct_atc *atc = snd_pcm_substream_chip(substream);264	struct snd_pcm_runtime *runtime = substream->runtime;265	struct ct_atc_pcm *apcm;266	int err;267 268	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);269	if (!apcm)270		return -ENOMEM;271 272	apcm->started = 0;273	apcm->substream = substream;274	apcm->interrupt = ct_atc_pcm_interrupt;275	runtime->hw = ct_pcm_capture_hw;276	runtime->hw.rate_max = atc->rsr * atc->msr;277 278	err = snd_pcm_hw_constraint_integer(runtime,279					    SNDRV_PCM_HW_PARAM_PERIODS);280	if (err < 0)281		goto free_pcm;282 283	err = snd_pcm_hw_constraint_minmax(runtime,284					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,285					   1024, UINT_MAX);286	if (err < 0)287		goto free_pcm;288 289	apcm->timer = ct_timer_instance_new(atc->timer, apcm);290	if (!apcm->timer) {291		err = -ENOMEM;292		goto free_pcm;293	}294	runtime->private_data = apcm;295	runtime->private_free = ct_atc_pcm_free_substream;296 297	return 0;298 299free_pcm:300	kfree(apcm);301	return err;302}303 304static int ct_pcm_capture_close(struct snd_pcm_substream *substream)305{306	/* The ct_atc_pcm object will be freed by runtime->private_free */307	/* TODO: Notify mixer inactive. */308	return 0;309}310 311static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)312{313	int err;314	struct ct_atc *atc = snd_pcm_substream_chip(substream);315	struct snd_pcm_runtime *runtime = substream->runtime;316	struct ct_atc_pcm *apcm = runtime->private_data;317 318	err = atc->pcm_capture_prepare(atc, apcm);319	if (err < 0) {320		dev_err(atc->card->dev,321			"Preparing pcm capture failed!!!\n");322		return err;323	}324 325	return 0;326}327 328static int329ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)330{331	struct ct_atc *atc = snd_pcm_substream_chip(substream);332	struct snd_pcm_runtime *runtime = substream->runtime;333	struct ct_atc_pcm *apcm = runtime->private_data;334 335	switch (cmd) {336	case SNDRV_PCM_TRIGGER_START:337		atc->pcm_capture_start(atc, apcm);338		break;339	case SNDRV_PCM_TRIGGER_STOP:340		atc->pcm_capture_stop(atc, apcm);341		break;342	default:343		atc->pcm_capture_stop(atc, apcm);344		break;345	}346 347	return 0;348}349 350static snd_pcm_uframes_t351ct_pcm_capture_pointer(struct snd_pcm_substream *substream)352{353	unsigned long position;354	struct ct_atc *atc = snd_pcm_substream_chip(substream);355	struct snd_pcm_runtime *runtime = substream->runtime;356	struct ct_atc_pcm *apcm = runtime->private_data;357 358	/* Read out playback position */359	position = atc->pcm_capture_position(atc, apcm);360	position = bytes_to_frames(runtime, position);361	if (position >= runtime->buffer_size)362		position = 0;363	return position;364}365 366/* PCM operators for playback */367static const struct snd_pcm_ops ct_pcm_playback_ops = {368	.open	 	= ct_pcm_playback_open,369	.close		= ct_pcm_playback_close,370	.hw_params	= ct_pcm_hw_params,371	.hw_free	= ct_pcm_hw_free,372	.prepare	= ct_pcm_playback_prepare,373	.trigger	= ct_pcm_playback_trigger,374	.pointer	= ct_pcm_playback_pointer,375};376 377/* PCM operators for capture */378static const struct snd_pcm_ops ct_pcm_capture_ops = {379	.open	 	= ct_pcm_capture_open,380	.close		= ct_pcm_capture_close,381	.hw_params	= ct_pcm_hw_params,382	.hw_free	= ct_pcm_hw_free,383	.prepare	= ct_pcm_capture_prepare,384	.trigger	= ct_pcm_capture_trigger,385	.pointer	= ct_pcm_capture_pointer,386};387 388static const struct snd_pcm_chmap_elem surround_map[] = {389	{ .channels = 1,390	  .map = { SNDRV_CHMAP_MONO } },391	{ .channels = 2,392	  .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },393	{ }394};395 396static const struct snd_pcm_chmap_elem clfe_map[] = {397	{ .channels = 1,398	  .map = { SNDRV_CHMAP_MONO } },399	{ .channels = 2,400	  .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },401	{ }402};403 404static const struct snd_pcm_chmap_elem side_map[] = {405	{ .channels = 1,406	  .map = { SNDRV_CHMAP_MONO } },407	{ .channels = 2,408	  .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },409	{ }410};411 412/* Create ALSA pcm device */413int ct_alsa_pcm_create(struct ct_atc *atc,414		       enum CTALSADEVS device,415		       const char *device_name)416{417	struct snd_pcm *pcm;418	const struct snd_pcm_chmap_elem *map;419	int chs;420	int err;421	int playback_count, capture_count;422 423	playback_count = (IEC958 == device) ? 1 : 256;424	capture_count = (FRONT == device) ? 1 : 0;425	err = snd_pcm_new(atc->card, "ctxfi", device,426			  playback_count, capture_count, &pcm);427	if (err < 0) {428		dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",429			err);430		return err;431	}432 433	pcm->private_data = atc;434	pcm->info_flags = 0;435	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;436	strscpy(pcm->name, device_name, sizeof(pcm->name));437 438	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);439 440	if (FRONT == device)441		snd_pcm_set_ops(pcm,442				SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);443 444	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,445				       &atc->pci->dev, 128*1024, 128*1024);446 447	chs = 2;448	switch (device) {449	case FRONT:450		chs = 8;451		map = snd_pcm_std_chmaps;452		break;453	case SURROUND:454		map = surround_map;455		break;456	case CLFE:457		map = clfe_map;458		break;459	case SIDE:460		map = side_map;461		break;462	default:463		map = snd_pcm_std_chmaps;464		break;465	}466	err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,467				     0, NULL);468	if (err < 0)469		return err;470 471#ifdef CONFIG_PM_SLEEP472	atc->pcms[device] = pcm;473#endif474 475	return 0;476}477