/* tuner-xc2028
*
* Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
*
* Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
* - frontend interface
*
* This code is placed under the terms of the GNU General Public License v2
*/
#include <linux/i2c.h>
#include <asm/div64.h>
#include <linux/firmware.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
#include <media/tuner.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "tuner-i2c.h"
#include "tuner-xc2028.h"
#include "tuner-xc2028-types.h"
#include <linux/dvb/frontend.h>
#include "dvb_frontend.h"
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 80
/* Registers (Write-only) */
#define XREG_INIT 0x00
#define XREG_RF_FREQ 0x02
#define XREG_POWER_DOWN 0x08
/* Registers (Read-only) */
#define XREG_FREQ_ERROR 0x01
#define XREG_LOCK 0x02
#define XREG_VERSION 0x04
#define XREG_PRODUCT_ID 0x08
#define XREG_HSYNC_FREQ 0x10
#define XREG_FRAME_LINES 0x20
#define XREG_SNR 0x40
#define XREG_ADC_ENV 0x0100
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
static int no_poweroff;
module_param(no_poweroff, int, 0644);
MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
"1 keep device energized and with tuner ready all the times.\n"
" Faster, but consumes more power and keeps the device hotter\n");
static char audio_std[8];
module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
MODULE_PARM_DESC(audio_std,
"Audio standard. XC3028 audio decoder explicitly "
"needs to know what audio\n"
"standard is needed for some video standards with audio A2 or NICAM.\n"
"The valid values are:\n"
"A2\n"
"A2/A\n"
"A2/B\n"
"NICAM\n"
"NICAM/A\n"
"NICAM/B\n");
static char firmware_name[30];
module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
"default firmware name\n");
static LIST_HEAD(hybrid_tuner_instance_list);
static DEFINE_MUTEX(xc2028_list_mutex);
/* struct for storing firmware table */
struct firmware_description {
unsigned int type;
v4l2_std_id id;
__u16 int_freq;
unsigned char *ptr;
unsigned int size;
};
struct firmware_properties {
unsigned int type;
v4l2_std_id id;
v4l2_std_id std_req;
__u16 int_freq;
unsigned int scode_table;
int scode_nr;
};
enum xc2028_state {
XC2028_NO_FIRMWARE = 0,
XC2028_WAITING_FIRMWARE,
XC2028_ACTIVE,
XC2028_SLEEP,
XC2028_NODEV,
};
struct xc2028_data {
struct list_head hybrid_tuner_instance_list;
struct tuner_i2c_props i2c_props;
__u32 frequency;
enum xc2028_state state;
const char *fname;
struct firmware_description *firm;
int firm_size;
__u16 firm_version;
__u16 hwmodel;
__u16 hwvers;
struct xc2028_ctrl ctrl;
struct firmware_properties cur_fw;
struct mutex lock;
};
#define i2c_send(priv, buf, size) ({ \
int _rc; \
_rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
if (size != _rc) \
tuner_info("i2c output error: rc = %d (should be %d)\n",\
_rc, (int)size); \
if (priv->ctrl.msleep) \
msleep(priv->ctrl.msleep); \
_rc; \
})
#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
int _rc; \
_rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
ibuf, isize); \
if (isize != _rc) \
tuner_err("i2c input error: rc = %d (should be %d)\n", \
_rc, (int)isize); \
if (priv->ctrl.msleep) \
msleep(priv->ctrl.msleep); \
_rc; \
})
#define send_seq(priv, data...) ({ \
static u8 _val[] = data; \
int _rc; \
if (sizeof(_val) != \
(_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
_val, sizeof(_val)))) { \
tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
} else if (priv->ctrl.msleep) \
msleep(priv->ctrl.msleep); \
_rc; \
})
static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
{
unsigned char buf[2];
unsigned char ibuf[2];
tuner_dbg("%s %04x called\n", __func__, reg);
buf[0] = reg >> 8;
buf[1] = (unsigned char) reg;
if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
return -EIO;
*val = (ibuf[1]) | (ibuf[0] << 8);
return 0;
}
#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
{
if (type & BASE)
printk("BASE ");
if (type & INIT1)
printk("INIT1 ");
if (type & F8MHZ)
printk("F8MHZ ");
if (type & MTS)
printk("MTS ");
if (type & D2620)
printk("D2620 ");
if (type & D2633)
printk("D2633 ");
if (type & DTV6)
printk("DTV6 ");
if (type & QAM)
printk("QAM ");
if (type & DTV7)
printk("DTV7 ");
if (type & DTV78)
printk("DTV78 ");
if (type & DTV8)
printk("DTV8 ");
if (type & FM)
printk("FM ");
if (type & INPUT1)
printk("INPUT1 ");
if (type & LCD)
printk("LCD ");
if (type & NOGD)
printk("NOGD ");
if (type & MONO)
printk("MONO ");
if (type & ATSC)
printk("ATSC ");
if (type & IF)
printk("IF ");
if (type & LG60)
printk("LG60 ");
if (type & ATI638)
printk("ATI638 ");
if (type & OREN538)
printk("OREN538 ");
if (type & OREN36)
printk("OREN36 ");
if (type & TOYOTA388)
printk("TOYOTA388 ");
if (type & TOYOTA794)
printk("TOYOTA794 ");
if (type & DIBCOM52)
printk("DIBCOM52 ");
if (type & ZARLINK456)
printk("ZARLINK456 ");
if (type & CHINA)
printk("CHINA ");
if (type & F6MHZ)
printk("F6MHZ ");
if (type & INPUT2)
printk("INPUT2 ");
if (type & SCODE)
printk("SCODE ");
if (type & HAS_IF)
printk("HAS_IF_%d ", int_freq);
}
static v4l2_std_id parse_audio_std_option(void)
{
if (strcasecmp(audio_std, "A2") == 0)
return V4L2_STD_A2;
if (strcasecmp(audio_std, "A2/A") == 0)
return V4L2_STD_A2_A;
if (strcasecmp(audio_std, "A2/B") == 0)
return V4L2_STD_A2_B;
if (strcasecmp(audio_std, "NICAM") == 0)
return V4L2_STD_NICAM;
if (strcasecmp(audio_std, "NICAM/A") == 0)
return V4L2_STD_NICAM_A;
if (strcasecmp(audio_std, "NICAM/B") == 0)
return V4L2_STD_NICAM_B;
return 0;
}
static int check_device_status(struct xc2028_data *priv)
{
switch (priv->state) {
case XC2028_NO_FIRMWARE:
case XC2028_WAITING_FIRMWARE:
return -EAGAIN;
case XC2028_ACTIVE:
return 1;
case XC2028_SLEEP:
return 0;
case XC2028_NODEV:
return -ENODEV;
}
return 0;
}
static void free_firmware(struct xc2028_data *priv)
{
int i;
tuner_dbg("%s called\n", __func__);
if (!priv->firm)
return;
for (i = 0; i < priv->firm_size; i++)
kfree(priv->firm[i].ptr);
kfree(priv->firm);
priv->firm = NULL;
priv->firm_size = 0;
priv->state = XC2028_NO_FIRMWARE;
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
}
static int load_all_firmwares(struct dvb_frontend *fe,
const struct firmware *
|