blob: 31572a0b88eafd89bc5778b42378567d08f042d0 [file] [log] [blame]
#include "viddec_pm_utils_bstream.h"
#include "viddec_fw_debug.h"
/* Internal data structure for calculating required bits. */
typedef union
{
uint8_t byte[8];
uint32_t word[2];
}viddec_pm_utils_getbits_t;
void viddec_pm_utils_bstream_reload(viddec_pm_utils_bstream_cxt_t *cxt);
uint32_t viddec_pm_utils_bstream_getphys(viddec_pm_utils_bstream_cxt_t *cxt, uint32_t pos, uint32_t lst_index);
extern uint32_t cp_using_dma(uint32_t ddr_addr, uint32_t local_addr, uint32_t size, char to_ddr, char swap);
/* Bytes left in cubby buffer which were not consumed yet */
static inline uint32_t viddec_pm_utils_bstream_bytesincubby(viddec_pm_utils_bstream_buf_cxt_t *cxt)
{
return (cxt->buf_end - cxt->buf_index);
}
/*
This function checks to see if we are at the last valid byte for current access unit.
*/
uint8_t viddec_pm_utils_bstream_nomorerbspdata(viddec_pm_utils_bstream_cxt_t *cxt)
{
uint32_t data_remaining = 0;
uint8_t ret = false;
/* How much data is remaining including current byte to be processed.*/
data_remaining = cxt->list->total_bytes - (cxt->au_pos + (cxt->bstrm_buf.buf_index - cxt->bstrm_buf.buf_st));
/* Start code prefix can be 000001 or 0000001. We always only check for 000001.
data_reamining should be 1 for 000001, as we don't count sc prefix and 1 represents current byte.
data_reamining should be 2 for 00000001, as we don't count sc prefix its current byte and extra 00 as we check for 000001.
NOTE: This is used for H264 only.
*/
switch(data_remaining)
{
case 2:
/* If next byte is 0 and its the last byte in access unit */
ret = (cxt->bstrm_buf.buf[cxt->bstrm_buf.buf_index+1] == 0x0);
break;
case 1:
/* if the current byte is last byte */
ret = true;
break;
default:
break;
}
return ret;
}
/*
This function returns true if cubby buffer has the last byte of access unit.
*/
uint8_t viddec_pm_utils_bstream_nomoredata(viddec_pm_utils_bstream_cxt_t *cxt)
{
uint32_t last_byte_offset_plus_one=0;
uint8_t ret = false;
/* Check to see if the last byte Acces unit offset is the last byte for current access unit.
End represents the first invalid byte, so (end - st) will give number of bytes.*/
last_byte_offset_plus_one = cxt->au_pos + (cxt->bstrm_buf.buf_end - cxt->bstrm_buf.buf_st);
if((int32_t)last_byte_offset_plus_one >= cxt->list->total_bytes)
{
ret = true;
}
return ret;
}
/* This function initializes scratch buffer, which is used for staging already read data, due to DMA limitations */
static inline void viddec_pm_utils_bstream_scratch_init(viddec_pm_utils_bstream_scratch_cxt_t *cxt)
{
cxt->st = cxt->size = cxt->bitoff=0;
}
/* This function tells us how much more data is in the current es buffer from current position. Its used to figure out if
we need to go to next es buffer
*/
static inline uint32_t viddec_pm_utils_bstream_datafromindex(viddec_pm_utils_list_t *list, uint32_t index, uint32_t offset)
{
uint32_t ret=0;
int32_t val=0;
val = (list->data[index].edpos <= (uint32_t)list->total_bytes) ? list->data[index].edpos: (uint32_t)list->total_bytes;
val = val - (int32_t)offset;
if(val > 0) ret = (uint32_t)val;
return val;
}
/* This function seeks to byte offset position starting from lst_index, if more data is present in current ES buffer pointed by
lst_index returns the remaining data in current buffer along with physical address of byte offset. The lst_index parameter
at returns index of ES buffer in list which has byte_offset
*/
static inline uint32_t viddec_pm_utils_bstream_maxbytes_from_index(viddec_pm_utils_bstream_cxt_t *cxt,
uint32_t *lst_index,
uint32_t byte_offset,
uint32_t *physaddr)
{
viddec_pm_utils_list_t *list;
uint32_t last_byte_offst=0, bytes_left=0;/* default return value is 0 bytes */
list = cxt->list;
while(*lst_index < list->num_items)
{
/* Check to see if we reached the buffer with last valid byte of current access unit, List can have data beyond current access unit */
last_byte_offst = (list->data[*lst_index].edpos <= (uint32_t)list->total_bytes) ? list->data[*lst_index].edpos: (uint32_t)list->total_bytes;
if(byte_offset < last_byte_offst)
{/* Found a match so return with data remaining */
bytes_left = viddec_pm_utils_bstream_datafromindex(list, *lst_index, byte_offset);
*physaddr = viddec_pm_utils_bstream_getphys(cxt, byte_offset, *lst_index);
break;
}
*lst_index+=1;
}
return bytes_left;
}
/* This function is for copying trailing bytes of cubby bitstream buffer to scratch buffer */
static inline void viddec_pm_utils_bstream_scratch_copyto(viddec_pm_utils_bstream_scratch_cxt_t *cxt, uint8_t *data, uint32_t num_bytes)
{
uint32_t i=0;
for(i=0; i<num_bytes;i++)
{
cxt->buf_scratch[i] = *data;
data++;cxt->size++;
}
}
/* This function is for copying trailing bytes from scratch buffer to bitstream buffer */
static inline void viddec_pm_utils_bstream_scratch_copyfrom(viddec_pm_utils_bstream_scratch_cxt_t *cxt, uint8_t *data)
{
uint32_t i=0;
for(i=0; i<cxt->size;i++)
{
*data = cxt->buf_scratch[i];
data++;
}
}
/* This function populates requested number of bytes into data parameter, skips emulation prevention bytes if needed */
static inline int32_t viddec_pm_utils_getbytes(viddec_pm_utils_bstream_buf_cxt_t *bstream,
viddec_pm_utils_getbits_t *data,/* gets populated with read bytes*/
uint32_t *act_bytes, /* actual number of bytes read can be more due to emulation prev bytes*/
uint32_t *phase, /* Phase for emulation */
uint32_t num_bytes,/* requested number of bytes*/
uint32_t emul_reqd, /* On true we look for emulation prevention */
uint8_t is_offset_zero /* Are we on aligned byte position for first byte*/
)
{
int32_t ret = 1;
uint8_t cur_byte = 0, valid_bytes_read = 0;
*act_bytes = 0;
while(valid_bytes_read < num_bytes)
{
cur_byte = bstream->buf[bstream->buf_index + *act_bytes];
if((cur_byte == 0x3) &&(*phase == 2))
{/* skip emulation byte. we update the phase only if emulation prevention is enabled */
*phase = 0;
}
else
{
data->byte[valid_bytes_read] = cur_byte;
/*
We only update phase for first byte if bit offset is 0. If its not 0 then it was already accounted for in the past.
From second byte onwards we always look to update phase.
*/
if((*act_bytes != 0) || (is_offset_zero))
{
if(cur_byte == 0)
{
/* Update phase only if emulation prevention is required */
*phase +=( ((*phase < 2) && emul_reqd ) ? 1: 0 );
}
else
{
*phase=0;
}
}
valid_bytes_read++;
}
*act_bytes +=1;
}
/* Check to see if we reached end during above operation. We might be out of range buts it safe since our array
has at least MIN_DATA extra bytes and the maximum out of bounds we will go is 5 bytes */
if((bstream->buf_index + *act_bytes -1) >= bstream->buf_end)
{
ret = -1;
}
return ret;
}
/*
This function checks to see if we have minimum amount of data else tries to reload as much as it can.
Always returns the data left in current buffer in parameter.
*/
static inline void viddec_pm_utils_check_bstream_reload(viddec_pm_utils_bstream_cxt_t *cxt, uint32_t *data_left)
{
#ifdef VBP
*data_left = viddec_pm_utils_bstream_bytesincubby(&(cxt->bstrm_buf));
#else
uint8_t isReload=0;
*data_left = viddec_pm_utils_bstream_bytesincubby(&(cxt->bstrm_buf));
/* If we have minimum data we should continue, else try to read more data */
if(*data_left <MIN_DATA)
{
/* Check to see if we already read last byte of current access unit */
isReload = !(viddec_pm_utils_bstream_nomoredata(cxt) == 1);
while(isReload)
{
/* We have more data in access unit so keep reading until we get at least minimum data */
viddec_pm_utils_bstream_reload(cxt);
*data_left = viddec_pm_utils_bstream_bytesincubby(&(cxt->bstrm_buf));
/* Break out of loop if we reached last byte or we have enough data */
isReload = !((*data_left >= MIN_DATA) || (viddec_pm_utils_bstream_nomoredata(cxt) == 1));
}
}
#endif
}
/*
This function moves the stream position by N bits(parameter bits). The bytes parameter tells us how many bytes were
read for this N bits(can be different due to emulation bytes).
*/
static inline void viddec_pm_utils_update_skipoffsets(viddec_pm_utils_bstream_buf_cxt_t *bstream, uint32_t bits, uint32_t bytes)
{
if((bits & 0x7) == 0)
{
bstream->buf_bitoff = 0;
bstream->buf_index +=bytes;
}
else
{
bstream->buf_bitoff = bits & 0x7;
bstream->buf_index +=(bytes - 1);
}
}
/*
This function skips emulation byte if necessary.
During Normal flow we skip emulation byte only if we read at least one bit after the the two zero bytes.
However in some cases we might send data to HW without reading the next bit, in which case we are on
emulation byte. To avoid sending invalid data, this function has to be called first to skip.
*/
void viddec_pm_utils_skip_if_current_is_emulation(viddec_pm_utils_bstream_cxt_t *cxt)
{
viddec_pm_utils_bstream_buf_cxt_t *bstream = &(cxt->bstrm_buf);
if(cxt->is_emul_reqd &&
(cxt->phase >= 2) &&
(bstream->buf_bitoff == 0) &&
(bstream->buf[bstream->buf_index] == 0x3) )
{
bstream->buf_index += 1;
cxt->phase = 0;
}
}
/*
This function gets physical address of the requested au offset(pos).
*/
uint32_t viddec_pm_utils_bstream_getphys(viddec_pm_utils_bstream_cxt_t *cxt, uint32_t pos, uint32_t lst_index)
{
uint32_t ret = 0, last_byte_offst=0;
viddec_pm_utils_list_t *list;
list = cxt->list;
while(lst_index < list->num_items)
{
last_byte_offst = (list->data[lst_index].edpos <= (uint32_t)list->total_bytes) ? list->data[lst_index].edpos: (uint32_t)list->total_bytes;
if(pos < last_byte_offst)
{
#ifndef MFDBIGENDIAN
ret = (uint32_t)list->sc_ibuf[lst_index].buf;
#else
ret = list->sc_ibuf[lst_index].phys;
#endif
ret +=(pos - list->data[lst_index].stpos);
if(lst_index == 0) ret+=list->start_offset;
break;
}
lst_index++;
}
return ret;
}
/*
Actual reload function which uses dma to refill bitstream buffer.
*/
void viddec_pm_utils_bstream_reload(viddec_pm_utils_bstream_cxt_t *cxt)
{
viddec_pm_utils_bstream_buf_cxt_t *bstream;
bstream = &(cxt->bstrm_buf);
/* Update current offset positions */
cxt->au_pos += (bstream->buf_index - bstream->buf_st);
bstream->buf_st = bstream->buf_index;
/* copy leftover bytes into scratch */
{
int32_t cur_bytes=0;
viddec_pm_utils_bstream_scratch_init(&(cxt->scratch));
cur_bytes = viddec_pm_utils_bstream_bytesincubby(&(cxt->bstrm_buf));
if(cur_bytes > 0)
{
viddec_pm_utils_bstream_scratch_copyto(&(cxt->scratch), &(bstream->buf[bstream->buf_index]), cur_bytes);
cxt->scratch.bitoff = bstream->buf_bitoff;
}
}
/* Initiate DMA and copyback scratch data */
{
uint32_t data_left = 0, ddr_mask=0;
/* calculate necesary aligmnets and copy data */
{
uint32_t ddr_addr=0, data_wrote=0;
uint32_t byte_pos;
/* byte pos points to the position from where we want to read data.*/
byte_pos = cxt->au_pos + cxt->scratch.size;
data_left = viddec_pm_utils_bstream_maxbytes_from_index(cxt, &(cxt->list_off), byte_pos, &ddr_addr);
if(data_left > CUBBY_SIZE)
{
data_left = CUBBY_SIZE;
}
if(data_left != 0)
{
ddr_mask = ddr_addr & 0x3;
ddr_addr = ddr_addr & ~0x3;
data_wrote = cp_using_dma(ddr_addr, (uint32_t)&(bstream->buf[MIN_DATA]), (data_left + ddr_mask), 0, 1);
}
}
/* copy scratch data back to buffer and update offsets */
{
uint32_t index=0;
index = MIN_DATA + ddr_mask;
index -= cxt->scratch.size;
viddec_pm_utils_bstream_scratch_copyfrom(&(cxt->scratch), &(bstream->buf[index]));
bstream->buf_st = bstream->buf_index = index;
bstream->buf_end = data_left + cxt->scratch.size + bstream->buf_st;
bstream->buf_bitoff = cxt->scratch.bitoff;
}
}
}
/*
Init function called by parser manager after sc code detected.
*/
void viddec_pm_utils_bstream_init(viddec_pm_utils_bstream_cxt_t *cxt, viddec_pm_utils_list_t *list, uint32_t is_emul)
{
#ifdef VBP
cxt->emulation_byte_counter = 0;
#endif
cxt->au_pos = 0;
cxt->list = list;
cxt->list_off = 0;
cxt->phase = 0;
cxt->is_emul_reqd = is_emul;
cxt->bstrm_buf.buf_st = cxt->bstrm_buf.buf_end = cxt->bstrm_buf.buf_index = cxt->bstrm_buf.buf_bitoff = 0;
}
/* Get the requested byte position. If the byte is already present in cubby its returned
else we seek forward and get the requested byte.
Limitation:Once we seek forward we can't return back.
*/
int32_t viddec_pm_utils_bstream_get_current_byte(viddec_pm_utils_bstream_cxt_t *cxt, uint8_t *byte)
{
int32_t ret = -1;
uint32_t data_left=0;
viddec_pm_utils_bstream_buf_cxt_t *bstream;
bstream = &(cxt->bstrm_buf);
viddec_pm_utils_check_bstream_reload(cxt, &data_left);
if(data_left != 0)
{
*byte = bstream->buf[bstream->buf_index];
ret = 1;
}
return ret;
}
/*
Function to skip N bits ( N<= 32).
*/
int32_t viddec_pm_utils_bstream_skipbits(viddec_pm_utils_bstream_cxt_t *cxt, uint32_t num_bits)
{
int32_t ret = -1;
uint32_t data_left=0;
viddec_pm_utils_bstream_buf_cxt_t *bstream;
bstream = &(cxt->bstrm_buf);
viddec_pm_utils_check_bstream_reload(cxt, &data_left);
if((num_bits <= 32) && (num_bits > 0) && (data_left != 0))
{
uint8_t bytes_required=0;
bytes_required = (bstream->buf_bitoff + num_bits + 7)>>3;
if(bytes_required <= data_left)
{
viddec_pm_utils_getbits_t data;
uint32_t act_bytes =0;
if(viddec_pm_utils_getbytes(bstream, &data, &act_bytes, &(cxt->phase), bytes_required, cxt->is_emul_reqd, (bstream->buf_bitoff == 0)) != -1)
{
uint32_t total_bits=0;
total_bits=num_bits+bstream->buf_bitoff;
viddec_pm_utils_update_skipoffsets(bstream, total_bits, act_bytes);
ret=1;
#ifdef VBP
if (act_bytes > bytes_required)
{
cxt->emulation_byte_counter = act_bytes - bytes_required;
}
#endif
}
}
}
return ret;
}
/*
Function to get N bits ( N<= 32).
*/
int32_t viddec_pm_utils_bstream_peekbits(viddec_pm_utils_bstream_cxt_t *cxt, uint32_t *out, uint32_t num_bits, uint8_t skip)
{
uint32_t data_left=0;
int32_t ret = -1;
/* STEP 1: Make sure that we have at least minimum data before we calculate bits */
viddec_pm_utils_check_bstream_reload(cxt, &data_left);
if((num_bits <= 32) && (num_bits > 0) && (data_left != 0))
{
uint32_t bytes_required=0;
viddec_pm_utils_bstream_buf_cxt_t *bstream;
bstream = &(cxt->bstrm_buf);
bytes_required = (bstream->buf_bitoff + num_bits + 7)>>3;
/* Step 2: Make sure we have bytes for requested bits */
if(bytes_required <= data_left)
{
uint32_t act_bytes, phase;
viddec_pm_utils_getbits_t data;
phase = cxt->phase;
/* Step 3: Due to emualtion prevention bytes sometimes the bytes_required > actual_required bytes */
if(viddec_pm_utils_getbytes(bstream, &data, &act_bytes, &phase, bytes_required, cxt->is_emul_reqd, (bstream->buf_bitoff == 0)) != -1)
{
uint32_t total_bits=0;
uint32_t shift_by=0;
/* zero out upper bits */
/* LIMITATION:For some reason compiler is optimizing it to NOP if i do both shifts
in single statement */
data.byte[0] <<= bstream->buf_bitoff;
data.byte[0] >>= bstream->buf_bitoff;
#ifndef MFDBIGENDIAN
data.word[0] = SWAP_WORD(data.word[0]);
data.word[1] = SWAP_WORD(data.word[1]);
#endif
total_bits = num_bits+bstream->buf_bitoff;
if(total_bits > 32)
{
/* We have to use both the words to get required data */
shift_by = total_bits - 32;
data.word[0] = (data.word[0] << shift_by) | ( data.word[1] >> (32 - shift_by));
}
else
{
shift_by = 32 - total_bits;
data.word[0] = data.word[0] >> shift_by;
}
*out = data.word[0];
if(skip)
{
/* update au byte position if needed */
viddec_pm_utils_update_skipoffsets(bstream, total_bits, act_bytes);
cxt->phase = phase;
#ifdef VBP
if (act_bytes > bytes_required)
{
cxt->emulation_byte_counter += act_bytes - bytes_required;
}
#endif
}
ret =1;
}
}
}
return ret;
}