.. | .. |
---|
8 | 8 | #include <linux/platform_device.h> |
---|
9 | 9 | #include <linux/slab.h> |
---|
10 | 10 | #include <sound/soc.h> |
---|
11 | | -#include <sound/soc.h> |
---|
12 | 11 | #include <sound/soc-dapm.h> |
---|
13 | 12 | #include <sound/pcm.h> |
---|
| 13 | +#include <linux/spinlock.h> |
---|
| 14 | +#include <sound/compress_driver.h> |
---|
14 | 15 | #include <asm/dma.h> |
---|
15 | 16 | #include <linux/dma-mapping.h> |
---|
16 | 17 | #include <linux/of_device.h> |
---|
.. | .. |
---|
31 | 32 | #define CAPTURE_MIN_PERIOD_SIZE 320 |
---|
32 | 33 | #define SID_MASK_DEFAULT 0xF |
---|
33 | 34 | |
---|
| 35 | +/* Default values used if user space does not set */ |
---|
| 36 | +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) |
---|
| 37 | +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) |
---|
| 38 | +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) |
---|
| 39 | +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) |
---|
| 40 | + |
---|
| 41 | +#define ALAC_CH_LAYOUT_MONO ((101 << 16) | 1) |
---|
| 42 | +#define ALAC_CH_LAYOUT_STEREO ((101 << 16) | 2) |
---|
| 43 | + |
---|
34 | 44 | enum stream_state { |
---|
35 | 45 | Q6ASM_STREAM_IDLE = 0, |
---|
36 | 46 | Q6ASM_STREAM_STOPPED, |
---|
.. | .. |
---|
39 | 49 | |
---|
40 | 50 | struct q6asm_dai_rtd { |
---|
41 | 51 | struct snd_pcm_substream *substream; |
---|
| 52 | + struct snd_compr_stream *cstream; |
---|
| 53 | + struct snd_codec codec; |
---|
| 54 | + struct snd_dma_buffer dma_buffer; |
---|
| 55 | + spinlock_t lock; |
---|
42 | 56 | phys_addr_t phys; |
---|
43 | 57 | unsigned int pcm_size; |
---|
44 | 58 | unsigned int pcm_count; |
---|
45 | 59 | unsigned int pcm_irq_pos; /* IRQ position */ |
---|
46 | 60 | unsigned int periods; |
---|
| 61 | + unsigned int bytes_sent; |
---|
| 62 | + unsigned int bytes_received; |
---|
| 63 | + unsigned int copied_total; |
---|
47 | 64 | uint16_t bits_per_sample; |
---|
48 | 65 | uint16_t source; /* Encoding source bit mask */ |
---|
49 | 66 | struct audio_client *audio_client; |
---|
| 67 | + uint32_t next_track_stream_id; |
---|
| 68 | + bool next_track; |
---|
| 69 | + uint32_t stream_id; |
---|
50 | 70 | uint16_t session_id; |
---|
51 | 71 | enum stream_state state; |
---|
| 72 | + uint32_t initial_samples_drop; |
---|
| 73 | + uint32_t trailing_samples_drop; |
---|
| 74 | + bool notify_on_drain; |
---|
52 | 75 | }; |
---|
53 | 76 | |
---|
54 | 77 | struct q6asm_dai_data { |
---|
| 78 | + struct snd_soc_dai_driver *dais; |
---|
| 79 | + int num_dais; |
---|
55 | 80 | long long int sid; |
---|
56 | 81 | }; |
---|
57 | 82 | |
---|
58 | | -static struct snd_pcm_hardware q6asm_dai_hardware_capture = { |
---|
59 | | - .info = (SNDRV_PCM_INFO_MMAP | |
---|
| 83 | +static const struct snd_pcm_hardware q6asm_dai_hardware_capture = { |
---|
| 84 | + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | |
---|
60 | 85 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
---|
61 | 86 | SNDRV_PCM_INFO_MMAP_VALID | |
---|
62 | 87 | SNDRV_PCM_INFO_INTERLEAVED | |
---|
.. | .. |
---|
78 | 103 | }; |
---|
79 | 104 | |
---|
80 | 105 | static struct snd_pcm_hardware q6asm_dai_hardware_playback = { |
---|
81 | | - .info = (SNDRV_PCM_INFO_MMAP | |
---|
| 106 | + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | |
---|
82 | 107 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
---|
83 | 108 | SNDRV_PCM_INFO_MMAP_VALID | |
---|
84 | 109 | SNDRV_PCM_INFO_INTERLEAVED | |
---|
.. | .. |
---|
123 | 148 | .rate_max = 48000, \ |
---|
124 | 149 | }, \ |
---|
125 | 150 | .name = "MultiMedia"#num, \ |
---|
126 | | - .probe = fe_dai_probe, \ |
---|
127 | 151 | .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \ |
---|
128 | 152 | } |
---|
129 | 153 | |
---|
.. | .. |
---|
139 | 163 | .mask = 0, |
---|
140 | 164 | }; |
---|
141 | 165 | |
---|
| 166 | +static const struct snd_compr_codec_caps q6asm_compr_caps = { |
---|
| 167 | + .num_descriptors = 1, |
---|
| 168 | + .descriptor[0].max_ch = 2, |
---|
| 169 | + .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, |
---|
| 170 | + 24000, 32000, 44100, 48000, 88200, |
---|
| 171 | + 96000, 176400, 192000 }, |
---|
| 172 | + .descriptor[0].num_sample_rates = 13, |
---|
| 173 | + .descriptor[0].bit_rate[0] = 320, |
---|
| 174 | + .descriptor[0].bit_rate[1] = 128, |
---|
| 175 | + .descriptor[0].num_bitrates = 2, |
---|
| 176 | + .descriptor[0].profiles = 0, |
---|
| 177 | + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, |
---|
| 178 | + .descriptor[0].formats = 0, |
---|
| 179 | +}; |
---|
| 180 | + |
---|
142 | 181 | static void event_handler(uint32_t opcode, uint32_t token, |
---|
143 | | - uint32_t *payload, void *priv) |
---|
| 182 | + void *payload, void *priv) |
---|
144 | 183 | { |
---|
145 | 184 | struct q6asm_dai_rtd *prtd = priv; |
---|
146 | 185 | struct snd_pcm_substream *substream = prtd->substream; |
---|
.. | .. |
---|
148 | 187 | switch (opcode) { |
---|
149 | 188 | case ASM_CLIENT_EVENT_CMD_RUN_DONE: |
---|
150 | 189 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
151 | | - q6asm_write_async(prtd->audio_client, |
---|
152 | | - prtd->pcm_count, 0, 0, NO_TIMESTAMP); |
---|
| 190 | + q6asm_write_async(prtd->audio_client, prtd->stream_id, |
---|
| 191 | + prtd->pcm_count, 0, 0, 0); |
---|
153 | 192 | break; |
---|
154 | 193 | case ASM_CLIENT_EVENT_CMD_EOS_DONE: |
---|
155 | 194 | prtd->state = Q6ASM_STREAM_STOPPED; |
---|
.. | .. |
---|
158 | 197 | prtd->pcm_irq_pos += prtd->pcm_count; |
---|
159 | 198 | snd_pcm_period_elapsed(substream); |
---|
160 | 199 | if (prtd->state == Q6ASM_STREAM_RUNNING) |
---|
161 | | - q6asm_write_async(prtd->audio_client, |
---|
162 | | - prtd->pcm_count, 0, 0, NO_TIMESTAMP); |
---|
| 200 | + q6asm_write_async(prtd->audio_client, prtd->stream_id, |
---|
| 201 | + prtd->pcm_count, 0, 0, 0); |
---|
163 | 202 | |
---|
164 | 203 | break; |
---|
165 | 204 | } |
---|
.. | .. |
---|
167 | 206 | prtd->pcm_irq_pos += prtd->pcm_count; |
---|
168 | 207 | snd_pcm_period_elapsed(substream); |
---|
169 | 208 | if (prtd->state == Q6ASM_STREAM_RUNNING) |
---|
170 | | - q6asm_read(prtd->audio_client); |
---|
| 209 | + q6asm_read(prtd->audio_client, prtd->stream_id); |
---|
171 | 210 | |
---|
172 | 211 | break; |
---|
173 | 212 | default: |
---|
.. | .. |
---|
175 | 214 | } |
---|
176 | 215 | } |
---|
177 | 216 | |
---|
178 | | -static int q6asm_dai_prepare(struct snd_pcm_substream *substream) |
---|
| 217 | +static int q6asm_dai_prepare(struct snd_soc_component *component, |
---|
| 218 | + struct snd_pcm_substream *substream) |
---|
179 | 219 | { |
---|
180 | 220 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
181 | | - struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; |
---|
| 221 | + struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); |
---|
182 | 222 | struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
183 | | - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); |
---|
184 | 223 | struct q6asm_dai_data *pdata; |
---|
| 224 | + struct device *dev = component->dev; |
---|
185 | 225 | int ret, i; |
---|
186 | 226 | |
---|
187 | | - pdata = snd_soc_component_get_drvdata(c); |
---|
| 227 | + pdata = snd_soc_component_get_drvdata(component); |
---|
188 | 228 | if (!pdata) |
---|
189 | 229 | return -EINVAL; |
---|
190 | 230 | |
---|
191 | 231 | if (!prtd || !prtd->audio_client) { |
---|
192 | | - pr_err("%s: private data null or audio client freed\n", |
---|
| 232 | + dev_err(dev, "%s: private data null or audio client freed\n", |
---|
193 | 233 | __func__); |
---|
194 | 234 | return -EINVAL; |
---|
195 | 235 | } |
---|
.. | .. |
---|
199 | 239 | /* rate and channels are sent to audio driver */ |
---|
200 | 240 | if (prtd->state) { |
---|
201 | 241 | /* clear the previous setup if any */ |
---|
202 | | - q6asm_cmd(prtd->audio_client, CMD_CLOSE); |
---|
| 242 | + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); |
---|
203 | 243 | q6asm_unmap_memory_regions(substream->stream, |
---|
204 | 244 | prtd->audio_client); |
---|
205 | 245 | q6routing_stream_close(soc_prtd->dai_link->id, |
---|
.. | .. |
---|
212 | 252 | prtd->periods); |
---|
213 | 253 | |
---|
214 | 254 | if (ret < 0) { |
---|
215 | | - pr_err("Audio Start: Buffer Allocation failed rc = %d\n", |
---|
| 255 | + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", |
---|
216 | 256 | ret); |
---|
217 | 257 | return -ENOMEM; |
---|
218 | 258 | } |
---|
219 | 259 | |
---|
220 | 260 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
---|
221 | | - ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, |
---|
222 | | - prtd->bits_per_sample); |
---|
| 261 | + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, |
---|
| 262 | + FORMAT_LINEAR_PCM, |
---|
| 263 | + 0, prtd->bits_per_sample, false); |
---|
223 | 264 | } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
---|
224 | | - ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, |
---|
225 | | - prtd->bits_per_sample); |
---|
| 265 | + ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, |
---|
| 266 | + FORMAT_LINEAR_PCM, |
---|
| 267 | + prtd->bits_per_sample); |
---|
226 | 268 | } |
---|
227 | 269 | |
---|
228 | 270 | if (ret < 0) { |
---|
229 | | - pr_err("%s: q6asm_open_write failed\n", __func__); |
---|
230 | | - q6asm_audio_client_free(prtd->audio_client); |
---|
231 | | - prtd->audio_client = NULL; |
---|
232 | | - return -ENOMEM; |
---|
| 271 | + dev_err(dev, "%s: q6asm_open_write failed\n", __func__); |
---|
| 272 | + goto open_err; |
---|
233 | 273 | } |
---|
234 | 274 | |
---|
235 | 275 | prtd->session_id = q6asm_get_session_id(prtd->audio_client); |
---|
236 | 276 | ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, |
---|
237 | 277 | prtd->session_id, substream->stream); |
---|
238 | 278 | if (ret) { |
---|
239 | | - pr_err("%s: stream reg failed ret:%d\n", __func__, ret); |
---|
240 | | - return ret; |
---|
| 279 | + dev_err(dev, "%s: stream reg failed ret:%d\n", __func__, ret); |
---|
| 280 | + goto routing_err; |
---|
241 | 281 | } |
---|
242 | 282 | |
---|
243 | 283 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
---|
244 | 284 | ret = q6asm_media_format_block_multi_ch_pcm( |
---|
245 | | - prtd->audio_client, runtime->rate, |
---|
246 | | - runtime->channels, NULL, |
---|
| 285 | + prtd->audio_client, prtd->stream_id, |
---|
| 286 | + runtime->rate, runtime->channels, NULL, |
---|
247 | 287 | prtd->bits_per_sample); |
---|
248 | 288 | } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
---|
249 | 289 | ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, |
---|
250 | | - runtime->rate, runtime->channels, |
---|
251 | | - prtd->bits_per_sample); |
---|
| 290 | + prtd->stream_id, |
---|
| 291 | + runtime->rate, |
---|
| 292 | + runtime->channels, |
---|
| 293 | + prtd->bits_per_sample); |
---|
252 | 294 | |
---|
253 | 295 | /* Queue the buffers */ |
---|
254 | 296 | for (i = 0; i < runtime->periods; i++) |
---|
255 | | - q6asm_read(prtd->audio_client); |
---|
| 297 | + q6asm_read(prtd->audio_client, prtd->stream_id); |
---|
256 | 298 | |
---|
257 | 299 | } |
---|
258 | 300 | if (ret < 0) |
---|
259 | | - pr_info("%s: CMD Format block failed\n", __func__); |
---|
| 301 | + dev_info(dev, "%s: CMD Format block failed\n", __func__); |
---|
| 302 | + else |
---|
| 303 | + prtd->state = Q6ASM_STREAM_RUNNING; |
---|
260 | 304 | |
---|
261 | | - prtd->state = Q6ASM_STREAM_RUNNING; |
---|
| 305 | + return ret; |
---|
262 | 306 | |
---|
263 | | - return 0; |
---|
| 307 | +routing_err: |
---|
| 308 | + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); |
---|
| 309 | +open_err: |
---|
| 310 | + q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); |
---|
| 311 | + q6asm_audio_client_free(prtd->audio_client); |
---|
| 312 | + prtd->audio_client = NULL; |
---|
| 313 | + |
---|
| 314 | + return ret; |
---|
264 | 315 | } |
---|
265 | 316 | |
---|
266 | | -static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) |
---|
| 317 | +static int q6asm_dai_trigger(struct snd_soc_component *component, |
---|
| 318 | + struct snd_pcm_substream *substream, int cmd) |
---|
267 | 319 | { |
---|
268 | 320 | int ret = 0; |
---|
269 | 321 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
.. | .. |
---|
273 | 325 | case SNDRV_PCM_TRIGGER_START: |
---|
274 | 326 | case SNDRV_PCM_TRIGGER_RESUME: |
---|
275 | 327 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
---|
276 | | - ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); |
---|
| 328 | + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 329 | + 0, 0, 0); |
---|
277 | 330 | break; |
---|
278 | 331 | case SNDRV_PCM_TRIGGER_STOP: |
---|
279 | 332 | prtd->state = Q6ASM_STREAM_STOPPED; |
---|
280 | | - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); |
---|
| 333 | + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 334 | + CMD_EOS); |
---|
281 | 335 | break; |
---|
282 | 336 | case SNDRV_PCM_TRIGGER_SUSPEND: |
---|
283 | 337 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
---|
284 | | - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); |
---|
| 338 | + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 339 | + CMD_PAUSE); |
---|
285 | 340 | break; |
---|
286 | 341 | default: |
---|
287 | 342 | ret = -EINVAL; |
---|
.. | .. |
---|
291 | 346 | return ret; |
---|
292 | 347 | } |
---|
293 | 348 | |
---|
294 | | -static int q6asm_dai_open(struct snd_pcm_substream *substream) |
---|
| 349 | +static int q6asm_dai_open(struct snd_soc_component *component, |
---|
| 350 | + struct snd_pcm_substream *substream) |
---|
295 | 351 | { |
---|
296 | 352 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
297 | | - struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; |
---|
298 | | - struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai; |
---|
299 | | - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); |
---|
| 353 | + struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); |
---|
| 354 | + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0); |
---|
300 | 355 | struct q6asm_dai_rtd *prtd; |
---|
301 | 356 | struct q6asm_dai_data *pdata; |
---|
302 | | - struct device *dev = c->dev; |
---|
| 357 | + struct device *dev = component->dev; |
---|
303 | 358 | int ret = 0; |
---|
304 | 359 | int stream_id; |
---|
305 | 360 | |
---|
306 | 361 | stream_id = cpu_dai->driver->id; |
---|
307 | 362 | |
---|
308 | | - pdata = snd_soc_component_get_drvdata(c); |
---|
| 363 | + pdata = snd_soc_component_get_drvdata(component); |
---|
309 | 364 | if (!pdata) { |
---|
310 | | - pr_err("Drv data not found ..\n"); |
---|
| 365 | + dev_err(dev, "Drv data not found ..\n"); |
---|
311 | 366 | return -EINVAL; |
---|
312 | 367 | } |
---|
313 | 368 | |
---|
.. | .. |
---|
320 | 375 | (q6asm_cb)event_handler, prtd, stream_id, |
---|
321 | 376 | LEGACY_PCM_MODE); |
---|
322 | 377 | if (IS_ERR(prtd->audio_client)) { |
---|
323 | | - pr_info("%s: Could not allocate memory\n", __func__); |
---|
| 378 | + dev_info(dev, "%s: Could not allocate memory\n", __func__); |
---|
324 | 379 | ret = PTR_ERR(prtd->audio_client); |
---|
325 | 380 | kfree(prtd); |
---|
326 | 381 | return ret; |
---|
327 | 382 | } |
---|
| 383 | + |
---|
| 384 | + /* DSP expects stream id from 1 */ |
---|
| 385 | + prtd->stream_id = 1; |
---|
328 | 386 | |
---|
329 | 387 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
330 | 388 | runtime->hw = q6asm_dai_hardware_playback; |
---|
.. | .. |
---|
335 | 393 | SNDRV_PCM_HW_PARAM_RATE, |
---|
336 | 394 | &constraints_sample_rates); |
---|
337 | 395 | if (ret < 0) |
---|
338 | | - pr_info("snd_pcm_hw_constraint_list failed\n"); |
---|
| 396 | + dev_info(dev, "snd_pcm_hw_constraint_list failed\n"); |
---|
339 | 397 | /* Ensure that buffer size is a multiple of period size */ |
---|
340 | 398 | ret = snd_pcm_hw_constraint_integer(runtime, |
---|
341 | 399 | SNDRV_PCM_HW_PARAM_PERIODS); |
---|
342 | 400 | if (ret < 0) |
---|
343 | | - pr_info("snd_pcm_hw_constraint_integer failed\n"); |
---|
| 401 | + dev_info(dev, "snd_pcm_hw_constraint_integer failed\n"); |
---|
344 | 402 | |
---|
345 | 403 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
---|
346 | 404 | ret = snd_pcm_hw_constraint_minmax(runtime, |
---|
.. | .. |
---|
348 | 406 | PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, |
---|
349 | 407 | PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); |
---|
350 | 408 | if (ret < 0) { |
---|
351 | | - pr_err("constraint for buffer bytes min max ret = %d\n", |
---|
352 | | - ret); |
---|
| 409 | + dev_err(dev, "constraint for buffer bytes min max ret = %d\n", |
---|
| 410 | + ret); |
---|
353 | 411 | } |
---|
354 | 412 | } |
---|
355 | 413 | |
---|
356 | 414 | ret = snd_pcm_hw_constraint_step(runtime, 0, |
---|
357 | 415 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); |
---|
358 | 416 | if (ret < 0) { |
---|
359 | | - pr_err("constraint for period bytes step ret = %d\n", |
---|
| 417 | + dev_err(dev, "constraint for period bytes step ret = %d\n", |
---|
360 | 418 | ret); |
---|
361 | 419 | } |
---|
362 | 420 | ret = snd_pcm_hw_constraint_step(runtime, 0, |
---|
363 | 421 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); |
---|
364 | 422 | if (ret < 0) { |
---|
365 | | - pr_err("constraint for buffer bytes step ret = %d\n", |
---|
| 423 | + dev_err(dev, "constraint for buffer bytes step ret = %d\n", |
---|
366 | 424 | ret); |
---|
367 | 425 | } |
---|
368 | 426 | |
---|
.. | .. |
---|
383 | 441 | return 0; |
---|
384 | 442 | } |
---|
385 | 443 | |
---|
386 | | -static int q6asm_dai_close(struct snd_pcm_substream *substream) |
---|
| 444 | +static int q6asm_dai_close(struct snd_soc_component *component, |
---|
| 445 | + struct snd_pcm_substream *substream) |
---|
387 | 446 | { |
---|
388 | 447 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
389 | | - struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; |
---|
| 448 | + struct snd_soc_pcm_runtime *soc_prtd = asoc_substream_to_rtd(substream); |
---|
390 | 449 | struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
391 | 450 | |
---|
392 | 451 | if (prtd->audio_client) { |
---|
393 | 452 | if (prtd->state) |
---|
394 | | - q6asm_cmd(prtd->audio_client, CMD_CLOSE); |
---|
| 453 | + q6asm_cmd(prtd->audio_client, prtd->stream_id, |
---|
| 454 | + CMD_CLOSE); |
---|
395 | 455 | |
---|
396 | 456 | q6asm_unmap_memory_regions(substream->stream, |
---|
397 | 457 | prtd->audio_client); |
---|
.. | .. |
---|
404 | 464 | return 0; |
---|
405 | 465 | } |
---|
406 | 466 | |
---|
407 | | -static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream) |
---|
| 467 | +static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component, |
---|
| 468 | + struct snd_pcm_substream *substream) |
---|
408 | 469 | { |
---|
409 | 470 | |
---|
410 | 471 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
.. | .. |
---|
416 | 477 | return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); |
---|
417 | 478 | } |
---|
418 | 479 | |
---|
419 | | -static int q6asm_dai_mmap(struct snd_pcm_substream *substream, |
---|
420 | | - struct vm_area_struct *vma) |
---|
| 480 | +static int q6asm_dai_mmap(struct snd_soc_component *component, |
---|
| 481 | + struct snd_pcm_substream *substream, |
---|
| 482 | + struct vm_area_struct *vma) |
---|
421 | 483 | { |
---|
422 | | - |
---|
423 | 484 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
424 | | - struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; |
---|
425 | | - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); |
---|
426 | | - struct device *dev = c->dev; |
---|
| 485 | + struct device *dev = component->dev; |
---|
427 | 486 | |
---|
428 | 487 | return dma_mmap_coherent(dev, vma, |
---|
429 | 488 | runtime->dma_area, runtime->dma_addr, |
---|
430 | 489 | runtime->dma_bytes); |
---|
431 | 490 | } |
---|
432 | 491 | |
---|
433 | | -static int q6asm_dai_hw_params(struct snd_pcm_substream *substream, |
---|
434 | | - struct snd_pcm_hw_params *params) |
---|
| 492 | +static int q6asm_dai_hw_params(struct snd_soc_component *component, |
---|
| 493 | + struct snd_pcm_substream *substream, |
---|
| 494 | + struct snd_pcm_hw_params *params) |
---|
435 | 495 | { |
---|
436 | 496 | struct snd_pcm_runtime *runtime = substream->runtime; |
---|
437 | 497 | struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
.. | .. |
---|
451 | 511 | return 0; |
---|
452 | 512 | } |
---|
453 | 513 | |
---|
454 | | -static struct snd_pcm_ops q6asm_dai_ops = { |
---|
455 | | - .open = q6asm_dai_open, |
---|
456 | | - .hw_params = q6asm_dai_hw_params, |
---|
457 | | - .close = q6asm_dai_close, |
---|
458 | | - .ioctl = snd_pcm_lib_ioctl, |
---|
459 | | - .prepare = q6asm_dai_prepare, |
---|
460 | | - .trigger = q6asm_dai_trigger, |
---|
461 | | - .pointer = q6asm_dai_pointer, |
---|
462 | | - .mmap = q6asm_dai_mmap, |
---|
| 514 | +static void compress_event_handler(uint32_t opcode, uint32_t token, |
---|
| 515 | + void *payload, void *priv) |
---|
| 516 | +{ |
---|
| 517 | + struct q6asm_dai_rtd *prtd = priv; |
---|
| 518 | + struct snd_compr_stream *substream = prtd->cstream; |
---|
| 519 | + unsigned long flags; |
---|
| 520 | + u32 wflags = 0; |
---|
| 521 | + uint64_t avail; |
---|
| 522 | + uint32_t bytes_written, bytes_to_write; |
---|
| 523 | + bool is_last_buffer = false; |
---|
| 524 | + |
---|
| 525 | + switch (opcode) { |
---|
| 526 | + case ASM_CLIENT_EVENT_CMD_RUN_DONE: |
---|
| 527 | + spin_lock_irqsave(&prtd->lock, flags); |
---|
| 528 | + if (!prtd->bytes_sent) { |
---|
| 529 | + q6asm_stream_remove_initial_silence(prtd->audio_client, |
---|
| 530 | + prtd->stream_id, |
---|
| 531 | + prtd->initial_samples_drop); |
---|
| 532 | + |
---|
| 533 | + q6asm_write_async(prtd->audio_client, prtd->stream_id, |
---|
| 534 | + prtd->pcm_count, 0, 0, 0); |
---|
| 535 | + prtd->bytes_sent += prtd->pcm_count; |
---|
| 536 | + } |
---|
| 537 | + |
---|
| 538 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 539 | + break; |
---|
| 540 | + |
---|
| 541 | + case ASM_CLIENT_EVENT_CMD_EOS_DONE: |
---|
| 542 | + spin_lock_irqsave(&prtd->lock, flags); |
---|
| 543 | + if (prtd->notify_on_drain) { |
---|
| 544 | + if (substream->partial_drain) { |
---|
| 545 | + /* |
---|
| 546 | + * Close old stream and make it stale, switch |
---|
| 547 | + * the active stream now! |
---|
| 548 | + */ |
---|
| 549 | + q6asm_cmd_nowait(prtd->audio_client, |
---|
| 550 | + prtd->stream_id, |
---|
| 551 | + CMD_CLOSE); |
---|
| 552 | + /* |
---|
| 553 | + * vaild stream ids start from 1, So we are |
---|
| 554 | + * toggling this between 1 and 2. |
---|
| 555 | + */ |
---|
| 556 | + prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); |
---|
| 557 | + } |
---|
| 558 | + |
---|
| 559 | + snd_compr_drain_notify(prtd->cstream); |
---|
| 560 | + prtd->notify_on_drain = false; |
---|
| 561 | + |
---|
| 562 | + } else { |
---|
| 563 | + prtd->state = Q6ASM_STREAM_STOPPED; |
---|
| 564 | + } |
---|
| 565 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 566 | + break; |
---|
| 567 | + |
---|
| 568 | + case ASM_CLIENT_EVENT_DATA_WRITE_DONE: |
---|
| 569 | + spin_lock_irqsave(&prtd->lock, flags); |
---|
| 570 | + |
---|
| 571 | + bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; |
---|
| 572 | + prtd->copied_total += bytes_written; |
---|
| 573 | + snd_compr_fragment_elapsed(substream); |
---|
| 574 | + |
---|
| 575 | + if (prtd->state != Q6ASM_STREAM_RUNNING) { |
---|
| 576 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 577 | + break; |
---|
| 578 | + } |
---|
| 579 | + |
---|
| 580 | + avail = prtd->bytes_received - prtd->bytes_sent; |
---|
| 581 | + if (avail > prtd->pcm_count) { |
---|
| 582 | + bytes_to_write = prtd->pcm_count; |
---|
| 583 | + } else { |
---|
| 584 | + if (substream->partial_drain || prtd->notify_on_drain) |
---|
| 585 | + is_last_buffer = true; |
---|
| 586 | + bytes_to_write = avail; |
---|
| 587 | + } |
---|
| 588 | + |
---|
| 589 | + if (bytes_to_write) { |
---|
| 590 | + if (substream->partial_drain && is_last_buffer) { |
---|
| 591 | + wflags |= ASM_LAST_BUFFER_FLAG; |
---|
| 592 | + q6asm_stream_remove_trailing_silence(prtd->audio_client, |
---|
| 593 | + prtd->stream_id, |
---|
| 594 | + prtd->trailing_samples_drop); |
---|
| 595 | + } |
---|
| 596 | + |
---|
| 597 | + q6asm_write_async(prtd->audio_client, prtd->stream_id, |
---|
| 598 | + bytes_to_write, 0, 0, wflags); |
---|
| 599 | + |
---|
| 600 | + prtd->bytes_sent += bytes_to_write; |
---|
| 601 | + } |
---|
| 602 | + |
---|
| 603 | + if (prtd->notify_on_drain && is_last_buffer) |
---|
| 604 | + q6asm_cmd_nowait(prtd->audio_client, |
---|
| 605 | + prtd->stream_id, CMD_EOS); |
---|
| 606 | + |
---|
| 607 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 608 | + break; |
---|
| 609 | + |
---|
| 610 | + default: |
---|
| 611 | + break; |
---|
| 612 | + } |
---|
| 613 | +} |
---|
| 614 | + |
---|
| 615 | +static int q6asm_dai_compr_open(struct snd_soc_component *component, |
---|
| 616 | + struct snd_compr_stream *stream) |
---|
| 617 | +{ |
---|
| 618 | + struct snd_soc_pcm_runtime *rtd = stream->private_data; |
---|
| 619 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 620 | + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); |
---|
| 621 | + struct q6asm_dai_data *pdata; |
---|
| 622 | + struct device *dev = component->dev; |
---|
| 623 | + struct q6asm_dai_rtd *prtd; |
---|
| 624 | + int stream_id, size, ret; |
---|
| 625 | + |
---|
| 626 | + stream_id = cpu_dai->driver->id; |
---|
| 627 | + pdata = snd_soc_component_get_drvdata(component); |
---|
| 628 | + if (!pdata) { |
---|
| 629 | + dev_err(dev, "Drv data not found ..\n"); |
---|
| 630 | + return -EINVAL; |
---|
| 631 | + } |
---|
| 632 | + |
---|
| 633 | + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); |
---|
| 634 | + if (!prtd) |
---|
| 635 | + return -ENOMEM; |
---|
| 636 | + |
---|
| 637 | + /* DSP expects stream id from 1 */ |
---|
| 638 | + prtd->stream_id = 1; |
---|
| 639 | + |
---|
| 640 | + prtd->cstream = stream; |
---|
| 641 | + prtd->audio_client = q6asm_audio_client_alloc(dev, |
---|
| 642 | + (q6asm_cb)compress_event_handler, |
---|
| 643 | + prtd, stream_id, LEGACY_PCM_MODE); |
---|
| 644 | + if (IS_ERR(prtd->audio_client)) { |
---|
| 645 | + dev_err(dev, "Could not allocate memory\n"); |
---|
| 646 | + ret = PTR_ERR(prtd->audio_client); |
---|
| 647 | + goto free_prtd; |
---|
| 648 | + } |
---|
| 649 | + |
---|
| 650 | + size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * |
---|
| 651 | + COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; |
---|
| 652 | + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, |
---|
| 653 | + &prtd->dma_buffer); |
---|
| 654 | + if (ret) { |
---|
| 655 | + dev_err(dev, "Cannot allocate buffer(s)\n"); |
---|
| 656 | + goto free_client; |
---|
| 657 | + } |
---|
| 658 | + |
---|
| 659 | + if (pdata->sid < 0) |
---|
| 660 | + prtd->phys = prtd->dma_buffer.addr; |
---|
| 661 | + else |
---|
| 662 | + prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); |
---|
| 663 | + |
---|
| 664 | + snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); |
---|
| 665 | + spin_lock_init(&prtd->lock); |
---|
| 666 | + runtime->private_data = prtd; |
---|
| 667 | + |
---|
| 668 | + return 0; |
---|
| 669 | + |
---|
| 670 | +free_client: |
---|
| 671 | + q6asm_audio_client_free(prtd->audio_client); |
---|
| 672 | +free_prtd: |
---|
| 673 | + kfree(prtd); |
---|
| 674 | + |
---|
| 675 | + return ret; |
---|
| 676 | +} |
---|
| 677 | + |
---|
| 678 | +static int q6asm_dai_compr_free(struct snd_soc_component *component, |
---|
| 679 | + struct snd_compr_stream *stream) |
---|
| 680 | +{ |
---|
| 681 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 682 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 683 | + struct snd_soc_pcm_runtime *rtd = stream->private_data; |
---|
| 684 | + |
---|
| 685 | + if (prtd->audio_client) { |
---|
| 686 | + if (prtd->state) { |
---|
| 687 | + q6asm_cmd(prtd->audio_client, prtd->stream_id, |
---|
| 688 | + CMD_CLOSE); |
---|
| 689 | + if (prtd->next_track_stream_id) { |
---|
| 690 | + q6asm_cmd(prtd->audio_client, |
---|
| 691 | + prtd->next_track_stream_id, |
---|
| 692 | + CMD_CLOSE); |
---|
| 693 | + } |
---|
| 694 | + } |
---|
| 695 | + |
---|
| 696 | + snd_dma_free_pages(&prtd->dma_buffer); |
---|
| 697 | + q6asm_unmap_memory_regions(stream->direction, |
---|
| 698 | + prtd->audio_client); |
---|
| 699 | + q6asm_audio_client_free(prtd->audio_client); |
---|
| 700 | + prtd->audio_client = NULL; |
---|
| 701 | + } |
---|
| 702 | + q6routing_stream_close(rtd->dai_link->id, stream->direction); |
---|
| 703 | + kfree(prtd); |
---|
| 704 | + |
---|
| 705 | + return 0; |
---|
| 706 | +} |
---|
| 707 | + |
---|
| 708 | +static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, |
---|
| 709 | + struct snd_compr_stream *stream, |
---|
| 710 | + struct snd_codec *codec, |
---|
| 711 | + int stream_id) |
---|
| 712 | +{ |
---|
| 713 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 714 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 715 | + struct q6asm_flac_cfg flac_cfg; |
---|
| 716 | + struct q6asm_wma_cfg wma_cfg; |
---|
| 717 | + struct q6asm_alac_cfg alac_cfg; |
---|
| 718 | + struct q6asm_ape_cfg ape_cfg; |
---|
| 719 | + unsigned int wma_v9 = 0; |
---|
| 720 | + struct device *dev = component->dev; |
---|
| 721 | + int ret; |
---|
| 722 | + union snd_codec_options *codec_options; |
---|
| 723 | + struct snd_dec_flac *flac; |
---|
| 724 | + struct snd_dec_wma *wma; |
---|
| 725 | + struct snd_dec_alac *alac; |
---|
| 726 | + struct snd_dec_ape *ape; |
---|
| 727 | + |
---|
| 728 | + codec_options = &(prtd->codec.options); |
---|
| 729 | + |
---|
| 730 | + memcpy(&prtd->codec, codec, sizeof(*codec)); |
---|
| 731 | + |
---|
| 732 | + switch (codec->id) { |
---|
| 733 | + case SND_AUDIOCODEC_FLAC: |
---|
| 734 | + |
---|
| 735 | + memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); |
---|
| 736 | + flac = &codec_options->flac_d; |
---|
| 737 | + |
---|
| 738 | + flac_cfg.ch_cfg = codec->ch_in; |
---|
| 739 | + flac_cfg.sample_rate = codec->sample_rate; |
---|
| 740 | + flac_cfg.stream_info_present = 1; |
---|
| 741 | + flac_cfg.sample_size = flac->sample_size; |
---|
| 742 | + flac_cfg.min_blk_size = flac->min_blk_size; |
---|
| 743 | + flac_cfg.max_blk_size = flac->max_blk_size; |
---|
| 744 | + flac_cfg.max_frame_size = flac->max_frame_size; |
---|
| 745 | + flac_cfg.min_frame_size = flac->min_frame_size; |
---|
| 746 | + |
---|
| 747 | + ret = q6asm_stream_media_format_block_flac(prtd->audio_client, |
---|
| 748 | + stream_id, |
---|
| 749 | + &flac_cfg); |
---|
| 750 | + if (ret < 0) { |
---|
| 751 | + dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); |
---|
| 752 | + return -EIO; |
---|
| 753 | + } |
---|
| 754 | + break; |
---|
| 755 | + |
---|
| 756 | + case SND_AUDIOCODEC_WMA: |
---|
| 757 | + wma = &codec_options->wma_d; |
---|
| 758 | + |
---|
| 759 | + memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); |
---|
| 760 | + |
---|
| 761 | + wma_cfg.sample_rate = codec->sample_rate; |
---|
| 762 | + wma_cfg.num_channels = codec->ch_in; |
---|
| 763 | + wma_cfg.bytes_per_sec = codec->bit_rate / 8; |
---|
| 764 | + wma_cfg.block_align = codec->align; |
---|
| 765 | + wma_cfg.bits_per_sample = prtd->bits_per_sample; |
---|
| 766 | + wma_cfg.enc_options = wma->encoder_option; |
---|
| 767 | + wma_cfg.adv_enc_options = wma->adv_encoder_option; |
---|
| 768 | + wma_cfg.adv_enc_options2 = wma->adv_encoder_option2; |
---|
| 769 | + |
---|
| 770 | + if (wma_cfg.num_channels == 1) |
---|
| 771 | + wma_cfg.channel_mask = 4; /* Mono Center */ |
---|
| 772 | + else if (wma_cfg.num_channels == 2) |
---|
| 773 | + wma_cfg.channel_mask = 3; /* Stereo FL/FR */ |
---|
| 774 | + else |
---|
| 775 | + return -EINVAL; |
---|
| 776 | + |
---|
| 777 | + /* check the codec profile */ |
---|
| 778 | + switch (codec->profile) { |
---|
| 779 | + case SND_AUDIOPROFILE_WMA9: |
---|
| 780 | + wma_cfg.fmtag = 0x161; |
---|
| 781 | + wma_v9 = 1; |
---|
| 782 | + break; |
---|
| 783 | + |
---|
| 784 | + case SND_AUDIOPROFILE_WMA10: |
---|
| 785 | + wma_cfg.fmtag = 0x166; |
---|
| 786 | + break; |
---|
| 787 | + |
---|
| 788 | + case SND_AUDIOPROFILE_WMA9_PRO: |
---|
| 789 | + wma_cfg.fmtag = 0x162; |
---|
| 790 | + break; |
---|
| 791 | + |
---|
| 792 | + case SND_AUDIOPROFILE_WMA9_LOSSLESS: |
---|
| 793 | + wma_cfg.fmtag = 0x163; |
---|
| 794 | + break; |
---|
| 795 | + |
---|
| 796 | + case SND_AUDIOPROFILE_WMA10_LOSSLESS: |
---|
| 797 | + wma_cfg.fmtag = 0x167; |
---|
| 798 | + break; |
---|
| 799 | + |
---|
| 800 | + default: |
---|
| 801 | + dev_err(dev, "Unknown WMA profile:%x\n", |
---|
| 802 | + codec->profile); |
---|
| 803 | + return -EIO; |
---|
| 804 | + } |
---|
| 805 | + |
---|
| 806 | + if (wma_v9) |
---|
| 807 | + ret = q6asm_stream_media_format_block_wma_v9( |
---|
| 808 | + prtd->audio_client, stream_id, |
---|
| 809 | + &wma_cfg); |
---|
| 810 | + else |
---|
| 811 | + ret = q6asm_stream_media_format_block_wma_v10( |
---|
| 812 | + prtd->audio_client, stream_id, |
---|
| 813 | + &wma_cfg); |
---|
| 814 | + if (ret < 0) { |
---|
| 815 | + dev_err(dev, "WMA9 CMD failed:%d\n", ret); |
---|
| 816 | + return -EIO; |
---|
| 817 | + } |
---|
| 818 | + break; |
---|
| 819 | + |
---|
| 820 | + case SND_AUDIOCODEC_ALAC: |
---|
| 821 | + memset(&alac_cfg, 0x0, sizeof(alac_cfg)); |
---|
| 822 | + alac = &codec_options->alac_d; |
---|
| 823 | + |
---|
| 824 | + alac_cfg.sample_rate = codec->sample_rate; |
---|
| 825 | + alac_cfg.avg_bit_rate = codec->bit_rate; |
---|
| 826 | + alac_cfg.bit_depth = prtd->bits_per_sample; |
---|
| 827 | + alac_cfg.num_channels = codec->ch_in; |
---|
| 828 | + |
---|
| 829 | + alac_cfg.frame_length = alac->frame_length; |
---|
| 830 | + alac_cfg.pb = alac->pb; |
---|
| 831 | + alac_cfg.mb = alac->mb; |
---|
| 832 | + alac_cfg.kb = alac->kb; |
---|
| 833 | + alac_cfg.max_run = alac->max_run; |
---|
| 834 | + alac_cfg.compatible_version = alac->compatible_version; |
---|
| 835 | + alac_cfg.max_frame_bytes = alac->max_frame_bytes; |
---|
| 836 | + |
---|
| 837 | + switch (codec->ch_in) { |
---|
| 838 | + case 1: |
---|
| 839 | + alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; |
---|
| 840 | + break; |
---|
| 841 | + case 2: |
---|
| 842 | + alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_STEREO; |
---|
| 843 | + break; |
---|
| 844 | + } |
---|
| 845 | + ret = q6asm_stream_media_format_block_alac(prtd->audio_client, |
---|
| 846 | + stream_id, |
---|
| 847 | + &alac_cfg); |
---|
| 848 | + if (ret < 0) { |
---|
| 849 | + dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); |
---|
| 850 | + return -EIO; |
---|
| 851 | + } |
---|
| 852 | + break; |
---|
| 853 | + |
---|
| 854 | + case SND_AUDIOCODEC_APE: |
---|
| 855 | + memset(&ape_cfg, 0x0, sizeof(ape_cfg)); |
---|
| 856 | + ape = &codec_options->ape_d; |
---|
| 857 | + |
---|
| 858 | + ape_cfg.sample_rate = codec->sample_rate; |
---|
| 859 | + ape_cfg.num_channels = codec->ch_in; |
---|
| 860 | + ape_cfg.bits_per_sample = prtd->bits_per_sample; |
---|
| 861 | + |
---|
| 862 | + ape_cfg.compatible_version = ape->compatible_version; |
---|
| 863 | + ape_cfg.compression_level = ape->compression_level; |
---|
| 864 | + ape_cfg.format_flags = ape->format_flags; |
---|
| 865 | + ape_cfg.blocks_per_frame = ape->blocks_per_frame; |
---|
| 866 | + ape_cfg.final_frame_blocks = ape->final_frame_blocks; |
---|
| 867 | + ape_cfg.total_frames = ape->total_frames; |
---|
| 868 | + ape_cfg.seek_table_present = ape->seek_table_present; |
---|
| 869 | + |
---|
| 870 | + ret = q6asm_stream_media_format_block_ape(prtd->audio_client, |
---|
| 871 | + stream_id, |
---|
| 872 | + &ape_cfg); |
---|
| 873 | + if (ret < 0) { |
---|
| 874 | + dev_err(dev, "APE CMD Format block failed:%d\n", ret); |
---|
| 875 | + return -EIO; |
---|
| 876 | + } |
---|
| 877 | + break; |
---|
| 878 | + |
---|
| 879 | + default: |
---|
| 880 | + break; |
---|
| 881 | + } |
---|
| 882 | + |
---|
| 883 | + return 0; |
---|
| 884 | +} |
---|
| 885 | + |
---|
| 886 | +static int q6asm_dai_compr_set_params(struct snd_soc_component *component, |
---|
| 887 | + struct snd_compr_stream *stream, |
---|
| 888 | + struct snd_compr_params *params) |
---|
| 889 | +{ |
---|
| 890 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 891 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 892 | + struct snd_soc_pcm_runtime *rtd = stream->private_data; |
---|
| 893 | + int dir = stream->direction; |
---|
| 894 | + struct q6asm_dai_data *pdata; |
---|
| 895 | + struct device *dev = component->dev; |
---|
| 896 | + int ret; |
---|
| 897 | + |
---|
| 898 | + pdata = snd_soc_component_get_drvdata(component); |
---|
| 899 | + if (!pdata) |
---|
| 900 | + return -EINVAL; |
---|
| 901 | + |
---|
| 902 | + if (!prtd || !prtd->audio_client) { |
---|
| 903 | + dev_err(dev, "private data null or audio client freed\n"); |
---|
| 904 | + return -EINVAL; |
---|
| 905 | + } |
---|
| 906 | + |
---|
| 907 | + prtd->periods = runtime->fragments; |
---|
| 908 | + prtd->pcm_count = runtime->fragment_size; |
---|
| 909 | + prtd->pcm_size = runtime->fragments * runtime->fragment_size; |
---|
| 910 | + prtd->bits_per_sample = 16; |
---|
| 911 | + |
---|
| 912 | + if (dir == SND_COMPRESS_PLAYBACK) { |
---|
| 913 | + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, |
---|
| 914 | + params->codec.profile, prtd->bits_per_sample, |
---|
| 915 | + true); |
---|
| 916 | + |
---|
| 917 | + if (ret < 0) { |
---|
| 918 | + dev_err(dev, "q6asm_open_write failed\n"); |
---|
| 919 | + q6asm_audio_client_free(prtd->audio_client); |
---|
| 920 | + prtd->audio_client = NULL; |
---|
| 921 | + return ret; |
---|
| 922 | + } |
---|
| 923 | + } |
---|
| 924 | + |
---|
| 925 | + prtd->session_id = q6asm_get_session_id(prtd->audio_client); |
---|
| 926 | + ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, |
---|
| 927 | + prtd->session_id, dir); |
---|
| 928 | + if (ret) { |
---|
| 929 | + dev_err(dev, "Stream reg failed ret:%d\n", ret); |
---|
| 930 | + return ret; |
---|
| 931 | + } |
---|
| 932 | + |
---|
| 933 | + ret = __q6asm_dai_compr_set_codec_params(component, stream, |
---|
| 934 | + ¶ms->codec, |
---|
| 935 | + prtd->stream_id); |
---|
| 936 | + if (ret) { |
---|
| 937 | + dev_err(dev, "codec param setup failed ret:%d\n", ret); |
---|
| 938 | + return ret; |
---|
| 939 | + } |
---|
| 940 | + |
---|
| 941 | + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, |
---|
| 942 | + (prtd->pcm_size / prtd->periods), |
---|
| 943 | + prtd->periods); |
---|
| 944 | + |
---|
| 945 | + if (ret < 0) { |
---|
| 946 | + dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); |
---|
| 947 | + return -ENOMEM; |
---|
| 948 | + } |
---|
| 949 | + |
---|
| 950 | + prtd->state = Q6ASM_STREAM_RUNNING; |
---|
| 951 | + |
---|
| 952 | + return 0; |
---|
| 953 | +} |
---|
| 954 | + |
---|
| 955 | +static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, |
---|
| 956 | + struct snd_compr_stream *stream, |
---|
| 957 | + struct snd_compr_metadata *metadata) |
---|
| 958 | +{ |
---|
| 959 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 960 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 961 | + int ret = 0; |
---|
| 962 | + |
---|
| 963 | + switch (metadata->key) { |
---|
| 964 | + case SNDRV_COMPRESS_ENCODER_PADDING: |
---|
| 965 | + prtd->trailing_samples_drop = metadata->value[0]; |
---|
| 966 | + break; |
---|
| 967 | + case SNDRV_COMPRESS_ENCODER_DELAY: |
---|
| 968 | + prtd->initial_samples_drop = metadata->value[0]; |
---|
| 969 | + if (prtd->next_track_stream_id) { |
---|
| 970 | + ret = q6asm_open_write(prtd->audio_client, |
---|
| 971 | + prtd->next_track_stream_id, |
---|
| 972 | + prtd->codec.id, |
---|
| 973 | + prtd->codec.profile, |
---|
| 974 | + prtd->bits_per_sample, |
---|
| 975 | + true); |
---|
| 976 | + if (ret < 0) { |
---|
| 977 | + dev_err(component->dev, "q6asm_open_write failed\n"); |
---|
| 978 | + return ret; |
---|
| 979 | + } |
---|
| 980 | + ret = __q6asm_dai_compr_set_codec_params(component, stream, |
---|
| 981 | + &prtd->codec, |
---|
| 982 | + prtd->next_track_stream_id); |
---|
| 983 | + if (ret < 0) { |
---|
| 984 | + dev_err(component->dev, "q6asm_open_write failed\n"); |
---|
| 985 | + return ret; |
---|
| 986 | + } |
---|
| 987 | + |
---|
| 988 | + ret = q6asm_stream_remove_initial_silence(prtd->audio_client, |
---|
| 989 | + prtd->next_track_stream_id, |
---|
| 990 | + prtd->initial_samples_drop); |
---|
| 991 | + prtd->next_track_stream_id = 0; |
---|
| 992 | + |
---|
| 993 | + } |
---|
| 994 | + |
---|
| 995 | + break; |
---|
| 996 | + default: |
---|
| 997 | + ret = -EINVAL; |
---|
| 998 | + break; |
---|
| 999 | + } |
---|
| 1000 | + |
---|
| 1001 | + return ret; |
---|
| 1002 | +} |
---|
| 1003 | + |
---|
| 1004 | +static int q6asm_dai_compr_trigger(struct snd_soc_component *component, |
---|
| 1005 | + struct snd_compr_stream *stream, int cmd) |
---|
| 1006 | +{ |
---|
| 1007 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 1008 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 1009 | + int ret = 0; |
---|
| 1010 | + |
---|
| 1011 | + switch (cmd) { |
---|
| 1012 | + case SNDRV_PCM_TRIGGER_START: |
---|
| 1013 | + case SNDRV_PCM_TRIGGER_RESUME: |
---|
| 1014 | + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
---|
| 1015 | + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 1016 | + 0, 0, 0); |
---|
| 1017 | + break; |
---|
| 1018 | + case SNDRV_PCM_TRIGGER_STOP: |
---|
| 1019 | + prtd->state = Q6ASM_STREAM_STOPPED; |
---|
| 1020 | + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 1021 | + CMD_EOS); |
---|
| 1022 | + break; |
---|
| 1023 | + case SNDRV_PCM_TRIGGER_SUSPEND: |
---|
| 1024 | + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
---|
| 1025 | + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, |
---|
| 1026 | + CMD_PAUSE); |
---|
| 1027 | + break; |
---|
| 1028 | + case SND_COMPR_TRIGGER_NEXT_TRACK: |
---|
| 1029 | + prtd->next_track = true; |
---|
| 1030 | + prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); |
---|
| 1031 | + break; |
---|
| 1032 | + case SND_COMPR_TRIGGER_DRAIN: |
---|
| 1033 | + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: |
---|
| 1034 | + prtd->notify_on_drain = true; |
---|
| 1035 | + break; |
---|
| 1036 | + default: |
---|
| 1037 | + ret = -EINVAL; |
---|
| 1038 | + break; |
---|
| 1039 | + } |
---|
| 1040 | + |
---|
| 1041 | + return ret; |
---|
| 1042 | +} |
---|
| 1043 | + |
---|
| 1044 | +static int q6asm_dai_compr_pointer(struct snd_soc_component *component, |
---|
| 1045 | + struct snd_compr_stream *stream, |
---|
| 1046 | + struct snd_compr_tstamp *tstamp) |
---|
| 1047 | +{ |
---|
| 1048 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 1049 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 1050 | + unsigned long flags; |
---|
| 1051 | + |
---|
| 1052 | + spin_lock_irqsave(&prtd->lock, flags); |
---|
| 1053 | + |
---|
| 1054 | + tstamp->copied_total = prtd->copied_total; |
---|
| 1055 | + tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; |
---|
| 1056 | + |
---|
| 1057 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 1058 | + |
---|
| 1059 | + return 0; |
---|
| 1060 | +} |
---|
| 1061 | + |
---|
| 1062 | +static int q6asm_compr_copy(struct snd_soc_component *component, |
---|
| 1063 | + struct snd_compr_stream *stream, char __user *buf, |
---|
| 1064 | + size_t count) |
---|
| 1065 | +{ |
---|
| 1066 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 1067 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 1068 | + unsigned long flags; |
---|
| 1069 | + u32 wflags = 0; |
---|
| 1070 | + int avail, bytes_in_flight = 0; |
---|
| 1071 | + void *dstn; |
---|
| 1072 | + size_t copy; |
---|
| 1073 | + u32 app_pointer; |
---|
| 1074 | + u32 bytes_received; |
---|
| 1075 | + |
---|
| 1076 | + bytes_received = prtd->bytes_received; |
---|
| 1077 | + |
---|
| 1078 | + /** |
---|
| 1079 | + * Make sure that next track data pointer is aligned at 32 bit boundary |
---|
| 1080 | + * This is a Mandatory requirement from DSP data buffers alignment |
---|
| 1081 | + */ |
---|
| 1082 | + if (prtd->next_track) |
---|
| 1083 | + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); |
---|
| 1084 | + |
---|
| 1085 | + app_pointer = bytes_received/prtd->pcm_size; |
---|
| 1086 | + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); |
---|
| 1087 | + dstn = prtd->dma_buffer.area + app_pointer; |
---|
| 1088 | + |
---|
| 1089 | + if (count < prtd->pcm_size - app_pointer) { |
---|
| 1090 | + if (copy_from_user(dstn, buf, count)) |
---|
| 1091 | + return -EFAULT; |
---|
| 1092 | + } else { |
---|
| 1093 | + copy = prtd->pcm_size - app_pointer; |
---|
| 1094 | + if (copy_from_user(dstn, buf, copy)) |
---|
| 1095 | + return -EFAULT; |
---|
| 1096 | + if (copy_from_user(prtd->dma_buffer.area, buf + copy, |
---|
| 1097 | + count - copy)) |
---|
| 1098 | + return -EFAULT; |
---|
| 1099 | + } |
---|
| 1100 | + |
---|
| 1101 | + spin_lock_irqsave(&prtd->lock, flags); |
---|
| 1102 | + |
---|
| 1103 | + bytes_in_flight = prtd->bytes_received - prtd->copied_total; |
---|
| 1104 | + |
---|
| 1105 | + if (prtd->next_track) { |
---|
| 1106 | + prtd->next_track = false; |
---|
| 1107 | + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); |
---|
| 1108 | + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); |
---|
| 1109 | + } |
---|
| 1110 | + |
---|
| 1111 | + prtd->bytes_received = bytes_received + count; |
---|
| 1112 | + |
---|
| 1113 | + /* Kick off the data to dsp if its starving!! */ |
---|
| 1114 | + if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { |
---|
| 1115 | + uint32_t bytes_to_write = prtd->pcm_count; |
---|
| 1116 | + |
---|
| 1117 | + avail = prtd->bytes_received - prtd->bytes_sent; |
---|
| 1118 | + |
---|
| 1119 | + if (avail < prtd->pcm_count) |
---|
| 1120 | + bytes_to_write = avail; |
---|
| 1121 | + |
---|
| 1122 | + q6asm_write_async(prtd->audio_client, prtd->stream_id, |
---|
| 1123 | + bytes_to_write, 0, 0, wflags); |
---|
| 1124 | + prtd->bytes_sent += bytes_to_write; |
---|
| 1125 | + } |
---|
| 1126 | + |
---|
| 1127 | + spin_unlock_irqrestore(&prtd->lock, flags); |
---|
| 1128 | + |
---|
| 1129 | + return count; |
---|
| 1130 | +} |
---|
| 1131 | + |
---|
| 1132 | +static int q6asm_dai_compr_mmap(struct snd_soc_component *component, |
---|
| 1133 | + struct snd_compr_stream *stream, |
---|
| 1134 | + struct vm_area_struct *vma) |
---|
| 1135 | +{ |
---|
| 1136 | + struct snd_compr_runtime *runtime = stream->runtime; |
---|
| 1137 | + struct q6asm_dai_rtd *prtd = runtime->private_data; |
---|
| 1138 | + struct device *dev = component->dev; |
---|
| 1139 | + |
---|
| 1140 | + return dma_mmap_coherent(dev, vma, |
---|
| 1141 | + prtd->dma_buffer.area, prtd->dma_buffer.addr, |
---|
| 1142 | + prtd->dma_buffer.bytes); |
---|
| 1143 | +} |
---|
| 1144 | + |
---|
| 1145 | +static int q6asm_dai_compr_get_caps(struct snd_soc_component *component, |
---|
| 1146 | + struct snd_compr_stream *stream, |
---|
| 1147 | + struct snd_compr_caps *caps) |
---|
| 1148 | +{ |
---|
| 1149 | + caps->direction = SND_COMPRESS_PLAYBACK; |
---|
| 1150 | + caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; |
---|
| 1151 | + caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; |
---|
| 1152 | + caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; |
---|
| 1153 | + caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; |
---|
| 1154 | + caps->num_codecs = 5; |
---|
| 1155 | + caps->codecs[0] = SND_AUDIOCODEC_MP3; |
---|
| 1156 | + caps->codecs[1] = SND_AUDIOCODEC_FLAC; |
---|
| 1157 | + caps->codecs[2] = SND_AUDIOCODEC_WMA; |
---|
| 1158 | + caps->codecs[3] = SND_AUDIOCODEC_ALAC; |
---|
| 1159 | + caps->codecs[4] = SND_AUDIOCODEC_APE; |
---|
| 1160 | + |
---|
| 1161 | + return 0; |
---|
| 1162 | +} |
---|
| 1163 | + |
---|
| 1164 | +static int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component, |
---|
| 1165 | + struct snd_compr_stream *stream, |
---|
| 1166 | + struct snd_compr_codec_caps *codec) |
---|
| 1167 | +{ |
---|
| 1168 | + switch (codec->codec) { |
---|
| 1169 | + case SND_AUDIOCODEC_MP3: |
---|
| 1170 | + *codec = q6asm_compr_caps; |
---|
| 1171 | + break; |
---|
| 1172 | + default: |
---|
| 1173 | + break; |
---|
| 1174 | + } |
---|
| 1175 | + |
---|
| 1176 | + return 0; |
---|
| 1177 | +} |
---|
| 1178 | + |
---|
| 1179 | +static struct snd_compress_ops q6asm_dai_compress_ops = { |
---|
| 1180 | + .open = q6asm_dai_compr_open, |
---|
| 1181 | + .free = q6asm_dai_compr_free, |
---|
| 1182 | + .set_params = q6asm_dai_compr_set_params, |
---|
| 1183 | + .set_metadata = q6asm_dai_compr_set_metadata, |
---|
| 1184 | + .pointer = q6asm_dai_compr_pointer, |
---|
| 1185 | + .trigger = q6asm_dai_compr_trigger, |
---|
| 1186 | + .get_caps = q6asm_dai_compr_get_caps, |
---|
| 1187 | + .get_codec_caps = q6asm_dai_compr_get_codec_caps, |
---|
| 1188 | + .mmap = q6asm_dai_compr_mmap, |
---|
| 1189 | + .copy = q6asm_compr_copy, |
---|
463 | 1190 | }; |
---|
464 | 1191 | |
---|
465 | | -static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) |
---|
| 1192 | +static int q6asm_dai_pcm_new(struct snd_soc_component *component, |
---|
| 1193 | + struct snd_soc_pcm_runtime *rtd) |
---|
466 | 1194 | { |
---|
467 | 1195 | struct snd_pcm_substream *psubstream, *csubstream; |
---|
468 | | - struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); |
---|
469 | 1196 | struct snd_pcm *pcm = rtd->pcm; |
---|
470 | 1197 | struct device *dev; |
---|
471 | 1198 | int size, ret; |
---|
472 | 1199 | |
---|
473 | | - dev = c->dev; |
---|
| 1200 | + dev = component->dev; |
---|
474 | 1201 | size = q6asm_dai_hardware_playback.buffer_bytes_max; |
---|
475 | 1202 | psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; |
---|
476 | 1203 | if (psubstream) { |
---|
.. | .. |
---|
494 | 1221 | } |
---|
495 | 1222 | } |
---|
496 | 1223 | |
---|
497 | | - return ret; |
---|
| 1224 | + return 0; |
---|
498 | 1225 | } |
---|
499 | 1226 | |
---|
500 | | -static void q6asm_dai_pcm_free(struct snd_pcm *pcm) |
---|
| 1227 | +static void q6asm_dai_pcm_free(struct snd_soc_component *component, |
---|
| 1228 | + struct snd_pcm *pcm) |
---|
501 | 1229 | { |
---|
502 | 1230 | struct snd_pcm_substream *substream; |
---|
503 | 1231 | int i; |
---|
.. | .. |
---|
512 | 1240 | } |
---|
513 | 1241 | } |
---|
514 | 1242 | |
---|
515 | | -static const struct snd_soc_dapm_route afe_pcm_routes[] = { |
---|
516 | | - {"MM_DL1", NULL, "MultiMedia1 Playback" }, |
---|
517 | | - {"MM_DL2", NULL, "MultiMedia2 Playback" }, |
---|
518 | | - {"MM_DL3", NULL, "MultiMedia3 Playback" }, |
---|
519 | | - {"MM_DL4", NULL, "MultiMedia4 Playback" }, |
---|
520 | | - {"MM_DL5", NULL, "MultiMedia5 Playback" }, |
---|
521 | | - {"MM_DL6", NULL, "MultiMedia6 Playback" }, |
---|
522 | | - {"MM_DL7", NULL, "MultiMedia7 Playback" }, |
---|
523 | | - {"MM_DL7", NULL, "MultiMedia8 Playback" }, |
---|
524 | | - {"MultiMedia1 Capture", NULL, "MM_UL1"}, |
---|
525 | | - {"MultiMedia2 Capture", NULL, "MM_UL2"}, |
---|
526 | | - {"MultiMedia3 Capture", NULL, "MM_UL3"}, |
---|
527 | | - {"MultiMedia4 Capture", NULL, "MM_UL4"}, |
---|
528 | | - {"MultiMedia5 Capture", NULL, "MM_UL5"}, |
---|
529 | | - {"MultiMedia6 Capture", NULL, "MM_UL6"}, |
---|
530 | | - {"MultiMedia7 Capture", NULL, "MM_UL7"}, |
---|
531 | | - {"MultiMedia8 Capture", NULL, "MM_UL8"}, |
---|
532 | | - |
---|
| 1243 | +static const struct snd_soc_dapm_widget q6asm_dapm_widgets[] = { |
---|
| 1244 | + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1245 | + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1246 | + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1247 | + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1248 | + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1249 | + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1250 | + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1251 | + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1252 | + SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1253 | + SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1254 | + SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1255 | + SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1256 | + SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1257 | + SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1258 | + SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
| 1259 | + SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, SND_SOC_NOPM, 0, 0), |
---|
533 | 1260 | }; |
---|
534 | | - |
---|
535 | | -static int fe_dai_probe(struct snd_soc_dai *dai) |
---|
536 | | -{ |
---|
537 | | - struct snd_soc_dapm_context *dapm; |
---|
538 | | - |
---|
539 | | - dapm = snd_soc_component_get_dapm(dai->component); |
---|
540 | | - snd_soc_dapm_add_routes(dapm, afe_pcm_routes, |
---|
541 | | - ARRAY_SIZE(afe_pcm_routes)); |
---|
542 | | - |
---|
543 | | - return 0; |
---|
544 | | -} |
---|
545 | | - |
---|
546 | 1261 | |
---|
547 | 1262 | static const struct snd_soc_component_driver q6asm_fe_dai_component = { |
---|
548 | 1263 | .name = DRV_NAME, |
---|
549 | | - .ops = &q6asm_dai_ops, |
---|
550 | | - .pcm_new = q6asm_dai_pcm_new, |
---|
551 | | - .pcm_free = q6asm_dai_pcm_free, |
---|
552 | | - |
---|
| 1264 | + .open = q6asm_dai_open, |
---|
| 1265 | + .hw_params = q6asm_dai_hw_params, |
---|
| 1266 | + .close = q6asm_dai_close, |
---|
| 1267 | + .prepare = q6asm_dai_prepare, |
---|
| 1268 | + .trigger = q6asm_dai_trigger, |
---|
| 1269 | + .pointer = q6asm_dai_pointer, |
---|
| 1270 | + .mmap = q6asm_dai_mmap, |
---|
| 1271 | + .pcm_construct = q6asm_dai_pcm_new, |
---|
| 1272 | + .pcm_destruct = q6asm_dai_pcm_free, |
---|
| 1273 | + .compress_ops = &q6asm_dai_compress_ops, |
---|
| 1274 | + .dapm_widgets = q6asm_dapm_widgets, |
---|
| 1275 | + .num_dapm_widgets = ARRAY_SIZE(q6asm_dapm_widgets), |
---|
553 | 1276 | }; |
---|
554 | 1277 | |
---|
555 | | -static struct snd_soc_dai_driver q6asm_fe_dais[] = { |
---|
| 1278 | +static struct snd_soc_dai_driver q6asm_fe_dais_template[] = { |
---|
556 | 1279 | Q6ASM_FEDAI_DRIVER(1), |
---|
557 | 1280 | Q6ASM_FEDAI_DRIVER(2), |
---|
558 | 1281 | Q6ASM_FEDAI_DRIVER(3), |
---|
.. | .. |
---|
562 | 1285 | Q6ASM_FEDAI_DRIVER(7), |
---|
563 | 1286 | Q6ASM_FEDAI_DRIVER(8), |
---|
564 | 1287 | }; |
---|
| 1288 | + |
---|
| 1289 | +static int of_q6asm_parse_dai_data(struct device *dev, |
---|
| 1290 | + struct q6asm_dai_data *pdata) |
---|
| 1291 | +{ |
---|
| 1292 | + struct snd_soc_dai_driver *dai_drv; |
---|
| 1293 | + struct snd_soc_pcm_stream empty_stream; |
---|
| 1294 | + struct device_node *node; |
---|
| 1295 | + int ret, id, dir, idx = 0; |
---|
| 1296 | + |
---|
| 1297 | + |
---|
| 1298 | + pdata->num_dais = of_get_child_count(dev->of_node); |
---|
| 1299 | + if (!pdata->num_dais) { |
---|
| 1300 | + dev_err(dev, "No dais found in DT\n"); |
---|
| 1301 | + return -EINVAL; |
---|
| 1302 | + } |
---|
| 1303 | + |
---|
| 1304 | + pdata->dais = devm_kcalloc(dev, pdata->num_dais, sizeof(*dai_drv), |
---|
| 1305 | + GFP_KERNEL); |
---|
| 1306 | + if (!pdata->dais) |
---|
| 1307 | + return -ENOMEM; |
---|
| 1308 | + |
---|
| 1309 | + memset(&empty_stream, 0, sizeof(empty_stream)); |
---|
| 1310 | + |
---|
| 1311 | + for_each_child_of_node(dev->of_node, node) { |
---|
| 1312 | + ret = of_property_read_u32(node, "reg", &id); |
---|
| 1313 | + if (ret || id >= MAX_SESSIONS || id < 0) { |
---|
| 1314 | + dev_err(dev, "valid dai id not found:%d\n", ret); |
---|
| 1315 | + continue; |
---|
| 1316 | + } |
---|
| 1317 | + |
---|
| 1318 | + dai_drv = &pdata->dais[idx++]; |
---|
| 1319 | + *dai_drv = q6asm_fe_dais_template[id]; |
---|
| 1320 | + |
---|
| 1321 | + ret = of_property_read_u32(node, "direction", &dir); |
---|
| 1322 | + if (ret) |
---|
| 1323 | + continue; |
---|
| 1324 | + |
---|
| 1325 | + if (dir == Q6ASM_DAI_RX) |
---|
| 1326 | + dai_drv->capture = empty_stream; |
---|
| 1327 | + else if (dir == Q6ASM_DAI_TX) |
---|
| 1328 | + dai_drv->playback = empty_stream; |
---|
| 1329 | + |
---|
| 1330 | + if (of_property_read_bool(node, "is-compress-dai")) |
---|
| 1331 | + dai_drv->compress_new = snd_soc_new_compress; |
---|
| 1332 | + } |
---|
| 1333 | + |
---|
| 1334 | + return 0; |
---|
| 1335 | +} |
---|
565 | 1336 | |
---|
566 | 1337 | static int q6asm_dai_probe(struct platform_device *pdev) |
---|
567 | 1338 | { |
---|
.. | .. |
---|
583 | 1354 | |
---|
584 | 1355 | dev_set_drvdata(dev, pdata); |
---|
585 | 1356 | |
---|
| 1357 | + rc = of_q6asm_parse_dai_data(dev, pdata); |
---|
| 1358 | + if (rc) |
---|
| 1359 | + return rc; |
---|
| 1360 | + |
---|
586 | 1361 | return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, |
---|
587 | | - q6asm_fe_dais, |
---|
588 | | - ARRAY_SIZE(q6asm_fe_dais)); |
---|
| 1362 | + pdata->dais, pdata->num_dais); |
---|
589 | 1363 | } |
---|
590 | 1364 | |
---|
| 1365 | +#ifdef CONFIG_OF |
---|
591 | 1366 | static const struct of_device_id q6asm_dai_device_id[] = { |
---|
592 | 1367 | { .compatible = "qcom,q6asm-dais" }, |
---|
593 | 1368 | {}, |
---|
594 | 1369 | }; |
---|
595 | 1370 | MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); |
---|
| 1371 | +#endif |
---|
596 | 1372 | |
---|
597 | 1373 | static struct platform_driver q6asm_dai_platform_driver = { |
---|
598 | 1374 | .driver = { |
---|