]>
Commit | Line | Data |
---|---|---|
fffa1cca VK |
1 | /* |
2 | * intel_sst_pvt.c - Intel SST Driver for audio engine | |
3 | * | |
4 | * Copyright (C) 2008-10 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 | * KP Jeeja <jeeja.kp@intel.com> | |
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; version 2 of the License. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, write to the Free Software Foundation, Inc., | |
22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
23 | * | |
24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
25 | * | |
26 | * This driver exposes the audio engine functionalities to the ALSA | |
27 | * and middleware. | |
28 | * | |
29 | * This file contains all private functions | |
30 | */ | |
31 | ||
d0f40c50 JP |
32 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
33 | ||
fffa1cca VK |
34 | #include <linux/pci.h> |
35 | #include <linux/fs.h> | |
36 | #include <linux/firmware.h> | |
37 | #include <linux/sched.h> | |
38 | #include "intel_sst.h" | |
39 | #include "intel_sst_ioctl.h" | |
40 | #include "intel_sst_fw_ipc.h" | |
41 | #include "intel_sst_common.h" | |
42 | ||
43 | /* | |
44 | * sst_get_block_stream - get a new block stream | |
45 | * | |
46 | * @sst_drv_ctx: Driver context structure | |
47 | * | |
48 | * This function assigns a block for the calls that dont have stream context yet | |
49 | * the blocks are used for waiting on Firmware's response for any operation | |
50 | * Should be called with stream lock held | |
51 | */ | |
52 | int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx) | |
53 | { | |
54 | int i; | |
55 | ||
56 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | |
57 | if (sst_drv_ctx->alloc_block[i].sst_id == BLOCK_UNINIT) { | |
58 | sst_drv_ctx->alloc_block[i].ops_block.condition = false; | |
59 | sst_drv_ctx->alloc_block[i].ops_block.ret_code = 0; | |
60 | sst_drv_ctx->alloc_block[i].sst_id = 0; | |
61 | break; | |
62 | } | |
63 | } | |
64 | if (i == MAX_ACTIVE_STREAM) { | |
d0f40c50 | 65 | pr_err("max alloc_stream reached\n"); |
fffa1cca VK |
66 | i = -EBUSY; /* active stream limit reached */ |
67 | } | |
68 | return i; | |
69 | } | |
70 | ||
71 | /* | |
72 | * sst_wait_interruptible - wait on event | |
73 | * | |
74 | * @sst_drv_ctx: Driver context | |
75 | * @block: Driver block to wait on | |
76 | * | |
77 | * This function waits without a timeout (and is interruptable) for a | |
78 | * given block event | |
79 | */ | |
80 | int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, | |
81 | struct sst_block *block) | |
82 | { | |
83 | int retval = 0; | |
84 | ||
85 | if (!wait_event_interruptible(sst_drv_ctx->wait_queue, | |
86 | block->condition)) { | |
87 | /* event wake */ | |
88 | if (block->ret_code < 0) { | |
d0f40c50 | 89 | pr_err("stream failed %d\n", block->ret_code); |
fffa1cca VK |
90 | retval = -EBUSY; |
91 | } else { | |
d0f40c50 | 92 | pr_debug("event up\n"); |
fffa1cca VK |
93 | retval = 0; |
94 | } | |
95 | } else { | |
d0f40c50 | 96 | pr_err("signal interrupted\n"); |
fffa1cca VK |
97 | retval = -EINTR; |
98 | } | |
99 | return retval; | |
100 | ||
101 | } | |
102 | ||
103 | ||
104 | /* | |
105 | * sst_wait_interruptible_timeout - wait on event interruptable | |
106 | * | |
107 | * @sst_drv_ctx: Driver context | |
108 | * @block: Driver block to wait on | |
109 | * @timeout: time for wait on | |
110 | * | |
111 | * This function waits with a timeout value (and is interruptible) on a | |
112 | * given block event | |
113 | */ | |
114 | int sst_wait_interruptible_timeout( | |
115 | struct intel_sst_drv *sst_drv_ctx, | |
116 | struct sst_block *block, int timeout) | |
117 | { | |
118 | int retval = 0; | |
119 | ||
d0f40c50 | 120 | pr_debug("sst_wait_interruptible_timeout - waiting....\n"); |
fffa1cca VK |
121 | if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, |
122 | block->condition, | |
123 | msecs_to_jiffies(timeout))) { | |
124 | if (block->ret_code < 0) | |
d0f40c50 | 125 | pr_err("stream failed %d\n", block->ret_code); |
fffa1cca | 126 | else |
d0f40c50 | 127 | pr_debug("event up\n"); |
fffa1cca VK |
128 | retval = block->ret_code; |
129 | } else { | |
130 | block->on = false; | |
d0f40c50 | 131 | pr_err("timeout occurred...\n"); |
fffa1cca VK |
132 | /*setting firmware state as uninit so that the |
133 | firmware will get re-downloaded on next request | |
134 | this is because firmare not responding for 5 sec | |
135 | is equalant to some unrecoverable error of FW | |
136 | sst_drv_ctx->sst_state = SST_UN_INIT;*/ | |
137 | retval = -EBUSY; | |
138 | } | |
139 | return retval; | |
140 | ||
141 | } | |
142 | ||
143 | ||
144 | /* | |
145 | * sst_wait_timeout - wait on event for timeout | |
146 | * | |
147 | * @sst_drv_ctx: Driver context | |
148 | * @block: Driver block to wait on | |
149 | * | |
150 | * This function waits with a timeout value (and is not interruptible) on a | |
151 | * given block event | |
152 | */ | |
153 | int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, | |
154 | struct stream_alloc_block *block) | |
155 | { | |
156 | int retval = 0; | |
157 | ||
158 | /* NOTE: | |
159 | Observed that FW processes the alloc msg and replies even | |
160 | before the alloc thread has finished execution */ | |
d0f40c50 | 161 | pr_debug("waiting for %x, condition %x\n", |
fffa1cca VK |
162 | block->sst_id, block->ops_block.condition); |
163 | if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, | |
164 | block->ops_block.condition, | |
165 | msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { | |
166 | /* event wake */ | |
d0f40c50 JP |
167 | pr_debug("Event wake %x\n", block->ops_block.condition); |
168 | pr_debug("message ret: %d\n", block->ops_block.ret_code); | |
fffa1cca VK |
169 | retval = block->ops_block.ret_code; |
170 | } else { | |
171 | block->ops_block.on = false; | |
d0f40c50 | 172 | pr_err("Wait timed-out %x\n", block->ops_block.condition); |
fffa1cca VK |
173 | /* settign firmware state as uninit so that the |
174 | firmware will get redownloaded on next request | |
175 | this is because firmare not responding for 5 sec | |
176 | is equalant to some unrecoverable error of FW | |
177 | sst_drv_ctx->sst_state = SST_UN_INIT;*/ | |
178 | retval = -EBUSY; | |
179 | } | |
180 | return retval; | |
181 | ||
182 | } | |
183 | ||
184 | /* | |
185 | * sst_create_large_msg - create a large IPC message | |
186 | * | |
187 | * @arg: ipc message | |
188 | * | |
189 | * this function allocates structures to send a large message to the firmware | |
190 | */ | |
191 | int sst_create_large_msg(struct ipc_post **arg) | |
192 | { | |
193 | struct ipc_post *msg; | |
194 | ||
195 | msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); | |
196 | if (!msg) { | |
d0f40c50 | 197 | pr_err("kzalloc msg failed\n"); |
fffa1cca VK |
198 | return -ENOMEM; |
199 | } | |
200 | ||
201 | msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); | |
202 | if (!msg->mailbox_data) { | |
203 | kfree(msg); | |
d0f40c50 | 204 | pr_err("kzalloc mailbox_data failed"); |
fffa1cca VK |
205 | return -ENOMEM; |
206 | }; | |
207 | *arg = msg; | |
208 | return 0; | |
209 | } | |
210 | ||
211 | /* | |
212 | * sst_create_short_msg - create a short IPC message | |
213 | * | |
214 | * @arg: ipc message | |
215 | * | |
216 | * this function allocates structures to send a short message to the firmware | |
217 | */ | |
218 | int sst_create_short_msg(struct ipc_post **arg) | |
219 | { | |
220 | struct ipc_post *msg; | |
221 | ||
222 | msg = kzalloc(sizeof(*msg), GFP_ATOMIC); | |
223 | if (!msg) { | |
d0f40c50 | 224 | pr_err("kzalloc msg failed\n"); |
fffa1cca VK |
225 | return -ENOMEM; |
226 | } | |
227 | msg->mailbox_data = NULL; | |
228 | *arg = msg; | |
229 | return 0; | |
230 | } | |
231 | ||
232 | /* | |
233 | * sst_clean_stream - clean the stream context | |
234 | * | |
235 | * @stream: stream structure | |
236 | * | |
237 | * this function resets the stream contexts | |
238 | * should be called in free | |
239 | */ | |
240 | void sst_clean_stream(struct stream_info *stream) | |
241 | { | |
242 | struct sst_stream_bufs *bufs = NULL, *_bufs; | |
243 | stream->status = STREAM_UN_INIT; | |
244 | stream->prev = STREAM_UN_INIT; | |
245 | mutex_lock(&stream->lock); | |
246 | list_for_each_entry_safe(bufs, _bufs, &stream->bufs, node) { | |
247 | list_del(&bufs->node); | |
248 | kfree(bufs); | |
249 | } | |
250 | mutex_unlock(&stream->lock); | |
251 | ||
252 | if (stream->ops != STREAM_OPS_PLAYBACK_DRM) | |
253 | kfree(stream->decode_ibuf); | |
254 | } | |
255 | ||
256 | /* | |
257 | * sst_wake_up_alloc_block - wake up waiting block | |
258 | * | |
259 | * @sst_drv_ctx: Driver context | |
260 | * @sst_id: stream id | |
261 | * @status: status of wakeup | |
262 | * @data: data pointer of wakeup | |
263 | * | |
264 | * This function wakes up a sleeping block event based on the response | |
265 | */ | |
266 | void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, | |
267 | u8 sst_id, int status, void *data) | |
268 | { | |
269 | int i; | |
270 | ||
271 | /* Unblock with retval code */ | |
272 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | |
273 | if (sst_id == sst_drv_ctx->alloc_block[i].sst_id) { | |
274 | sst_drv_ctx->alloc_block[i].ops_block.condition = true; | |
275 | sst_drv_ctx->alloc_block[i].ops_block.ret_code = status; | |
276 | sst_drv_ctx->alloc_block[i].ops_block.data = data; | |
277 | wake_up(&sst_drv_ctx->wait_queue); | |
278 | break; | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | /* | |
284 | * sst_enable_rx_timeslot - Send msg to query for stream parameters | |
285 | * @status: rx timeslot to be enabled | |
286 | * | |
287 | * This function is called when the RX timeslot is required to be enabled | |
288 | */ | |
289 | int sst_enable_rx_timeslot(int status) | |
290 | { | |
291 | int retval = 0; | |
292 | struct ipc_post *msg = NULL; | |
293 | ||
294 | if (sst_create_short_msg(&msg)) { | |
d0f40c50 | 295 | pr_err("mem allocation failed\n"); |
fffa1cca VK |
296 | return -ENOMEM; |
297 | } | |
d0f40c50 | 298 | pr_debug("ipc message sending: ENABLE_RX_TIME_SLOT\n"); |
fffa1cca VK |
299 | sst_fill_header(&msg->header, IPC_IA_ENABLE_RX_TIME_SLOT, 0, 0); |
300 | msg->header.part.data = status; | |
301 | sst_drv_ctx->hs_info_blk.condition = false; | |
302 | sst_drv_ctx->hs_info_blk.ret_code = 0; | |
303 | sst_drv_ctx->hs_info_blk.on = true; | |
304 | spin_lock(&sst_drv_ctx->list_spin_lock); | |
305 | list_add_tail(&msg->node, | |
306 | &sst_drv_ctx->ipc_dispatch_list); | |
307 | spin_unlock(&sst_drv_ctx->list_spin_lock); | |
308 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | |
309 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | |
310 | &sst_drv_ctx->hs_info_blk, SST_BLOCK_TIMEOUT); | |
311 | return retval; | |
312 | } | |
313 |