android_kernel_samsung_on5x.../drivers/media/radio/s610/fm_rds.c
2018-06-19 23:16:04 +02:00

277 lines
6.8 KiB
C
Executable file

#include "fm_low_struc.h"
#include "radio-s610.h"
#include "fm_rds.h"
void fm_rds_write_data(struct s610_radio *radio, u16 rds_data,
fm_rds_block_type_enum blk_type, fm_host_rds_errors_enum errors)
{
u8 *buf_ptr;
u16 usage;
u16 capa;
/* API_ENTRY(radio);*/
capa = radio->low->rds_buffer->size;
if (radio->low->rds_buffer->outdex
> radio->low->rds_buffer->index)
usage = radio->low->rds_buffer->size
- radio->low->rds_buffer->outdex
+ radio->low->rds_buffer->index;
else
usage = radio->low->rds_buffer->index
- radio->low->rds_buffer->outdex;
if ((capa - usage) >= (HOST_RDS_BLOCK_SIZE * 4)) {
buf_ptr = radio->low->rds_buffer->base
+ radio->low->rds_buffer->index;
buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff);
buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8) (rds_data >> 8);
buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] = (blk_type
<< HOST_RDS_DATA_BLKTYPE_POSI)
| (errors << HOST_RDS_DATA_ERRORS_POSI)
| HOST_RDS_DATA_AVAIL_MASK;
/* Advances the buffer's index */
radio->low->rds_buffer->index
+= HOST_RDS_BLOCK_SIZE;
/* Check if the buffer's index wraps */
if (radio->low->rds_buffer->index >=
radio->low->rds_buffer->size) {
radio->low->rds_buffer->index -=
radio->low->rds_buffer->size;
}
if (usage >= HOST_RDS_BLOCK_SIZE)
radio->low->fm_state.status |= STATUS_MASK_RDS_AVA;
}
if (radio->low->fm_state.rds_mem_thresh != 0) {
if (usage
>= (radio->low->fm_state.rds_mem_thresh
* HOST_RDS_BLOCK_SIZE)) {
fm_set_flag_bits(radio, FLAG_BUF_FUL);
radio->rds_cnt_mod++;
if (!(radio->rds_cnt_mod % 100))
APIEBUG(radio, ">N ");
radio->rds_n_count++;
radio->rds_new = TRUE;
atomic_set(&radio->is_rds_new, 1);
wake_up_interruptible(&radio->core->rds_read_queue);
}
}
/* API_EXIT(radio);*/
}
u16 fm_rds_get_avail_bytes(struct s610_radio *radio)
{
u16 avail_bytes;
if (radio->low->rds_buffer->outdex >
radio->low->rds_buffer->index)
avail_bytes = (radio->low->rds_buffer->size
- radio->low->rds_buffer->outdex
+ radio->low->rds_buffer->index);
else
avail_bytes = (radio->low->rds_buffer->index
- radio->low->rds_buffer->outdex);
return avail_bytes;
}
bool fm_read_rds_data(struct s610_radio *radio, u16 *buffer,
u16 *size, u16 *blocks)
{
u16 avail_bytes;
s16 avail_blocks;
s16 orig_avail;
u8 *buf_ptr;
if (radio->low->rds_buffer == NULL) {
*size = 0;
if (blocks)
*blocks = 0;
return FALSE;
}
orig_avail = avail_bytes = fm_rds_get_avail_bytes(radio);
if (avail_bytes > *size)
avail_bytes = *size;
avail_blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
avail_bytes = avail_blocks * HOST_RDS_BLOCK_SIZE;
if (avail_bytes == 0) {
*size = 0;
if (blocks)
*blocks = 0;
return FALSE;
}
buf_ptr = radio->low->rds_buffer->base
+ radio->low->rds_buffer->outdex;
(void) memcpy(buffer, buf_ptr, avail_bytes);
/* advances the buffer's outdex */
radio->low->rds_buffer->outdex += avail_bytes;
/* Check if the buffer's outdex wraps */
if (radio->low->rds_buffer->outdex >= radio->low->rds_buffer->size)
radio->low->rds_buffer->outdex -= radio->low->rds_buffer->size;
if (orig_avail == avail_bytes) {
buffer[(avail_blocks - 1) * HOST_RDS_BLOCK_SIZE
+ HOST_RDS_BLOCK_FMT_STATUS] &=
~HOST_RDS_DATA_AVAIL_MASK;
radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA;
}
*size = avail_bytes; /* number of bytes read */
if (blocks)
*blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
/* Update RDS flags */
if ((avail_bytes / HOST_RDS_BLOCK_SIZE)
< radio->low->fm_state.rds_mem_thresh)
fm_clear_flag_bits(radio, FLAG_BUF_FUL);
return TRUE;
}
void fm_rds_change_state(struct s610_radio *radio,
fm_rds_state_enum new_state)
{
fm_rds_state_enum old_state =
(fm_rds_state_enum) radio->low->fm_rds_state.current_state;
radio->low->fm_rds_state.current_state = new_state;
if ((old_state == RDS_STATE_FULL_SYNC)
&& (new_state == RDS_STATE_HAVE_DATA)) {
fm_update_rds_sync_status(radio, FALSE); /* unsynced */
} else if ((old_state != RDS_STATE_FULL_SYNC)
&& (new_state == RDS_STATE_FULL_SYNC)) {
fm_update_rds_sync_status(radio, TRUE); /* synced */
}
}
void fm_rds_update_error_status(struct s610_radio *radio, u16 errors)
{
if (errors == 0) {
radio->low->fm_rds_state.error_bits = 0;
radio->low->fm_rds_state.error_blks = 0;
} else {
radio->low->fm_rds_state.error_bits += errors;
radio->low->fm_rds_state.error_blks++;
}
if (radio->low->fm_rds_state.error_blks
>= radio->low->fm_state.rds_unsync_blk_cnt) {
if (radio->low->fm_rds_state.error_bits
>= radio->low->fm_state.rds_unsync_bit_cnt) {
/* sync-loss */
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
}
radio->low->fm_rds_state.error_bits = 0;
radio->low->fm_rds_state.error_blks = 0;
}
}
static fm_host_rds_errors_enum fm_rds_process_block(
struct s610_radio *radio,
u16 data, fm_rds_block_type_enum block_type,
u16 err_count)
{
fm_host_rds_errors_enum error_type;
if (err_count == 0) {
error_type = HOST_RDS_ERRS_NONE;
} else if ((err_count <= 2)
&& (err_count
<= radio->low->fm_config.rds_error_limit)) {
error_type = HOST_RDS_ERRS_2CORR;
} else if ((err_count <= 5)
&& (err_count
<= radio->low->fm_config.rds_error_limit)) {
error_type = HOST_RDS_ERRS_5CORR;
} else {
error_type = HOST_RDS_ERRS_UNCORR;
}
/* Write the data into the buffer */
if ((block_type != RDS_BLKTYPE_E)
|| (radio->low->fm_state.save_eblks)) {
fm_rds_write_data(radio, data, block_type, error_type);
}
return error_type;
}
void fm_process_rds_data(struct s610_radio *radio)
{
u32 fifo_data;
u16 i;
u16 avail_blocks;
u16 data;
u8 status;
u16 err_count;
fm_rds_block_type_enum block_type;
if (!radio->low->fm_state.rds_rx_enabled)
return;
avail_blocks = (fmspeedy_get_reg_int(0xFFF2BF) + 1) / 4;
for (i = 0; i < avail_blocks; i++) {
/* Fetch the RDS word data. */
fifo_data = fmspeedy_get_reg_int(0xFFF3C0);
data = (u16)((fifo_data >> 16) & 0xFFFF);
status = (u8)((fifo_data >> 8) & 0xFF);
block_type =
(fm_rds_block_type_enum) ((status & RDS_BLK_TYPE_MASK)
>> RDS_BLK_TYPE_SHIFT);
err_count = (status & RDS_ERR_CNT_MASK);
switch (radio->low->fm_rds_state.current_state) {
case RDS_STATE_INIT:
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
break;
case RDS_STATE_HAVE_DATA:
if ((block_type == RDS_BLKTYPE_A)
&& (err_count == 0)) {
/* Move to full sync */
fm_rds_change_state(radio,
RDS_STATE_FULL_SYNC);
fm_rds_process_block(radio,
data, block_type, err_count);
}
break;
case RDS_STATE_PRE_SYNC:
break;
case RDS_STATE_FULL_SYNC:
if (fm_rds_process_block(radio,
data, block_type, err_count)
== HOST_RDS_ERRS_UNCORR) {
fm_rds_update_error_status(radio,
radio->low->fm_state.rds_unsync_uncorr_weight);
} else {
fm_rds_update_error_status(radio, err_count);
}
break;
}
}
}