brintos

brintos / linux-shallow public Read only

0
0
Text · 31.1 KiB · 81a2f03 Raw
1200 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2020 Intel Corporation4//5// Author: Cezary Rojewski <cezary.rojewski@intel.com>6//7 8#include <linux/pm_runtime.h>9#include <sound/soc.h>10#include <sound/pcm_params.h>11#include <uapi/sound/tlv.h>12#include "core.h"13#include "messages.h"14 15struct catpt_stream_template {16	enum catpt_path_id path_id;17	enum catpt_stream_type type;18	u32 persistent_size;19	u8 num_entries;20	struct catpt_module_entry entries[];21};22 23static struct catpt_stream_template system_pb = {24	.path_id = CATPT_PATH_SSP0_OUT,25	.type = CATPT_STRM_TYPE_SYSTEM,26	.num_entries = 1,27	.entries = {{ CATPT_MODID_PCM_SYSTEM, 0 }},28};29 30static struct catpt_stream_template system_cp = {31	.path_id = CATPT_PATH_SSP0_IN,32	.type = CATPT_STRM_TYPE_CAPTURE,33	.num_entries = 1,34	.entries = {{ CATPT_MODID_PCM_CAPTURE, 0 }},35};36 37static struct catpt_stream_template offload_pb = {38	.path_id = CATPT_PATH_SSP0_OUT,39	.type = CATPT_STRM_TYPE_RENDER,40	.num_entries = 1,41	.entries = {{ CATPT_MODID_PCM, 0 }},42};43 44static struct catpt_stream_template loopback_cp = {45	.path_id = CATPT_PATH_SSP0_OUT,46	.type = CATPT_STRM_TYPE_LOOPBACK,47	.num_entries = 1,48	.entries = {{ CATPT_MODID_PCM_REFERENCE, 0 }},49};50 51static struct catpt_stream_template bluetooth_pb = {52	.path_id = CATPT_PATH_SSP1_OUT,53	.type = CATPT_STRM_TYPE_BLUETOOTH_RENDER,54	.num_entries = 1,55	.entries = {{ CATPT_MODID_BLUETOOTH_RENDER, 0 }},56};57 58static struct catpt_stream_template bluetooth_cp = {59	.path_id = CATPT_PATH_SSP1_IN,60	.type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE,61	.num_entries = 1,62	.entries = {{ CATPT_MODID_BLUETOOTH_CAPTURE, 0 }},63};64 65static struct catpt_stream_template *catpt_topology[] = {66	[CATPT_STRM_TYPE_RENDER]		= &offload_pb,67	[CATPT_STRM_TYPE_SYSTEM]		= &system_pb,68	[CATPT_STRM_TYPE_CAPTURE]		= &system_cp,69	[CATPT_STRM_TYPE_LOOPBACK]		= &loopback_cp,70	[CATPT_STRM_TYPE_BLUETOOTH_RENDER]	= &bluetooth_pb,71	[CATPT_STRM_TYPE_BLUETOOTH_CAPTURE]	= &bluetooth_cp,72};73 74static struct catpt_stream_template *75catpt_get_stream_template(struct snd_pcm_substream *substream)76{77	struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);78	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);79	enum catpt_stream_type type;80 81	type = cpu_dai->driver->id;82 83	/* account for capture in bidirectional dais */84	switch (type) {85	case CATPT_STRM_TYPE_SYSTEM:86		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)87			type = CATPT_STRM_TYPE_CAPTURE;88		break;89	case CATPT_STRM_TYPE_BLUETOOTH_RENDER:90		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)91			type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE;92		break;93	default:94		break;95	}96 97	return catpt_topology[type];98}99 100struct catpt_stream_runtime *101catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)102{103	struct catpt_stream_runtime *pos, *result = NULL;104 105	spin_lock(&cdev->list_lock);106	list_for_each_entry(pos, &cdev->stream_list, node) {107		if (pos->info.stream_hw_id == stream_hw_id) {108			result = pos;109			break;110		}111	}112 113	spin_unlock(&cdev->list_lock);114	return result;115}116 117static u32 catpt_stream_read_position(struct catpt_dev *cdev,118				      struct catpt_stream_runtime *stream)119{120	u32 pos;121 122	memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,123		      sizeof(pos));124	return pos;125}126 127static u32 catpt_stream_volume(struct catpt_dev *cdev,128			       struct catpt_stream_runtime *stream, u32 channel)129{130	u32 volume, offset;131 132	if (channel >= CATPT_CHANNELS_MAX)133		channel = 0;134 135	offset = stream->info.volume_regaddr[channel];136	memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));137	return volume;138}139 140static u32 catpt_mixer_volume(struct catpt_dev *cdev,141			      struct catpt_mixer_stream_info *info, u32 channel)142{143	u32 volume, offset;144 145	if (channel >= CATPT_CHANNELS_MAX)146		channel = 0;147 148	offset = info->volume_regaddr[channel];149	memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume));150	return volume;151}152 153static void catpt_arrange_page_table(struct snd_pcm_substream *substream,154				     struct snd_dma_buffer *pgtbl)155{156	struct snd_pcm_runtime *rtm = substream->runtime;157	struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream);158	int i, pages;159 160	pages = snd_sgbuf_aligned_pages(rtm->dma_bytes);161 162	for (i = 0; i < pages; i++) {163		u32 pfn, offset;164		u32 *page_table;165 166		pfn = PFN_DOWN(snd_sgbuf_get_addr(databuf, i * PAGE_SIZE));167		/* incrementing by 2 on even and 3 on odd */168		offset = ((i << 2) + i) >> 1;169		page_table = (u32 *)(pgtbl->area + offset);170 171		if (i & 1)172			*page_table |= (pfn << 4);173		else174			*page_table |= pfn;175	}176}177 178static u32 catpt_get_channel_map(enum catpt_channel_config config)179{180	switch (config) {181	case CATPT_CHANNEL_CONFIG_MONO:182		return GENMASK(31, 4) | CATPT_CHANNEL_CENTER;183 184	case CATPT_CHANNEL_CONFIG_STEREO:185		return GENMASK(31, 8) | CATPT_CHANNEL_LEFT186				      | (CATPT_CHANNEL_RIGHT << 4);187 188	case CATPT_CHANNEL_CONFIG_2_POINT_1:189		return GENMASK(31, 12) | CATPT_CHANNEL_LEFT190				       | (CATPT_CHANNEL_RIGHT << 4)191				       | (CATPT_CHANNEL_LFE << 8);192 193	case CATPT_CHANNEL_CONFIG_3_POINT_0:194		return GENMASK(31, 12) | CATPT_CHANNEL_LEFT195				       | (CATPT_CHANNEL_CENTER << 4)196				       | (CATPT_CHANNEL_RIGHT << 8);197 198	case CATPT_CHANNEL_CONFIG_3_POINT_1:199		return GENMASK(31, 16) | CATPT_CHANNEL_LEFT200				       | (CATPT_CHANNEL_CENTER << 4)201				       | (CATPT_CHANNEL_RIGHT << 8)202				       | (CATPT_CHANNEL_LFE << 12);203 204	case CATPT_CHANNEL_CONFIG_QUATRO:205		return GENMASK(31, 16) | CATPT_CHANNEL_LEFT206				       | (CATPT_CHANNEL_RIGHT << 4)207				       | (CATPT_CHANNEL_LEFT_SURROUND << 8)208				       | (CATPT_CHANNEL_RIGHT_SURROUND << 12);209 210	case CATPT_CHANNEL_CONFIG_4_POINT_0:211		return GENMASK(31, 16) | CATPT_CHANNEL_LEFT212				       | (CATPT_CHANNEL_CENTER << 4)213				       | (CATPT_CHANNEL_RIGHT << 8)214				       | (CATPT_CHANNEL_CENTER_SURROUND << 12);215 216	case CATPT_CHANNEL_CONFIG_5_POINT_0:217		return GENMASK(31, 20) | CATPT_CHANNEL_LEFT218				       | (CATPT_CHANNEL_CENTER << 4)219				       | (CATPT_CHANNEL_RIGHT << 8)220				       | (CATPT_CHANNEL_LEFT_SURROUND << 12)221				       | (CATPT_CHANNEL_RIGHT_SURROUND << 16);222 223	case CATPT_CHANNEL_CONFIG_5_POINT_1:224		return GENMASK(31, 24) | CATPT_CHANNEL_CENTER225				       | (CATPT_CHANNEL_LEFT << 4)226				       | (CATPT_CHANNEL_RIGHT << 8)227				       | (CATPT_CHANNEL_LEFT_SURROUND << 12)228				       | (CATPT_CHANNEL_RIGHT_SURROUND << 16)229				       | (CATPT_CHANNEL_LFE << 20);230 231	case CATPT_CHANNEL_CONFIG_DUAL_MONO:232		return GENMASK(31, 8) | CATPT_CHANNEL_LEFT233				      | (CATPT_CHANNEL_LEFT << 4);234 235	default:236		return U32_MAX;237	}238}239 240static enum catpt_channel_config catpt_get_channel_config(u32 num_channels)241{242	switch (num_channels) {243	case 6:244		return CATPT_CHANNEL_CONFIG_5_POINT_1;245	case 5:246		return CATPT_CHANNEL_CONFIG_5_POINT_0;247	case 4:248		return CATPT_CHANNEL_CONFIG_QUATRO;249	case 3:250		return CATPT_CHANNEL_CONFIG_2_POINT_1;251	case 1:252		return CATPT_CHANNEL_CONFIG_MONO;253	case 2:254	default:255		return CATPT_CHANNEL_CONFIG_STEREO;256	}257}258 259static int catpt_dai_startup(struct snd_pcm_substream *substream,260			     struct snd_soc_dai *dai)261{262	struct catpt_stream_template *template;263	struct catpt_stream_runtime *stream;264	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);265	struct resource *res;266	int ret;267 268	template = catpt_get_stream_template(substream);269 270	stream = kzalloc(sizeof(*stream), GFP_KERNEL);271	if (!stream)272		return -ENOMEM;273 274	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE,275				  &stream->pgtbl);276	if (ret)277		goto err_pgtbl;278 279	res = catpt_request_region(&cdev->dram, template->persistent_size);280	if (!res) {281		ret = -EBUSY;282		goto err_request;283	}284 285	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);286 287	stream->template = template;288	stream->persistent = res;289	stream->substream = substream;290	INIT_LIST_HEAD(&stream->node);291	snd_soc_dai_set_dma_data(dai, substream, stream);292 293	spin_lock(&cdev->list_lock);294	list_add_tail(&stream->node, &cdev->stream_list);295	spin_unlock(&cdev->list_lock);296 297	return 0;298 299err_request:300	snd_dma_free_pages(&stream->pgtbl);301err_pgtbl:302	kfree(stream);303	return ret;304}305 306static void catpt_dai_shutdown(struct snd_pcm_substream *substream,307			       struct snd_soc_dai *dai)308{309	struct catpt_stream_runtime *stream;310	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);311 312	stream = snd_soc_dai_get_dma_data(dai, substream);313 314	spin_lock(&cdev->list_lock);315	list_del(&stream->node);316	spin_unlock(&cdev->list_lock);317 318	release_resource(stream->persistent);319	kfree(stream->persistent);320	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);321 322	snd_dma_free_pages(&stream->pgtbl);323	kfree(stream);324	snd_soc_dai_set_dma_data(dai, substream, NULL);325}326 327static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);328 329static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,330				     struct catpt_stream_runtime *stream)331{332	struct snd_soc_component *component = dai->component;333	struct snd_kcontrol *pos;334	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);335	const char *name;336	int ret;337	u32 id = stream->info.stream_hw_id;338 339	/* only selected streams have individual controls */340	switch (id) {341	case CATPT_PIN_ID_OFFLOAD1:342		name = "Media0 Playback Volume";343		break;344	case CATPT_PIN_ID_OFFLOAD2:345		name = "Media1 Playback Volume";346		break;347	case CATPT_PIN_ID_CAPTURE1:348		name = "Mic Capture Volume";349		break;350	case CATPT_PIN_ID_REFERENCE:351		name = "Loopback Mute";352		break;353	default:354		return 0;355	}356 357	list_for_each_entry(pos, &component->card->snd_card->controls, list) {358		if (pos->private_data == component &&359		    !strncmp(name, pos->id.name, sizeof(pos->id.name)))360			break;361	}362	if (list_entry_is_head(pos, &component->card->snd_card->controls, list))363		return -ENOENT;364 365	if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)366		return catpt_set_dspvol(cdev, id, (long *)pos->private_value);367	ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);368	if (ret)369		return CATPT_IPC_ERROR(ret);370	return 0;371}372 373static int catpt_dai_hw_params(struct snd_pcm_substream *substream,374			       struct snd_pcm_hw_params *params,375			       struct snd_soc_dai *dai)376{377	struct snd_pcm_runtime *rtm = substream->runtime;378	struct snd_dma_buffer *dmab;379	struct catpt_stream_runtime *stream;380	struct catpt_audio_format afmt;381	struct catpt_ring_info rinfo;382	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);383	int ret;384 385	stream = snd_soc_dai_get_dma_data(dai, substream);386	if (stream->allocated)387		return 0;388 389	memset(&afmt, 0, sizeof(afmt));390	afmt.sample_rate = params_rate(params);391	afmt.bit_depth = params_physical_width(params);392	afmt.valid_bit_depth = params_width(params);393	afmt.num_channels = params_channels(params);394	afmt.channel_config = catpt_get_channel_config(afmt.num_channels);395	afmt.channel_map = catpt_get_channel_map(afmt.channel_config);396	afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;397 398	dmab = snd_pcm_get_dma_buf(substream);399	catpt_arrange_page_table(substream, &stream->pgtbl);400 401	memset(&rinfo, 0, sizeof(rinfo));402	rinfo.page_table_addr = stream->pgtbl.addr;403	rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);404	rinfo.size = rtm->dma_bytes;405	rinfo.offset = 0;406	rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0));407 408	ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,409				     stream->template->type,410				     &afmt, &rinfo,411				     stream->template->num_entries,412				     stream->template->entries,413				     stream->persistent,414				     cdev->scratch,415				     &stream->info);416	if (ret)417		return CATPT_IPC_ERROR(ret);418 419	ret = catpt_dai_apply_usettings(dai, stream);420	if (ret)421		return ret;422 423	stream->allocated = true;424	return 0;425}426 427static int catpt_dai_hw_free(struct snd_pcm_substream *substream,428			     struct snd_soc_dai *dai)429{430	struct catpt_stream_runtime *stream;431	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);432 433	stream = snd_soc_dai_get_dma_data(dai, substream);434	if (!stream->allocated)435		return 0;436 437	catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);438	catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);439 440	stream->allocated = false;441	return 0;442}443 444static int catpt_dai_prepare(struct snd_pcm_substream *substream,445			     struct snd_soc_dai *dai)446{447	struct catpt_stream_runtime *stream;448	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);449	int ret;450 451	stream = snd_soc_dai_get_dma_data(dai, substream);452	if (stream->prepared)453		return 0;454 455	ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);456	if (ret)457		return CATPT_IPC_ERROR(ret);458 459	ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);460	if (ret)461		return CATPT_IPC_ERROR(ret);462 463	stream->prepared = true;464	return 0;465}466 467static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,468			     struct snd_soc_dai *dai)469{470	struct snd_pcm_runtime *runtime = substream->runtime;471	struct catpt_stream_runtime *stream;472	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);473	snd_pcm_uframes_t pos;474	int ret;475 476	stream = snd_soc_dai_get_dma_data(dai, substream);477 478	switch (cmd) {479	case SNDRV_PCM_TRIGGER_START:480		/* only offload is set_write_pos driven */481		if (stream->template->type != CATPT_STRM_TYPE_RENDER)482			goto resume_stream;483 484		pos = frames_to_bytes(runtime, runtime->start_threshold);485		/*486		 * Dsp operates on buffer halves, thus max 2x set_write_pos487		 * (entire buffer filled) prior to stream start.488		 */489		ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,490					      pos, false, false);491		if (ret)492			return CATPT_IPC_ERROR(ret);493		fallthrough;494	case SNDRV_PCM_TRIGGER_RESUME:495	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:496	resume_stream:497		catpt_dsp_update_lpclock(cdev);498		ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);499		if (ret)500			return CATPT_IPC_ERROR(ret);501		break;502 503	case SNDRV_PCM_TRIGGER_STOP:504		stream->prepared = false;505		fallthrough;506	case SNDRV_PCM_TRIGGER_SUSPEND:507	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:508		ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);509		catpt_dsp_update_lpclock(cdev);510		if (ret)511			return CATPT_IPC_ERROR(ret);512		break;513 514	default:515		break;516	}517 518	return 0;519}520 521void catpt_stream_update_position(struct catpt_dev *cdev,522				  struct catpt_stream_runtime *stream,523				  struct catpt_notify_position *pos)524{525	struct snd_pcm_substream *substream = stream->substream;526	struct snd_pcm_runtime *r = substream->runtime;527	snd_pcm_uframes_t dsppos, newpos;528	int ret;529 530	dsppos = bytes_to_frames(r, pos->stream_position);531 532	if (!stream->prepared)533		goto exit;534	/* only offload is set_write_pos driven */535	if (stream->template->type != CATPT_STRM_TYPE_RENDER)536		goto exit;537 538	if (dsppos >= r->buffer_size / 2)539		newpos = r->buffer_size / 2;540	else541		newpos = 0;542	/*543	 * Dsp operates on buffer halves, thus on every notify position544	 * (buffer half consumed) update wp to allow stream progression.545	 */546	ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,547				      frames_to_bytes(r, newpos),548				      false, false);549	if (ret) {550		dev_err(cdev->dev, "update position for stream %d failed: %d\n",551			stream->info.stream_hw_id, ret);552		return;553	}554exit:555	snd_pcm_period_elapsed(substream);556}557 558/* 200 ms for 2 32-bit channels at 48kHz (native format) */559#define CATPT_BUFFER_MAX_SIZE	76800560#define CATPT_PCM_PERIODS_MAX	4561#define CATPT_PCM_PERIODS_MIN	2562 563static const struct snd_pcm_hardware catpt_pcm_hardware = {564	.info			= SNDRV_PCM_INFO_MMAP |565				  SNDRV_PCM_INFO_MMAP_VALID |566				  SNDRV_PCM_INFO_INTERLEAVED |567				  SNDRV_PCM_INFO_PAUSE |568				  SNDRV_PCM_INFO_RESUME |569				  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,570	.formats		= SNDRV_PCM_FMTBIT_S16_LE |571				  SNDRV_PCM_FMTBIT_S24_LE |572				  SNDRV_PCM_FMTBIT_S32_LE,573	.period_bytes_min	= PAGE_SIZE,574	.period_bytes_max	= CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,575	.periods_min		= CATPT_PCM_PERIODS_MIN,576	.periods_max		= CATPT_PCM_PERIODS_MAX,577	.buffer_bytes_max	= CATPT_BUFFER_MAX_SIZE,578};579 580static int catpt_component_pcm_construct(struct snd_soc_component *component,581					 struct snd_soc_pcm_runtime *rtm)582{583	struct catpt_dev *cdev = dev_get_drvdata(component->dev);584 585	snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,586				       cdev->dev,587				       catpt_pcm_hardware.buffer_bytes_max,588				       catpt_pcm_hardware.buffer_bytes_max);589 590	return 0;591}592 593static int catpt_component_open(struct snd_soc_component *component,594				struct snd_pcm_substream *substream)595{596	struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);597 598	if (!rtm->dai_link->no_pcm)599		snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);600	return 0;601}602 603static snd_pcm_uframes_t604catpt_component_pointer(struct snd_soc_component *component,605			struct snd_pcm_substream *substream)606{607	struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);608	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);609	struct catpt_stream_runtime *stream;610	struct catpt_dev *cdev = dev_get_drvdata(component->dev);611	u32 pos;612 613	if (rtm->dai_link->no_pcm)614		return 0;615 616	stream = snd_soc_dai_get_dma_data(cpu_dai, substream);617	pos = catpt_stream_read_position(cdev, stream);618 619	return bytes_to_frames(substream->runtime, pos);620}621 622static const struct snd_soc_dai_ops catpt_fe_dai_ops = {623	.startup = catpt_dai_startup,624	.shutdown = catpt_dai_shutdown,625	.hw_params = catpt_dai_hw_params,626	.hw_free = catpt_dai_hw_free,627	.prepare = catpt_dai_prepare,628	.trigger = catpt_dai_trigger,629};630 631static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,632			     struct snd_soc_dai *dai)633{634	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);635	struct catpt_ssp_device_format devfmt;636	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);637	int ret;638 639	devfmt.iface = dai->driver->id;640	devfmt.channels = codec_dai->driver->capture.channels_max;641 642	switch (devfmt.iface) {643	case CATPT_SSP_IFACE_0:644		devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ;645 646		switch (devfmt.channels) {647		case 4:648			devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER;649			devfmt.clock_divider = 4;650			break;651		case 2:652		default:653			devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER;654			devfmt.clock_divider = 9;655			break;656		}657		break;658 659	case CATPT_SSP_IFACE_1:660		devfmt.mclk = CATPT_MCLK_OFF;661		devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER;662		devfmt.clock_divider = 0;663		break;664	}665 666	/* see if this is a new configuration */667	if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))668		return 0;669 670	ret = pm_runtime_resume_and_get(cdev->dev);671	if (ret < 0 && ret != -EACCES)672		return ret;673 674	ret = catpt_ipc_set_device_format(cdev, &devfmt);675 676	pm_runtime_mark_last_busy(cdev->dev);677	pm_runtime_put_autosuspend(cdev->dev);678 679	if (ret)680		return CATPT_IPC_ERROR(ret);681 682	/* store device format set for given SSP */683	memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));684	return 0;685}686 687static const struct snd_soc_dai_ops catpt_dai_ops = {688	.pcm_new = catpt_dai_pcm_new,689};690 691static struct snd_soc_dai_driver dai_drivers[] = {692/* FE DAIs */693{694	.name  = "System Pin",695	.id = CATPT_STRM_TYPE_SYSTEM,696	.ops = &catpt_fe_dai_ops,697	.playback = {698		.stream_name = "System Playback",699		.channels_min = 2,700		.channels_max = 2,701		.rates = SNDRV_PCM_RATE_48000,702		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,703	},704	.capture = {705		.stream_name = "Analog Capture",706		.channels_min = 2,707		.channels_max = 4,708		.rates = SNDRV_PCM_RATE_48000,709		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,710	},711},712{713	.name  = "Offload0 Pin",714	.id = CATPT_STRM_TYPE_RENDER,715	.ops = &catpt_fe_dai_ops,716	.playback = {717		.stream_name = "Offload0 Playback",718		.channels_min = 2,719		.channels_max = 2,720		.rates = SNDRV_PCM_RATE_8000_192000,721		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,722	},723},724{725	.name  = "Offload1 Pin",726	.id = CATPT_STRM_TYPE_RENDER,727	.ops = &catpt_fe_dai_ops,728	.playback = {729		.stream_name = "Offload1 Playback",730		.channels_min = 2,731		.channels_max = 2,732		.rates = SNDRV_PCM_RATE_8000_192000,733		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,734	},735},736{737	.name  = "Loopback Pin",738	.id = CATPT_STRM_TYPE_LOOPBACK,739	.ops = &catpt_fe_dai_ops,740	.capture = {741		.stream_name = "Loopback Capture",742		.channels_min = 2,743		.channels_max = 2,744		.rates = SNDRV_PCM_RATE_48000,745		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,746	},747},748{749	.name  = "Bluetooth Pin",750	.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,751	.ops = &catpt_fe_dai_ops,752	.playback = {753		.stream_name = "Bluetooth Playback",754		.channels_min = 1,755		.channels_max = 1,756		.rates = SNDRV_PCM_RATE_8000,757		.formats = SNDRV_PCM_FMTBIT_S16_LE,758	},759	.capture = {760		.stream_name = "Bluetooth Capture",761		.channels_min = 1,762		.channels_max = 1,763		.rates = SNDRV_PCM_RATE_8000,764		.formats = SNDRV_PCM_FMTBIT_S16_LE,765	},766},767/* BE DAIs */768{769	.name = "ssp0-port",770	.id = CATPT_SSP_IFACE_0,771	.playback = {772		.channels_min = 1,773		.channels_max = 8,774	},775	.capture = {776		.channels_min = 1,777		.channels_max = 8,778	},779	.ops = &catpt_dai_ops,780},781{782	.name = "ssp1-port",783	.id = CATPT_SSP_IFACE_1,784	.playback = {785		.channels_min = 1,786		.channels_max = 8,787	},788	.capture = {789		.channels_min = 1,790		.channels_max = 8,791	},792	.ops = &catpt_dai_ops,793},794};795 796#define DSP_VOLUME_MAX		S32_MAX /* 0db */797#define DSP_VOLUME_STEP_MAX	30798 799static u32 ctlvol_to_dspvol(u32 value)800{801	if (value > DSP_VOLUME_STEP_MAX)802		value = 0;803	return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);804}805 806static u32 dspvol_to_ctlvol(u32 volume)807{808	if (volume > DSP_VOLUME_MAX)809		return DSP_VOLUME_STEP_MAX;810	return volume ? __fls(volume) : 0;811}812 813static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)814{815	u32 dspvol;816	int ret, i;817 818	for (i = 1; i < CATPT_CHANNELS_MAX; i++)819		if (ctlvol[i] != ctlvol[0])820			break;821 822	if (i == CATPT_CHANNELS_MAX) {823		dspvol = ctlvol_to_dspvol(ctlvol[0]);824 825		ret = catpt_ipc_set_volume(cdev, stream_id,826					   CATPT_ALL_CHANNELS_MASK, dspvol,827					   0, CATPT_AUDIO_CURVE_NONE);828	} else {829		for (i = 0; i < CATPT_CHANNELS_MAX; i++) {830			dspvol = ctlvol_to_dspvol(ctlvol[i]);831 832			ret = catpt_ipc_set_volume(cdev, stream_id,833						   i, dspvol,834						   0, CATPT_AUDIO_CURVE_NONE);835			if (ret)836				break;837		}838	}839 840	if (ret)841		return CATPT_IPC_ERROR(ret);842	return 0;843}844 845static int catpt_volume_info(struct snd_kcontrol *kcontrol,846			     struct snd_ctl_elem_info *uinfo)847{848	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;849	uinfo->count = CATPT_CHANNELS_MAX;850	uinfo->value.integer.min = 0;851	uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;852	return 0;853}854 855static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,856				  struct snd_ctl_elem_value *ucontrol)857{858	struct snd_soc_component *component =859		snd_soc_kcontrol_component(kcontrol);860	struct catpt_dev *cdev = dev_get_drvdata(component->dev);861	u32 dspvol;862	int ret;863	int i;864 865	ret = pm_runtime_resume_and_get(cdev->dev);866	if (ret < 0 && ret != -EACCES)867		return ret;868 869	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {870		dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);871		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);872	}873 874	pm_runtime_mark_last_busy(cdev->dev);875	pm_runtime_put_autosuspend(cdev->dev);876 877	return 0;878}879 880static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,881				  struct snd_ctl_elem_value *ucontrol)882{883	struct snd_soc_component *component =884		snd_soc_kcontrol_component(kcontrol);885	struct catpt_dev *cdev = dev_get_drvdata(component->dev);886	int ret;887 888	ret = pm_runtime_resume_and_get(cdev->dev);889	if (ret < 0 && ret != -EACCES)890		return ret;891 892	ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,893			       ucontrol->value.integer.value);894 895	pm_runtime_mark_last_busy(cdev->dev);896	pm_runtime_put_autosuspend(cdev->dev);897 898	return ret;899}900 901static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,902				   struct snd_ctl_elem_value *ucontrol,903				   enum catpt_pin_id pin_id)904{905	struct snd_soc_component *component =906		snd_soc_kcontrol_component(kcontrol);907	struct catpt_stream_runtime *stream;908	struct catpt_dev *cdev = dev_get_drvdata(component->dev);909	long *ctlvol = (long *)kcontrol->private_value;910	u32 dspvol;911	int ret;912	int i;913 914	stream = catpt_stream_find(cdev, pin_id);915	if (!stream) {916		for (i = 0; i < CATPT_CHANNELS_MAX; i++)917			ucontrol->value.integer.value[i] = ctlvol[i];918		return 0;919	}920 921	ret = pm_runtime_resume_and_get(cdev->dev);922	if (ret < 0 && ret != -EACCES)923		return ret;924 925	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {926		dspvol = catpt_stream_volume(cdev, stream, i);927		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);928	}929 930	pm_runtime_mark_last_busy(cdev->dev);931	pm_runtime_put_autosuspend(cdev->dev);932 933	return 0;934}935 936static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,937				   struct snd_ctl_elem_value *ucontrol,938				   enum catpt_pin_id pin_id)939{940	struct snd_soc_component *component =941		snd_soc_kcontrol_component(kcontrol);942	struct catpt_stream_runtime *stream;943	struct catpt_dev *cdev = dev_get_drvdata(component->dev);944	long *ctlvol = (long *)kcontrol->private_value;945	int ret, i;946 947	stream = catpt_stream_find(cdev, pin_id);948	if (!stream) {949		for (i = 0; i < CATPT_CHANNELS_MAX; i++)950			ctlvol[i] = ucontrol->value.integer.value[i];951		return 0;952	}953 954	ret = pm_runtime_resume_and_get(cdev->dev);955	if (ret < 0 && ret != -EACCES)956		return ret;957 958	ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,959			       ucontrol->value.integer.value);960 961	pm_runtime_mark_last_busy(cdev->dev);962	pm_runtime_put_autosuspend(cdev->dev);963 964	if (ret)965		return ret;966 967	for (i = 0; i < CATPT_CHANNELS_MAX; i++)968		ctlvol[i] = ucontrol->value.integer.value[i];969	return 0;970}971 972static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,973				     struct snd_ctl_elem_value *uctl)974{975	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);976}977 978static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,979				     struct snd_ctl_elem_value *uctl)980{981	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);982}983 984static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,985				     struct snd_ctl_elem_value *uctl)986{987	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);988}989 990static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,991				     struct snd_ctl_elem_value *uctl)992{993	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);994}995 996static int catpt_capture_volume_get(struct snd_kcontrol *kctl,997				    struct snd_ctl_elem_value *uctl)998{999	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);1000}1001 1002static int catpt_capture_volume_put(struct snd_kcontrol *kctl,1003				    struct snd_ctl_elem_value *uctl)1004{1005	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);1006}1007 1008static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,1009				     struct snd_ctl_elem_value *ucontrol)1010{1011	ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;1012	return 0;1013}1014 1015static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,1016				     struct snd_ctl_elem_value *ucontrol)1017{1018	struct snd_soc_component *component =1019		snd_soc_kcontrol_component(kcontrol);1020	struct catpt_stream_runtime *stream;1021	struct catpt_dev *cdev = dev_get_drvdata(component->dev);1022	bool mute;1023	int ret;1024 1025	mute = (bool)ucontrol->value.integer.value[0];1026	stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);1027	if (!stream) {1028		*(bool *)kcontrol->private_value = mute;1029		return 0;1030	}1031 1032	ret = pm_runtime_resume_and_get(cdev->dev);1033	if (ret < 0 && ret != -EACCES)1034		return ret;1035 1036	ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);1037 1038	pm_runtime_mark_last_busy(cdev->dev);1039	pm_runtime_put_autosuspend(cdev->dev);1040 1041	if (ret)1042		return CATPT_IPC_ERROR(ret);1043 1044	*(bool *)kcontrol->private_value = mute;1045	return 0;1046}1047 1048static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,1049				  struct snd_ctl_elem_value *ucontrol)1050{1051	return 0;1052}1053 1054static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,1055				  struct snd_ctl_elem_value *ucontrol)1056{1057	return 0;1058}1059 1060static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,1061				 unsigned int __user *bytes,1062				 unsigned int size)1063{1064	return 0;1065}1066 1067static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,1068				 const unsigned int __user *bytes,1069				 unsigned int size)1070{1071	return 0;1072}1073 1074static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);1075 1076#define CATPT_VOLUME_CTL(kname, sname) \1077{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \1078	.name = (kname), \1079	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \1080		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \1081	.info = catpt_volume_info, \1082	.get = catpt_##sname##_volume_get, \1083	.put = catpt_##sname##_volume_put, \1084	.tlv.p = catpt_volume_tlv, \1085	.private_value = (unsigned long) \1086		&(long[CATPT_CHANNELS_MAX]) {0} }1087 1088static const struct snd_kcontrol_new component_kcontrols[] = {1089/* Master volume (mixer stream) */1090CATPT_VOLUME_CTL("Master Playback Volume", mixer),1091/* Individual volume controls for offload and capture */1092CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),1093CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),1094CATPT_VOLUME_CTL("Mic Capture Volume", capture),1095SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},1096		    catpt_loopback_switch_get, catpt_loopback_switch_put),1097/* Enable or disable WAVES module */1098SOC_SINGLE_BOOL_EXT("Waves Switch", 0,1099		    catpt_waves_switch_get, catpt_waves_switch_put),1100/* WAVES module parameter control */1101SND_SOC_BYTES_TLV("Waves Set Param", 128,1102		  catpt_waves_param_get, catpt_waves_param_put),1103};1104 1105static const struct snd_soc_dapm_widget component_widgets[] = {1106	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),1107	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),1108	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),1109	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),1110 1111	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),1112};1113 1114static const struct snd_soc_dapm_route component_routes[] = {1115	{"Playback VMixer", NULL, "System Playback"},1116	{"Playback VMixer", NULL, "Offload0 Playback"},1117	{"Playback VMixer", NULL, "Offload1 Playback"},1118 1119	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},1120 1121	{"Analog Capture", NULL, "SSP0 CODEC IN"},1122	{"Loopback Capture", NULL, "SSP0 CODEC IN"},1123 1124	{"SSP1 BT OUT", NULL, "Bluetooth Playback"},1125	{"Bluetooth Capture", NULL, "SSP1 BT IN"},1126};1127 1128static const struct snd_soc_component_driver catpt_comp_driver = {1129	.name = "catpt-platform",1130 1131	.pcm_construct = catpt_component_pcm_construct,1132	.open = catpt_component_open,1133	.pointer = catpt_component_pointer,1134 1135	.controls = component_kcontrols,1136	.num_controls = ARRAY_SIZE(component_kcontrols),1137	.dapm_widgets = component_widgets,1138	.num_dapm_widgets = ARRAY_SIZE(component_widgets),1139	.dapm_routes = component_routes,1140	.num_dapm_routes = ARRAY_SIZE(component_routes),1141};1142 1143int catpt_arm_stream_templates(struct catpt_dev *cdev)1144{1145	struct resource *res;1146	u32 scratch_size = 0;1147	int i, j;1148 1149	for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {1150		struct catpt_stream_template *template;1151		struct catpt_module_entry *entry;1152		struct catpt_module_type *type;1153 1154		template = catpt_topology[i];1155		template->persistent_size = 0;1156 1157		for (j = 0; j < template->num_entries; j++) {1158			entry = &template->entries[j];1159			type = &cdev->modules[entry->module_id];1160 1161			if (!type->loaded)1162				return -ENOENT;1163 1164			entry->entry_point = type->entry_point;1165			template->persistent_size += type->persistent_size;1166			if (type->scratch_size > scratch_size)1167				scratch_size = type->scratch_size;1168		}1169	}1170 1171	if (scratch_size) {1172		/* allocate single scratch area for all modules */1173		res = catpt_request_region(&cdev->dram, scratch_size);1174		if (!res)1175			return -EBUSY;1176		cdev->scratch = res;1177	}1178 1179	return 0;1180}1181 1182int catpt_register_plat_component(struct catpt_dev *cdev)1183{1184	struct snd_soc_component *component;1185	int ret;1186 1187	component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);1188	if (!component)1189		return -ENOMEM;1190 1191	ret = snd_soc_component_initialize(component, &catpt_comp_driver,1192					   cdev->dev);1193	if (ret)1194		return ret;1195 1196	component->name = catpt_comp_driver.name;1197	return snd_soc_add_component(component, dai_drivers,1198				     ARRAY_SIZE(dai_drivers));1199}1200