ASoC: Intel: sst - add pcm ops handling
[deliverable/linux.git] / sound / soc / intel / sst / sst_drv_interface.c
CommitLineData
cc547054
VK
1/*
2 * sst_drv_interface.c - Intel SST Driver for audio engine
3 *
4 * Copyright (C) 2008-14 Intel Corp
5 * Authors: Vinod Koul <vinod.koul@intel.com>
6 * Harsha Priya <priya.harsha@intel.com>
7 * Dharageswari R <dharageswari.r@intel.com)
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21#include <linux/delay.h>
22#include <linux/pci.h>
23#include <linux/fs.h>
24#include <linux/firmware.h>
25#include <linux/pm_runtime.h>
26#include <linux/pm_qos.h>
27#include <linux/math64.h>
28#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/soc.h>
31#include <sound/compress_driver.h>
32#include <asm/platform_sst_audio.h>
33#include "../sst-mfld-platform.h"
34#include "sst.h"
35#include "../sst-dsp.h"
36
37
38
39#define NUM_CODEC 2
40#define MIN_FRAGMENT 2
41#define MAX_FRAGMENT 4
42#define MIN_FRAGMENT_SIZE (50 * 1024)
43#define MAX_FRAGMENT_SIZE (1024 * 1024)
44#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1)
45
46int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
47{
48 struct stream_info *stream;
49 int ret = 0;
50
51 stream = get_stream_info(ctx, str_id);
52 if (stream) {
53 /* str_id is valid, so stream is alloacted */
54 ret = sst_free_stream(ctx, str_id);
55 if (ret)
56 sst_clean_stream(&ctx->streams[str_id]);
57 return ret;
58 }
59 return ret;
60}
61
62int sst_get_stream_allocated(struct intel_sst_drv *ctx,
63 struct snd_sst_params *str_param,
64 struct snd_sst_lib_download **lib_dnld)
65{
66 int retval;
67
68 retval = ctx->ops->alloc_stream(ctx, str_param);
69 if (retval > 0)
70 dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
71 return retval;
72
73}
74
75/*
76 * sst_get_sfreq - this function returns the frequency of the stream
77 *
78 * @str_param : stream params
79 */
80int sst_get_sfreq(struct snd_sst_params *str_param)
81{
82 switch (str_param->codec) {
83 case SST_CODEC_TYPE_PCM:
84 return str_param->sparams.uc.pcm_params.sfreq;
85 case SST_CODEC_TYPE_AAC:
86 return str_param->sparams.uc.aac_params.externalsr;
87 case SST_CODEC_TYPE_MP3:
88 return 0;
89 default:
90 return -EINVAL;
91 }
92}
93
94/*
95 * sst_get_sfreq - this function returns the frequency of the stream
96 *
97 * @str_param : stream params
98 */
99int sst_get_num_channel(struct snd_sst_params *str_param)
100{
101 switch (str_param->codec) {
102 case SST_CODEC_TYPE_PCM:
103 return str_param->sparams.uc.pcm_params.num_chan;
104 case SST_CODEC_TYPE_MP3:
105 return str_param->sparams.uc.mp3_params.num_chan;
106 case SST_CODEC_TYPE_AAC:
107 return str_param->sparams.uc.aac_params.num_chan;
108 default:
109 return -EINVAL;
110 }
111}
112
113/*
114 * sst_get_stream - this function prepares for stream allocation
115 *
116 * @str_param : stream param
117 */
118int sst_get_stream(struct intel_sst_drv *ctx,
119 struct snd_sst_params *str_param)
120{
121 int retval;
122 struct stream_info *str_info;
123
124 /* stream is not allocated, we are allocating */
125 retval = ctx->ops->alloc_stream(ctx, str_param);
126 if (retval <= 0) {
127 return -EIO;
128 }
129 /* store sampling freq */
130 str_info = &ctx->streams[retval];
131 str_info->sfreq = sst_get_sfreq(str_param);
132
133 return retval;
134}
135
136static int sst_power_control(struct device *dev, bool state)
137{
138 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
139
140 dev_dbg(ctx->dev, "state:%d", state);
141 if (state == true)
142 return pm_runtime_get_sync(dev);
143 else
144 return sst_pm_runtime_put(ctx);
145}
146
147/*
148 * sst_open_pcm_stream - Open PCM interface
149 *
150 * @str_param: parameters of pcm stream
151 *
152 * This function is called by MID sound card driver to open
153 * a new pcm interface
154 */
155static int sst_open_pcm_stream(struct device *dev,
156 struct snd_sst_params *str_param)
157{
158 int retval;
159 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
160
161 if (!str_param)
162 return -EINVAL;
163
164 retval = pm_runtime_get_sync(ctx->dev);
165 if (retval < 0)
166 return retval;
167 retval = sst_get_stream(ctx, str_param);
168 if (retval > 0) {
169 ctx->stream_cnt++;
170 } else {
171 dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
172 sst_pm_runtime_put(ctx);
173 }
174
175 return retval;
176}
177
178/*
179 * sst_close_pcm_stream - Close PCM interface
180 *
181 * @str_id: stream id to be closed
182 *
183 * This function is called by MID sound card driver to close
184 * an existing pcm interface
185 */
186static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
187{
188 struct stream_info *stream;
189 int retval = 0;
190 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
191
192 stream = get_stream_info(ctx, str_id);
193 if (!stream) {
194 dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
195 return -EINVAL;
196 }
197
198 if (stream->status == STREAM_RESET) {
199 /* silently fail here as we have cleaned the stream earlier */
200 dev_dbg(ctx->dev, "stream in reset state...\n");
201
202 retval = 0;
203 goto put;
204 }
205
206 retval = free_stream_context(ctx, str_id);
207put:
208 stream->pcm_substream = NULL;
209 stream->status = STREAM_UN_INIT;
210 stream->period_elapsed = NULL;
211 ctx->stream_cnt--;
212
213 sst_pm_runtime_put(ctx);
214
215 dev_dbg(ctx->dev, "Exit\n");
216 return 0;
217}
218
219static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
220 struct pcm_stream_info *info,
221 struct snd_pcm_substream *substream,
222 struct snd_sst_tstamp *fw_tstamp)
223{
224 size_t delay_bytes, delay_frames;
225 size_t buffer_sz;
226 u32 pointer_bytes, pointer_samples;
227
228 dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
229 fw_tstamp->ring_buffer_counter);
230 dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
231 fw_tstamp->hardware_counter);
232 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
233 delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
234 fw_tstamp->hardware_counter);
235 else
236 delay_bytes = (size_t) (fw_tstamp->hardware_counter -
237 fw_tstamp->ring_buffer_counter);
238 delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
239 buffer_sz = snd_pcm_lib_buffer_bytes(substream);
240 div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
241 pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
242
243 dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
244
245 info->buffer_ptr = pointer_samples / substream->runtime->channels;
246
247 info->pcm_delay = delay_frames / substream->runtime->channels;
248 dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
249 info->buffer_ptr, info->pcm_delay);
250 return 0;
251}
252
253static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
254{
255 struct stream_info *stream;
256 struct snd_pcm_substream *substream;
257 struct snd_sst_tstamp fw_tstamp;
258 unsigned int str_id;
259 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
260
261 str_id = info->str_id;
262 stream = get_stream_info(ctx, str_id);
263 if (!stream)
264 return -EINVAL;
265
266 if (!stream->pcm_substream)
267 return -EINVAL;
268 substream = stream->pcm_substream;
269
270 memcpy_fromio(&fw_tstamp,
271 ((void *)(ctx->mailbox + ctx->tstamp)
272 + (str_id * sizeof(fw_tstamp))),
273 sizeof(fw_tstamp));
274 return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
275}
276
277static int sst_stream_start(struct device *dev, int str_id)
278{
279 struct stream_info *str_info;
280 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
281
282 if (ctx->sst_state != SST_FW_RUNNING)
283 return 0;
284 str_info = get_stream_info(ctx, str_id);
285 if (!str_info)
286 return -EINVAL;
287 str_info->prev = str_info->status;
288 str_info->status = STREAM_RUNNING;
289 sst_start_stream(ctx, str_id);
290
291 return 0;
292}
293
294static int sst_stream_drop(struct device *dev, int str_id)
295{
296 struct stream_info *str_info;
297 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
298
299 if (ctx->sst_state != SST_FW_RUNNING)
300 return 0;
301
302 str_info = get_stream_info(ctx, str_id);
303 if (!str_info)
304 return -EINVAL;
305 str_info->prev = STREAM_UN_INIT;
306 str_info->status = STREAM_INIT;
307 return sst_drop_stream(ctx, str_id);
308}
309
310static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
311{
312 int str_id = 0;
313 struct stream_info *stream;
314 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
315
316 str_id = str_info->str_id;
317
318 if (ctx->sst_state != SST_FW_RUNNING)
319 return 0;
320
321 stream = get_stream_info(ctx, str_id);
322 if (!stream)
323 return -EINVAL;
324
325 dev_dbg(ctx->dev, "setting the period ptrs\n");
326 stream->pcm_substream = str_info->arg;
327 stream->period_elapsed = str_info->period_elapsed;
328 stream->sfreq = str_info->sfreq;
329 stream->prev = stream->status;
330 stream->status = STREAM_INIT;
331 dev_dbg(ctx->dev,
332 "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
333 stream->pcm_substream, stream->period_elapsed,
334 stream->sfreq, stream->status);
335
336 return 0;
337}
338
339/*
340 * sst_set_byte_stream - Set generic params
341 *
342 * @cmd: control cmd to be set
343 * @arg: command argument
344 *
345 * This function is called by MID sound card driver to configure
346 * SST runtime params.
347 */
348static int sst_send_byte_stream(struct device *dev,
349 struct snd_sst_bytes_v2 *bytes)
350{
351 int ret_val = 0;
352 struct intel_sst_drv *ctx = dev_get_drvdata(dev);
353
354 if (NULL == bytes)
355 return -EINVAL;
356 ret_val = pm_runtime_get_sync(ctx->dev);
357 if (ret_val < 0)
358 return ret_val;
359
360 ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
361 sst_pm_runtime_put(ctx);
362
363 return ret_val;
364}
365
366static struct sst_ops pcm_ops = {
367 .open = sst_open_pcm_stream,
368 .stream_init = sst_stream_init,
369 .stream_start = sst_stream_start,
370 .stream_drop = sst_stream_drop,
371 .stream_read_tstamp = sst_read_timestamp,
372 .send_byte_stream = sst_send_byte_stream,
373 .close = sst_close_pcm_stream,
374 .power = sst_power_control,
375};
376
377static struct sst_device sst_dsp_device = {
378 .name = "Intel(R) SST LPE",
379 .dev = NULL,
380 .ops = &pcm_ops,
381};
382
383/*
384 * sst_register - function to register DSP
385 *
386 * This functions registers DSP with the platform driver
387 */
388int sst_register(struct device *dev)
389{
390 int ret_val;
391
392 sst_dsp_device.dev = dev;
393 ret_val = sst_register_dsp(&sst_dsp_device);
394 if (ret_val)
395 dev_err(dev, "Unable to register DSP with platform driver\n");
396
397 return ret_val;
398}
399
400int sst_unregister(struct device *dev)
401{
402 return sst_unregister_dsp(&sst_dsp_device);
403}
This page took 0.038705 seconds and 5 git commands to generate.