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