android-kernel-a71/drivers/media/tdmb/tdmb_port_mtv319.c
2024-11-30 08:51:05 +01:00

397 lines
9.2 KiB
C

/*
*
* drivers/media/tdmb/tdmb_port_mtv319.c
*
* tdmb driver
*
* Copyright (C) (2013, Samsung Electronics)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/cache.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/vmalloc.h>
/*#include <mach/gpio.h>*/
#include "mtv319.h"
#include "mtv319_internal.h"
#include "mtv319_ficdec_internal.h"
#include "mtv319_ficdec.h"
#include "mtv319_port.h"
#include "tdmb.h"
#define MTV319_INTERRUPT_SIZE (188*16)
#define MTV319_TS_BUF_SIZE (MTV319_SPI_CMD_SIZE + MTV319_INTERRUPT_SIZE)
#if defined(CONFIG_TDMB_TSIF_SLSI) || defined(CONFIG_TDMB_TSIF_QC)
#define TDMB_FIC_USE_TSIF
#define FIC_PACKET_COUNT 3
#define MSC_PACKET_COUNT 16
#endif
/* #define TDMB_DEBUG_SCAN */
static bool mtv319_on_air;
static bool mtv319_pwr_on;
#ifdef CONFIG_TDMB_SPI
/* static U8 stream_buff[MTV319_TS_BUF_SIZE] __cacheline_aligned; */
static U8 *stream_buff;
#endif
/* static U8 fic_buf[MTV319_FIC_BUF_SIZE] __cacheline_aligned; */
static U8 *fic_buf;
#ifdef TDMB_DEBUG_SCAN
static void __print_ensemble_info(struct ensemble_info_type *e_info)
{
int i = 0;
DPRINTK("ensem_freq(%ld)\n", e_info->ensem_freq);
DPRINTK("ensem_label(%s)\n", e_info->ensem_label);
for (i = 0; i < e_info->tot_sub_ch; i++) {
DPRINTK("sub_ch_id(0x%x)\n", e_info->sub_ch[i].sub_ch_id);
DPRINTK("start_addr(0x%x)\n", e_info->sub_ch[i].start_addr);
DPRINTK("tmid(0x%x)\n", e_info->sub_ch[i].tmid);
DPRINTK("svc_type(0x%x)\n", e_info->sub_ch[i].svc_type);
DPRINTK("svc_label(%s)\n", e_info->sub_ch[i].svc_label);
DPRINTK("scids(0x%x)\n", e_info->sub_ch[i].scids);
DPRINTK("ecc(0x%x)\n", e_info->sub_ch[i].ecc);
}
}
#endif
static bool __get_ensemble_info(struct ensemble_info_type *e_info
, unsigned long freq)
{
DPRINTK("%s\n", __func__);
rtvFICDEC_GetEnsembleInfo(e_info, freq);
if (e_info->tot_sub_ch) {
#ifdef TDMB_DEBUG_SCAN
__print_ensemble_info(e_info);
#endif
return true;
} else {
return false;
}
}
static void mtv319_power_off(void)
{
DPRINTK("%s\n", __func__);
if (mtv319_pwr_on) {
mtv319_on_air = false;
#if defined(CONFIG_TDMB_TSIF_SLSI) || defined(CONFIG_TDMB_TSIF_QC)
tdmb_tsi_stop();
#else
tdmb_control_irq(false);
#endif
tdmb_control_gpio(false);
mtv319_pwr_on = false;
RTV_GUARD_DEINIT;
}
}
static bool mtv319_power_on(int param)
{
DPRINTK("%s\n", __func__);
if (mtv319_pwr_on)
return true;
tdmb_control_gpio(true);
RTV_GUARD_INIT;
if (rtvTDMB_Initialize(tdmb_get_if_handle()) != RTV_SUCCESS) {
tdmb_control_gpio(false);
return false;
}
#if !defined(CONFIG_TDMB_TSIF_SLSI) && !defined(CONFIG_TDMB_TSIF_QC)
tdmb_control_irq(true);
#endif
mtv319_pwr_on = true;
return true;
}
static void mtv319_get_dm(struct tdmb_dm *info)
{
if (mtv319_pwr_on == true && mtv319_on_air == true) {
info->rssi = (rtvTDMB_GetRSSI() / RTV_TDMB_RSSI_DIVIDER);
info->per = rtvTDMB_GetPER();
info->ber = rtvTDMB_GetCER();
info->antenna = rtvTDMB_GetAntennaLevel(info->ber);
} else {
info->rssi = 100;
info->ber = 2000;
info->per = 0;
info->antenna = 0;
}
}
#if defined(CONFIG_TDMB_TSIF_SLSI) || defined(CONFIG_TDMB_TSIF_QC)
#ifdef TDMB_FIC_USE_TSIF
#define FIC_TIMEOUT 1200
#define FIC_WAIT_TIME 20
static int rtv_fic_dec_result;
static int rtv_fic_dec_timeout;
static void dmb_drv_fic_cb(u8 *data, u32 length)
{
int fic_size;
if (rtv_fic_dec_result == RTV_FIC_RET_DONE)
return;
fic_size = mtv319_assemble_fic(fic_buf, data, length);
if (fic_size >= 96) {
enum E_RTV_FIC_DEC_RET_TYPE dc;
dc = rtvFICDEC_Decode(fic_buf, fic_size);
rtv_fic_dec_result = dc;
}
#ifdef TDMB_DEBUG_SCAN
DPRINTK("%s : length:%d fic_size:%d decode:%d\n",
__func__, length, fic_size, rtv_fic_dec_result);
#endif
}
#endif
static void dmb_drv_msc_cb(u8 *data, u32 length)
{
/* DPRINTK("%s : 0x%x 0x%x 0x%x 0x%x\n", __func__, data[0], data[1], data[3], data[4]); */
tdmb_store_data(data, length);
}
#endif
static bool mtv319_set_ch(unsigned long freq, unsigned char subchid
, bool factory_test)
{
bool ret = false;
enum E_RTV_SERVICE_TYPE svc_type;
DPRINTK("%s : %ld %d\n", __func__, freq, subchid);
if (mtv319_pwr_on) {
int ch_ret;
rtvTDMB_CloseAllSubChannels();
#if defined(CONFIG_TDMB_TSIF_SLSI) || defined(CONFIG_TDMB_TSIF_QC)
tdmb_tsi_stop();
if (tdmb_tsi_start(dmb_drv_msc_cb, MSC_PACKET_COUNT) != 0)
return false;
#endif
if (subchid >= 64) {
subchid -= 64;
svc_type = RTV_SERVICE_DMB;
} else
svc_type = RTV_SERVICE_DAB;
ch_ret = rtvTDMB_OpenSubChannel((freq/1000),
subchid,
svc_type,
MTV319_INTERRUPT_SIZE);
if (ch_ret == RTV_SUCCESS
|| ch_ret == RTV_ALREADY_OPENED_SUBCHANNEL_ID) {
mtv319_on_air = true;
ret = TRUE;
DPRINTK("%s Success\n", __func__);
} else {
DPRINTK("%s Fail (%d)\n", __func__, ch_ret);
}
}
return ret;
}
static bool mtv319_scan_ch(struct ensemble_info_type *e_info
, unsigned long freq)
{
bool ret = false;
int scan_result = 1;
if (mtv319_pwr_on == true && e_info != NULL) {
rtvTDMB_CloseAllSubChannels();
#if defined(TDMB_FIC_USE_TSIF)
rtv_fic_dec_result = RTV_FIC_RET_GOING;
rtv_fic_dec_timeout = FIC_TIMEOUT;
tdmb_tsi_stop();
if (tdmb_tsi_start(dmb_drv_fic_cb, FIC_PACKET_COUNT) != 0)
return false;
rtvFICDEC_Init();
#endif
scan_result = rtvTDMB_ScanFrequency(freq/1000);
if (scan_result == RTV_SUCCESS) {
#if defined(TDMB_FIC_USE_TSIF)
unsigned int lock_s;
while (rtv_fic_dec_result == RTV_FIC_RET_GOING
&& rtv_fic_dec_timeout > 0) {
lock_s = tdmb_GetOfdmLockStatus();
if (!(lock_s & RTV_TDMB_OFDM_LOCK_MASK)) {
DPRINTK("##lock_s(0x%02X)\n", lock_s);
break;
}
RTV_DELAY_MS(FIC_WAIT_TIME);
rtv_fic_dec_timeout -= FIC_WAIT_TIME;
};
rtvTDMB_CloseFIC();
if (rtv_fic_dec_result == RTV_FIC_RET_DONE)
ret = true;
#else
enum E_RTV_FIC_DEC_RET_TYPE dc;
unsigned int i;
int parser_status = 0;
rtvFICDEC_Init(); /* FIC parser Init */
for (i = 0; i < 30; i++) {
int ret_size;
ret_size = rtvTDMB_ReadFIC(fic_buf);
if (ret_size > 0) {
dc = rtvFICDEC_Decode(fic_buf, 384);
if (dc == RTV_FIC_RET_GOING)
continue;
if (dc == RTV_FIC_RET_DONE) {
parser_status = 1;
break; /* Stop */
}
if (dc == RTV_FIC_RET_SEMI_DONE) {
parser_status = 2;
continue;
}
} else {
DPRINTK("mtv319_scan_ch READ Fail\n");
}
}
rtvTDMB_CloseFIC();
#endif
if ((parser_status == 1) || (parser_status == 2))
ret = __get_ensemble_info(e_info, (freq));
DPRINTK("%s : parser_status : %d\n", __func__, parser_status);
} else {
DPRINTK("%s : Scan fail : %ld : %d\n", __func__, freq, scan_result);
ret = false;
}
}
return ret;
}
#if !defined(CONFIG_TDMB_TSIF_SLSI) && !defined(CONFIG_TDMB_TSIF_QC)
static void mtv319_pull_data(void)
{
U8 ifreg, istatus;
RTV_GUARD_LOCK;
RTV_REG_MAP_SEL(SPI_CTRL_PAGE);
ifreg = RTV_REG_GET(0x55);
if (ifreg != 0xAA) {
mtv319_spi_recover(stream_buff, MTV319_INTERRUPT_SIZE);
DPRINTK("Interface error 1\n");
}
istatus = RTV_REG_GET(0x10);
if (istatus & (U8)(~SPI_INTR_BITS)) {
mtv319_spi_recover(stream_buff, MTV319_INTERRUPT_SIZE);
DPRINTK("Interface error 2 (0x%02X)\n", istatus);
goto exit_read_mem;
}
if (istatus & SPI_UNDERFLOW_INTR) {
RTV_REG_SET(0x2A, 1);
RTV_REG_SET(0x2A, 0);
DPRINTK("UDF: 0x%02X\n", istatus);
goto exit_read_mem;
}
if (istatus & SPI_THRESHOLD_INTR) {
RTV_REG_MAP_SEL(SPI_MEM_PAGE);
RTV_REG_BURST_GET(0x10, stream_buff, MTV319_INTERRUPT_SIZE);
tdmb_store_data(stream_buff, MTV319_INTERRUPT_SIZE);
if (istatus & SPI_OVERFLOW_INTR)
DPRINTK("OVF: 0x%02X\n", istatus); /* To debug */
} else
DPRINTK("No data interrupt (0x%02X)\n", istatus);
exit_read_mem:
RTV_GUARD_FREE;
}
#endif
static int mtv319_int_size(void)
{
return MTV319_INTERRUPT_SIZE;
}
static bool mtv319_init(void)
{
stream_buff = kzalloc(MTV319_TS_BUF_SIZE, GFP_KERNEL);
if (!stream_buff)
pr_err("%s : failed to allocate stream_buff\n", __func__);
fic_buf = kzalloc(MTV319_FIC_BUF_SIZE, GFP_KERNEL);
if (!fic_buf)
pr_err("%s : failed to allocate fic_buf\n", __func__);
return true;
}
static struct tdmb_drv_func raontech_mtv319_drv_func = {
.init = mtv319_init,
.power_on = mtv319_power_on,
.power_off = mtv319_power_off,
.scan_ch = mtv319_scan_ch,
.get_dm = mtv319_get_dm,
.set_ch = mtv319_set_ch,
#if !defined(CONFIG_TDMB_TSIF_SLSI) && !defined(CONFIG_TDMB_TSIF_QC)
.pull_data = mtv319_pull_data,
#endif
.get_int_size = mtv319_int_size,
};
struct tdmb_drv_func *mtv319_drv_func(void)
{
DPRINTK("tdmb_get_drv_func : mtv319\n");
return &raontech_mtv319_drv_func;
}