File Index
Files and directories
Sign in
FILE

pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/intel/catpt/pcm.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2//
   3// Copyright(c) 2020 Intel Corporation
   4//
   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		else
 174			*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_LEFT
 186				      | (CATPT_CHANNEL_RIGHT << 4);
 187
 188	case CATPT_CHANNEL_CONFIG_2_POINT_1:
 189		return GENMASK(31, 12) | CATPT_CHANNEL_LEFT
 190				       | (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_LEFT
 195				       | (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_LEFT
 200				       | (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_LEFT
 206				       | (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_LEFT
 212				       | (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_LEFT
 218				       | (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_CENTER
 225				       | (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_LEFT
 233				      | (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		catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
 422		return ret;
 423	}
 424
 425	stream->allocated = true;
 426	return 0;
 427}
 428
 429static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
 430			     struct snd_soc_dai *dai)
 431{
 432	struct catpt_stream_runtime *stream;
 433	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
 434
 435	stream = snd_soc_dai_get_dma_data(dai, substream);
 436	if (!stream->allocated)
 437		return 0;
 438
 439	catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
 440	catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
 441
 442	stream->allocated = false;
 443	return 0;
 444}
 445
 446static int catpt_dai_prepare(struct snd_pcm_substream *substream,
 447			     struct snd_soc_dai *dai)
 448{
 449	struct catpt_stream_runtime *stream;
 450	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
 451	int ret;
 452
 453	stream = snd_soc_dai_get_dma_data(dai, substream);
 454	if (stream->prepared)
 455		return 0;
 456
 457	ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
 458	if (ret)
 459		return CATPT_IPC_ERROR(ret);
 460
 461	ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
 462	if (ret)
 463		return CATPT_IPC_ERROR(ret);
 464
 465	stream->prepared = true;
 466	return 0;
 467}
 468
 469static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 470			     struct snd_soc_dai *dai)
 471{
 472	struct snd_pcm_runtime *runtime = substream->runtime;
 473	struct catpt_stream_runtime *stream;
 474	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
 475	snd_pcm_uframes_t pos;
 476	int ret;
 477
 478	stream = snd_soc_dai_get_dma_data(dai, substream);
 479
 480	switch (cmd) {
 481	case SNDRV_PCM_TRIGGER_START:
 482		/* only offload is set_write_pos driven */
 483		if (stream->template->type != CATPT_STRM_TYPE_RENDER)
 484			goto resume_stream;
 485
 486		pos = frames_to_bytes(runtime, runtime->start_threshold);
 487		/*
 488		 * Dsp operates on buffer halves, thus max 2x set_write_pos
 489		 * (entire buffer filled) prior to stream start.
 490		 */
 491		ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
 492					      pos, false, false);
 493		if (ret)
 494			return CATPT_IPC_ERROR(ret);
 495		fallthrough;
 496	case SNDRV_PCM_TRIGGER_RESUME:
 497	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 498	resume_stream:
 499		catpt_dsp_update_lpclock(cdev);
 500		ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
 501		if (ret)
 502			return CATPT_IPC_ERROR(ret);
 503		break;
 504
 505	case SNDRV_PCM_TRIGGER_STOP:
 506		stream->prepared = false;
 507		fallthrough;
 508	case SNDRV_PCM_TRIGGER_SUSPEND:
 509	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 510		ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
 511		catpt_dsp_update_lpclock(cdev);
 512		if (ret)
 513			return CATPT_IPC_ERROR(ret);
 514		break;
 515
 516	default:
 517		break;
 518	}
 519
 520	return 0;
 521}
 522
 523void catpt_stream_update_position(struct catpt_dev *cdev,
 524				  struct catpt_stream_runtime *stream,
 525				  struct catpt_notify_position *pos)
 526{
 527	struct snd_pcm_substream *substream = stream->substream;
 528	struct snd_pcm_runtime *r = substream->runtime;
 529	snd_pcm_uframes_t dsppos, newpos;
 530	int ret;
 531
 532	dsppos = bytes_to_frames(r, pos->stream_position);
 533
 534	if (!stream->prepared)
 535		goto exit;
 536	/* only offload is set_write_pos driven */
 537	if (stream->template->type != CATPT_STRM_TYPE_RENDER)
 538		goto exit;
 539
 540	if (dsppos >= r->buffer_size / 2)
 541		newpos = r->buffer_size / 2;
 542	else
 543		newpos = 0;
 544	/*
 545	 * Dsp operates on buffer halves, thus on every notify position
 546	 * (buffer half consumed) update wp to allow stream progression.
 547	 */
 548	ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
 549				      frames_to_bytes(r, newpos),
 550				      false, false);
 551	if (ret) {
 552		dev_err(cdev->dev, "update position for stream %d failed: %d\n",
 553			stream->info.stream_hw_id, ret);
 554		return;
 555	}
 556exit:
 557	snd_pcm_period_elapsed(substream);
 558}
 559
 560/* 200 ms for 2 32-bit channels at 48kHz (native format) */
 561#define CATPT_BUFFER_MAX_SIZE	76800
 562#define CATPT_PCM_PERIODS_MAX	4
 563#define CATPT_PCM_PERIODS_MIN	2
 564
 565static const struct snd_pcm_hardware catpt_pcm_hardware = {
 566	.info			= SNDRV_PCM_INFO_MMAP |
 567				  SNDRV_PCM_INFO_MMAP_VALID |
 568				  SNDRV_PCM_INFO_INTERLEAVED |
 569				  SNDRV_PCM_INFO_PAUSE |
 570				  SNDRV_PCM_INFO_RESUME |
 571				  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 572	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
 573				  SNDRV_PCM_FMTBIT_S32_LE,
 574	.subformats		= SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 575				  SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 576	.period_bytes_min	= PAGE_SIZE,
 577	.period_bytes_max	= CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,
 578	.periods_min		= CATPT_PCM_PERIODS_MIN,
 579	.periods_max		= CATPT_PCM_PERIODS_MAX,
 580	.buffer_bytes_max	= CATPT_BUFFER_MAX_SIZE,
 581};
 582
 583static int catpt_component_pcm_construct(struct snd_soc_component *component,
 584					 struct snd_soc_pcm_runtime *rtm)
 585{
 586	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 587
 588	snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,
 589				       cdev->dev,
 590				       catpt_pcm_hardware.buffer_bytes_max,
 591				       catpt_pcm_hardware.buffer_bytes_max);
 592
 593	return 0;
 594}
 595
 596static int catpt_component_open(struct snd_soc_component *component,
 597				struct snd_pcm_substream *substream)
 598{
 599	struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
 600
 601	if (!rtm->dai_link->no_pcm)
 602		snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware);
 603	return 0;
 604}
 605
 606static snd_pcm_uframes_t
 607catpt_component_pointer(struct snd_soc_component *component,
 608			struct snd_pcm_substream *substream)
 609{
 610	struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream);
 611	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0);
 612	struct catpt_stream_runtime *stream;
 613	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 614	u32 pos;
 615
 616	if (rtm->dai_link->no_pcm)
 617		return 0;
 618
 619	stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
 620	pos = catpt_stream_read_position(cdev, stream);
 621
 622	return bytes_to_frames(substream->runtime, pos);
 623}
 624
 625static const struct snd_soc_dai_ops catpt_fe_dai_ops = {
 626	.startup = catpt_dai_startup,
 627	.shutdown = catpt_dai_shutdown,
 628	.hw_params = catpt_dai_hw_params,
 629	.hw_free = catpt_dai_hw_free,
 630	.prepare = catpt_dai_prepare,
 631	.trigger = catpt_dai_trigger,
 632};
 633
 634static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
 635			     struct snd_soc_dai *dai)
 636{
 637	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0);
 638	struct catpt_ssp_device_format devfmt;
 639	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
 640	int ret;
 641
 642	devfmt.iface = dai->driver->id;
 643	devfmt.channels = codec_dai->driver->capture.channels_max;
 644
 645	switch (devfmt.iface) {
 646	case CATPT_SSP_IFACE_0:
 647		devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ;
 648
 649		switch (devfmt.channels) {
 650		case 4:
 651			devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER;
 652			devfmt.clock_divider = 4;
 653			break;
 654		case 2:
 655		default:
 656			devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER;
 657			devfmt.clock_divider = 9;
 658			break;
 659		}
 660		break;
 661
 662	case CATPT_SSP_IFACE_1:
 663		devfmt.mclk = CATPT_MCLK_OFF;
 664		devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER;
 665		devfmt.clock_divider = 0;
 666		break;
 667	}
 668
 669	/* see if this is a new configuration */
 670	if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))
 671		return 0;
 672
 673	ret = pm_runtime_resume_and_get(cdev->dev);
 674	if (ret)
 675		return ret;
 676
 677	ret = catpt_ipc_set_device_format(cdev, &devfmt);
 678
 679	pm_runtime_put_autosuspend(cdev->dev);
 680
 681	if (ret)
 682		return CATPT_IPC_ERROR(ret);
 683
 684	/* store device format set for given SSP */
 685	memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
 686	return 0;
 687}
 688
 689static const struct snd_soc_dai_ops catpt_dai_ops = {
 690	.pcm_new = catpt_dai_pcm_new,
 691};
 692
 693static struct snd_soc_dai_driver dai_drivers[] = {
 694/* FE DAIs */
 695{
 696	.name  = "System Pin",
 697	.id = CATPT_STRM_TYPE_SYSTEM,
 698	.ops = &catpt_fe_dai_ops,
 699	.playback = {
 700		.stream_name = "System Playback",
 701		.channels_min = 2,
 702		.channels_max = 2,
 703		.rates = SNDRV_PCM_RATE_48000,
 704		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 705		.subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 706			      SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 707	},
 708	.capture = {
 709		.stream_name = "Analog Capture",
 710		.channels_min = 2,
 711		.channels_max = 4,
 712		.rates = SNDRV_PCM_RATE_48000,
 713		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 714		.subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 715			      SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 716	},
 717},
 718{
 719	.name  = "Offload0 Pin",
 720	.id = CATPT_STRM_TYPE_RENDER,
 721	.ops = &catpt_fe_dai_ops,
 722	.playback = {
 723		.stream_name = "Offload0 Playback",
 724		.channels_min = 2,
 725		.channels_max = 2,
 726		.rates = SNDRV_PCM_RATE_8000_192000,
 727		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 728		.subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 729			      SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 730	},
 731},
 732{
 733	.name  = "Offload1 Pin",
 734	.id = CATPT_STRM_TYPE_RENDER,
 735	.ops = &catpt_fe_dai_ops,
 736	.playback = {
 737		.stream_name = "Offload1 Playback",
 738		.channels_min = 2,
 739		.channels_max = 2,
 740		.rates = SNDRV_PCM_RATE_8000_192000,
 741		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 742		.subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 743			      SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 744	},
 745},
 746{
 747	.name  = "Loopback Pin",
 748	.id = CATPT_STRM_TYPE_LOOPBACK,
 749	.ops = &catpt_fe_dai_ops,
 750	.capture = {
 751		.stream_name = "Loopback Capture",
 752		.channels_min = 2,
 753		.channels_max = 2,
 754		.rates = SNDRV_PCM_RATE_48000,
 755		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 756		.subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
 757			      SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
 758	},
 759},
 760{
 761	.name  = "Bluetooth Pin",
 762	.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
 763	.ops = &catpt_fe_dai_ops,
 764	.playback = {
 765		.stream_name = "Bluetooth Playback",
 766		.channels_min = 1,
 767		.channels_max = 1,
 768		.rates = SNDRV_PCM_RATE_8000,
 769		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 770	},
 771	.capture = {
 772		.stream_name = "Bluetooth Capture",
 773		.channels_min = 1,
 774		.channels_max = 1,
 775		.rates = SNDRV_PCM_RATE_8000,
 776		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 777	},
 778},
 779/* BE DAIs */
 780{
 781	.name = "ssp0-port",
 782	.id = CATPT_SSP_IFACE_0,
 783	.playback = {
 784		.channels_min = 1,
 785		.channels_max = 8,
 786	},
 787	.capture = {
 788		.channels_min = 1,
 789		.channels_max = 8,
 790	},
 791	.ops = &catpt_dai_ops,
 792},
 793{
 794	.name = "ssp1-port",
 795	.id = CATPT_SSP_IFACE_1,
 796	.playback = {
 797		.channels_min = 1,
 798		.channels_max = 8,
 799	},
 800	.capture = {
 801		.channels_min = 1,
 802		.channels_max = 8,
 803	},
 804	.ops = &catpt_dai_ops,
 805},
 806};
 807
 808#define DSP_VOLUME_MAX		S32_MAX /* 0db */
 809#define DSP_VOLUME_STEP_MAX	30
 810
 811static u32 ctlvol_to_dspvol(u32 value)
 812{
 813	if (value > DSP_VOLUME_STEP_MAX)
 814		value = 0;
 815	return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);
 816}
 817
 818static u32 dspvol_to_ctlvol(u32 volume)
 819{
 820	if (volume > DSP_VOLUME_MAX)
 821		return DSP_VOLUME_STEP_MAX;
 822	return volume ? __fls(volume) : 0;
 823}
 824
 825static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
 826{
 827	u32 dspvol;
 828	int ret, i;
 829
 830	for (i = 1; i < CATPT_CHANNELS_MAX; i++)
 831		if (ctlvol[i] != ctlvol[0])
 832			break;
 833
 834	if (i == CATPT_CHANNELS_MAX) {
 835		dspvol = ctlvol_to_dspvol(ctlvol[0]);
 836
 837		ret = catpt_ipc_set_volume(cdev, stream_id,
 838					   CATPT_ALL_CHANNELS_MASK, dspvol,
 839					   0, CATPT_AUDIO_CURVE_NONE);
 840	} else {
 841		for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
 842			dspvol = ctlvol_to_dspvol(ctlvol[i]);
 843
 844			ret = catpt_ipc_set_volume(cdev, stream_id,
 845						   i, dspvol,
 846						   0, CATPT_AUDIO_CURVE_NONE);
 847			if (ret)
 848				break;
 849		}
 850	}
 851
 852	if (ret)
 853		return CATPT_IPC_ERROR(ret);
 854	return 0;
 855}
 856
 857static int catpt_volume_info(struct snd_kcontrol *kcontrol,
 858			     struct snd_ctl_elem_info *uinfo)
 859{
 860	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 861	uinfo->count = CATPT_CHANNELS_MAX;
 862	uinfo->value.integer.min = 0;
 863	uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;
 864	return 0;
 865}
 866
 867static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
 868				  struct snd_ctl_elem_value *ucontrol)
 869{
 870	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 871	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 872	u32 dspvol;
 873	int ret;
 874	int i;
 875
 876	ret = pm_runtime_resume_and_get(cdev->dev);
 877	if (ret)
 878		return ret;
 879
 880	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
 881		dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
 882		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
 883	}
 884
 885	pm_runtime_put_autosuspend(cdev->dev);
 886
 887	return 0;
 888}
 889
 890static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
 891				  struct snd_ctl_elem_value *ucontrol)
 892{
 893	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 894	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 895	int ret;
 896
 897	ret = pm_runtime_resume_and_get(cdev->dev);
 898	if (ret)
 899		return ret;
 900
 901	ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
 902			       ucontrol->value.integer.value);
 903
 904	pm_runtime_put_autosuspend(cdev->dev);
 905
 906	return ret;
 907}
 908
 909static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
 910				   struct snd_ctl_elem_value *ucontrol,
 911				   enum catpt_pin_id pin_id)
 912{
 913	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 914	struct catpt_stream_runtime *stream;
 915	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 916	long *ctlvol = (long *)kcontrol->private_value;
 917	u32 dspvol;
 918	int ret;
 919	int i;
 920
 921	stream = catpt_stream_find(cdev, pin_id);
 922	if (!stream) {
 923		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
 924			ucontrol->value.integer.value[i] = ctlvol[i];
 925		return 0;
 926	}
 927
 928	ret = pm_runtime_resume_and_get(cdev->dev);
 929	if (ret)
 930		return ret;
 931
 932	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
 933		dspvol = catpt_stream_volume(cdev, stream, i);
 934		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
 935	}
 936
 937	pm_runtime_put_autosuspend(cdev->dev);
 938
 939	return 0;
 940}
 941
 942static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
 943				   struct snd_ctl_elem_value *ucontrol,
 944				   enum catpt_pin_id pin_id)
 945{
 946	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 947	struct catpt_stream_runtime *stream;
 948	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
 949	long *ctlvol = (long *)kcontrol->private_value;
 950	int ret, i;
 951
 952	stream = catpt_stream_find(cdev, pin_id);
 953	if (!stream) {
 954		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
 955			ctlvol[i] = ucontrol->value.integer.value[i];
 956		return 0;
 957	}
 958
 959	ret = pm_runtime_resume_and_get(cdev->dev);
 960	if (ret)
 961		return ret;
 962
 963	ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
 964			       ucontrol->value.integer.value);
 965
 966	pm_runtime_put_autosuspend(cdev->dev);
 967
 968	if (ret)
 969		return ret;
 970
 971	for (i = 0; i < CATPT_CHANNELS_MAX; i++)
 972		ctlvol[i] = ucontrol->value.integer.value[i];
 973	return 0;
 974}
 975
 976static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,
 977				     struct snd_ctl_elem_value *uctl)
 978{
 979	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
 980}
 981
 982static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,
 983				     struct snd_ctl_elem_value *uctl)
 984{
 985	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
 986}
 987
 988static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,
 989				     struct snd_ctl_elem_value *uctl)
 990{
 991	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
 992}
 993
 994static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,
 995				     struct snd_ctl_elem_value *uctl)
 996{
 997	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
 998}
 999
