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