1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * sst_mfld_platform.c - Intel MID Platform driver |
4 | * |
5 | * Copyright (C) 2010-2014 Intel Corp |
6 | * Author: Vinod Koul <vinod.koul@intel.com> |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
8 | * |
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
10 | */ |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #include <linux/slab.h> |
14 | #include <linux/io.h> |
15 | #include <linux/module.h> |
16 | #include <sound/core.h> |
17 | #include <sound/pcm.h> |
18 | #include <sound/pcm_params.h> |
19 | #include <sound/soc.h> |
20 | #include <sound/compress_driver.h> |
21 | #include "sst-mfld-platform.h" |
22 | |
23 | /* compress stream operations */ |
24 | static void sst_compr_fragment_elapsed(void *arg) |
25 | { |
26 | struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg; |
27 | |
28 | pr_debug("fragment elapsed by driver\n" ); |
29 | if (cstream) |
30 | snd_compr_fragment_elapsed(stream: cstream); |
31 | } |
32 | |
33 | static void sst_drain_notify(void *arg) |
34 | { |
35 | struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg; |
36 | |
37 | pr_debug("drain notify by driver\n" ); |
38 | if (cstream) |
39 | snd_compr_drain_notify(stream: cstream); |
40 | } |
41 | |
42 | static int sst_platform_compr_open(struct snd_soc_component *component, |
43 | struct snd_compr_stream *cstream) |
44 | { |
45 | int ret_val; |
46 | struct snd_compr_runtime *runtime = cstream->runtime; |
47 | struct sst_runtime_stream *stream; |
48 | |
49 | stream = kzalloc(size: sizeof(*stream), GFP_KERNEL); |
50 | if (!stream) |
51 | return -ENOMEM; |
52 | |
53 | spin_lock_init(&stream->status_lock); |
54 | |
55 | /* get the sst ops */ |
56 | if (!sst || !try_module_get(module: sst->dev->driver->owner)) { |
57 | pr_err("no device available to run\n" ); |
58 | ret_val = -ENODEV; |
59 | goto out_ops; |
60 | } |
61 | stream->compr_ops = sst->compr_ops; |
62 | stream->id = 0; |
63 | |
64 | /* Turn on LPE */ |
65 | sst->compr_ops->power(sst->dev, true); |
66 | |
67 | sst_set_stream_status(stream, state: SST_PLATFORM_INIT); |
68 | runtime->private_data = stream; |
69 | return 0; |
70 | out_ops: |
71 | kfree(objp: stream); |
72 | return ret_val; |
73 | } |
74 | |
75 | static int sst_platform_compr_free(struct snd_soc_component *component, |
76 | struct snd_compr_stream *cstream) |
77 | { |
78 | struct sst_runtime_stream *stream; |
79 | int ret_val = 0, str_id; |
80 | |
81 | stream = cstream->runtime->private_data; |
82 | /* Turn off LPE */ |
83 | sst->compr_ops->power(sst->dev, false); |
84 | |
85 | /*need to check*/ |
86 | str_id = stream->id; |
87 | if (str_id) |
88 | ret_val = stream->compr_ops->close(sst->dev, str_id); |
89 | module_put(module: sst->dev->driver->owner); |
90 | kfree(objp: stream); |
91 | pr_debug("%s: %d\n" , __func__, ret_val); |
92 | return 0; |
93 | } |
94 | |
95 | static int sst_platform_compr_set_params(struct snd_soc_component *component, |
96 | struct snd_compr_stream *cstream, |
97 | struct snd_compr_params *params) |
98 | { |
99 | struct sst_runtime_stream *stream; |
100 | int retval; |
101 | struct snd_sst_params str_params; |
102 | struct sst_compress_cb cb; |
103 | struct sst_data *ctx = snd_soc_component_get_drvdata(c: component); |
104 | |
105 | stream = cstream->runtime->private_data; |
106 | /* construct fw structure for this*/ |
107 | memset(&str_params, 0, sizeof(str_params)); |
108 | |
109 | /* fill the device type and stream id to pass to SST driver */ |
110 | retval = sst_fill_stream_params(substream: cstream, ctx, str_params: &str_params, is_compress: true); |
111 | pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n" , retval); |
112 | if (retval < 0) |
113 | return retval; |
114 | |
115 | switch (params->codec.id) { |
116 | case SND_AUDIOCODEC_MP3: { |
117 | str_params.codec = SST_CODEC_TYPE_MP3; |
118 | str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in; |
119 | str_params.sparams.uc.mp3_params.pcm_wd_sz = 16; |
120 | break; |
121 | } |
122 | |
123 | case SND_AUDIOCODEC_AAC: { |
124 | str_params.codec = SST_CODEC_TYPE_AAC; |
125 | str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in; |
126 | str_params.sparams.uc.aac_params.pcm_wd_sz = 16; |
127 | if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS) |
128 | str_params.sparams.uc.aac_params.bs_format = |
129 | AAC_BIT_STREAM_ADTS; |
130 | else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW) |
131 | str_params.sparams.uc.aac_params.bs_format = |
132 | AAC_BIT_STREAM_RAW; |
133 | else { |
134 | pr_err("Undefined format%d\n" , params->codec.format); |
135 | return -EINVAL; |
136 | } |
137 | str_params.sparams.uc.aac_params.externalsr = |
138 | params->codec.sample_rate; |
139 | break; |
140 | } |
141 | |
142 | default: |
143 | pr_err("codec not supported, id =%d\n" , params->codec.id); |
144 | return -EINVAL; |
145 | } |
146 | |
147 | str_params.aparams.ring_buf_info[0].addr = |
148 | virt_to_phys(address: cstream->runtime->buffer); |
149 | str_params.aparams.ring_buf_info[0].size = |
150 | cstream->runtime->buffer_size; |
151 | str_params.aparams.sg_count = 1; |
152 | str_params.aparams.frag_size = cstream->runtime->fragment_size; |
153 | |
154 | cb.param = cstream; |
155 | cb.compr_cb = sst_compr_fragment_elapsed; |
156 | cb.drain_cb_param = cstream; |
157 | cb.drain_notify = sst_drain_notify; |
158 | |
159 | retval = stream->compr_ops->open(sst->dev, &str_params, &cb); |
160 | if (retval < 0) { |
161 | pr_err("stream allocation failed %d\n" , retval); |
162 | return retval; |
163 | } |
164 | |
165 | stream->id = retval; |
166 | return 0; |
167 | } |
168 | |
169 | static int sst_platform_compr_trigger(struct snd_soc_component *component, |
170 | struct snd_compr_stream *cstream, int cmd) |
171 | { |
172 | struct sst_runtime_stream *stream = cstream->runtime->private_data; |
173 | |
174 | switch (cmd) { |
175 | case SNDRV_PCM_TRIGGER_START: |
176 | if (stream->compr_ops->stream_start) |
177 | return stream->compr_ops->stream_start(sst->dev, stream->id); |
178 | break; |
179 | case SNDRV_PCM_TRIGGER_STOP: |
180 | if (stream->compr_ops->stream_drop) |
181 | return stream->compr_ops->stream_drop(sst->dev, stream->id); |
182 | break; |
183 | case SND_COMPR_TRIGGER_DRAIN: |
184 | if (stream->compr_ops->stream_drain) |
185 | return stream->compr_ops->stream_drain(sst->dev, stream->id); |
186 | break; |
187 | case SND_COMPR_TRIGGER_PARTIAL_DRAIN: |
188 | if (stream->compr_ops->stream_partial_drain) |
189 | return stream->compr_ops->stream_partial_drain(sst->dev, stream->id); |
190 | break; |
191 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
192 | if (stream->compr_ops->stream_pause) |
193 | return stream->compr_ops->stream_pause(sst->dev, stream->id); |
194 | break; |
195 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
196 | if (stream->compr_ops->stream_pause_release) |
197 | return stream->compr_ops->stream_pause_release(sst->dev, stream->id); |
198 | break; |
199 | } |
200 | return -EINVAL; |
201 | } |
202 | |
203 | static int sst_platform_compr_pointer(struct snd_soc_component *component, |
204 | struct snd_compr_stream *cstream, |
205 | struct snd_compr_tstamp *tstamp) |
206 | { |
207 | struct sst_runtime_stream *stream; |
208 | |
209 | stream = cstream->runtime->private_data; |
210 | stream->compr_ops->tstamp(sst->dev, stream->id, tstamp); |
211 | tstamp->byte_offset = tstamp->copied_total % |
212 | (u32)cstream->runtime->buffer_size; |
213 | pr_debug("calc bytes offset/copied bytes as %d\n" , tstamp->byte_offset); |
214 | return 0; |
215 | } |
216 | |
217 | static int sst_platform_compr_ack(struct snd_soc_component *component, |
218 | struct snd_compr_stream *cstream, |
219 | size_t bytes) |
220 | { |
221 | struct sst_runtime_stream *stream; |
222 | |
223 | stream = cstream->runtime->private_data; |
224 | stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes); |
225 | stream->bytes_written += bytes; |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static int sst_platform_compr_get_caps(struct snd_soc_component *component, |
231 | struct snd_compr_stream *cstream, |
232 | struct snd_compr_caps *caps) |
233 | { |
234 | struct sst_runtime_stream *stream = |
235 | cstream->runtime->private_data; |
236 | |
237 | return stream->compr_ops->get_caps(caps); |
238 | } |
239 | |
240 | static int sst_platform_compr_get_codec_caps(struct snd_soc_component *component, |
241 | struct snd_compr_stream *cstream, |
242 | struct snd_compr_codec_caps *codec) |
243 | { |
244 | struct sst_runtime_stream *stream = |
245 | cstream->runtime->private_data; |
246 | |
247 | return stream->compr_ops->get_codec_caps(codec); |
248 | } |
249 | |
250 | static int sst_platform_compr_set_metadata(struct snd_soc_component *component, |
251 | struct snd_compr_stream *cstream, |
252 | struct snd_compr_metadata *metadata) |
253 | { |
254 | struct sst_runtime_stream *stream = |
255 | cstream->runtime->private_data; |
256 | |
257 | return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata); |
258 | } |
259 | |
260 | const struct snd_compress_ops sst_platform_compress_ops = { |
261 | |
262 | .open = sst_platform_compr_open, |
263 | .free = sst_platform_compr_free, |
264 | .set_params = sst_platform_compr_set_params, |
265 | .set_metadata = sst_platform_compr_set_metadata, |
266 | .trigger = sst_platform_compr_trigger, |
267 | .pointer = sst_platform_compr_pointer, |
268 | .ack = sst_platform_compr_ack, |
269 | .get_caps = sst_platform_compr_get_caps, |
270 | .get_codec_caps = sst_platform_compr_get_codec_caps, |
271 | }; |
272 | |