1000static int catpt_capture_volume_get(struct snd_kcontrol *kctl,
1001				    struct snd_ctl_elem_value *uctl)
1002{
1003	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
1004}
1005
1006static int catpt_capture_volume_put(struct snd_kcontrol *kctl,
1007				    struct snd_ctl_elem_value *uctl)
1008{
1009	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
1010}
1011
1012static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
1013				     struct snd_ctl_elem_value *ucontrol)
1014{
1015	ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
1016	return 0;
1017}
1018
1019static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
1020				     struct snd_ctl_elem_value *ucontrol)
1021{
1022	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
1023	struct catpt_stream_runtime *stream;
1024	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
1025	bool mute;
1026	int ret;
1027
1028	mute = (bool)ucontrol->value.integer.value[0];
1029	stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
1030	if (!stream) {
1031		*(bool *)kcontrol->private_value = mute;
1032		return 0;
1033	}
1034
1035	ret = pm_runtime_resume_and_get(cdev->dev);
1036	if (ret)
1037		return ret;
1038
1039	ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
1040
1041	pm_runtime_put_autosuspend(cdev->dev);
1042
1043	if (ret)
1044		return CATPT_IPC_ERROR(ret);
1045
1046	*(bool *)kcontrol->private_value = mute;
1047	return 0;
1048}
1049
1050static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
1051				  struct snd_ctl_elem_value *ucontrol)
1052{
1053	return 0;
1054}
1055
1056static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
1057				  struct snd_ctl_elem_value *ucontrol)
1058{
1059	return 0;
1060}
1061
1062static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
1063				 unsigned int __user *bytes,
1064				 unsigned int size)
1065{
1066	return 0;
1067}
1068
1069static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
1070				 const unsigned int __user *bytes,
1071				 unsigned int size)
1072{
1073	return 0;
1074}
1075
1076static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
1077
1078#define CATPT_VOLUME_CTL(kname, sname) \
1079{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1080	.name = (kname), \
1081	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
1082		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
1083	.info = catpt_volume_info, \
1084	.get = catpt_##sname##_volume_get, \
1085	.put = catpt_##sname##_volume_put, \
1086	.tlv.p = catpt_volume_tlv, \
1087	.private_value = (unsigned long) \
1088		&(long[CATPT_CHANNELS_MAX]) {0} }
1089
1090static const struct snd_kcontrol_new component_kcontrols[] = {
1091/* Master volume (mixer stream) */
1092CATPT_VOLUME_CTL("Master Playback Volume", mixer),
1093/* Individual volume controls for offload and capture */
1094CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),
1095CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),
1096CATPT_VOLUME_CTL("Mic Capture Volume", capture),
1097SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},
1098		    catpt_loopback_switch_get, catpt_loopback_switch_put),
1099/* Enable or disable WAVES module */
1100SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
1101		    catpt_waves_switch_get, catpt_waves_switch_put),
1102/* WAVES module parameter control */
1103SND_SOC_BYTES_TLV("Waves Set Param", 128,
1104		  catpt_waves_param_get, catpt_waves_param_put),
1105};
1106
1107static const struct snd_soc_dapm_widget component_widgets[] = {
1108	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1109	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1110	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1111	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1112
1113	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
1114};
1115
1116static const struct snd_soc_dapm_route component_routes[] = {
1117	{"Playback VMixer", NULL, "System Playback"},
1118	{"Playback VMixer", NULL, "Offload0 Playback"},
1119	{"Playback VMixer", NULL, "Offload1 Playback"},
1120
1121	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},
1122
1123	{"Analog Capture", NULL, "SSP0 CODEC IN"},
1124	{"Loopback Capture", NULL, "SSP0 CODEC IN"},
1125
1126	{"SSP1 BT OUT", NULL, "Bluetooth Playback"},
1127	{"Bluetooth Capture", NULL, "SSP1 BT IN"},
1128};
1129
1130static const struct snd_soc_component_driver catpt_comp_driver = {
1131	.name = "catpt-platform",
1132
1133	.pcm_construct = catpt_component_pcm_construct,
1134	.open = catpt_component_open,
1135	.pointer = catpt_component_pointer,
1136
1137	.controls = component_kcontrols,
1138	.num_controls = ARRAY_SIZE(component_kcontrols),
1139	.dapm_widgets = component_widgets,
1140	.num_dapm_widgets = ARRAY_SIZE(component_widgets),
1141	.dapm_routes = component_routes,
1142	.num_dapm_routes = ARRAY_SIZE(component_routes),
1143};
1144
1145int catpt_arm_stream_templates(struct catpt_dev *cdev)
1146{
1147	struct resource *res;
1148	u32 scratch_size = 0;
1149	int i, j;
1150
1151	for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
1152		struct catpt_stream_template *template;
1153		struct catpt_module_entry *entry;
1154		struct catpt_module_type *type;
1155
1156		template = catpt_topology[i];
1157		template->persistent_size = 0;
1158
1159		for (j = 0; j < template->num_entries; j++) {
1160			entry = &template->entries[j];
1161			type = &cdev->modules[entry->module_id];
1162
1163			if (!type->loaded)
1164				return -ENOENT;
1165
1166			entry->entry_point = type->entry_point;
1167			template->persistent_size += type->persistent_size;
1168			if (type->scratch_size > scratch_size)
1169				scratch_size = type->scratch_size;
1170		}
1171	}
1172
1173	if (scratch_size) {
1174		/* allocate single scratch area for all modules */
1175		res = catpt_request_region(&cdev->dram, scratch_size);
1176		if (!res)
1177			return -EBUSY;
1178		cdev->scratch = res;
1179	}
1180
1181	return 0;
1182}
1183
1184int catpt_register_plat_component(struct catpt_dev *cdev)
1185{
1186	struct snd_soc_component *component;
1187	int ret;
1188
1189	component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
1190	if (!component)
1191		return -ENOMEM;
1192
1193	ret = snd_soc_component_initialize(component, &catpt_comp_driver,
1194					   cdev->dev);
1195	if (ret)
1196		return ret;
1197
1198	component->name = catpt_comp_driver.name;
1199	return snd_soc_add_component(component, dai_drivers,
1200				     ARRAY_SIZE(dai_drivers));
1201}
~/ Read-only file preview.