1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Digigram miXart soundcards |
4 | * |
5 | * low level interface with interrupt handling and mail box implementation |
6 | * |
7 | * Copyright (c) 2003 by Digigram <alsa@digigram.com> |
8 | */ |
9 | |
10 | #include <linux/interrupt.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/pci.h> |
13 | #include <linux/io.h> |
14 | |
15 | #include <sound/core.h> |
16 | #include "mixart.h" |
17 | #include "mixart_hwdep.h" |
18 | #include "mixart_core.h" |
19 | |
20 | |
21 | #define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */ |
22 | |
23 | #define MSG_DESCRIPTOR_SIZE 0x24 |
24 | #define (MSG_DESCRIPTOR_SIZE + 4) |
25 | |
26 | #define MSG_TYPE_MASK 0x00000003 /* mask for following types */ |
27 | #define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ |
28 | #define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ |
29 | #define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */ |
30 | #define MSG_TYPE_ANSWER 3 /* embedded -> driver */ |
31 | #define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */ |
32 | |
33 | |
34 | static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame) |
35 | { |
36 | /* read the message frame fifo */ |
37 | u32 headptr, tailptr; |
38 | |
39 | tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); |
40 | headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD)); |
41 | |
42 | if (tailptr == headptr) |
43 | return 0; /* no message posted */ |
44 | |
45 | if (tailptr < MSG_OUTBOUND_POST_STACK) |
46 | return 0; /* error */ |
47 | if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE) |
48 | return 0; /* error */ |
49 | |
50 | *msg_frame = readl_be(MIXART_MEM(mgr, tailptr)); |
51 | |
52 | /* increment the tail index */ |
53 | tailptr += 4; |
54 | if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) |
55 | tailptr = MSG_OUTBOUND_POST_STACK; |
56 | writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); |
57 | |
58 | return 1; |
59 | } |
60 | |
61 | static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, |
62 | u32 msg_frame_address ) |
63 | { |
64 | u32 headptr; |
65 | u32 size; |
66 | int err; |
67 | #ifndef __BIG_ENDIAN |
68 | unsigned int i; |
69 | #endif |
70 | |
71 | err = 0; |
72 | |
73 | /* copy message descriptor from miXart to driver */ |
74 | size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */ |
75 | resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */ |
76 | resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */ |
77 | resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */ |
78 | |
79 | if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) { |
80 | err = -EINVAL; |
81 | dev_err(&mgr->pci->dev, |
82 | "problem with response size = %d\n" , size); |
83 | goto _clean_exit; |
84 | } |
85 | size -= MSG_DESCRIPTOR_SIZE; |
86 | |
87 | memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size); |
88 | resp->size = size; |
89 | |
90 | /* swap if necessary */ |
91 | #ifndef __BIG_ENDIAN |
92 | size /= 4; /* u32 size */ |
93 | for(i=0; i < size; i++) { |
94 | ((u32*)resp->data)[i] = be32_to_cpu(((__be32*)resp->data)[i]); |
95 | } |
96 | #endif |
97 | |
98 | /* |
99 | * free message frame address |
100 | */ |
101 | headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); |
102 | |
103 | if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { |
104 | err = -EINVAL; |
105 | goto _clean_exit; |
106 | } |
107 | |
108 | /* give address back to outbound fifo */ |
109 | writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); |
110 | |
111 | /* increment the outbound free head */ |
112 | headptr += 4; |
113 | if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) |
114 | headptr = MSG_OUTBOUND_FREE_STACK; |
115 | |
116 | writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); |
117 | |
118 | _clean_exit: |
119 | return err; |
120 | } |
121 | |
122 | |
123 | /* |
124 | * send a message to miXart. return: the msg_frame used for this message |
125 | */ |
126 | /* call with mgr->msg_lock held! */ |
127 | static int send_msg( struct mixart_mgr *mgr, |
128 | struct mixart_msg *msg, |
129 | int max_answersize, |
130 | int mark_pending, |
131 | u32 *msg_event) |
132 | { |
133 | u32 headptr, tailptr; |
134 | u32 msg_frame_address; |
135 | int i; |
136 | |
137 | if (snd_BUG_ON(msg->size % 4)) |
138 | return -EINVAL; |
139 | |
140 | /* get message frame address */ |
141 | tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); |
142 | headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD)); |
143 | |
144 | if (tailptr == headptr) { |
145 | dev_err(&mgr->pci->dev, "error: no message frame available\n" ); |
146 | return -EBUSY; |
147 | } |
148 | |
149 | if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { |
150 | return -EINVAL; |
151 | } |
152 | |
153 | msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr)); |
154 | writel(val: 0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */ |
155 | |
156 | /* increment the inbound free tail */ |
157 | tailptr += 4; |
158 | if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) |
159 | tailptr = MSG_INBOUND_FREE_STACK; |
160 | |
161 | writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); |
162 | |
163 | /* TODO : use memcpy_toio() with intermediate buffer to copy the message */ |
164 | |
165 | /* copy message descriptor to card memory */ |
166 | writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */ |
167 | writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */ |
168 | writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */ |
169 | writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */ |
170 | writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */ |
171 | writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */ |
172 | writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */ |
173 | writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */ |
174 | writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */ |
175 | writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */ |
176 | |
177 | /* copy message data to card memory */ |
178 | for( i=0; i < msg->size; i+=4 ) { |
179 | writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) ); |
180 | } |
181 | |
182 | if( mark_pending ) { |
183 | if( *msg_event ) { |
184 | /* the pending event is the notification we wait for ! */ |
185 | mgr->pending_event = *msg_event; |
186 | } |
187 | else { |
188 | /* the pending event is the answer we wait for (same address than the request)! */ |
189 | mgr->pending_event = msg_frame_address; |
190 | |
191 | /* copy address back to caller */ |
192 | *msg_event = msg_frame_address; |
193 | } |
194 | } |
195 | |
196 | /* mark the frame as a request (will have an answer) */ |
197 | msg_frame_address |= MSG_TYPE_REQUEST; |
198 | |
199 | /* post the frame */ |
200 | headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); |
201 | |
202 | if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) { |
203 | return -EINVAL; |
204 | } |
205 | |
206 | writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); |
207 | |
208 | /* increment the inbound post head */ |
209 | headptr += 4; |
210 | if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) |
211 | headptr = MSG_INBOUND_POST_STACK; |
212 | |
213 | writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | |
219 | int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int max_resp_size, void *resp_data) |
220 | { |
221 | struct mixart_msg resp; |
222 | u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */ |
223 | int err; |
224 | wait_queue_entry_t wait; |
225 | long timeout; |
226 | |
227 | init_waitqueue_entry(wq_entry: &wait, current); |
228 | |
229 | mutex_lock(&mgr->msg_lock); |
230 | /* send the message */ |
231 | err = send_msg(mgr, msg: request, max_answersize: max_resp_size, mark_pending: 1, msg_event: &msg_frame); /* send and mark the answer pending */ |
232 | if (err) { |
233 | mutex_unlock(lock: &mgr->msg_lock); |
234 | return err; |
235 | } |
236 | |
237 | set_current_state(TASK_UNINTERRUPTIBLE); |
238 | add_wait_queue(wq_head: &mgr->msg_sleep, wq_entry: &wait); |
239 | mutex_unlock(lock: &mgr->msg_lock); |
240 | timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); |
241 | remove_wait_queue(wq_head: &mgr->msg_sleep, wq_entry: &wait); |
242 | |
243 | if (! timeout) { |
244 | /* error - no ack */ |
245 | dev_err(&mgr->pci->dev, |
246 | "error: no response on msg %x\n" , msg_frame); |
247 | return -EIO; |
248 | } |
249 | |
250 | /* retrieve the answer into the same struct mixart_msg */ |
251 | resp.message_id = 0; |
252 | resp.uid = (struct mixart_uid){0,0}; |
253 | resp.data = resp_data; |
254 | resp.size = max_resp_size; |
255 | |
256 | mutex_lock(&mgr->msg_lock); |
257 | err = get_msg(mgr, resp: &resp, msg_frame_address: msg_frame); |
258 | mutex_unlock(lock: &mgr->msg_lock); |
259 | |
260 | if( request->message_id != resp.message_id ) |
261 | dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n" ); |
262 | |
263 | return err; |
264 | } |
265 | |
266 | |
267 | int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr, |
268 | struct mixart_msg *request, u32 notif_event) |
269 | { |
270 | int err; |
271 | wait_queue_entry_t wait; |
272 | long timeout; |
273 | |
274 | if (snd_BUG_ON(!notif_event)) |
275 | return -EINVAL; |
276 | if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY)) |
277 | return -EINVAL; |
278 | if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK)) |
279 | return -EINVAL; |
280 | |
281 | init_waitqueue_entry(wq_entry: &wait, current); |
282 | |
283 | mutex_lock(&mgr->msg_lock); |
284 | /* send the message */ |
285 | err = send_msg(mgr, msg: request, MSG_DEFAULT_SIZE, mark_pending: 1, msg_event: ¬if_event); /* send and mark the notification event pending */ |
286 | if(err) { |
287 | mutex_unlock(lock: &mgr->msg_lock); |
288 | return err; |
289 | } |
290 | |
291 | set_current_state(TASK_UNINTERRUPTIBLE); |
292 | add_wait_queue(wq_head: &mgr->msg_sleep, wq_entry: &wait); |
293 | mutex_unlock(lock: &mgr->msg_lock); |
294 | timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); |
295 | remove_wait_queue(wq_head: &mgr->msg_sleep, wq_entry: &wait); |
296 | |
297 | if (! timeout) { |
298 | /* error - no ack */ |
299 | dev_err(&mgr->pci->dev, |
300 | "error: notification %x not received\n" , notif_event); |
301 | return -EIO; |
302 | } |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | |
308 | int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request) |
309 | { |
310 | u32 message_frame; |
311 | int err; |
312 | |
313 | /* just send the message (do not mark it as a pending one) */ |
314 | mutex_lock(&mgr->msg_lock); |
315 | err = send_msg(mgr, msg: request, MSG_DEFAULT_SIZE, mark_pending: 0, msg_event: &message_frame); |
316 | mutex_unlock(lock: &mgr->msg_lock); |
317 | |
318 | /* the answer will be handled by snd_struct mixart_msgasklet() */ |
319 | atomic_inc(v: &mgr->msg_processed); |
320 | |
321 | return err; |
322 | } |
323 | |
324 | |
325 | /* common buffer of interrupt to send/receive messages */ |
326 | static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4]; |
327 | |
328 | |
329 | static void snd_mixart_process_msg(struct mixart_mgr *mgr) |
330 | { |
331 | struct mixart_msg resp; |
332 | u32 msg, addr, type; |
333 | int err; |
334 | |
335 | while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) { |
336 | msg = mgr->msg_fifo[mgr->msg_fifo_readptr]; |
337 | mgr->msg_fifo_readptr++; |
338 | mgr->msg_fifo_readptr %= MSG_FIFO_SIZE; |
339 | |
340 | /* process the message ... */ |
341 | addr = msg & ~MSG_TYPE_MASK; |
342 | type = msg & MSG_TYPE_MASK; |
343 | |
344 | switch (type) { |
345 | case MSG_TYPE_ANSWER: |
346 | /* answer to a message on that we did not wait for (send_msg_nonblock) */ |
347 | resp.message_id = 0; |
348 | resp.data = mixart_msg_data; |
349 | resp.size = sizeof(mixart_msg_data); |
350 | err = get_msg(mgr, resp: &resp, msg_frame_address: addr); |
351 | if( err < 0 ) { |
352 | dev_err(&mgr->pci->dev, |
353 | "error(%d) reading mf %x\n" , |
354 | err, msg); |
355 | break; |
356 | } |
357 | |
358 | switch(resp.message_id) { |
359 | case MSG_STREAM_START_INPUT_STAGE_PACKET: |
360 | case MSG_STREAM_START_OUTPUT_STAGE_PACKET: |
361 | case MSG_STREAM_STOP_INPUT_STAGE_PACKET: |
362 | case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET: |
363 | if(mixart_msg_data[0]) |
364 | dev_err(&mgr->pci->dev, |
365 | "error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n" , |
366 | mixart_msg_data[0]); |
367 | break; |
368 | default: |
369 | dev_dbg(&mgr->pci->dev, |
370 | "received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n" , |
371 | msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size); |
372 | break; |
373 | } |
374 | break; |
375 | case MSG_TYPE_NOTIFY: |
376 | /* msg contains no address ! do not get_msg() ! */ |
377 | case MSG_TYPE_COMMAND: |
378 | /* get_msg() necessary */ |
379 | default: |
380 | dev_err(&mgr->pci->dev, |
381 | "doesn't know what to do with message %x\n" , |
382 | msg); |
383 | } /* switch type */ |
384 | |
385 | /* decrement counter */ |
386 | atomic_dec(v: &mgr->msg_processed); |
387 | |
388 | } /* while there is a msg in fifo */ |
389 | } |
390 | |
391 | |
392 | irqreturn_t snd_mixart_interrupt(int irq, void *dev_id) |
393 | { |
394 | struct mixart_mgr *mgr = dev_id; |
395 | u32 it_reg; |
396 | |
397 | it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); |
398 | if( !(it_reg & MIXART_OIDI) ) { |
399 | /* this device did not cause the interrupt */ |
400 | return IRQ_NONE; |
401 | } |
402 | |
403 | /* mask all interrupts */ |
404 | writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET)); |
405 | |
406 | /* outdoorbell register clear */ |
407 | it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); |
408 | writel(val: it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); |
409 | |
410 | /* clear interrupt */ |
411 | writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); |
412 | |
413 | return IRQ_WAKE_THREAD; |
414 | } |
415 | |
416 | irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) |
417 | { |
418 | struct mixart_mgr *mgr = dev_id; |
419 | int err; |
420 | struct mixart_msg resp; |
421 | u32 msg; |
422 | |
423 | mutex_lock(&mgr->lock); |
424 | /* process interrupt */ |
425 | while (retrieve_msg_frame(mgr, msg_frame: &msg)) { |
426 | |
427 | switch (msg & MSG_TYPE_MASK) { |
428 | case MSG_TYPE_COMMAND: |
429 | resp.message_id = 0; |
430 | resp.data = mixart_msg_data; |
431 | resp.size = sizeof(mixart_msg_data); |
432 | err = get_msg(mgr, resp: &resp, msg_frame_address: msg & ~MSG_TYPE_MASK); |
433 | if( err < 0 ) { |
434 | dev_err(&mgr->pci->dev, |
435 | "interrupt: error(%d) reading mf %x\n" , |
436 | err, msg); |
437 | break; |
438 | } |
439 | |
440 | if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) { |
441 | int i; |
442 | struct mixart_timer_notify *notify; |
443 | notify = (struct mixart_timer_notify *)mixart_msg_data; |
444 | |
445 | BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data)); |
446 | if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams))) |
447 | break; |
448 | for(i=0; i<notify->stream_count; i++) { |
449 | |
450 | u32 buffer_id = notify->streams[i].buffer_id; |
451 | unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */ |
452 | unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */ |
453 | unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */ |
454 | unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */ |
455 | |
456 | struct snd_mixart *chip = mgr->chip[chip_number]; |
457 | struct mixart_stream *stream; |
458 | |
459 | if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) { |
460 | dev_err(&mgr->pci->dev, |
461 | "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n" , |
462 | buffer_id, notify->streams[i].sample_pos_low_part); |
463 | break; |
464 | } |
465 | |
466 | if (is_capture) |
467 | stream = &chip->capture_stream[pcm_number]; |
468 | else |
469 | stream = &chip->playback_stream[pcm_number][sub_number]; |
470 | |
471 | if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) { |
472 | struct snd_pcm_runtime *runtime = stream->substream->runtime; |
473 | int elapsed = 0; |
474 | u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32; |
475 | sample_count |= notify->streams[i].sample_pos_low_part; |
476 | |
477 | while (1) { |
478 | u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size; |
479 | |
480 | if (new_elapse_pos > sample_count) { |
481 | break; /* while */ |
482 | } |
483 | else { |
484 | elapsed = 1; |
485 | stream->buf_periods++; |
486 | if (stream->buf_periods >= runtime->periods) |
487 | stream->buf_periods = 0; |
488 | |
489 | stream->abs_period_elapsed = new_elapse_pos; |
490 | } |
491 | } |
492 | stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed ); |
493 | |
494 | if(elapsed) { |
495 | mutex_unlock(lock: &mgr->lock); |
496 | snd_pcm_period_elapsed(substream: stream->substream); |
497 | mutex_lock(&mgr->lock); |
498 | } |
499 | } |
500 | } |
501 | break; |
502 | } |
503 | if(resp.message_id == MSG_SERVICES_REPORT_TRACES) { |
504 | if(resp.size > 1) { |
505 | #ifndef __BIG_ENDIAN |
506 | /* Traces are text: the swapped msg_data has to be swapped back ! */ |
507 | int i; |
508 | for(i=0; i<(resp.size/4); i++) { |
509 | ((__be32*)mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]); |
510 | } |
511 | #endif |
512 | ((char*)mixart_msg_data)[resp.size - 1] = 0; |
513 | dev_dbg(&mgr->pci->dev, |
514 | "MIXART TRACE : %s\n" , |
515 | (char *)mixart_msg_data); |
516 | } |
517 | break; |
518 | } |
519 | |
520 | dev_dbg(&mgr->pci->dev, "command %x not handled\n" , |
521 | resp.message_id); |
522 | break; |
523 | |
524 | case MSG_TYPE_NOTIFY: |
525 | if(msg & MSG_CANCEL_NOTIFY_MASK) { |
526 | msg &= ~MSG_CANCEL_NOTIFY_MASK; |
527 | dev_err(&mgr->pci->dev, |
528 | "canceled notification %x !\n" , msg); |
529 | } |
530 | fallthrough; |
531 | case MSG_TYPE_ANSWER: |
532 | /* answer or notification to a message we are waiting for*/ |
533 | mutex_lock(&mgr->msg_lock); |
534 | if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) { |
535 | wake_up(&mgr->msg_sleep); |
536 | mgr->pending_event = 0; |
537 | } |
538 | /* answer to a message we did't want to wait for */ |
539 | else { |
540 | mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg; |
541 | mgr->msg_fifo_writeptr++; |
542 | mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE; |
543 | snd_mixart_process_msg(mgr); |
544 | } |
545 | mutex_unlock(lock: &mgr->msg_lock); |
546 | break; |
547 | case MSG_TYPE_REQUEST: |
548 | default: |
549 | dev_dbg(&mgr->pci->dev, |
550 | "interrupt received request %x\n" , msg); |
551 | /* TODO : are there things to do here ? */ |
552 | break; |
553 | } /* switch on msg type */ |
554 | } /* while there are msgs */ |
555 | |
556 | /* allow interrupt again */ |
557 | writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); |
558 | |
559 | mutex_unlock(lock: &mgr->lock); |
560 | |
561 | return IRQ_HANDLED; |
562 | } |
563 | |
564 | |
565 | void snd_mixart_init_mailbox(struct mixart_mgr *mgr) |
566 | { |
567 | writel( val: 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) ); |
568 | writel( val: 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) ); |
569 | |
570 | /* allow outbound messagebox to generate interrupts */ |
571 | if(mgr->irq >= 0) { |
572 | writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); |
573 | } |
574 | return; |
575 | } |
576 | |
577 | void snd_mixart_exit_mailbox(struct mixart_mgr *mgr) |
578 | { |
579 | /* no more interrupts on outbound messagebox */ |
580 | writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); |
581 | return; |
582 | } |
583 | |
584 | void snd_mixart_reset_board(struct mixart_mgr *mgr) |
585 | { |
586 | /* reset miXart */ |
587 | writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) ); |
588 | return; |
589 | } |
590 | |