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