xref: /kernel/linux/linux-5.10/sound/soc/intel/catpt/pcm.c (revision 8c2ecf20)
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 = substream->private_data;
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_dev *cdev = dev_get_drvdata(dai->dev);
263	struct catpt_stream_template *template;
264	struct catpt_stream_runtime *stream;
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_dev *cdev = dev_get_drvdata(dai->dev);
310	struct catpt_stream_runtime *stream;
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_dai_hw_params(struct snd_pcm_substream *substream,
328			       struct snd_pcm_hw_params *params,
329			       struct snd_soc_dai *dai)
330{
331	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
332	struct catpt_stream_runtime *stream;
333	struct catpt_audio_format afmt;
334	struct catpt_ring_info rinfo;
335	struct snd_pcm_runtime *rtm = substream->runtime;
336	struct snd_dma_buffer *dmab;
337	int ret;
338
339	stream = snd_soc_dai_get_dma_data(dai, substream);
340	if (stream->allocated)
341		return 0;
342
343	memset(&afmt, 0, sizeof(afmt));
344	afmt.sample_rate = params_rate(params);
345	afmt.bit_depth = params_physical_width(params);
346	afmt.valid_bit_depth = params_width(params);
347	afmt.num_channels = params_channels(params);
348	afmt.channel_config = catpt_get_channel_config(afmt.num_channels);
349	afmt.channel_map = catpt_get_channel_map(afmt.channel_config);
350	afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;
351
352	dmab = snd_pcm_get_dma_buf(substream);
353	catpt_arrange_page_table(substream, &stream->pgtbl);
354
355	memset(&rinfo, 0, sizeof(rinfo));
356	rinfo.page_table_addr = stream->pgtbl.addr;
357	rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);
358	rinfo.size = rtm->dma_bytes;
359	rinfo.offset = 0;
360	rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0));
361
362	ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,
363				     stream->template->type,
364				     &afmt, &rinfo,
365				     stream->template->num_entries,
366				     stream->template->entries,
367				     stream->persistent,
368				     cdev->scratch,
369				     &stream->info);
370	if (ret)
371		return CATPT_IPC_ERROR(ret);
372
373	stream->allocated = true;
374	return 0;
375}
376
377static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
378			     struct snd_soc_dai *dai)
379{
380	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
381	struct catpt_stream_runtime *stream;
382
383	stream = snd_soc_dai_get_dma_data(dai, substream);
384	if (!stream->allocated)
385		return 0;
386
387	catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
388	catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
389
390	stream->allocated = false;
391	return 0;
392}
393
394static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
395
396static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
397				     struct catpt_stream_runtime *stream)
398{
399	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
400	struct snd_soc_component *component = dai->component;
401	struct snd_kcontrol *pos, *kctl = NULL;
402	const char *name;
403	int ret;
404	u32 id = stream->info.stream_hw_id;
405
406	/* only selected streams have individual controls */
407	switch (id) {
408	case CATPT_PIN_ID_OFFLOAD1:
409		name = "Media0 Playback Volume";
410		break;
411	case CATPT_PIN_ID_OFFLOAD2:
412		name = "Media1 Playback Volume";
413		break;
414	case CATPT_PIN_ID_CAPTURE1:
415		name = "Mic Capture Volume";
416		break;
417	case CATPT_PIN_ID_REFERENCE:
418		name = "Loopback Mute";
419		break;
420	default:
421		return 0;
422	};
423
424	list_for_each_entry(pos, &component->card->snd_card->controls, list) {
425		if (pos->private_data == component &&
426		    !strncmp(name, pos->id.name, sizeof(pos->id.name))) {
427			kctl = pos;
428			break;
429		}
430	}
431	if (!kctl)
432		return -ENOENT;
433
434	if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
435		return catpt_set_dspvol(cdev, id, (long *)kctl->private_value);
436	ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)kctl->private_value);
437	if (ret)
438		return CATPT_IPC_ERROR(ret);
439	return 0;
440}
441
442static int catpt_dai_prepare(struct snd_pcm_substream *substream,
443			     struct snd_soc_dai *dai)
444{
445	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
446	struct catpt_stream_runtime *stream;
447	int ret;
448
449	stream = snd_soc_dai_get_dma_data(dai, substream);
450	if (stream->prepared)
451		return 0;
452
453	ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
454	if (ret)
455		return CATPT_IPC_ERROR(ret);
456
457	ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
458	if (ret)
459		return CATPT_IPC_ERROR(ret);
460
461	ret = catpt_dai_apply_usettings(dai, stream);
462	if (ret)
463		return 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 catpt_dev *cdev = dev_get_drvdata(dai->dev);
473	struct catpt_stream_runtime *stream;
474	struct snd_pcm_runtime *runtime = substream->runtime;
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_S24_LE |
574				  SNDRV_PCM_FMTBIT_S32_LE,
575	.period_bytes_min	= PAGE_SIZE,
576	.period_bytes_max	= CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN,
577	.periods_min		= CATPT_PCM_PERIODS_MIN,
578	.periods_max		= CATPT_PCM_PERIODS_MAX,
579	.buffer_bytes_max	= CATPT_BUFFER_MAX_SIZE,
580};
581
582static int catpt_component_pcm_construct(struct snd_soc_component *component,
583					 struct snd_soc_pcm_runtime *rtm)
584{
585	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
586
587	snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG,
588				       cdev->dev,
589				       catpt_pcm_hardware.buffer_bytes_max,
590				       catpt_pcm_hardware.buffer_bytes_max);
591
592	return 0;
593}
594
595static int catpt_component_open(struct snd_soc_component *component,
596				struct snd_pcm_substream *substream)
597{
598	struct snd_soc_pcm_runtime *rtm = substream->private_data;
599
600	if (rtm->dai_link->no_pcm)
601		return 0;
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 catpt_dev *cdev = dev_get_drvdata(component->dev);
611	struct catpt_stream_runtime *stream;
612	struct snd_soc_pcm_runtime *rtm = substream->private_data;
613	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0);
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 = asoc_rtd_to_codec(rtm, 0);
638	struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
639	struct catpt_ssp_device_format devfmt;
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	pm_runtime_get_sync(cdev->dev);
674
675	ret = catpt_ipc_set_device_format(cdev, &devfmt);
676
677	pm_runtime_mark_last_busy(cdev->dev);
678	pm_runtime_put_autosuspend(cdev->dev);
679
680	if (ret)
681		return CATPT_IPC_ERROR(ret);
682
683	/* store device format set for given SSP */
684	memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
685	return 0;
686}
687
688static struct snd_soc_dai_driver dai_drivers[] = {
689/* FE DAIs */
690{
691	.name  = "System Pin",
692	.id = CATPT_STRM_TYPE_SYSTEM,
693	.ops = &catpt_fe_dai_ops,
694	.playback = {
695		.stream_name = "System Playback",
696		.channels_min = 2,
697		.channels_max = 2,
698		.rates = SNDRV_PCM_RATE_48000,
699		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
700	},
701	.capture = {
702		.stream_name = "Analog Capture",
703		.channels_min = 2,
704		.channels_max = 4,
705		.rates = SNDRV_PCM_RATE_48000,
706		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
707	},
708},
709{
710	.name  = "Offload0 Pin",
711	.id = CATPT_STRM_TYPE_RENDER,
712	.ops = &catpt_fe_dai_ops,
713	.playback = {
714		.stream_name = "Offload0 Playback",
715		.channels_min = 2,
716		.channels_max = 2,
717		.rates = SNDRV_PCM_RATE_8000_192000,
718		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
719	},
720},
721{
722	.name  = "Offload1 Pin",
723	.id = CATPT_STRM_TYPE_RENDER,
724	.ops = &catpt_fe_dai_ops,
725	.playback = {
726		.stream_name = "Offload1 Playback",
727		.channels_min = 2,
728		.channels_max = 2,
729		.rates = SNDRV_PCM_RATE_8000_192000,
730		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
731	},
732},
733{
734	.name  = "Loopback Pin",
735	.id = CATPT_STRM_TYPE_LOOPBACK,
736	.ops = &catpt_fe_dai_ops,
737	.capture = {
738		.stream_name = "Loopback Capture",
739		.channels_min = 2,
740		.channels_max = 2,
741		.rates = SNDRV_PCM_RATE_48000,
742		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
743	},
744},
745{
746	.name  = "Bluetooth Pin",
747	.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
748	.ops = &catpt_fe_dai_ops,
749	.playback = {
750		.stream_name = "Bluetooth Playback",
751		.channels_min = 1,
752		.channels_max = 1,
753		.rates = SNDRV_PCM_RATE_8000,
754		.formats = SNDRV_PCM_FMTBIT_S16_LE,
755	},
756	.capture = {
757		.stream_name = "Bluetooth Capture",
758		.channels_min = 1,
759		.channels_max = 1,
760		.rates = SNDRV_PCM_RATE_8000,
761		.formats = SNDRV_PCM_FMTBIT_S16_LE,
762	},
763},
764/* BE DAIs */
765{
766	.name = "ssp0-port",
767	.id = CATPT_SSP_IFACE_0,
768	.pcm_new = catpt_dai_pcm_new,
769	.playback = {
770		.channels_min = 1,
771		.channels_max = 8,
772	},
773	.capture = {
774		.channels_min = 1,
775		.channels_max = 8,
776	},
777},
778{
779	.name = "ssp1-port",
780	.id = CATPT_SSP_IFACE_1,
781	.pcm_new = catpt_dai_pcm_new,
782	.playback = {
783		.channels_min = 1,
784		.channels_max = 8,
785	},
786	.capture = {
787		.channels_min = 1,
788		.channels_max = 8,
789	},
790},
791};
792
793#define DSP_VOLUME_MAX		S32_MAX /* 0db */
794#define DSP_VOLUME_STEP_MAX	30
795
796static u32 ctlvol_to_dspvol(u32 value)
797{
798	if (value > DSP_VOLUME_STEP_MAX)
799		value = 0;
800	return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value);
801}
802
803static u32 dspvol_to_ctlvol(u32 volume)
804{
805	if (volume > DSP_VOLUME_MAX)
806		return DSP_VOLUME_STEP_MAX;
807	return volume ? __fls(volume) : 0;
808}
809
810static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
811{
812	u32 dspvol;
813	int ret, i;
814
815	for (i = 1; i < CATPT_CHANNELS_MAX; i++)
816		if (ctlvol[i] != ctlvol[0])
817			break;
818
819	if (i == CATPT_CHANNELS_MAX) {
820		dspvol = ctlvol_to_dspvol(ctlvol[0]);
821
822		ret = catpt_ipc_set_volume(cdev, stream_id,
823					   CATPT_ALL_CHANNELS_MASK, dspvol,
824					   0, CATPT_AUDIO_CURVE_NONE);
825	} else {
826		for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
827			dspvol = ctlvol_to_dspvol(ctlvol[i]);
828
829			ret = catpt_ipc_set_volume(cdev, stream_id,
830						   i, dspvol,
831						   0, CATPT_AUDIO_CURVE_NONE);
832			if (ret)
833				break;
834		}
835	}
836
837	if (ret)
838		return CATPT_IPC_ERROR(ret);
839	return 0;
840}
841
842static int catpt_volume_info(struct snd_kcontrol *kcontrol,
843			     struct snd_ctl_elem_info *uinfo)
844{
845	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
846	uinfo->count = CATPT_CHANNELS_MAX;
847	uinfo->value.integer.min = 0;
848	uinfo->value.integer.max = DSP_VOLUME_STEP_MAX;
849	return 0;
850}
851
852static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
853				  struct snd_ctl_elem_value *ucontrol)
854{
855	struct snd_soc_component *component =
856		snd_soc_kcontrol_component(kcontrol);
857	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
858	u32 dspvol;
859	int i;
860
861	pm_runtime_get_sync(cdev->dev);
862
863	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
864		dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
865		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
866	}
867
868	pm_runtime_mark_last_busy(cdev->dev);
869	pm_runtime_put_autosuspend(cdev->dev);
870
871	return 0;
872}
873
874static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
875				  struct snd_ctl_elem_value *ucontrol)
876{
877	struct snd_soc_component *component =
878		snd_soc_kcontrol_component(kcontrol);
879	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
880	int ret;
881
882	pm_runtime_get_sync(cdev->dev);
883
884	ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
885			       ucontrol->value.integer.value);
886
887	pm_runtime_mark_last_busy(cdev->dev);
888	pm_runtime_put_autosuspend(cdev->dev);
889
890	return ret;
891}
892
893static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
894				   struct snd_ctl_elem_value *ucontrol,
895				   enum catpt_pin_id pin_id)
896{
897	struct snd_soc_component *component =
898		snd_soc_kcontrol_component(kcontrol);
899	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
900	struct catpt_stream_runtime *stream;
901	long *ctlvol = (long *)kcontrol->private_value;
902	u32 dspvol;
903	int i;
904
905	stream = catpt_stream_find(cdev, pin_id);
906	if (!stream) {
907		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
908			ucontrol->value.integer.value[i] = ctlvol[i];
909		return 0;
910	}
911
912	pm_runtime_get_sync(cdev->dev);
913
914	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
915		dspvol = catpt_stream_volume(cdev, stream, i);
916		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
917	}
918
919	pm_runtime_mark_last_busy(cdev->dev);
920	pm_runtime_put_autosuspend(cdev->dev);
921
922	return 0;
923}
924
925static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
926				   struct snd_ctl_elem_value *ucontrol,
927				   enum catpt_pin_id pin_id)
928{
929	struct snd_soc_component *component =
930		snd_soc_kcontrol_component(kcontrol);
931	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
932	struct catpt_stream_runtime *stream;
933	long *ctlvol = (long *)kcontrol->private_value;
934	int ret, i;
935
936	stream = catpt_stream_find(cdev, pin_id);
937	if (!stream) {
938		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
939			ctlvol[i] = ucontrol->value.integer.value[i];
940		return 0;
941	}
942
943	pm_runtime_get_sync(cdev->dev);
944
945	ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
946			       ucontrol->value.integer.value);
947
948	pm_runtime_mark_last_busy(cdev->dev);
949	pm_runtime_put_autosuspend(cdev->dev);
950
951	if (ret)
952		return ret;
953
954	for (i = 0; i < CATPT_CHANNELS_MAX; i++)
955		ctlvol[i] = ucontrol->value.integer.value[i];
956	return 0;
957}
958
959static int catpt_offload1_volume_get(struct snd_kcontrol *kctl,
960				     struct snd_ctl_elem_value *uctl)
961{
962	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
963}
964
965static int catpt_offload1_volume_put(struct snd_kcontrol *kctl,
966				     struct snd_ctl_elem_value *uctl)
967{
968	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1);
969}
970
971static int catpt_offload2_volume_get(struct snd_kcontrol *kctl,
972				     struct snd_ctl_elem_value *uctl)
973{
974	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
975}
976
977static int catpt_offload2_volume_put(struct snd_kcontrol *kctl,
978				     struct snd_ctl_elem_value *uctl)
979{
980	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2);
981}
982
983static int catpt_capture_volume_get(struct snd_kcontrol *kctl,
984				    struct snd_ctl_elem_value *uctl)
985{
986	return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
987}
988
989static int catpt_capture_volume_put(struct snd_kcontrol *kctl,
990				    struct snd_ctl_elem_value *uctl)
991{
992	return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1);
993}
994
995static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
996				     struct snd_ctl_elem_value *ucontrol)
997{
998	ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
999	return 0;
1000}
1001
1002static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
1003				     struct snd_ctl_elem_value *ucontrol)
1004{
1005	struct snd_soc_component *component =
1006		snd_soc_kcontrol_component(kcontrol);
1007	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
1008	struct catpt_stream_runtime *stream;
1009	bool mute;
1010	int ret;
1011
1012	mute = (bool)ucontrol->value.integer.value[0];
1013	stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
1014	if (!stream) {
1015		*(bool *)kcontrol->private_value = mute;
1016		return 0;
1017	}
1018
1019	pm_runtime_get_sync(cdev->dev);
1020
1021	ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
1022
1023	pm_runtime_mark_last_busy(cdev->dev);
1024	pm_runtime_put_autosuspend(cdev->dev);
1025
1026	if (ret)
1027		return CATPT_IPC_ERROR(ret);
1028
1029	*(bool *)kcontrol->private_value = mute;
1030	return 0;
1031}
1032
1033static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
1034				  struct snd_ctl_elem_value *ucontrol)
1035{
1036	return 0;
1037}
1038
1039static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
1040				  struct snd_ctl_elem_value *ucontrol)
1041{
1042	return 0;
1043}
1044
1045static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
1046				 unsigned int __user *bytes,
1047				 unsigned int size)
1048{
1049	return 0;
1050}
1051
1052static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
1053				 const unsigned int __user *bytes,
1054				 unsigned int size)
1055{
1056	return 0;
1057}
1058
1059static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
1060
1061#define CATPT_VOLUME_CTL(kname, sname) \
1062{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
1063	.name = (kname), \
1064	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
1065		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
1066	.info = catpt_volume_info, \
1067	.get = catpt_##sname##_volume_get, \
1068	.put = catpt_##sname##_volume_put, \
1069	.tlv.p = catpt_volume_tlv, \
1070	.private_value = (unsigned long) \
1071		&(long[CATPT_CHANNELS_MAX]) {0} }
1072
1073static const struct snd_kcontrol_new component_kcontrols[] = {
1074/* Master volume (mixer stream) */
1075CATPT_VOLUME_CTL("Master Playback Volume", mixer),
1076/* Individual volume controls for offload and capture */
1077CATPT_VOLUME_CTL("Media0 Playback Volume", offload1),
1078CATPT_VOLUME_CTL("Media1 Playback Volume", offload2),
1079CATPT_VOLUME_CTL("Mic Capture Volume", capture),
1080SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0},
1081		    catpt_loopback_switch_get, catpt_loopback_switch_put),
1082/* Enable or disable WAVES module */
1083SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
1084		    catpt_waves_switch_get, catpt_waves_switch_put),
1085/* WAVES module parameter control */
1086SND_SOC_BYTES_TLV("Waves Set Param", 128,
1087		  catpt_waves_param_get, catpt_waves_param_put),
1088};
1089
1090static const struct snd_soc_dapm_widget component_widgets[] = {
1091	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1092	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1093	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
1094	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1095
1096	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
1097};
1098
1099static const struct snd_soc_dapm_route component_routes[] = {
1100	{"Playback VMixer", NULL, "System Playback"},
1101	{"Playback VMixer", NULL, "Offload0 Playback"},
1102	{"Playback VMixer", NULL, "Offload1 Playback"},
1103
1104	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},
1105
1106	{"Analog Capture", NULL, "SSP0 CODEC IN"},
1107	{"Loopback Capture", NULL, "SSP0 CODEC IN"},
1108
1109	{"SSP1 BT OUT", NULL, "Bluetooth Playback"},
1110	{"Bluetooth Capture", NULL, "SSP1 BT IN"},
1111};
1112
1113static const struct snd_soc_component_driver catpt_comp_driver = {
1114	.name = "catpt-platform",
1115
1116	.pcm_construct = catpt_component_pcm_construct,
1117	.open = catpt_component_open,
1118	.pointer = catpt_component_pointer,
1119
1120	.controls = component_kcontrols,
1121	.num_controls = ARRAY_SIZE(component_kcontrols),
1122	.dapm_widgets = component_widgets,
1123	.num_dapm_widgets = ARRAY_SIZE(component_widgets),
1124	.dapm_routes = component_routes,
1125	.num_dapm_routes = ARRAY_SIZE(component_routes),
1126};
1127
1128int catpt_arm_stream_templates(struct catpt_dev *cdev)
1129{
1130	struct resource *res;
1131	u32 scratch_size = 0;
1132	int i, j;
1133
1134	for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
1135		struct catpt_stream_template *template;
1136		struct catpt_module_entry *entry;
1137		struct catpt_module_type *type;
1138
1139		template = catpt_topology[i];
1140		template->persistent_size = 0;
1141
1142		for (j = 0; j < template->num_entries; j++) {
1143			entry = &template->entries[j];
1144			type = &cdev->modules[entry->module_id];
1145
1146			if (!type->loaded)
1147				return -ENOENT;
1148
1149			entry->entry_point = type->entry_point;
1150			template->persistent_size += type->persistent_size;
1151			if (type->scratch_size > scratch_size)
1152				scratch_size = type->scratch_size;
1153		}
1154	}
1155
1156	if (scratch_size) {
1157		/* allocate single scratch area for all modules */
1158		res = catpt_request_region(&cdev->dram, scratch_size);
1159		if (!res)
1160			return -EBUSY;
1161		cdev->scratch = res;
1162	}
1163
1164	return 0;
1165}
1166
1167int catpt_register_plat_component(struct catpt_dev *cdev)
1168{
1169	struct snd_soc_component *component;
1170	int ret;
1171
1172	component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
1173	if (!component)
1174		return -ENOMEM;
1175
1176	ret = snd_soc_component_initialize(component, &catpt_comp_driver,
1177					   cdev->dev);
1178	if (ret)
1179		return ret;
1180
1181	component->name = catpt_comp_driver.name;
1182	return snd_soc_add_component(component, dai_drivers,
1183				     ARRAY_SIZE(dai_drivers));
1184}
1185