1#include "viddec_pm.h"
2#include "viddec_fw_debug.h"
3#include "viddec_fw_common_defs.h"
4#include "viddec_pm_tags.h"
5#include "viddec_parser_ops.h"
6#include "viddec_vc1_parse.h"
7#include "viddec_mp4_parse.h"
8#include "viddec_mpeg2_parse.h"
9#include "viddec_h264_parse.h"
10/*
11  Overview of Parser manager:
12  Parser manager is the glue between Kernel(main.c) and actual codecs. We abstract common functionality as much as we can
13  in this module. The parser Manager context allocates memory for Parsers. At any point in time there is only one active stream.
14  During open stream we setup all necessary initialisation for the codec we are handling. The parser manager context is
15  stored on DDR when the current stream gets swapped out by the kernel. When the next stream comes in it has it's own
16  version of parser manager.
17  Parser manager is reponsible for providing information on when its a good time to swap a stream.
18  High level algorithm of parser Manager once a stream is opened and active(RET's are returns to Kernel):
19
20  1. create a list data structure to hold any incoming ES descriptors.
21  2. Check to see if any of the ES buffers Desc in current list has data to be processed. If not request kernel(RET) for a buffer.
22  3. If data is present parse until a scprefix+sc is found. If not goto step2.
23  4. If startcode detected update list state to make ES data look like Linear buffer.
24  5. Setup required state to provide getbits interface for codecs to access bit stream maximum 32bits at a time.
25  6. Setup Current & Next workloads provided by Kernel.
26  7. Call the codec to parse the data we collected between start codes.
27  8. Query to see if we parsed frame worth of data.
28  9. Do necessary TAG association and remove used buffers from List.
29  10. Send information to kernel on whether workload is done or Not.(RET). When kernel reschedules start from step2.
30
31  Kernel can swap current stream at RET points described above.
32
33  Other additional things supported:
34  - Generic start code detect function which is same for most of codecs.
35  - Memory Management.
36  - Flush of stream.
37  - Emulation prevention.
38  - Interface to emit necessary tags for codec specific types.
39*/
40
41
42/* check to see if codec needs emulation prevention */
43#define EMUL_REQD(codec) ((codec == MFD_STREAM_FORMAT_VC1) || (codec_type == MFD_STREAM_FORMAT_H264) ? 1: 0)
44
45#ifdef RTL_SIMULATION
46extern void output_omar_wires( unsigned int value );
47#else
48#define output_omar_wires(x)
49#endif
50
51/* Place to store Function pointers for all supported interfaces for each codec */
52viddec_parser_ops_t parser_ops[MFD_STREAM_FORMAT_MAX];
53
54
55
56/* we need to define as external function so that for host mode we can use the same code without
57   modifications by overloading dma function with a copy function
58*/
59extern uint32_t cp_using_dma(uint32_t ddr_addr, uint32_t local_addr, uint32_t size, char to_ddr, char swap);
60
61void viddec_pm_init_ops()
62{
63    viddec_vc1_get_ops(&parser_ops[MFD_STREAM_FORMAT_VC1]);
64    parser_ops[MFD_STREAM_FORMAT_VC1].parse_sc = viddec_parse_sc;
65    parser_ops[MFD_STREAM_FORMAT_VC1].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
66    parser_ops[MFD_STREAM_FORMAT_VC1].gen_assoc_tags = viddec_generic_add_association_tags;
67
68    viddec_mpeg2_get_ops(&parser_ops[MFD_STREAM_FORMAT_MPEG]);
69    parser_ops[MFD_STREAM_FORMAT_MPEG].parse_sc = viddec_parse_sc;
70    parser_ops[MFD_STREAM_FORMAT_MPEG].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
71    parser_ops[MFD_STREAM_FORMAT_MPEG].gen_assoc_tags = viddec_mpeg2_add_association_tags;
72
73    viddec_h264_get_ops(&parser_ops[MFD_STREAM_FORMAT_H264]);
74    parser_ops[MFD_STREAM_FORMAT_H264].parse_sc = viddec_parse_sc;
75    parser_ops[MFD_STREAM_FORMAT_H264].gen_contrib_tags = viddec_pm_lateframe_generate_contribution_tags;
76    parser_ops[MFD_STREAM_FORMAT_H264].gen_assoc_tags = viddec_h264_add_association_tags;
77
78    viddec_mp4_get_ops(&parser_ops[MFD_STREAM_FORMAT_MPEG42]);
79    parser_ops[MFD_STREAM_FORMAT_MPEG42].gen_contrib_tags = viddec_pm_generic_generate_contribution_tags;
80    parser_ops[MFD_STREAM_FORMAT_MPEG42].gen_assoc_tags = viddec_generic_add_association_tags;
81}
82
83/*
84  Returns size of persistent DDR memory required for the codec. If the required memory is less than max allocated
85  scratch memory in FW we always give the max scratch size.
86*/
87uint32_t viddec_pm_get_parser_sizes(uint32_t codec_type, viddec_parser_memory_sizes_t *size)
88{
89    parser_ops[codec_type].get_cxt_size(size);
90    if(size->context_size > MAX_CODEC_CXT_SIZE)
91    {
92        DEB("ERROR: size(%d) of context for codec=%d is greater than max=%d\n",size->context_size,codec_type,MAX_CODEC_CXT_SIZE);
93    }
94    size->context_size = sizeof(viddec_pm_cxt_t);
95    return 1;
96}
97
98/*
99  Initialize the scratch memory allocated to the stream based on clean. if clean is true initialize to
100  start state, if not then preserve stream information.
101*/
102void viddec_pm_init_context(viddec_pm_cxt_t *cxt, uint32_t codec_type, uint32_t *persist_mem, uint32_t clean)
103{
104    int i;
105
106    for(i=0; i<MAX_IBUFS_PER_SC; i++)
107    {
108        cxt->pending_tags.pending_tags[i] = INVALID_ENTRY;
109    }
110    cxt->frame_start_found = false;
111    cxt->found_fm_st_in_current_au = false;
112    cxt->late_frame_detect = (MFD_STREAM_FORMAT_H264 == codec_type) ? true:false;
113    cxt->pending_tags.first_buf_aligned = cxt->pending_tags.using_next = cxt->pending_tags.frame_done =false;
114    cxt->next_workload_error_eos = VIDDEC_FW_WORKLOAD_ERR_FLUSHED_FRAME | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE;
115    viddec_pm_utils_list_init(&(cxt->list));
116    cxt->cur_buf.list_index = -1;
117    cxt->parse_cubby.phase=0;
118    parser_ops[codec_type].init((void *)&(cxt->codec_data[0]), persist_mem, !clean);
119    if(clean)
120    {
121        cxt->pending_inband_tags = 0;
122    }
123    else
124    {
125        /* TODO: Enable this once codecs support this function */
126        //parser_ops[codec_type].flush_preserve((void *)&(cxt->codec_data[0]), persist_mem);
127    }
128
129}
130
131void viddec_pm_update_time(viddec_pm_cxt_t *cxt, uint32_t time)
132{
133    viddec_emit_time(&(cxt->emitter), time);
134}
135
136/* add an esbuffer to list */
137static inline uint32_t viddec_pm_add_es_buf_to_list(viddec_pm_cxt_t *cxt, viddec_input_buffer_t *es_buf)
138{
139    uint32_t val , ret = PM_OVERFLOW;
140
141    val = viddec_pm_utils_list_addbuf(&(cxt->list), es_buf);
142    if(val == 1) ret = PM_SUCCESS;
143    return ret;
144}
145
146static inline uint32_t viddec_pm_check_inband_messages(viddec_pm_sc_cur_buf_t *cur_buf, uint32_t *type)
147{
148    uint32_t ret=false;
149    if(cur_buf->cur_es->flags != 0)
150    {
151        /* update offset to point to next position for loading data */
152        cur_buf->cur_offset +=(cur_buf->cur_size);
153        cur_buf->cur_size = 0;
154        switch(cur_buf->cur_es->flags)
155        {
156            case VIDDEC_STREAM_EOS:
157            {
158                *type = PM_EOS;
159            }
160            break;
161            case VIDDEC_STREAM_DISCONTINUITY:
162            {
163                *type = PM_DISCONTINUITY;
164            }
165            default:
166                break;
167        }
168        ret =true;
169    }
170    return ret;
171}
172
173/* creates an ibuf from the current position in list. Fills sc_parse_cubby_cxt */
174uint32_t viddec_pm_create_ibuf(viddec_pm_cxt_t *cxt)
175{
176    uint32_t ret = PM_NO_DATA;
177#ifndef VBP
178    viddec_sc_parse_cubby_cxt_t *cubby = &(cxt->parse_cubby);
179#endif
180    viddec_pm_sc_cur_buf_t *cur_buf = &(cxt->cur_buf);
181    viddec_pm_utils_list_t *list = &(cxt->list);
182
183    /* Step1: check if list is Empty, If yes return No data */
184    if(list->num_items > 0)
185    {
186        /* Step 2: Check to see If current index into list is empty & we have data in list,
187           if so increment index and initialise it*/
188        if(cur_buf->list_index == -1)
189        {
190            if(viddec_pm_utils_list_getbyte_position(list,
191                                                     list->first_scprfx_length+1,
192                                                     (uint32_t *)&(cur_buf->list_index),
193                                                     &(cur_buf->cur_offset)) != 1)
194            {/* This return's offset and index from where we have to start for sc detect */
195                cur_buf->cur_size = 0;
196                cur_buf->cur_es = &(list->sc_ibuf[cur_buf->list_index]);
197            }
198            else
199            {
200                return PM_NO_DATA;
201            }
202        }
203
204        /* Step3: If we are done with current buffer then try to go to next item in list */
205        if((cur_buf->cur_offset + cur_buf->cur_size) >= cur_buf->cur_es->len)
206        {
207            /* Need to handle In band messages before going to next buffer */
208            //if(viddec_pm_check_inband_messages(cur_buf))
209            if(viddec_pm_check_inband_messages(cur_buf, &ret))
210            {
211                return ret;
212            }
213            /* If no items in list after the current buffer return no data */
214            if((uint32_t)(cur_buf->list_index + 1) >=  list->num_items)
215            {
216                return PM_NO_DATA;
217            }
218            cur_buf->list_index++;
219            cur_buf->cur_es = &(list->sc_ibuf[cur_buf->list_index]);
220            cur_buf->cur_offset = cur_buf->cur_size = 0;
221        }
222        /* Step4: Fill the cubby with data to send to parser sc code function */
223        {
224            int32_t data_left;
225            /* data left is the leftout size in current ES buffer */
226            data_left = cur_buf->cur_es->len -  (cur_buf->cur_offset + cur_buf->cur_size);
227
228            /* update offset to point to next position for loading data */
229            cur_buf->cur_offset +=(cur_buf->cur_size);
230
231#ifndef VBP
232            /* Load maximum of array size */
233            if(data_left >= SC_DETECT_BUF_SIZE)
234            {
235                data_left = SC_DETECT_BUF_SIZE;
236            }
237            /* can be zero if we have zero sized buffers in our list.EX:NEW segment */
238            if(data_left > 0)
239            {/* do a copy using Linear Dma */
240                uint32_t size , ddr_addr = 0, ddr_mask=0;
241                /* get ddr adress of current offset in ES buffer */
242#ifdef HOST_ONLY
243                ddr_addr = cur_buf->cur_offset + (uint32_t)cur_buf->cur_es->buf;
244#else
245                ddr_addr = cur_buf->cur_offset + cur_buf->cur_es->phys;
246#endif
247                ddr_mask = (ddr_addr & 3);
248                ddr_addr = ddr_addr & ~3;
249                /* return from this function can be more bytes based on input buf alignment.
250                   The adress for local memory we are sending is on DWORD boundary so it should be safe.
251                */
252
253                size = cp_using_dma(ddr_addr, (uint32_t)&(cxt->scbuf[0]), data_left+ddr_mask, 0,1);//false, true);
254                cubby->size = data_left;
255
256                /* point to actual memory location which has the data(skip aligment bytes) */
257                cubby->buf = &(cxt->scbuf[ddr_mask]);
258                cur_buf->cur_size = data_left;
259                ret = PM_SUCCESS;
260            }
261            else
262            {
263                /* If we completely consumed this buffer or this is a zero sized buffer we want to check inband messages */
264                //if(viddec_pm_check_inband_messages(cur_buf))
265                if(viddec_pm_check_inband_messages(cur_buf, &ret))
266                {
267                    return ret;
268                }
269            }
270#else
271      ret = PM_SUCCESS;
272#endif
273        }
274    }
275
276    return ret;
277}
278
279/*
280  Read data from esbuffer list and parse for start codes or EOS. If we consumed all the data we return no data left.
281*/
282static inline uint32_t viddec_pm_parse_for_sccode(viddec_pm_cxt_t *cxt, viddec_parser_ops_t *func)
283{
284    uint32_t ret = PM_NO_DATA;
285    uint32_t sc_boundary_found = 0;
286
287    while(!sc_boundary_found)
288    {
289        /* Create an buffer from list to parse */
290        ret = viddec_pm_create_ibuf(cxt);
291        switch(ret)
292        {
293            case PM_NO_DATA:
294            {/* No data in esbuffer list for parsing sc */
295                sc_boundary_found = 1;
296            }
297            break;
298            case PM_EOS:
299            case PM_DISCONTINUITY:
300            {
301                sc_boundary_found = 1;
302                cxt->list.end_offset = cxt->cur_buf.cur_offset+1;
303                cxt->parse_cubby.phase = 0;
304                /* we didn't find a start code so second start code length would be 0 */
305                cxt->sc_prefix_info.second_scprfx_length = 0;
306                //cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_EOS;
307                if(ret == PM_EOS)
308                {
309                    cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_EOS;
310                }
311                if(ret == PM_DISCONTINUITY)
312                {
313                    cxt->sc_prefix_info.next_sc = VIDDEC_PARSE_DISCONTINUITY;
314                }
315            }
316            break;
317            case PM_SUCCESS:
318            default:
319            {
320                /* parse the created buffer for sc */
321                ret = func->parse_sc((void *)&(cxt->parse_cubby), (void *)&(cxt->codec_data[0]), &(cxt->sc_prefix_info));
322                if(ret == 1)
323                {
324                    cxt->list.end_offset = cxt->parse_cubby.sc_end_pos + cxt->cur_buf.cur_offset;
325                    cxt->parse_cubby.phase = 0;
326                    cxt->list.total_bytes+=cxt->parse_cubby.sc_end_pos;
327                    ret = PM_SC_FOUND;
328                    sc_boundary_found = 1;
329                    break;
330                }
331                else
332                {
333                    cxt->list.total_bytes+=cxt->cur_buf.cur_size;
334                }
335            }
336            break;
337        }
338    }
339
340    return ret;
341}
342
343/*
344  Once we are ready to flush the current workload, we update current workload on DDR with our internal information
345  that was not written before like num of items in workload, errors in stream etc...
346*/
347void viddec_pm_finalize_workload(viddec_pm_cxt_t *cxt, uint32_t codec_type, uint32_t codec_errors)
348{
349    viddec_emit_set_codec(&(cxt->emitter), codec_type);
350    viddec_emit_set_codec_errors(&(cxt->emitter), codec_errors);
351    viddec_emit_flush_current_wkld(&(cxt->emitter));
352    output_omar_wires( 0x5 );
353    output_omar_wires( 0x1 );
354}
355
356/*
357  After parsing between start codes we cleanup our list so that it has only buffers that are not consumed yet.
358*/
359uint32_t viddec_pm_finalize_list(viddec_pm_cxt_t *cxt)
360{
361    uint32_t ret=1;
362
363    viddec_pm_utils_list_remove_used_entries(&(cxt->list), cxt->sc_prefix_info.second_scprfx_length);
364    cxt->cur_buf.list_index = -1;
365    cxt->list.first_scprfx_length = cxt->sc_prefix_info.second_scprfx_length;
366    return ret;
367}
368
369/* Case to handle if we encounter list overflow without seeing second start code */
370void viddec_pm_handle_buffer_overflow(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
371{
372    uint32_t indx=0;
373    while(indx< (uint32_t)cxt->list.num_items)
374    {/* Dump tags for all entries in list to prevent buffer leak */
375        viddec_emit_contr_tag(&(cxt->emitter), &(cxt->list.sc_ibuf[indx]), false, true);
376        viddec_emit_assoc_tag(&(cxt->emitter), cxt->list.sc_ibuf[indx].id, true);
377        indx++;
378    }
379    /* Dump tags for the new buffer that was received */
380    viddec_emit_contr_tag(&(cxt->emitter), es_buf, 0, true);
381    viddec_emit_assoc_tag(&(cxt->emitter), es_buf->id, true);
382    /* Set errors on both current and next as both can be invalid */
383    viddec_emit_set_workload_error(&(cxt->emitter),
384                                   (VIDDEC_FW_WORKLOAD_ERR_BUFFERS_OVERFLOW | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE),
385                                   true);
386    viddec_emit_set_workload_error(&(cxt->emitter),
387                                   (VIDDEC_FW_WORKLOAD_ERR_BUFFERS_OVERFLOW | VIDDEC_FW_WORKLOAD_ERR_NOTDECODABLE),
388                                   false);
389    /* cleanup the pending tags */
390    viddec_pm_generate_missed_association_tags(cxt, true);
391    viddec_pm_finalize_workload(cxt, codec_type, 0);
392    WRITE_SVEN(SVEN_MODULE_EVENT_GV_FW_FATAL_BUFFER_OVERLFOW, (int)es_buf->phys, (int)es_buf->len, 0, 0, 0, 0);
393}
394
395static inline void viddec_pm_handle_post_inband_messages(viddec_pm_cxt_t *cxt, uint32_t m_type)
396{
397    if((m_type & ~(0xFF))== PM_INBAND_MESSAGES)
398    {
399        /* If EOS decide set error on next workload too */
400        viddec_emit_set_workload_error(&(cxt->emitter), cxt->next_workload_error_eos, true);
401        if(m_type == PM_EOS)
402        {
403            viddec_emit_set_inband_tag(&(cxt->emitter), VIDDEC_WORKLOAD_IBUF_EOS, true);
404        }
405        if(m_type == PM_DISCONTINUITY)
406        {
407            cxt->pending_inband_tags = PM_DISCONTINUITY;
408        }
409    }
410}
411
412static inline uint32_t viddec_pm_handle_new_es_buffer(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
413{
414    uint32_t state = PM_SUCCESS;
415    if(es_buf != NULL)
416    {
417        state = viddec_pm_add_es_buf_to_list(cxt, es_buf);
418        if(state == PM_OVERFLOW)
419        {
420            viddec_pm_handle_buffer_overflow(cxt, codec_type, es_buf);
421        }
422    }
423    return state;
424}
425
426static inline void viddec_pm_handle_pre_inband_messages(viddec_pm_cxt_t *cxt)
427{
428    if(cxt->pending_inband_tags == PM_DISCONTINUITY)
429    {
430        viddec_emit_set_inband_tag(&(cxt->emitter), VIDDEC_WORKLOAD_IBUF_DISCONTINUITY, false);
431        cxt->pending_inband_tags = 0;
432    }
433}
434
435/*
436  Main function of parser manager.
437  It searches until start codes are found int he list if not through return type indicates kernel to provide more buffers.
438  If a start code is found it calls the codec to parse the syntax data it accumulated so far.
439  If codec says a frame is not done then continues to find the next start code.
440  If codec says frame is done it does tag association and indicates kernel a frame is done.
441*/
442uint32_t viddec_pm_parse_es_buffer(viddec_pm_cxt_t *cxt, uint32_t codec_type, viddec_input_buffer_t *es_buf)
443{
444    uint32_t state = PM_SUCCESS;
445
446    /* Step1: Append Es buffer to list */
447    viddec_pm_handle_pre_inband_messages(cxt);
448    state = viddec_pm_handle_new_es_buffer(cxt, codec_type, es_buf);
449    if(state == PM_SUCCESS)
450    {
451        uint32_t scdetect_ret;
452        output_omar_wires( 0x3 );
453        /* Step2: Phase1 of parsing, parse until a sc is found */
454        scdetect_ret = viddec_pm_parse_for_sccode(cxt,&parser_ops[codec_type]);
455        switch(scdetect_ret)
456        {
457            case PM_NO_DATA:
458            {
459                /* Step3: If we consumed all the data indicate we need more buffers */
460                state = PM_NO_DATA;
461                break;
462            }
463            case PM_EOS:
464            case PM_DISCONTINUITY:
465            case PM_SC_FOUND:
466            {
467                uint32_t codec_errors=0;
468                /* Create necessary state information to make the ES buffers look like linear data */
469                viddec_pm_utils_list_updatebytepos(&(cxt->list), cxt->sc_prefix_info.second_scprfx_length);
470                if(cxt->sc_prefix_info.first_sc_detect != 1)
471                {
472                    /* Step4: If we saw two start codes init state and call codec to parse */
473                    uint32_t codec_ret;
474                    /* Initialise the state to provide get bits for codecs */
475                    viddec_pm_utils_bstream_init(&(cxt->getbits), &(cxt->list), EMUL_REQD(codec_type));
476                    output_omar_wires( 0x1 );
477                    /* call the codec to do synatax parsing */
478                    parser_ops[codec_type].parse_syntax((void *)cxt, (void *)&(cxt->codec_data[0]));
479                    /* Check and see if frame start was detected. If we did update frame start in current au */
480                    if(parser_ops[codec_type].is_frame_start((void *)&(cxt->codec_data[0])) == true)
481                    {
482                        cxt->frame_start_found += 1;
483                        cxt->found_fm_st_in_current_au = true;
484                    }
485                    /* Query to see if we reached end of current frame */
486                    codec_ret = parser_ops[codec_type].is_wkld_done((void *)cxt,
487                                                                    (void *)&(cxt->codec_data[0]),
488                                                                    (uint32_t)(cxt->sc_prefix_info.next_sc),
489                                                                    &codec_errors);
490
491                    state = (codec_ret == VIDDEC_PARSE_FRMDONE) ? PM_WKLD_DONE : PM_SUCCESS;
492                    /* generate contribution and association tags */
493                    cxt->pending_tags.frame_done = (codec_ret == VIDDEC_PARSE_FRMDONE);
494                    parser_ops[codec_type].gen_assoc_tags(cxt);
495                    parser_ops[codec_type].gen_contrib_tags(cxt, (state != PM_WKLD_DONE));
496                }
497                else
498                {
499                    /* Step4: If this is the first start code in this stream, clean up and return */
500                    if(cxt->list.total_bytes != 0)
501                    {
502                        viddec_pm_generic_generate_contribution_tags(cxt, true);
503                        viddec_generic_add_association_tags(cxt);
504                    }
505                    else
506                    {
507                        if(cxt->list.num_items >= 1)
508                        {
509                            uint32_t indx=0;
510                            while((indx< (uint32_t)cxt->list.num_items) && (cxt->list.sc_ibuf[indx].len == 0))
511                            {/* Dump all zero sized buffers until we see a buffer with valid data */
512                                viddec_emit_contr_tag(&(cxt->emitter), &(cxt->list.sc_ibuf[indx]), false, false);
513                                viddec_emit_assoc_tag(&(cxt->emitter), cxt->list.sc_ibuf[indx].id, false);
514                                indx++;
515                            }
516                        }
517                    }
518                    if((scdetect_ret & ~(0xFF))!= PM_INBAND_MESSAGES)
519                    {
520                        state = PM_SUCCESS;//state = PM_FIRST_SC_FOUND;
521                        cxt->sc_prefix_info.first_sc_detect = 0;
522                    }
523                    else
524                    {
525                        state = PM_WKLD_DONE;
526                    }
527                }
528
529                viddec_pm_handle_post_inband_messages(cxt, scdetect_ret);
530
531                /* Step 5: If current frame is done, finalise the workload state with necessary information */
532                if(state == PM_WKLD_DONE)
533                {
534                    DEB("\nFRAME ... DONE\n");
535                    /* we decrement frame start. This can be 0 in cases like sending junk data with EOS */
536                    cxt->frame_start_found -= (cxt->frame_start_found)? 1: 0;
537                    if((scdetect_ret & ~(0xFF))== PM_INBAND_MESSAGES)
538                    {/* If EOS dump pending tags and set state */
539                        viddec_pm_generate_missed_association_tags(cxt, false);
540                        state = scdetect_ret;
541                    }
542                    /* Write back stored state of workloads to memory to prepare for psuhing to output queue */
543                    viddec_pm_finalize_workload(cxt, codec_type, codec_errors);
544                }
545                /* Step 6: Reset the list to prepare for next iteration */
546                viddec_pm_finalize_list(cxt);
547                break;
548            }
549            default:
550                break;
551        }
552    }//if(state == PM_SUCCESS)
553    return state;
554} // viddec_pm_parse_es_buffer
555