cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pcm.c (31397B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Copyright(c) 2020 Intel Corporation. All rights reserved.
      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 = asoc_substream_to_rtd(substream);
     78	struct snd_soc_dai *cpu_dai = asoc_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		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_pos
    487		 * (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	else
    541		newpos = 0;
    542	/*
    543	 * Dsp operates on buffer halves, thus on every notify position
    544	 * (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	76800
    560#define CATPT_PCM_PERIODS_MAX	4
    561#define CATPT_PCM_PERIODS_MIN	2
    562
    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 = asoc_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_t
    604catpt_component_pointer(struct snd_soc_component *component,
    605			struct snd_pcm_substream *substream)
    606{
    607	struct snd_soc_pcm_runtime *rtm = asoc_substream_to_rtd(substream);
    608	struct snd_soc_dai *cpu_dai = asoc_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 = asoc_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	pm_runtime_get_sync(cdev->dev);
    671
    672	ret = catpt_ipc_set_device_format(cdev, &devfmt);
    673
    674	pm_runtime_mark_last_busy(cdev->dev);
    675	pm_runtime_put_autosuspend(cdev->dev);
    676
    677	if (ret)
    678		return CATPT_IPC_ERROR(ret);
    679
    680	/* store device format set for given SSP */
    681	memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
    682	return 0;
    683}
    684
    685static struct snd_soc_dai_driver dai_drivers[] = {
    686/* FE DAIs */
    687{
    688	.name  = "System Pin",
    689	.id = CATPT_STRM_TYPE_SYSTEM,
    690	.ops = &catpt_fe_dai_ops,
    691	.playback = {
    692		.stream_name = "System Playback",
    693		.channels_min = 2,
    694		.channels_max = 2,
    695		.rates = SNDRV_PCM_RATE_48000,
    696		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
    697	},
    698	.capture = {
    699		.stream_name = "Analog Capture",
    700		.channels_min = 2,
    701		.channels_max = 4,
    702		.rates = SNDRV_PCM_RATE_48000,
    703		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
    704	},
    705},
    706{
    707	.name  = "Offload0 Pin",
    708	.id = CATPT_STRM_TYPE_RENDER,
    709	.ops = &catpt_fe_dai_ops,
    710	.playback = {
    711		.stream_name = "Offload0 Playback",
    712		.channels_min = 2,
    713		.channels_max = 2,
    714		.rates = SNDRV_PCM_RATE_8000_192000,
    715		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
    716	},
    717},
    718{
    719	.name  = "Offload1 Pin",
    720	.id = CATPT_STRM_TYPE_RENDER,
    721	.ops = &catpt_fe_dai_ops,
    722	.playback = {
    723		.stream_name = "Offload1 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_S24_LE,
    728	},
    729},
    730{
    731	.name  = "Loopback Pin",
    732	.id = CATPT_STRM_TYPE_LOOPBACK,
    733	.ops = &catpt_fe_dai_ops,
    734	.capture = {
    735		.stream_name = "Loopback Capture",
    736		.channels_min = 2,
    737		.channels_max = 2,
    738		.rates = SNDRV_PCM_RATE_48000,
    739		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
    740	},
    741},
    742{
    743	.name  = "Bluetooth Pin",
    744	.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
    745	.ops = &catpt_fe_dai_ops,
    746	.playback = {
    747		.stream_name = "Bluetooth Playback",
    748		.channels_min = 1,
    749		.channels_max = 1,
    750		.rates = SNDRV_PCM_RATE_8000,
    751		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    752	},
    753	.capture = {
    754		.stream_name = "Bluetooth Capture",
    755		.channels_min = 1,
    756		.channels_max = 1,
    757		.rates = SNDRV_PCM_RATE_8000,
    758		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    759	},
    760},
    761/* BE DAIs */
    762{
    763	.name = "ssp0-port",
    764	.id = CATPT_SSP_IFACE_0,
    765	.pcm_new = catpt_dai_pcm_new,
    766	.playback = {
    767		.channels_min = 1,
    768		.channels_max = 8,
    769	},
    770	.capture = {
    771		.channels_min = 1,
    772		.channels_max = 8,
    773	},
    774},
    775{
    776	.name = "ssp1-port",
    777	.id = CATPT_SSP_IFACE_1,
    778	.pcm_new = catpt_dai_pcm_new,
    779	.playback = {
    780		.channels_min = 1,
    781		.channels_max = 8,
    782	},
    783	.capture = {
    784		.channels_min = 1,
    785		.channels_max = 8,
    786	},
    787},
    788};
    789
    790#define DSP_VOLUME_MAX		S32_MAX /* 0db */
    791#define DSP_VOLUME_STEP_MAX	30
    792
    793static u32 ctlvol_to_dspvol(u32 value)
    794{
    795	if (value > DSP_VOLUME_STEP_MAX)
    796		value = 0;
    797	return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);
    798}
    799
    800static u32 dspvol_to_ctlvol(u32 volume)
    801{
    802	if (volume > DSP_VOLUME_MAX)
    803		return DSP_VOLUME_STEP_MAX;
    804	return volume ? __fls(volume) : 0;
    805}
    806
    807static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
    808{
    809	u32 dspvol;
    810	int ret, i;
    811
    812	for (i = 1; i < CATPT_CHANNELS_MAX; i++)
    813		if (ctlvol[i] != ctlvol[0])
    814			break;
    815
    816	if (i == CATPT_CHANNELS_MAX) {
    817		dspvol = ctlvol_to_dspvol(ctlvol[0]);
    818
    819		ret = catpt_ipc_set_volume(cdev, stream_id,
    820					   CATPT_ALL_CHANNELS_MASK, dspvol,
    821					   0, CATPT_AUDIO_CURVE_NONE);
    822	} else {
    823		for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
    824			dspvol = ctlvol_to_dspvol(ctlvol[i]);
    825
    826			ret = catpt_ipc_set_volume(cdev, stream_id,
    827						   i, dspvol,
    828						   0, CATPT_AUDIO_CURVE_NONE);
    829			if (ret)
    830				break;
    831		}
    832	}
    833
    834	if (ret)
    835		return CATPT_IPC_ERROR(ret);
    836	return 0;
    837}
    838
    839static int catpt_volume_info(struct snd_kcontrol *kcontrol,
    840			     struct snd_ctl_elem_info *uinfo)
    841{
    842	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
    843	uinfo->count = CATPT_CHANNELS_MAX;
    844	uinfo->value.integer.min = 0;
    845	uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;
    846	return 0;
    847}
    848
    849static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
    850				  struct snd_ctl_elem_value *ucontrol)
    851{
    852	struct snd_soc_component *component =
    853		snd_soc_kcontrol_component(kcontrol);
    854	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
    855	u32 dspvol;
    856	int i;
    857
    858	pm_runtime_get_sync(cdev->dev);
    859
    860	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
    861		dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
    862		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
    863	}
    864
    865	pm_runtime_mark_last_busy(cdev->dev);
    866	pm_runtime_put_autosuspend(cdev->dev);
    867
    868	return 0;
    869}
    870
    871static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
    872				  struct snd_ctl_elem_value *ucontrol)
    873{
    874	struct snd_soc_component *component =
    875		snd_soc_kcontrol_component(kcontrol);
    876	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
    877	int ret;
    878
    879	pm_runtime_get_sync(cdev->dev);
    880
    881	ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
    882			       ucontrol->value.integer.value);
    883
    884	pm_runtime_mark_last_busy(cdev->dev);
    885	pm_runtime_put_autosuspend(cdev->dev);
    886
    887	return ret;
    888}
    889
    890static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
    891				   struct snd_ctl_elem_value *ucontrol,
    892				   enum catpt_pin_id pin_id)
    893{
    894	struct snd_soc_component *component =
    895		snd_soc_kcontrol_component(kcontrol);
    896	struct catpt_stream_runtime *stream;
    897	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
    898	long *ctlvol = (long *)kcontrol->private_value;
    899	u32 dspvol;
    900	int i;
    901
    902	stream = catpt_stream_find(cdev, pin_id);
    903	if (!stream) {
    904		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
    905			ucontrol->value.integer.value[i] = ctlvol[i];
    906		return 0;
    907	}
    908
    909	pm_runtime_get_sync(cdev->dev);
    910
    911	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
    912		dspvol = catpt_stream_volume(cdev, stream, i);
    913		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
    914	}
    915
    916	pm_runtime_mark_last_busy(cdev->dev);
    917	pm_runtime_put_autosuspend(cdev->dev);
    918
    919	return 0;
    920}
    921
    922static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
    923				   struct snd_ctl_elem_value *ucontrol,
    924				   enum catpt_pin_id pin_id)
    925{
    926	struct snd_soc_component *component =
    927		snd_soc_kcontrol_component(kcontrol);
    928	struct catpt_stream_runtime *stream;
    929	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
    930	long *ctlvol = (long *)kcontrol->private_value;
    931	int ret, i;
    932
    933	stream = catpt_stream_find(cdev, pin_id);
    934	if (!stream) {
    935		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
    936			ctlvol[i] = ucontrol->value.integer.value[i];
    937		return 0;
    938	}
    939
    940	pm_runtime_get_sync(cdev->dev);
    941
    942	ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
    943			       ucontrol->value.integer.value);
    944
    945	pm_runtime_mark_last_busy(cdev->dev);
    946	pm_runtime_put_autosuspend(cdev->dev);
    947
    948	if (ret)
    949		return ret;
    950
    951	for (i = 0; i < CATPT_CHANNELS_MAX; i++)
    952		ctlvol[i] = ucontrol->value.integer.value[i];
    953	return 0;
    954}
    955
    956static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,
    957				     struct snd_ctl_elem_value *uctl)
    958{
    959	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
    960}
    961
    962static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,
    963				     struct snd_ctl_elem_value *uctl)
    964{
    965	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
    966}
    967
    968static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,
    969				     struct snd_ctl_elem_value *uctl)
    970{
    971	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
    972}
    973
    974static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,
    975				     struct snd_ctl_elem_value *uctl)
    976{
    977	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
    978}
    979
    980static int catpt_capture_volume_get(struct snd_kcontrol *kctl,
    981				    struct snd_ctl_elem_value *uctl)
    982{
    983	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
    984}
    985
    986static int catpt_capture_volume_put(struct snd_kcontrol *kctl,
    987				    struct snd_ctl_elem_value *uctl)
    988{
    989	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
    990}
    991
    992static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
    993				     struct snd_ctl_elem_value *ucontrol)
    994{
    995	ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
    996	return 0;
    997}
    998
    999static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
   1000				     struct snd_ctl_elem_value *ucontrol)
   1001{
   1002	struct snd_soc_component *component =
   1003		snd_soc_kcontrol_component(kcontrol);
   1004	struct catpt_stream_runtime *stream;
   1005	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
   1006	bool mute;
   1007	int ret;
   1008
   1009	mute = (bool)ucontrol->value.integer.value[0];
   1010	stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
   1011	if (!stream) {
   1012		*(bool *)kcontrol->private_value = mute;
   1013		return 0;
   1014	}
   1015
   1016	pm_runtime_get_sync(cdev->dev);
   1017
   1018	ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
   1019
   1020	pm_runtime_mark_last_busy(cdev->dev);
   1021	pm_runtime_put_autosuspend(cdev->dev);
   1022
   1023	if (ret)
   1024		return CATPT_IPC_ERROR(ret);
   1025
   1026	*(bool *)kcontrol->private_value = mute;
   1027	return 0;
   1028}
   1029
   1030static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
   1031				  struct snd_ctl_elem_value *ucontrol)
   1032{
   1033	return 0;
   1034}
   1035
   1036static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
   1037				  struct snd_ctl_elem_value *ucontrol)
   1038{
   1039	return 0;
   1040}
   1041
   1042static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
   1043				 unsigned int __user *bytes,
   1044				 unsigned int size)
   1045{
   1046	return 0;
   1047}
   1048
   1049static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
   1050				 const unsigned int __user *bytes,
   1051				 unsigned int size)
   1052{
   1053	return 0;
   1054}
   1055
   1056static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
   1057
   1058#define CATPT_VOLUME_CTL(kname, sname) \
   1059{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   1060	.name = (kname), \
   1061	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
   1062		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
   1063	.info = catpt_volume_info, \
   1064	.get = catpt_##sname##_volume_get, \
   1065	.put = catpt_##sname##_volume_put, \
   1066	.tlv.p = catpt_volume_tlv, \
   1067	.private_value = (unsigned long) \
   1068		&(long[CATPT_CHANNELS_MAX]) {0} }
   1069
   1070static const struct snd_kcontrol_new component_kcontrols[] = {
   1071/* Master volume (mixer stream) */
   1072CATPT_VOLUME_CTL("Master Playback Volume", mixer),
   1073/* Individual volume controls for offload and capture */
   1074CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),
   1075CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),
   1076CATPT_VOLUME_CTL("Mic Capture Volume", capture),
   1077SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},
   1078		    catpt_loopback_switch_get, catpt_loopback_switch_put),
   1079/* Enable or disable WAVES module */
   1080SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
   1081		    catpt_waves_switch_get, catpt_waves_switch_put),
   1082/* WAVES module parameter control */
   1083SND_SOC_BYTES_TLV("Waves Set Param", 128,
   1084		  catpt_waves_param_get, catpt_waves_param_put),
   1085};
   1086
   1087static const struct snd_soc_dapm_widget component_widgets[] = {
   1088	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
   1089	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
   1090	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
   1091	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
   1092
   1093	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
   1094};
   1095
   1096static const struct snd_soc_dapm_route component_routes[] = {
   1097	{"Playback VMixer", NULL, "System Playback"},
   1098	{"Playback VMixer", NULL, "Offload0 Playback"},
   1099	{"Playback VMixer", NULL, "Offload1 Playback"},
   1100
   1101	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},
   1102
   1103	{"Analog Capture", NULL, "SSP0 CODEC IN"},
   1104	{"Loopback Capture", NULL, "SSP0 CODEC IN"},
   1105
   1106	{"SSP1 BT OUT", NULL, "Bluetooth Playback"},
   1107	{"Bluetooth Capture", NULL, "SSP1 BT IN"},
   1108};
   1109
   1110static const struct snd_soc_component_driver catpt_comp_driver = {
   1111	.name = "catpt-platform",
   1112
   1113	.pcm_construct = catpt_component_pcm_construct,
   1114	.open = catpt_component_open,
   1115	.pointer = catpt_component_pointer,
   1116
   1117	.controls = component_kcontrols,
   1118	.num_controls = ARRAY_SIZE(component_kcontrols),
   1119	.dapm_widgets = component_widgets,
   1120	.num_dapm_widgets = ARRAY_SIZE(component_widgets),
   1121	.dapm_routes = component_routes,
   1122	.num_dapm_routes = ARRAY_SIZE(component_routes),
   1123};
   1124
   1125int catpt_arm_stream_templates(struct catpt_dev *cdev)
   1126{
   1127	struct resource *res;
   1128	u32 scratch_size = 0;
   1129	int i, j;
   1130
   1131	for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
   1132		struct catpt_stream_template *template;
   1133		struct catpt_module_entry *entry;
   1134		struct catpt_module_type *type;
   1135
   1136		template = catpt_topology[i];
   1137		template->persistent_size = 0;
   1138
   1139		for (j = 0; j < template->num_entries; j++) {
   1140			entry = &template->entries[j];
   1141			type = &cdev->modules[entry->module_id];
   1142
   1143			if (!type->loaded)
   1144				return -ENOENT;
   1145
   1146			entry->entry_point = type->entry_point;
   1147			template->persistent_size += type->persistent_size;
   1148			if (type->scratch_size > scratch_size)
   1149				scratch_size = type->scratch_size;
   1150		}
   1151	}
   1152
   1153	if (scratch_size) {
   1154		/* allocate single scratch area for all modules */
   1155		res = catpt_request_region(&cdev->dram, scratch_size);
   1156		if (!res)
   1157			return -EBUSY;
   1158		cdev->scratch = res;
   1159	}
   1160
   1161	return 0;
   1162}
   1163
   1164int catpt_register_plat_component(struct catpt_dev *cdev)
   1165{
   1166	struct snd_soc_component *component;
   1167	int ret;
   1168
   1169	component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
   1170	if (!component)
   1171		return -ENOMEM;
   1172
   1173	ret = snd_soc_component_initialize(component, &catpt_comp_driver,
   1174					   cdev->dev);
   1175	if (ret)
   1176		return ret;
   1177
   1178	component->name = catpt_comp_driver.name;
   1179	return snd_soc_add_component(component, dai_drivers,
   1180				     ARRAY_SIZE(dai_drivers));
   1181}