• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "fw_pvt.h"
2 #include "viddec_fw_parser_ipclib_config.h"
3 #include "viddec_fw_common_defs.h"
4 #include "viddec_fw_parser.h"
5 #include "viddec_fw_debug.h"
6 
7 /* This define makes sure that the structure is stored in Local memory.
8    This is shared memory between host and FW.*/
9 volatile dmem_t _dmem __attribute__ ((section (".exchange")));
10 /* Debug index should be disbaled for Production FW */
11 uint32_t dump_ptr=0;
12 uint32_t timer=0;
13 
14 /* Auto Api definitions */
15 ismd_api_group viddec_fw_api_array[2];
16 
17 extern void viddec_fw_parser_register_callbacks(void);
18 
19 /*------------------------------------------------------------------------------
20  * Function:  initialize firmware SVEN TX Output
21  *------------------------------------------------------------------------------
22  */
viddec_fw_parser_sven_init(struct SVEN_FW_Globals * sven_fw_globals)23 int SMDEXPORT viddec_fw_parser_sven_init(struct SVEN_FW_Globals  *sven_fw_globals )
24 {
25     extern int sven_fw_set_globals(struct SVEN_FW_Globals  *fw_globals );
26     return(sven_fw_set_globals(sven_fw_globals));
27 }
28 
29 /*------------------------------------------------------------------------------
30  * Function:  viddec_fw_check_watermark_boundary
31  * This function figures out if we crossesd watermark boundary on input data.
32  * before represents the ES Queue data when we started and current represents ES Queue data
33  * when we are ready to swap.Threshold is the amount of data specified by the driver to trigger an
34  * interrupt.
35  * We return true if threshold is between before and current.
36  *------------------------------------------------------------------------------
37  */
viddec_fw_check_watermark_boundary(uint32_t before,uint32_t current,uint32_t threshold)38 static inline uint32_t viddec_fw_check_watermark_boundary(uint32_t before, uint32_t current, uint32_t threshold)
39 {
40     return ((before >= threshold) && (current < threshold));
41 }
42 
43 /*------------------------------------------------------------------------------
44  * Function:  viddec_fw_get_total_input_Q_data
45  * This function figures out how much data is available in input queue of the FW
46  *------------------------------------------------------------------------------
47  */
viddec_fw_get_total_input_Q_data(uint32_t indx)48 static uint32_t viddec_fw_get_total_input_Q_data(uint32_t indx)
49 {
50     FW_IPC_Handle *fwipc = GET_IPC_HANDLE(_dmem);
51     uint32_t ret;
52     int32_t pos=0;
53     FW_IPC_ReceiveQue   *rcv_q;
54 
55     rcv_q = &fwipc->rcv_q[indx];
56     /* count the cubby buffer which we already read if present */
57     ret = (_dmem.stream_info[indx].buffered_data) ? CONFIG_IPC_MESSAGE_MAX_SIZE:0;
58     ret += ipc_mq_read_avail(&rcv_q->mq, (int32_t *)&pos);
59     return ret;
60 }
61 
62 /*------------------------------------------------------------------------------
63  * Function:  mfd_round_robin
64  * Params:
65  *        [in]  pri: Priority of the stream
66  *        [in] indx: stream id number of the last stream that was scheduled.
67  *        [out] qnum: Stream id of priority(pri) which has data.
68  * This function is responsible for figuring out which stream needs to be scheduled next.
69  * It starts after the last scheduled stream and walks through all streams until it finds
70  * a stream which is of required priority, in start state, has space on output and data in
71  * input.
72  * If no such stream is found qnum is not updated and return value is 0.
73  * If a stream is found then qnum is updated with that id and function returns 1.
74  *------------------------------------------------------------------------------
75  */
76 
mfd_round_robin(uint32_t pri,int32_t * qnum,int32_t indx)77 uint32_t mfd_round_robin(uint32_t pri, int32_t *qnum, int32_t indx)
78 {
79     FW_IPC_Handle *fwipc = GET_IPC_HANDLE(_dmem);
80     int32_t i = CONFIG_IPC_FW_MAX_RX_QUEUES;
81     uint32_t ret = 0;
82     /* Go through all queues until we find a valid queue of reqd priority */
83     while(i>0)
84     {
85         indx++;
86         if(indx >=  CONFIG_IPC_FW_MAX_RX_QUEUES) indx = 0;
87 
88         /* We should look only at queues which match priority and
89            in running state */
90         if( (_dmem.stream_info[indx].state == 1)
91             && (_dmem.stream_info[indx].priority == pri))
92         {
93             uint32_t inpt_avail=0, output_avail=0, wklds_avail =0 , pos;
94             FW_IPC_ReceiveQue   *rcv_q;
95             rcv_q = &fwipc->rcv_q[indx];
96             inpt_avail = (_dmem.stream_info[indx].buffered_data > 0) || (ipc_mq_read_avail(&rcv_q->mq, (int32_t *)&pos) > 0);
97             /* we have to check for two workloads to protect against error cases where we might have to push both current and next workloads */
98             output_avail = FwIPC_SpaceAvailForMessage(fwipc, &fwipc->snd_q[indx], CONFIG_IPC_MESSAGE_MAX_SIZE, &pos) >= 2;
99             pos = 0;
100             /* Need at least current and next to proceed */
101             wklds_avail =  (ipc_mq_read_avail(&fwipc->wkld_q[indx].mq, (int32_t *)&pos) >= (CONFIG_IPC_MESSAGE_MAX_SIZE << 1));
102             if(inpt_avail && output_avail && wklds_avail)
103             {/* Success condition: we have some data on input and enough space on output queue */
104                 *qnum = indx;
105                 ret =1;
106                 break;
107             }
108         }
109         i--;
110     }
111     return ret;
112 }
mfd_setup_emitter(FW_IPC_Handle * fwipc,FW_IPC_ReceiveQue * rcv_q,mfd_pk_strm_cxt * cxt)113 static inline void mfd_setup_emitter(FW_IPC_Handle *fwipc, FW_IPC_ReceiveQue *rcv_q, mfd_pk_strm_cxt *cxt)
114 {
115     int32_t ret1=0,ret=0;
116     /* We don't check return values for the peek as round robin guarantee's that we have required free workloads */
117     ret = FwIPC_PeekReadMessage(fwipc, rcv_q, (char *)&(cxt->wkld1), sizeof(ipc_msg_data), 0);
118     ret1 = FwIPC_PeekReadMessage(fwipc, rcv_q, (char *)&(cxt->wkld2), sizeof(ipc_msg_data), 1);
119     viddec_emit_update(&(cxt->pm.emitter), cxt->wkld1.phys, cxt->wkld2.phys, cxt->wkld1.len, cxt->wkld2.len);
120 }
121 
mfd_init_swap_memory(viddec_pm_cxt_t * pm,uint32_t codec_type,uint32_t start_addr,uint32_t clean)122 static inline void mfd_init_swap_memory(viddec_pm_cxt_t *pm, uint32_t codec_type, uint32_t start_addr, uint32_t clean)
123 {
124     uint32_t *persist_mem;
125     persist_mem = (uint32_t *)(start_addr | GV_DDR_MEM_MASK);
126     viddec_pm_init_context(pm,codec_type, persist_mem, clean);
127     pm->sc_prefix_info.first_sc_detect = 1;
128     viddec_emit_init(&(pm->emitter));
129 }
130 
output_omar_wires(unsigned int value)131 void output_omar_wires( unsigned int value )
132 {
133 #ifdef RTL_SIMULATION
134     reg_write(CONFIG_IPC_ROFF_HOST_DOORBELL, value );
135 #endif
136 }
137 
138 /*------------------------------------------------------------------------------
139  * Function:  viddec_fw_init_swap_memory
140  * This function is responsible for seeting the swap memory to a good state for current stream.
141  * The swap parameter tells us whether we need to dma the context to local memory.
142  * We call init on emitter and parser manager which inturn calls init of the codec we are opening the stream for.
143  *------------------------------------------------------------------------------
144  */
145 
viddec_fw_init_swap_memory(unsigned int stream_id,unsigned int swap,unsigned int clean)146 void viddec_fw_init_swap_memory(unsigned int stream_id, unsigned int swap, unsigned int clean)
147 {
148     mfd_pk_strm_cxt *cxt;
149     mfd_stream_info *cxt_swap;
150     cxt = (mfd_pk_strm_cxt *)&(_dmem.srm_cxt);
151     cxt_swap = (mfd_stream_info *)&(_dmem.stream_info[stream_id]);
152 
153     if(swap)
154     {/* Swap context into local memory */
155         cp_using_dma(cxt_swap->ddr_cxt, (uint32_t) &(cxt->pm), sizeof(viddec_pm_cxt_t), false, false);
156     }
157 
158     {
159         mfd_init_swap_memory(&(cxt->pm), cxt_swap->strm_type, cxt_swap->ddr_cxt+cxt_swap->cxt_size, clean);
160         cxt_swap->wl_time = 0;
161         cxt_swap->es_time = 0;
162     }
163     if(swap)
164     {/* Swap context into DDR */
165         cp_using_dma(cxt_swap->ddr_cxt, (uint32_t) &(cxt->pm), sizeof(viddec_pm_cxt_t), true, false);
166     }
167 }
168 
169 /*------------------------------------------------------------------------------
170  * Function: viddec_fw_push_current_frame_to_output
171  * This is a helper function to read a workload from input queue and push to output queue.
172  * This is called when are done with a frame.
173  *------------------------------------------------------------------------------
174  */
viddec_fw_push_current_frame_to_output(FW_IPC_Handle * fwipc,uint32_t cur)175 static inline void viddec_fw_push_current_frame_to_output(FW_IPC_Handle *fwipc, uint32_t cur)
176 {
177     ipc_msg_data wkld_to_push;
178     FwIPC_ReadMessage(fwipc, &fwipc->wkld_q[cur], (char *)&(wkld_to_push), sizeof(ipc_msg_data));
179     FwIPC_SendMessage(fwipc, cur, (char *)&(wkld_to_push),  sizeof(ipc_msg_data));
180 }
181 
182 /*------------------------------------------------------------------------------
183  * Function: viddec_fw_get_next_stream_to_schedule
184  * This is a helper function to figure out which active stream needs to be scheduled next.
185  * If none of the streams are active it returns -1.
186  *------------------------------------------------------------------------------
187  */
viddec_fw_get_next_stream_to_schedule(void)188 static inline int viddec_fw_get_next_stream_to_schedule(void)
189 {
190     int32_t cur = -1;
191 
192     if(mfd_round_robin(viddec_stream_priority_REALTIME, &cur, _dmem.g_pk_data.high_id))
193     {
194         /* On success store the stream id */
195         _dmem.g_pk_data.high_id = cur;
196     }
197     else
198     {
199         /* Check Low priority Queues, Since we couldn't find a valid realtime stream */
200         if(mfd_round_robin(viddec_stream_priority_BACKGROUND, &cur, _dmem.g_pk_data.low_id))
201         {
202             _dmem.g_pk_data.low_id = cur;
203         }
204     }
205 
206     return cur;
207 }
208 
209 /*------------------------------------------------------------------------------
210  * Function: viddec_fw_update_pending_interrupt_flag
211  * This is a helper function to figure out if we need to mark an interrupt pending for this stream.
212  * We update status value here if we find any of the interrupt conditions are true.
213  * If this stream has a interrupt pending which we could not send to host, we don't overwrite past status info.
214  *------------------------------------------------------------------------------
215  */
viddec_fw_update_pending_interrupt_flag(int32_t cur,mfd_stream_info * cxt_swap,uint8_t pushed_a_workload,uint32_t es_Q_data_at_start)216 static inline void viddec_fw_update_pending_interrupt_flag(int32_t cur, mfd_stream_info *cxt_swap, uint8_t pushed_a_workload,
217                                                            uint32_t es_Q_data_at_start)
218 {
219     if(_dmem.int_status[cur].mask)
220     {
221         if(!cxt_swap->pending_interrupt)
222         {
223             uint32_t es_Q_data_now;
224             uint8_t wmark_boundary_reached=false;
225             es_Q_data_now = viddec_fw_get_total_input_Q_data((uint32_t)cur);
226             wmark_boundary_reached = viddec_fw_check_watermark_boundary(es_Q_data_at_start, es_Q_data_now, cxt_swap->low_watermark);
227             _dmem.int_status[cur].status = 0;
228             if(pushed_a_workload)
229             {
230                 _dmem.int_status[cur].status |= VIDDEC_FW_WKLD_DATA_AVAIL;
231             }
232             if(wmark_boundary_reached)
233             {
234                 _dmem.int_status[cur].status |= VIDDEC_FW_INPUT_WATERMARK_REACHED;
235             }
236             cxt_swap->pending_interrupt = ( _dmem.int_status[cur].status != 0);
237         }
238     }
239     else
240     {
241         cxt_swap->pending_interrupt = false;
242     }
243 }
244 
viddec_fw_handle_error_and_inband_messages(int32_t cur,uint32_t pm_ret)245 static inline void viddec_fw_handle_error_and_inband_messages(int32_t cur, uint32_t pm_ret)
246 {
247     FW_IPC_Handle *fwipc = GET_IPC_HANDLE(_dmem);
248 
249     viddec_fw_push_current_frame_to_output(fwipc, cur);
250     switch(pm_ret)
251     {
252         case PM_EOS:
253         case PM_OVERFLOW:
254         {
255             viddec_fw_init_swap_memory(cur, false, true);
256         }
257         break;
258         case PM_DISCONTINUITY:
259         {
260             viddec_fw_init_swap_memory(cur, false, false);
261         }
262         break;
263         default:
264             break;
265     }
266 }
267 
viddec_fw_debug_scheduled_stream_state(int32_t indx,int32_t start)268 void viddec_fw_debug_scheduled_stream_state(int32_t indx, int32_t start)
269 {
270     FW_IPC_Handle *fwipc = GET_IPC_HANDLE(_dmem);
271     uint32_t inpt_avail=0, output_avail=0, wklds_avail =0 , pos;
272     FW_IPC_ReceiveQue   *rcv_q;
273     uint32_t message;
274 
275     message = (start) ? SVEN_MODULE_EVENT_GV_FW_PK_SCHDL_STRM_START: SVEN_MODULE_EVENT_GV_FW_PK_SCHDL_STRM_END;
276     rcv_q = &fwipc->rcv_q[indx];
277     inpt_avail = ipc_mq_read_avail(&rcv_q->mq, (int32_t *)&pos);
278     inpt_avail += ((_dmem.stream_info[indx].buffered_data > 0) ? CONFIG_IPC_MESSAGE_MAX_SIZE: 0);
279     inpt_avail = inpt_avail >> 4;
280     pos = 0;
281     output_avail = ipc_mq_read_avail(&fwipc->snd_q[indx].mq, (int32_t *)&pos);
282     output_avail = output_avail >> 4;
283     pos = 0;
284     wklds_avail =  ipc_mq_read_avail(&fwipc->wkld_q[indx].mq, (int32_t *)&pos);
285     wklds_avail = wklds_avail >> 4;
286     WRITE_SVEN(message, (int)indx, (int)inpt_avail, (int)output_avail,
287                (int)wklds_avail, 0, 0);
288 }
289 
290 /*------------------------------------------------------------------------------
291  * Function:  viddec_fw_process_async_queues(A.K.A -> Parser Kernel)
292  * This function is responsible for handling the asynchronous queues.
293  *
294  * The first step is to figure out which stream to run. The current algorithm
295  * will go through all high priority queues for a valid stream, if not found we
296  * go through lower priority queues.
297  *
298  * If a valid stream is found we swap the required context from DDR to DMEM and do all necessary
299  * things to setup the stream.
300  * Once a stream is setup we call the parser manager and wait until a wrkld is created or no more input
301  * data left.
302  * Once we find a wkld we push it to host and save the current context to DDR.
303  *------------------------------------------------------------------------------
304  */
305 
viddec_fw_process_async_queues()306 static inline int32_t viddec_fw_process_async_queues()
307 {
308     int32_t cur = -1;
309 
310     cur = viddec_fw_get_next_stream_to_schedule();
311 
312     if(cur != -1)
313     {
314         FW_IPC_Handle *fwipc = GET_IPC_HANDLE(_dmem);
315         FW_IPC_ReceiveQue   *rcv_q;
316         /* bits captured by OMAR */
317         output_omar_wires( 0x0 );
318         rcv_q = &fwipc->rcv_q[cur];
319         {
320             mfd_pk_strm_cxt *cxt;
321             mfd_stream_info *cxt_swap;
322             cxt = (mfd_pk_strm_cxt *)&(_dmem.srm_cxt);
323             cxt_swap = (mfd_stream_info *)&(_dmem.stream_info[cur]);
324 
325             /* Step 1: Swap rodata to local memory. Not doing this currently as all the rodata fits in local memory. */
326             {/* Step 2: Swap context into local memory */
327                 cp_using_dma(cxt_swap->ddr_cxt, (uint32_t) &(cxt->pm), sizeof(viddec_pm_cxt_t), false, false);
328             }
329             /* Step 3:setup emitter by reading input data and workloads and initialising it */
330             mfd_setup_emitter(fwipc, &fwipc->wkld_q[cur], cxt);
331             viddec_fw_debug_scheduled_stream_state(cur, true);
332             /* Step 4: Call Parser Manager until workload done or No more ES buffers */
333             {
334                 ipc_msg_data *data = 0;
335                 uint8_t stream_active = true, pushed_a_workload=false;
336                 uint32_t pm_ret = PM_SUCCESS, es_Q_data_at_start;
337                 uint32_t start_time, time=0;
338 
339                 start_time = set_wdog(VIDDEC_WATCHDOG_COUNTER_MAX);
340                 timer=0;
341                 es_Q_data_at_start = viddec_fw_get_total_input_Q_data((uint32_t)cur);
342                 do
343                 {
344                     output_omar_wires( 0x1 );
345                     {
346                         uint32_t es_t0,es_t1;
347                         get_wdog(&es_t0);
348                         pm_ret = viddec_pm_parse_es_buffer(&(cxt->pm), cxt_swap->strm_type, data);
349                         get_wdog(&es_t1);
350                         cxt_swap->es_time += get_total_ticks(es_t0, es_t1);
351                     }
352                     switch(pm_ret)
353                     {
354                         case PM_EOS:
355                         case PM_WKLD_DONE:
356                         case PM_OVERFLOW:
357                         case PM_DISCONTINUITY:
358                         {/* Finished a frame worth of data or encountered fatal error*/
359                             stream_active = false;
360                         }
361                         break;
362                         case PM_NO_DATA:
363                         {
364                             uint32_t next_ret=0;
365                             if ( (NULL != data) && (0 != cxt_swap->es_time) )
366                             {
367                                 /* print performance info for this buffer */
368                                 WRITE_SVEN(SVEN_MODULE_EVENT_GV_FW_PK_ES_DONE, (int)cur, (int)cxt_swap->es_time, (int)cxt->input.phys,
369                                            (int)cxt->input.len, (int)cxt->input.id, (int)cxt->input.flags );
370                                 cxt_swap->es_time = 0;
371                             }
372 
373                             next_ret = FwIPC_ReadMessage(fwipc, rcv_q, (char *)&(cxt->input), sizeof(ipc_msg_data));
374                             if(next_ret != 0)
375                             {
376                                 data = &(cxt->input);
377                                 WRITE_SVEN(SVEN_MODULE_EVENT_GV_FW_PK_ES_START, (int)cur, (int)cxt_swap->wl_time,
378                                            (int)cxt->input.phys, (int)cxt->input.len, (int)cxt->input.id, (int)cxt->input.flags );
379                             }
380                             else
381                             {/* No data on input queue */
382                                 cxt_swap->buffered_data = 0;
383                                 stream_active = false;
384                             }
385                         }
386                         break;
387                         default:
388                         {/* Not done with current buffer */
389                             data = NULL;
390                         }
391                         break;
392                     }
393                 }while(stream_active);
394                 get_wdog(&time);
395                 cxt_swap->wl_time += get_total_ticks(start_time, time);
396                 /* Step 5: If workload done push workload out */
397                 switch(pm_ret)
398                 {
399                     case PM_EOS:
400                     case PM_WKLD_DONE:
401                     case PM_OVERFLOW:
402                     case PM_DISCONTINUITY:
403                     {/* Push current workload as we are done with the frame */
404                         cxt_swap->buffered_data = (PM_WKLD_DONE == pm_ret) ? true: false;
405                         viddec_pm_update_time(&(cxt->pm), cxt_swap->wl_time);
406 
407                         /* xmit performance info for this workload output */
408                         WRITE_SVEN( SVEN_MODULE_EVENT_GV_FW_PK_WL_DONE, (int)cur, (int)cxt_swap->wl_time, (int)cxt->wkld1.phys,
409                                     (int)cxt->wkld1.len, (int)cxt->wkld1.id, (int)cxt->wkld1.flags );
410                         cxt_swap->wl_time = 0;
411 
412                         viddec_fw_push_current_frame_to_output(fwipc, cur);
413                         if(pm_ret != PM_WKLD_DONE)
414                         {
415                             viddec_fw_handle_error_and_inband_messages(cur, pm_ret);
416                         }
417                         pushed_a_workload = true;
418                     }
419                     break;
420                     default:
421                         break;
422                 }
423                 /* Update information on whether we have active interrupt for this stream */
424                 viddec_fw_update_pending_interrupt_flag(cur, cxt_swap, pushed_a_workload, es_Q_data_at_start);
425             }
426             viddec_fw_debug_scheduled_stream_state(cur, false);
427             /* Step 6: swap context into DDR */
428             {
429                 cp_using_dma(cxt_swap->ddr_cxt, (uint32_t) &(cxt->pm), sizeof(viddec_pm_cxt_t), true, false);
430             }
431         }
432 
433     }
434     return cur;
435 }
436 
437 
438 /*------------------------------------------------------------------------------
439  * Function:  process_command
440  * This magic function figures out which function to excute based on autoapi.
441  *------------------------------------------------------------------------------
442  */
443 
process_command(uint32_t cmd_id,unsigned char * command)444 static inline void process_command(uint32_t cmd_id, unsigned char *command)
445 {
446     int32_t groupid = ((cmd_id >> 24) - 13) & 0xff;
447     int32_t funcid = cmd_id & 0xffffff;
448     /* writing func pointer to hsot doorbell */
449     output_omar_wires( (int) viddec_fw_api_array[groupid].unmarshal[funcid] );
450     WRITE_SVEN( SVEN_MODULE_EVENT_GV_FW_AUTOAPI_CMD,(int) cmd_id, (int) command, ((int *)command)[0],
451                 ((int *)command)[1], ((int *)command)[2], ((int *)command)[3] );
452 
453     viddec_fw_api_array[groupid].unmarshal[funcid](0, command);
454 
455 }
456 
457 /*------------------------------------------------------------------------------
458  * Function:  viddec_fw_process_sync_queues(A.K.A auto api)
459  * Params:
460  *       [in] msg: common sync structure where all required parameters are present for autoapi.
461  *
462  * This function is responsible for handling synchronous messages. All synchronous messages
463  * are handled through auto api.
464  * what are synchronous messages?  Anything releated to teardown or opening a stream Ex: open, close, flush etc.
465  *
466  * Only once synchronous message at a time. When a synchronous message its id is usually in cp doorbell. Once
467  * we are done handling synchronous message through auto api we release doorbell to let the host write next
468  * message.
469  *------------------------------------------------------------------------------
470  */
471 
viddec_fw_process_sync_queues(unsigned char * msg)472 static inline int32_t viddec_fw_process_sync_queues(unsigned char *msg)
473 {
474     int32_t ret = -1;
475 
476     if(0 == reg_read(CONFIG_IPC_ROFF_RISC_DOORBELL_STATUS))
477     {
478         uint32_t command1=0;
479         command1 = reg_read(CONFIG_IPC_ROFF_RISC_RX_DOORBELL);
480         process_command(command1, msg);
481         reg_write(CONFIG_IPC_ROFF_RISC_DOORBELL_STATUS, 0x2); /* Inform Host we are done with this message */
482         ret = 0;
483     }
484     return ret;
485 }
486 
487 /*------------------------------------------------------------------------------
488  * Function:  viddec_fw_check_for_pending_int
489  * This function walks through all active streams to see if atleast one stream has a pending interrupt
490  * and returns true if it finds one.
491  *------------------------------------------------------------------------------
492  */
viddec_fw_check_for_pending_int(void)493 static inline uint32_t viddec_fw_check_for_pending_int(void)
494 {
495     uint32_t i=0, ret=false;
496     /* start from 0 to max streams that fw can handle*/
497     while(i < FW_SUPPORTED_STREAMS)
498     {
499         if(_dmem.stream_info[i].state == 1)
500         {
501             if((_dmem.stream_info[i].pending_interrupt) && _dmem.int_status[i].mask)
502             {
503                 ret = true;
504             }
505             else
506             {/* If this is not in INT state clear the status before sending it to host */
507                 _dmem.int_status[i].status = 0;
508             }
509         }
510         i++;
511     }
512     return ret;
513 }
514 
515 /*------------------------------------------------------------------------------
516  * Function:  viddec_fw_clear_processed_int
517  * This function walks through all active streams to clear pending interrupt state.This is
518  * called after a INT was issued.
519  *------------------------------------------------------------------------------
520  */
viddec_fw_clear_processed_int(void)521 static inline void viddec_fw_clear_processed_int(void)
522 {
523     uint32_t i=0;
524     /* start from 0 to max streams that fw can handle*/
525     while(i < FW_SUPPORTED_STREAMS)
526     {
527         //if(_dmem.stream_info[i].state == 1)
528         _dmem.stream_info[i].pending_interrupt = false;
529         i++;
530     }
531     return;
532 }
533 
534 /*------------------------------------------------------------------------------
535  * Function:  viddec_fw_int_host
536  * This function interrupts host if data is available for host or any other status
537  * is valid which the host configures the FW to.
538  * There is only one interrupt line so this is a shared Int for all streams, Host should
539  * look at status of all streams when it receives a Int.
540  * The FW will interrupt the host only if host doorbell is free, in other words the host
541  * should always make the doorbell free at the End of its ISR.
542  *------------------------------------------------------------------------------
543  */
544 
viddec_fw_int_host()545 static inline int32_t viddec_fw_int_host()
546 {
547     /* We Interrupt the host only if host is ready to receive an interrupt */
548     if((reg_read(CONFIG_IPC_ROFF_HOST_DOORBELL_STATUS) & GV_DOORBELL_STATS) == GV_DOORBELL_STATS)
549     {
550         if(viddec_fw_check_for_pending_int())
551         {
552             /* If a pending interrupt is found trigger INT */
553             reg_write(CONFIG_IPC_ROFF_HOST_DOORBELL, VIDDEC_FW_PARSER_IPC_HOST_INT);
554             /* Clear all stream's pending Interrupt info since we use a global INT for all streams */
555             viddec_fw_clear_processed_int();
556         }
557     }
558     return 1;
559 }
560 volatile unsigned int stack_corrupted __attribute__ ((section (".stckovrflwchk")));
561 /*------------------------------------------------------------------------------
562  * Function:  main
563  * This function is the main firmware function. Its a infinite loop where it polls
564  * for messages and processes them if they are available. Currently we ping pong between
565  * synchronous and asynchronous messages one at a time. If we have multiple aysnchronous
566  * queues we always process only one between synchronous messages.
567  *
568  * For multiple asynchronous queues we round robin through the high priorities first and pick
569  * the first one available. Next time when we come around for asynchronous message we start
570  * from the next stream onwards so this guarantees that we give equal time slices for same
571  * priority queues. If no high priority queues are active we go to low priority queues and repeat
572  * the same process.
573  *------------------------------------------------------------------------------
574  */
575 
main(void)576 int main(void)
577 {
578     unsigned char *msg = (uint8_t *)&(_dmem.buf.data[0]);
579 
580     /* We wait until host reads sync message */
581     reg_write(CONFIG_IPC_ROFF_HOST_RX_DOORBELL, GV_FW_IPC_HOST_SYNC);
582 
583     while ( GV_DOORBELL_STATS != reg_read(CONFIG_IPC_ROFF_HOST_DOORBELL_STATUS) )
584     { /*poll register until done bit is set */
585       /* Host re-writes Vsparc DRAM (BSS) in this loop and will hit the DONE bit when complete */
586     }
587     enable_intr();
588     /* Initialize State for queues */
589     viddec_fw_parser_register_callbacks();
590     FwIPC_Initialize(GET_IPC_HANDLE(_dmem), (volatile char *)msg);
591     _dmem.g_pk_data.high_id = _dmem.g_pk_data.low_id = -1;
592     viddec_pm_init_ops();
593     stack_corrupted = 0xDEADBEEF;
594     while(1)
595     {
596         viddec_fw_process_sync_queues(msg);
597         viddec_fw_process_async_queues();
598         viddec_fw_int_host();
599 #if 0
600         if(stack_corrupted != 0xDEADBEEF)
601         {
602             WRITE_SVEN(SVEN_MODULE_EVENT_GV_FW_FATAL_STACK_CORRPON, 0, 0, 0, 0, 0, 0);
603             while(1);
604         }
605 #endif
606     }
607     return 1;
608 }
609