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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
33
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 */
sst_get_block_stream(struct intel_sst_drv * sst_drv_ctx)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) {
65 pr_err("max alloc_stream reached\n");
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 */
sst_wait_interruptible(struct intel_sst_drv * sst_drv_ctx,struct sst_block * block)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) {
89 pr_err("stream failed %d\n", block->ret_code);
90 retval = -EBUSY;
91 } else {
92 pr_debug("event up\n");
93 retval = 0;
94 }
95 } else {
96 pr_err("signal interrupted\n");
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 */
sst_wait_interruptible_timeout(struct intel_sst_drv * sst_drv_ctx,struct sst_block * block,int timeout)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
120 pr_debug("sst_wait_interruptible_timeout - waiting....\n");
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)
125 pr_err("stream failed %d\n", block->ret_code);
126 else
127 pr_debug("event up\n");
128 retval = block->ret_code;
129 } else {
130 block->on = false;
131 pr_err("timeout occurred...\n");
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 */
sst_wait_timeout(struct intel_sst_drv * sst_drv_ctx,struct stream_alloc_block * block)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 */
161 pr_debug("waiting for %x, condition %x\n",
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 */
167 pr_debug("Event wake %x\n", block->ops_block.condition);
168 pr_debug("message ret: %d\n", block->ops_block.ret_code);
169 retval = block->ops_block.ret_code;
170 } else {
171 block->ops_block.on = false;
172 pr_err("Wait timed-out %x\n", block->ops_block.condition);
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 */
sst_create_large_msg(struct ipc_post ** arg)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) {
197 pr_err("kzalloc msg failed\n");
198 return -ENOMEM;
199 }
200
201 msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
202 if (!msg->mailbox_data) {
203 kfree(msg);
204 pr_err("kzalloc mailbox_data failed");
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 */
sst_create_short_msg(struct ipc_post ** arg)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) {
224 pr_err("kzalloc msg failed\n");
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 */
sst_clean_stream(struct stream_info * stream)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 */
sst_wake_up_alloc_block(struct intel_sst_drv * sst_drv_ctx,u8 sst_id,int status,void * data)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 */
sst_enable_rx_timeslot(int status)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)) {
295 pr_err("mem allocation failed\n");
296 return -ENOMEM;
297 }
298 pr_debug("ipc message sending: ENABLE_RX_TIME_SLOT\n");
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
314