diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-mailbox.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 527 |
1 files changed, 427 insertions, 100 deletions
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index acff7dfb60df..de5e723fdf44 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * cx18 mailbox functions | 2 | * cx18 mailbox functions |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * Copyright (C) 2008 Andy Walls <awalls@radix.net> | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
@@ -26,15 +27,14 @@ | |||
26 | #include "cx18-scb.h" | 27 | #include "cx18-scb.h" |
27 | #include "cx18-irq.h" | 28 | #include "cx18-irq.h" |
28 | #include "cx18-mailbox.h" | 29 | #include "cx18-mailbox.h" |
30 | #include "cx18-queue.h" | ||
31 | #include "cx18-streams.h" | ||
32 | |||
33 | static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; | ||
29 | 34 | ||
30 | #define API_FAST (1 << 2) /* Short timeout */ | 35 | #define API_FAST (1 << 2) /* Short timeout */ |
31 | #define API_SLOW (1 << 3) /* Additional 300ms timeout */ | 36 | #define API_SLOW (1 << 3) /* Additional 300ms timeout */ |
32 | 37 | ||
33 | #define APU 0 | ||
34 | #define CPU 1 | ||
35 | #define EPU 2 | ||
36 | #define HPU 3 | ||
37 | |||
38 | struct cx18_api_info { | 38 | struct cx18_api_info { |
39 | u32 cmd; | 39 | u32 cmd; |
40 | u8 flags; /* Flags, see above */ | 40 | u8 flags; /* Flags, see above */ |
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = { | |||
82 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), | 82 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), |
83 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), | 83 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), |
84 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), | 84 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), |
85 | API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), | ||
86 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), | 85 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), |
86 | API_ENTRY(APU, CX18_APU_RESETAI, 0), | ||
87 | API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), | ||
87 | API_ENTRY(0, 0, 0), | 88 | API_ENTRY(0, 0, 0), |
88 | }; | 89 | }; |
89 | 90 | ||
@@ -97,70 +98,175 @@ static const struct cx18_api_info *find_api_info(u32 cmd) | |||
97 | return NULL; | 98 | return NULL; |
98 | } | 99 | } |
99 | 100 | ||
100 | static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, | 101 | static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) |
101 | u32 *state, u32 *irq, u32 *req) | ||
102 | { | 102 | { |
103 | struct cx18_mailbox __iomem *mb = NULL; | 103 | char argstr[MAX_MB_ARGUMENTS*11+1]; |
104 | int wait_count = 0; | 104 | char *p; |
105 | u32 ack; | 105 | int i; |
106 | |||
107 | switch (rpu) { | ||
108 | case APU: | ||
109 | mb = &cx->scb->epu2apu_mb; | ||
110 | *state = cx18_readl(cx, &cx->scb->apu_state); | ||
111 | *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); | ||
112 | break; | ||
113 | 106 | ||
114 | case CPU: | 107 | if (!(cx18_debug & CX18_DBGFLG_API)) |
115 | mb = &cx->scb->epu2cpu_mb; | 108 | return; |
116 | *state = cx18_readl(cx, &cx->scb->cpu_state); | ||
117 | *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); | ||
118 | break; | ||
119 | 109 | ||
120 | case HPU: | 110 | for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) { |
121 | mb = &cx->scb->epu2hpu_mb; | 111 | /* kernel snprintf() appends '\0' always */ |
122 | *state = cx18_readl(cx, &cx->scb->hpu_state); | 112 | snprintf(p, 12, " %#010x", mb->args[i]); |
123 | *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); | ||
124 | break; | ||
125 | } | 113 | } |
114 | CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s" | ||
115 | "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr); | ||
116 | } | ||
126 | 117 | ||
127 | if (mb == NULL) | ||
128 | return mb; | ||
129 | 118 | ||
130 | do { | 119 | /* |
131 | *req = cx18_readl(cx, &mb->request); | 120 | * Functions that run in a work_queue work handling context |
132 | ack = cx18_readl(cx, &mb->ack); | 121 | */ |
133 | wait_count++; | ||
134 | } while (*req != ack && wait_count < 600); | ||
135 | 122 | ||
136 | if (*req == ack) { | 123 | static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) |
137 | (*req)++; | 124 | { |
138 | if (*req == 0 || *req == 0xffffffff) | 125 | u32 handle, mdl_ack_count, id; |
139 | *req = 1; | 126 | struct cx18_mailbox *mb; |
140 | return mb; | 127 | struct cx18_mdl_ack *mdl_ack; |
128 | struct cx18_stream *s; | ||
129 | struct cx18_buffer *buf; | ||
130 | int i; | ||
131 | |||
132 | mb = &order->mb; | ||
133 | handle = mb->args[0]; | ||
134 | s = cx18_handle_to_stream(cx, handle); | ||
135 | |||
136 | if (s == NULL) { | ||
137 | CX18_WARN("Got DMA done notification for unknown/inactive" | ||
138 | " handle %d, %s mailbox seq no %d\n", handle, | ||
139 | (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? | ||
140 | "stale" : "good", mb->request); | ||
141 | return; | ||
141 | } | 142 | } |
142 | return NULL; | 143 | |
144 | mdl_ack_count = mb->args[2]; | ||
145 | mdl_ack = order->mdl_ack; | ||
146 | for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { | ||
147 | id = mdl_ack->id; | ||
148 | /* | ||
149 | * Simple integrity check for processing a stale (and possibly | ||
150 | * inconsistent mailbox): make sure the buffer id is in the | ||
151 | * valid range for the stream. | ||
152 | * | ||
153 | * We go through the trouble of dealing with stale mailboxes | ||
154 | * because most of the time, the mailbox data is still valid and | ||
155 | * unchanged (and in practice the firmware ping-pongs the | ||
156 | * two mdl_ack buffers so mdl_acks are not stale). | ||
157 | * | ||
158 | * There are occasions when we get a half changed mailbox, | ||
159 | * which this check catches for a handle & id mismatch. If the | ||
160 | * handle and id do correspond, the worst case is that we | ||
161 | * completely lost the old buffer, but pick up the new buffer | ||
162 | * early (but the new mdl_ack is guaranteed to be good in this | ||
163 | * case as the firmware wouldn't point us to a new mdl_ack until | ||
164 | * it's filled in). | ||
165 | * | ||
166 | * cx18_queue_get buf() will detect the lost buffers | ||
167 | * and send them back to q_free for fw rotation eventually. | ||
168 | */ | ||
169 | if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && | ||
170 | !(id >= s->mdl_offset && | ||
171 | id < (s->mdl_offset + s->buffers))) { | ||
172 | CX18_WARN("Fell behind! Ignoring stale mailbox with " | ||
173 | " inconsistent data. Lost buffer for mailbox " | ||
174 | "seq no %d\n", mb->request); | ||
175 | break; | ||
176 | } | ||
177 | buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); | ||
178 | |||
179 | CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); | ||
180 | if (buf == NULL) { | ||
181 | CX18_WARN("Could not find buf %d for stream %s\n", | ||
182 | id, s->name); | ||
183 | /* Put as many buffers as possible back into fw use */ | ||
184 | cx18_stream_load_fw_queue(s); | ||
185 | continue; | ||
186 | } | ||
187 | |||
188 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { | ||
189 | CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", | ||
190 | buf->bytesused); | ||
191 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | ||
192 | buf->bytesused); | ||
193 | } | ||
194 | /* Put as many buffers as possible back into fw use */ | ||
195 | cx18_stream_load_fw_queue(s); | ||
196 | /* Put back TS buffer, since it was removed from all queues */ | ||
197 | if (s->type == CX18_ENC_STREAM_TYPE_TS) | ||
198 | cx18_stream_put_buf_fw(s, buf); | ||
199 | } | ||
200 | wake_up(&cx->dma_waitq); | ||
201 | if (s->id != -1) | ||
202 | wake_up(&s->waitq); | ||
143 | } | 203 | } |
144 | 204 | ||
145 | long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) | 205 | static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) |
146 | { | 206 | { |
147 | const struct cx18_api_info *info = find_api_info(mb->cmd); | 207 | char *p; |
148 | struct cx18_mailbox __iomem *ack_mb; | 208 | char *str = order->str; |
149 | u32 ack_irq; | ||
150 | u8 rpu = CPU; | ||
151 | 209 | ||
152 | if (info == NULL && mb->cmd) { | 210 | CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str); |
153 | CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); | 211 | p = strchr(str, '.'); |
154 | return -EINVAL; | 212 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) |
155 | } | 213 | CX18_INFO("FW version: %s\n", p - 1); |
156 | if (info) | 214 | } |
157 | rpu = info->rpu; | ||
158 | 215 | ||
159 | switch (rpu) { | 216 | static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) |
160 | case HPU: | 217 | { |
161 | ack_irq = IRQ_EPU_TO_HPU_ACK; | 218 | switch (order->rpu) { |
162 | ack_mb = &cx->scb->hpu2epu_mb; | 219 | case CPU: |
220 | { | ||
221 | switch (order->mb.cmd) { | ||
222 | case CX18_EPU_DMA_DONE: | ||
223 | epu_dma_done(cx, order); | ||
224 | break; | ||
225 | case CX18_EPU_DEBUG: | ||
226 | epu_debug(cx, order); | ||
227 | break; | ||
228 | default: | ||
229 | CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", | ||
230 | order->mb.cmd); | ||
231 | break; | ||
232 | } | ||
163 | break; | 233 | break; |
234 | } | ||
235 | case APU: | ||
236 | CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", | ||
237 | order->mb.cmd); | ||
238 | break; | ||
239 | default: | ||
240 | break; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static | ||
245 | void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order) | ||
246 | { | ||
247 | atomic_set(&order->pending, 0); | ||
248 | } | ||
249 | |||
250 | void cx18_epu_work_handler(struct work_struct *work) | ||
251 | { | ||
252 | struct cx18_epu_work_order *order = | ||
253 | container_of(work, struct cx18_epu_work_order, work); | ||
254 | struct cx18 *cx = order->cx; | ||
255 | epu_cmd(cx, order); | ||
256 | free_epu_work_order(cx, order); | ||
257 | } | ||
258 | |||
259 | |||
260 | /* | ||
261 | * Functions that run in an interrupt handling context | ||
262 | */ | ||
263 | |||
264 | static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | ||
265 | { | ||
266 | struct cx18_mailbox __iomem *ack_mb; | ||
267 | u32 ack_irq, req; | ||
268 | |||
269 | switch (order->rpu) { | ||
164 | case APU: | 270 | case APU: |
165 | ack_irq = IRQ_EPU_TO_APU_ACK; | 271 | ack_irq = IRQ_EPU_TO_APU_ACK; |
166 | ack_mb = &cx->scb->apu2epu_mb; | 272 | ack_mb = &cx->scb->apu2epu_mb; |
@@ -170,26 +276,197 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) | |||
170 | ack_mb = &cx->scb->cpu2epu_mb; | 276 | ack_mb = &cx->scb->cpu2epu_mb; |
171 | break; | 277 | break; |
172 | default: | 278 | default: |
173 | CX18_WARN("Unknown RPU for command %x\n", mb->cmd); | 279 | CX18_WARN("Unhandled RPU (%d) for command %x ack\n", |
174 | return -EINVAL; | 280 | order->rpu, order->mb.cmd); |
281 | return; | ||
175 | } | 282 | } |
176 | 283 | ||
177 | cx18_setup_page(cx, SCB_OFFSET); | 284 | req = order->mb.request; |
178 | cx18_write_sync(cx, mb->request, &ack_mb->ack); | 285 | /* Don't ack if the RPU has gotten impatient and timed us out */ |
286 | if (req != cx18_readl(cx, &ack_mb->request) || | ||
287 | req == cx18_readl(cx, &ack_mb->ack)) { | ||
288 | CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " | ||
289 | "incoming %s to EPU mailbox (sequence no. %u) " | ||
290 | "while processing\n", | ||
291 | rpu_str[order->rpu], rpu_str[order->rpu], req); | ||
292 | order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; | ||
293 | return; | ||
294 | } | ||
295 | cx18_writel(cx, req, &ack_mb->ack); | ||
179 | cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); | 296 | cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); |
180 | return 0; | 297 | return; |
298 | } | ||
299 | |||
300 | static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | ||
301 | { | ||
302 | u32 handle, mdl_ack_offset, mdl_ack_count; | ||
303 | struct cx18_mailbox *mb; | ||
304 | |||
305 | mb = &order->mb; | ||
306 | handle = mb->args[0]; | ||
307 | mdl_ack_offset = mb->args[1]; | ||
308 | mdl_ack_count = mb->args[2]; | ||
309 | |||
310 | if (handle == CX18_INVALID_TASK_HANDLE || | ||
311 | mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { | ||
312 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
313 | mb_ack_irq(cx, order); | ||
314 | return -1; | ||
315 | } | ||
316 | |||
317 | cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset, | ||
318 | sizeof(struct cx18_mdl_ack) * mdl_ack_count); | ||
319 | |||
320 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
321 | mb_ack_irq(cx, order); | ||
322 | return 1; | ||
323 | } | ||
324 | |||
325 | static | ||
326 | int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | ||
327 | { | ||
328 | u32 str_offset; | ||
329 | char *str = order->str; | ||
330 | |||
331 | str[0] = '\0'; | ||
332 | str_offset = order->mb.args[1]; | ||
333 | if (str_offset) { | ||
334 | cx18_setup_page(cx, str_offset); | ||
335 | cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252); | ||
336 | str[252] = '\0'; | ||
337 | cx18_setup_page(cx, SCB_OFFSET); | ||
338 | } | ||
339 | |||
340 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
341 | mb_ack_irq(cx, order); | ||
342 | |||
343 | return str_offset ? 1 : 0; | ||
181 | } | 344 | } |
182 | 345 | ||
346 | static inline | ||
347 | int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | ||
348 | { | ||
349 | int ret = -1; | ||
350 | |||
351 | switch (order->rpu) { | ||
352 | case CPU: | ||
353 | { | ||
354 | switch (order->mb.cmd) { | ||
355 | case CX18_EPU_DMA_DONE: | ||
356 | ret = epu_dma_done_irq(cx, order); | ||
357 | break; | ||
358 | case CX18_EPU_DEBUG: | ||
359 | ret = epu_debug_irq(cx, order); | ||
360 | break; | ||
361 | default: | ||
362 | CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", | ||
363 | order->mb.cmd); | ||
364 | break; | ||
365 | } | ||
366 | break; | ||
367 | } | ||
368 | case APU: | ||
369 | CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", | ||
370 | order->mb.cmd); | ||
371 | break; | ||
372 | default: | ||
373 | break; | ||
374 | } | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | static inline | ||
379 | struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) | ||
380 | { | ||
381 | int i; | ||
382 | struct cx18_epu_work_order *order = NULL; | ||
383 | |||
384 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { | ||
385 | /* | ||
386 | * We only need "pending" atomic to inspect its contents, | ||
387 | * and need not do a check and set because: | ||
388 | * 1. Any work handler thread only clears "pending" and only | ||
389 | * on one, particular work order at a time, per handler thread. | ||
390 | * 2. "pending" is only set here, and we're serialized because | ||
391 | * we're called in an IRQ handler context. | ||
392 | */ | ||
393 | if (atomic_read(&cx->epu_work_order[i].pending) == 0) { | ||
394 | order = &cx->epu_work_order[i]; | ||
395 | atomic_set(&order->pending, 1); | ||
396 | break; | ||
397 | } | ||
398 | } | ||
399 | return order; | ||
400 | } | ||
401 | |||
402 | void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) | ||
403 | { | ||
404 | struct cx18_mailbox __iomem *mb; | ||
405 | struct cx18_mailbox *order_mb; | ||
406 | struct cx18_epu_work_order *order; | ||
407 | int submit; | ||
408 | |||
409 | switch (rpu) { | ||
410 | case CPU: | ||
411 | mb = &cx->scb->cpu2epu_mb; | ||
412 | break; | ||
413 | case APU: | ||
414 | mb = &cx->scb->apu2epu_mb; | ||
415 | break; | ||
416 | default: | ||
417 | return; | ||
418 | } | ||
419 | |||
420 | order = alloc_epu_work_order_irq(cx); | ||
421 | if (order == NULL) { | ||
422 | CX18_WARN("Unable to find blank work order form to schedule " | ||
423 | "incoming mailbox command processing\n"); | ||
424 | return; | ||
425 | } | ||
426 | |||
427 | order->flags = 0; | ||
428 | order->rpu = rpu; | ||
429 | order_mb = &order->mb; | ||
430 | |||
431 | /* mb->cmd and mb->args[0] through mb->args[2] */ | ||
432 | cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32)); | ||
433 | /* mb->request and mb->ack. N.B. we want to read mb->ack last */ | ||
434 | cx18_memcpy_fromio(cx, &order_mb->request, &mb->request, | ||
435 | 2 * sizeof(u32)); | ||
436 | |||
437 | if (order_mb->request == order_mb->ack) { | ||
438 | CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " | ||
439 | "incoming %s to EPU mailbox (sequence no. %u)" | ||
440 | "\n", | ||
441 | rpu_str[rpu], rpu_str[rpu], order_mb->request); | ||
442 | dump_mb(cx, order_mb, "incoming"); | ||
443 | order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * Individual EPU command processing is responsible for ack-ing | ||
448 | * a non-stale mailbox as soon as possible | ||
449 | */ | ||
450 | submit = epu_cmd_irq(cx, order); | ||
451 | if (submit > 0) { | ||
452 | queue_work(cx->work_queue, &order->work); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | |||
457 | /* | ||
458 | * Functions called from a non-interrupt, non work_queue context | ||
459 | */ | ||
183 | 460 | ||
184 | static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | 461 | static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) |
185 | { | 462 | { |
186 | const struct cx18_api_info *info = find_api_info(cmd); | 463 | const struct cx18_api_info *info = find_api_info(cmd); |
187 | u32 state = 0, irq = 0, req, oldreq, err; | 464 | u32 state, irq, req, ack, err; |
188 | struct cx18_mailbox __iomem *mb; | 465 | struct cx18_mailbox __iomem *mb; |
466 | u32 __iomem *xpu_state; | ||
189 | wait_queue_head_t *waitq; | 467 | wait_queue_head_t *waitq; |
190 | int timeout = 100; | 468 | struct mutex *mb_lock; |
191 | int cnt = 0; | 469 | long int timeout, ret; |
192 | int sig = 0; | ||
193 | int i; | 470 | int i; |
194 | 471 | ||
195 | if (info == NULL) { | 472 | if (info == NULL) { |
@@ -201,50 +478,104 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | |||
201 | CX18_DEBUG_HI_API("%s\n", info->name); | 478 | CX18_DEBUG_HI_API("%s\n", info->name); |
202 | else | 479 | else |
203 | CX18_DEBUG_API("%s\n", info->name); | 480 | CX18_DEBUG_API("%s\n", info->name); |
204 | cx18_setup_page(cx, SCB_OFFSET); | ||
205 | mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); | ||
206 | 481 | ||
207 | if (mb == NULL) { | 482 | switch (info->rpu) { |
208 | CX18_ERR("mb %s busy\n", info->name); | 483 | case APU: |
209 | return -EBUSY; | 484 | waitq = &cx->mb_apu_waitq; |
485 | mb_lock = &cx->epu2apu_mb_lock; | ||
486 | irq = IRQ_EPU_TO_APU; | ||
487 | mb = &cx->scb->epu2apu_mb; | ||
488 | xpu_state = &cx->scb->apu_state; | ||
489 | break; | ||
490 | case CPU: | ||
491 | waitq = &cx->mb_cpu_waitq; | ||
492 | mb_lock = &cx->epu2cpu_mb_lock; | ||
493 | irq = IRQ_EPU_TO_CPU; | ||
494 | mb = &cx->scb->epu2cpu_mb; | ||
495 | xpu_state = &cx->scb->cpu_state; | ||
496 | break; | ||
497 | default: | ||
498 | CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); | ||
499 | return -EINVAL; | ||
210 | } | 500 | } |
211 | 501 | ||
212 | oldreq = req - 1; | 502 | mutex_lock(mb_lock); |
503 | /* | ||
504 | * Wait for an in-use mailbox to complete | ||
505 | * | ||
506 | * If the XPU is responding with Ack's, the mailbox shouldn't be in | ||
507 | * a busy state, since we serialize access to it on our end. | ||
508 | * | ||
509 | * If the wait for ack after sending a previous command was interrupted | ||
510 | * by a signal, we may get here and find a busy mailbox. After waiting, | ||
511 | * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. | ||
512 | */ | ||
513 | state = cx18_readl(cx, xpu_state); | ||
514 | req = cx18_readl(cx, &mb->request); | ||
515 | timeout = msecs_to_jiffies(10); | ||
516 | ret = wait_event_timeout(*waitq, | ||
517 | (ack = cx18_readl(cx, &mb->ack)) == req, | ||
518 | timeout); | ||
519 | if (req != ack) { | ||
520 | /* waited long enough, make the mbox "not busy" from our end */ | ||
521 | cx18_writel(cx, req, &mb->ack); | ||
522 | CX18_ERR("mbox was found stuck busy when setting up for %s; " | ||
523 | "clearing busy and trying to proceed\n", info->name); | ||
524 | } else if (ret != timeout) | ||
525 | CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n", | ||
526 | jiffies_to_msecs(timeout-ret)); | ||
527 | |||
528 | /* Build the outgoing mailbox */ | ||
529 | req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; | ||
530 | |||
213 | cx18_writel(cx, cmd, &mb->cmd); | 531 | cx18_writel(cx, cmd, &mb->cmd); |
214 | for (i = 0; i < args; i++) | 532 | for (i = 0; i < args; i++) |
215 | cx18_writel(cx, data[i], &mb->args[i]); | 533 | cx18_writel(cx, data[i], &mb->args[i]); |
216 | cx18_writel(cx, 0, &mb->error); | 534 | cx18_writel(cx, 0, &mb->error); |
217 | cx18_writel(cx, req, &mb->request); | 535 | cx18_writel(cx, req, &mb->request); |
536 | cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ | ||
218 | 537 | ||
219 | switch (info->rpu) { | 538 | /* |
220 | case APU: waitq = &cx->mb_apu_waitq; break; | 539 | * Notify the XPU and wait for it to send an Ack back |
221 | case CPU: waitq = &cx->mb_cpu_waitq; break; | 540 | */ |
222 | case EPU: waitq = &cx->mb_epu_waitq; break; | 541 | timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20); |
223 | case HPU: waitq = &cx->mb_hpu_waitq; break; | 542 | |
224 | default: return -EINVAL; | 543 | CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", |
225 | } | 544 | irq, info->name); |
226 | if (info->flags & API_FAST) | ||
227 | timeout /= 2; | ||
228 | cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); | 545 | cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); |
229 | 546 | ||
230 | while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) | 547 | ret = wait_event_timeout( |
231 | && cnt < 660) { | 548 | *waitq, |
232 | if (cnt > 200 && !in_atomic()) | 549 | cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), |
233 | sig = cx18_msleep_timeout(10, 1); | 550 | timeout); |
234 | cnt++; | 551 | |
235 | } | 552 | if (ret == 0) { |
236 | if (sig) | 553 | /* Timed out */ |
237 | return -EINTR; | 554 | mutex_unlock(mb_lock); |
238 | if (cnt == 660) { | 555 | CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " |
239 | cx18_writel(cx, oldreq, &mb->request); | 556 | "acknowledgement\n", |
240 | CX18_ERR("mb %s failed\n", info->name); | 557 | info->name, jiffies_to_msecs(timeout)); |
241 | return -EINVAL; | 558 | return -EINVAL; |
242 | } | 559 | } |
560 | |||
561 | if (ret != timeout) | ||
562 | CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", | ||
563 | jiffies_to_msecs(timeout-ret), info->name); | ||
564 | |||
565 | /* Collect data returned by the XPU */ | ||
243 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) | 566 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) |
244 | data[i] = cx18_readl(cx, &mb->args[i]); | 567 | data[i] = cx18_readl(cx, &mb->args[i]); |
245 | err = cx18_readl(cx, &mb->error); | 568 | err = cx18_readl(cx, &mb->error); |
246 | if (!in_atomic() && (info->flags & API_SLOW)) | 569 | mutex_unlock(mb_lock); |
570 | |||
571 | /* | ||
572 | * Wait for XPU to perform extra actions for the caller in some cases. | ||
573 | * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers | ||
574 | * back in a burst shortly thereafter | ||
575 | */ | ||
576 | if (info->flags & API_SLOW) | ||
247 | cx18_msleep_timeout(300, 0); | 577 | cx18_msleep_timeout(300, 0); |
578 | |||
248 | if (err) | 579 | if (err) |
249 | CX18_DEBUG_API("mailbox error %08x for command %s\n", err, | 580 | CX18_DEBUG_API("mailbox error %08x for command %s\n", err, |
250 | info->name); | 581 | info->name); |
@@ -253,12 +584,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | |||
253 | 584 | ||
254 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) | 585 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) |
255 | { | 586 | { |
256 | int res = cx18_api_call(cx, cmd, args, data); | 587 | return cx18_api_call(cx, cmd, args, data); |
257 | |||
258 | /* Allow a single retry, probably already too late though. | ||
259 | If there is no free mailbox then that is usually an indication | ||
260 | of a more serious problem. */ | ||
261 | return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; | ||
262 | } | 588 | } |
263 | 589 | ||
264 | static int cx18_set_filter_param(struct cx18_stream *s) | 590 | static int cx18_set_filter_param(struct cx18_stream *s) |
@@ -281,8 +607,9 @@ static int cx18_set_filter_param(struct cx18_stream *s) | |||
281 | int cx18_api_func(void *priv, u32 cmd, int in, int out, | 607 | int cx18_api_func(void *priv, u32 cmd, int in, int out, |
282 | u32 data[CX2341X_MBOX_MAX_DATA]) | 608 | u32 data[CX2341X_MBOX_MAX_DATA]) |
283 | { | 609 | { |
284 | struct cx18 *cx = priv; | 610 | struct cx18_api_func_private *api_priv = priv; |
285 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; | 611 | struct cx18 *cx = api_priv->cx; |
612 | struct cx18_stream *s = api_priv->s; | ||
286 | 613 | ||
287 | switch (cmd) { | 614 | switch (cmd) { |
288 | case CX2341X_ENC_SET_OUTPUT_PORT: | 615 | case CX2341X_ENC_SET_OUTPUT_PORT: |