Commit 3a509bb1 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

[media] Re-write the s921 frontend



On our tests with Leadership ISDBT, the s921 frontend were not work. As its
design contained some weird things, it ended to be easier to just re-write
it, getting another frontend as an example (cx24123).

As the old s921 driver weren't used, there's no regression. Some info from
the old frontend were used as a way to double check the behavior that were
noticed on the USB dumps retrieved from Leadership driver.

Signed-off-by: default avatarDouglas Schilling Landgraf <dougsland@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 387c31c7
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -497,7 +497,7 @@ comment "ISDB-T (terrestrial) frontends"
	depends on DVB_CORE
	depends on DVB_CORE


config DVB_S921
config DVB_S921
	tristate "Sharp S921 tuner"
	tristate "Sharp S921 frontend"
	depends on DVB_CORE && I2C
	depends on DVB_CORE && I2C
	default m if DVB_FE_CUSTOMISE
	default m if DVB_FE_CUSTOMISE
	help
	help
+0 −1
Original line number Original line Diff line number Diff line
@@ -5,7 +5,6 @@
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
EXTRA_CFLAGS += -Idrivers/media/common/tuners/


s921-objs := s921_module.o s921_core.o
stb0899-objs = stb0899_drv.o stb0899_algo.o
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
stv0900-objs = stv0900_core.o stv0900_sw.o
au8522-objs = au8522_dig.o au8522_decoder.o
au8522-objs = au8522_dig.o au8522_decoder.o
+548 −0
Original line number Original line Diff line number Diff line
/*
 *   Sharp VA3A5JZ921 One Seg Broadcast Module driver
 *   This device is labeled as just S. 921 at the top of the frontend can
 *
 *   Copyright (C) 2009-2010 Mauro Carvalho Chehab <mchehab@redhat.com>
 *   Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
 *
 *   Developed for Leadership SBTVD 1seg device sold in Brazil
 *
 *   Frontend module based on cx24123 driver, getting some info from
 *	the old s921 driver.
 *
 *   FIXME: Need to port to DVB v5.2 API
 *
 *   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 in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; 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 <asm/div64.h>

#include "dvb_frontend.h"
#include "s921.h"

static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");

#define rc(args...)  do {						\
	printk(KERN_ERR  "s921: " args);				\
} while (0)

#define dprintk(args...)						\
	do {								\
		if (debug) {						\
			printk(KERN_DEBUG "s921: %s: ", __func__);	\
			printk(args);					\
		}							\
	} while (0)

struct s921_state {
	struct i2c_adapter *i2c;
	const struct s921_config *config;

	struct dvb_frontend frontend;

	/* The Demod can't easily provide these, we cache them */
	u32 currentfreq;
};

/*
 * Various tuner defaults need to be established for a given frequency kHz.
 * fixme: The bounds on the bands do not match the doc in real life.
 * fixme: Some of them have been moved, other might need adjustment.
 */
static struct s921_bandselect_val {
	u32 freq_low;
	u8  band_reg;
} s921_bandselect[] = {
	{         0, 0x7b },
	{ 485140000, 0x5b },
	{ 515140000, 0x3b },
	{ 545140000, 0x1b },
	{ 599140000, 0xfb },
	{ 623140000, 0xdb },
	{ 659140000, 0xbb },
	{ 713140000, 0x9b },
};

struct regdata {
	u8 reg;
	u8 data;
};

static struct regdata s921_init[] = {
	{ 0x01, 0x80 },		/* Probably, a reset sequence */
	{ 0x01, 0x40 },
	{ 0x01, 0x80 },
	{ 0x01, 0x40 },

	{ 0x02, 0x00 },
	{ 0x03, 0x40 },
	{ 0x04, 0x01 },
	{ 0x05, 0x00 },
	{ 0x06, 0x00 },
	{ 0x07, 0x00 },
	{ 0x08, 0x00 },
	{ 0x09, 0x00 },
	{ 0x0a, 0x00 },
	{ 0x0b, 0x5a },
	{ 0x0c, 0x00 },
	{ 0x0d, 0x00 },
	{ 0x0f, 0x00 },
	{ 0x13, 0x1b },
	{ 0x14, 0x80 },
	{ 0x15, 0x40 },
	{ 0x17, 0x70 },
	{ 0x18, 0x01 },
	{ 0x19, 0x12 },
	{ 0x1a, 0x01 },
	{ 0x1b, 0x12 },
	{ 0x1c, 0xa0 },
	{ 0x1d, 0x00 },
	{ 0x1e, 0x0a },
	{ 0x1f, 0x08 },
	{ 0x20, 0x40 },
	{ 0x21, 0xff },
	{ 0x22, 0x4c },
	{ 0x23, 0x4e },
	{ 0x24, 0x4c },
	{ 0x25, 0x00 },
	{ 0x26, 0x00 },
	{ 0x27, 0xf4 },
	{ 0x28, 0x60 },
	{ 0x29, 0x88 },
	{ 0x2a, 0x40 },
	{ 0x2b, 0x40 },
	{ 0x2c, 0xff },
	{ 0x2d, 0x00 },
	{ 0x2e, 0xff },
	{ 0x2f, 0x00 },
	{ 0x30, 0x20 },
	{ 0x31, 0x06 },
	{ 0x32, 0x0c },
	{ 0x34, 0x0f },
	{ 0x37, 0xfe },
	{ 0x38, 0x00 },
	{ 0x39, 0x63 },
	{ 0x3a, 0x10 },
	{ 0x3b, 0x10 },
	{ 0x47, 0x00 },
	{ 0x49, 0xe5 },
	{ 0x4b, 0x00 },
	{ 0x50, 0xc0 },
	{ 0x52, 0x20 },
	{ 0x54, 0x5a },
	{ 0x55, 0x5b },
	{ 0x56, 0x40 },
	{ 0x57, 0x70 },
	{ 0x5c, 0x50 },
	{ 0x5d, 0x00 },
	{ 0x62, 0x17 },
	{ 0x63, 0x2f },
	{ 0x64, 0x6f },
	{ 0x68, 0x00 },
	{ 0x69, 0x89 },
	{ 0x6a, 0x00 },
	{ 0x6b, 0x00 },
	{ 0x6c, 0x00 },
	{ 0x6d, 0x00 },
	{ 0x6e, 0x00 },
	{ 0x70, 0x10 },
	{ 0x71, 0x00 },
	{ 0x75, 0x00 },
	{ 0x76, 0x30 },
	{ 0x77, 0x01 },
	{ 0xaf, 0x00 },
	{ 0xb0, 0xa0 },
	{ 0xb2, 0x3d },
	{ 0xb3, 0x25 },
	{ 0xb4, 0x8b },
	{ 0xb5, 0x4b },
	{ 0xb6, 0x3f },
	{ 0xb7, 0xff },
	{ 0xb8, 0xff },
	{ 0xb9, 0xfc },
	{ 0xba, 0x00 },
	{ 0xbb, 0x00 },
	{ 0xbc, 0x00 },
	{ 0xd0, 0x30 },
	{ 0xe4, 0x84 },
	{ 0xf0, 0x48 },
	{ 0xf1, 0x19 },
	{ 0xf2, 0x5a },
	{ 0xf3, 0x8e },
	{ 0xf4, 0x2d },
	{ 0xf5, 0x07 },
	{ 0xf6, 0x5a },
	{ 0xf7, 0xba },
	{ 0xf8, 0xd7 },
};

static struct regdata s921_prefreq[] = {
	{ 0x47, 0x60 },
	{ 0x68, 0x00 },
	{ 0x69, 0x89 },
	{ 0xf0, 0x48 },
	{ 0xf1, 0x19 },
};

static struct regdata s921_postfreq[] = {
	{ 0xf5, 0xae },
	{ 0xf6, 0xb7 },
	{ 0xf7, 0xba },
	{ 0xf8, 0xd7 },
	{ 0x68, 0x0a },
	{ 0x69, 0x09 },
};

static int s921_i2c_writereg(struct s921_state *state,
			     u8 i2c_addr, int reg, int data)
{
	u8 buf[] = { reg, data };
	struct i2c_msg msg = {
		.addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
	};
	int rc;

	rc = i2c_transfer(state->i2c, &msg, 1);
	if (rc != 1) {
		printk("%s: writereg rcor(rc == %i, reg == 0x%02x,"
			 " data == 0x%02x)\n", __func__, rc, reg, data);
		return rc;
	}

	return 0;
}

static int s921_i2c_writeregdata(struct s921_state *state, u8 i2c_addr,
				 struct regdata *rd, int size)
{
	int i, rc;

	for (i = 0; i < size; i++) {
		rc = s921_i2c_writereg(state, i2c_addr, rd[i].reg, rd[i].data);
		if (rc < 0)
			return rc;
	}
	return 0;
}

static int s921_i2c_readreg(struct s921_state *state, u8 i2c_addr, u8 reg)
{
	u8 val;
	int rc;
	struct i2c_msg msg[] = {
		{ .addr = i2c_addr, .flags = 0, .buf = &reg, .len = 1 },
		{ .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
	};

	rc = i2c_transfer(state->i2c, msg, 2);

	if (rc != 2) {
		rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc);
		return rc;
	}

	return val;
}

#define s921_readreg(state, reg) \
	s921_i2c_readreg(state, state->config->demod_address, reg)
#define s921_writereg(state, reg, val) \
	s921_i2c_writereg(state, state->config->demod_address, reg, val)
#define s921_writeregdata(state, regdata) \
	s921_i2c_writeregdata(state, state->config->demod_address, \
	regdata, ARRAY_SIZE(regdata))

static int s921_pll_tune(struct dvb_frontend *fe,
	struct dvb_frontend_parameters *p)
{
	struct s921_state *state = fe->demodulator_priv;
	int band, rc, i;
	unsigned long f_offset;
	u8 f_switch;
	u64 offset;

	dprintk("frequency=%i\n", p->frequency);

	for (band = 0; band < ARRAY_SIZE(s921_bandselect); band++)
		if (p->frequency < s921_bandselect[band].freq_low)
			break;
	band--;

	if (band < 0) {
		rc("%s: frequency out of range\n", __func__);
		return -EINVAL;
	}

	f_switch = s921_bandselect[band].band_reg;

	offset = ((u64)p->frequency) * 258;
	do_div(offset, 6000000);
	f_offset = ((unsigned long)offset) + 2321;

	rc = s921_writeregdata(state, s921_prefreq);
	if (rc < 0)
		return rc;

	rc = s921_writereg(state, 0xf2, (f_offset >> 8) & 0xff);
	if (rc < 0)
		return rc;

	rc = s921_writereg(state, 0xf3, f_offset & 0xff);
	if (rc < 0)
		return rc;

	rc = s921_writereg(state, 0xf4, f_switch);
	if (rc < 0)
		return rc;

	rc = s921_writeregdata(state, s921_postfreq);
	if (rc < 0)
		return rc;

	for (i = 0 ; i < 6; i++) {
		rc = s921_readreg(state, 0x80);
		dprintk("status 0x80: %02x\n", rc);
	}
	rc = s921_writereg(state, 0x01, 0x40);
	if (rc < 0)
		return rc;

	rc = s921_readreg(state, 0x01);
	dprintk("status 0x01: %02x\n", rc);

	rc = s921_readreg(state, 0x80);
	dprintk("status 0x80: %02x\n", rc);

	rc = s921_readreg(state, 0x80);
	dprintk("status 0x80: %02x\n", rc);

	rc = s921_readreg(state, 0x32);
	dprintk("status 0x32: %02x\n", rc);

	dprintk("pll tune band=%d, pll=%d\n", f_switch, (int)f_offset);

	return 0;
}

static int s921_initfe(struct dvb_frontend *fe)
{
	struct s921_state *state = fe->demodulator_priv;
	int rc;

	dprintk("\n");

	rc = s921_writeregdata(state, s921_init);
	if (rc < 0)
		return rc;

	return 0;
}

static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
	struct s921_state *state = fe->demodulator_priv;
	int regstatus, rc;

	*status = 0;

	rc = s921_readreg(state, 0x81);
	if (rc < 0)
		return rc;

	regstatus = rc << 8;

	rc = s921_readreg(state, 0x82);
	if (rc < 0)
		return rc;

	regstatus |= rc;

	dprintk("status = %04x\n", regstatus);

	/* Full Sync - We don't know what each bit means on regs 0x81/0x82 */
	if ((regstatus & 0xff) == 0x40) {
		*status = FE_HAS_SIGNAL  |
			  FE_HAS_CARRIER |
			  FE_HAS_VITERBI |
			  FE_HAS_SYNC    |
			  FE_HAS_LOCK;
	} else if (regstatus & 0x40) {
		/* This is close to Full Sync, but not enough to get useful info */
		*status = FE_HAS_SIGNAL  |
			  FE_HAS_CARRIER |
			  FE_HAS_VITERBI |
			  FE_HAS_SYNC;
	}

	return 0;
}

static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
	fe_status_t	status;
	struct s921_state *state = fe->demodulator_priv;
	int rc;

	/* FIXME: Use the proper register for it... 0x80? */
	rc = s921_read_status(fe, &status);
	if (rc < 0)
		return rc;

	*strength = (status & FE_HAS_LOCK) ? 0xffff : 0;

	dprintk("strength = 0x%04x\n", *strength);

	rc = s921_readreg(state, 0x01);
	dprintk("status 0x01: %02x\n", rc);

	rc = s921_readreg(state, 0x80);
	dprintk("status 0x80: %02x\n", rc);

	rc = s921_readreg(state, 0x32);
	dprintk("status 0x32: %02x\n", rc);

	return 0;
}

static int s921_set_frontend(struct dvb_frontend *fe,
	struct dvb_frontend_parameters *p)
{
	struct s921_state *state = fe->demodulator_priv;
	int rc;

	dprintk("\n");

	/* FIXME: We don't know how to use non-auto mode */

	rc = s921_pll_tune(fe, p);
	if (rc < 0)
		return rc;

	state->currentfreq = p->frequency;

	return 0;
}

static int s921_get_frontend(struct dvb_frontend *fe,
	struct dvb_frontend_parameters *p)
{
	struct s921_state *state = fe->demodulator_priv;

	/* FIXME: Probably it is possible to get it from regs f1 and f2 */
	p->frequency = state->currentfreq;

	return 0;
}

static int s921_tune(struct dvb_frontend *fe,
			struct dvb_frontend_parameters *params,
			unsigned int mode_flags,
			unsigned int *delay,
			fe_status_t *status)
{
	int rc = 0;

	dprintk("\n");

	if (params != NULL)
		rc = s921_set_frontend(fe, params);

	if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
		s921_read_status(fe, status);

	return rc;
}

static int s921_get_algo(struct dvb_frontend *fe)
{
	return 1; /* FE_ALGO_HW */
}

static void s921_release(struct dvb_frontend *fe)
{
	struct s921_state *state = fe->demodulator_priv;

	dprintk("\n");
	kfree(state);
}

static struct dvb_frontend_ops s921_ops;

struct dvb_frontend *s921_attach(const struct s921_config *config,
				    struct i2c_adapter *i2c)
{
	/* allocate memory for the internal state */
	struct s921_state *state =
		kzalloc(sizeof(struct s921_state), GFP_KERNEL);

	dprintk("\n");
	if (state == NULL) {
		rc("Unable to kzalloc\n");
		goto rcor;
	}

	/* setup the state */
	state->config = config;
	state->i2c = i2c;

	/* create dvb_frontend */
	memcpy(&state->frontend.ops, &s921_ops,
		sizeof(struct dvb_frontend_ops));
	state->frontend.demodulator_priv = state;

	return &state->frontend;

rcor:
	kfree(state);

	return NULL;
}
EXPORT_SYMBOL(s921_attach);

static struct dvb_frontend_ops s921_ops = {
	/* Use dib8000 values per default */
	.info = {
		.name = "Sharp S921",
		.type = FE_OFDM,
		.frequency_min = 470000000,
		/*
		 * Max should be 770MHz instead, according with Sharp docs,
		 * but Leadership doc says it works up to 806 MHz. This is
		 * required to get channel 69, used in Brazil
		 */
		.frequency_max = 806000000,
		.frequency_tolerance = 0,
		 .caps = FE_CAN_INVERSION_AUTO |
			 FE_CAN_FEC_1_2  | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
			 FE_CAN_FEC_5_6  | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
			 FE_CAN_QPSK     | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
			 FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
			 FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER |
			 FE_CAN_HIERARCHY_AUTO,
	},

	.release = s921_release,

	.init = s921_initfe,
	.set_frontend = s921_set_frontend,
	.get_frontend = s921_get_frontend,
	.read_status = s921_read_status,
	.read_signal_strength = s921_read_signal_strength,
	.tune = s921_tune,
	.get_frontend_algo = s921_get_algo,
};

MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>");
MODULE_LICENSE("GPL");
+47 −0
Original line number Original line Diff line number Diff line
/*
 *   Sharp s921 driver
 *
 *   Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
 *   Copyright (C) 2009 Douglas Landgraf <dougsland@redhat.com>
 *
 *   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 in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   General Public License for more details.
 */

#ifndef S921_H
#define S921_H

#include <linux/dvb/frontend.h>

struct s921_config {
	/* the demodulator's i2c address */
	u8 demod_address;
};

#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) \
	&& defined(MODULE))
extern struct dvb_frontend *s921_attach(const struct s921_config *config,
					   struct i2c_adapter *i2c);
extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *);
#else
static inline struct dvb_frontend *s921_attach(
	const struct s921_config *config, struct i2c_adapter *i2c)
{
	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
	return NULL;
}
static struct i2c_adapter *
	s921_get_tuner_i2c_adapter(struct dvb_frontend *fe)
{
	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
	return NULL;
}
#endif

#endif /* S921_H */
+0 −216
Original line number Original line Diff line number Diff line
/*
 * Driver for Sharp s921 driver
 *
 * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
 *
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include "s921_core.h"

static int s921_isdb_init(struct s921_isdb_t *dev);
static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params);
static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params);
static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data);

static u8 init_table[]={ 0x01, 0x40, 0x02, 0x00, 0x03, 0x40, 0x04, 0x01,
			 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
			 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x5a, 0x0c, 0x00,
			 0x0d, 0x00, 0x0f, 0x00, 0x13, 0x1b, 0x14, 0x80,
			 0x15, 0x40, 0x17, 0x70, 0x18, 0x01, 0x19, 0x12,
			 0x1a, 0x01, 0x1b, 0x12, 0x1c, 0xa0, 0x1d, 0x00,
			 0x1e, 0x0a, 0x1f, 0x08, 0x20, 0x40, 0x21, 0xff,
			 0x22, 0x4c, 0x23, 0x4e, 0x24, 0x4c, 0x25, 0x00,
			 0x26, 0x00, 0x27, 0xf4, 0x28, 0x60, 0x29, 0x88,
			 0x2a, 0x40, 0x2b, 0x40, 0x2c, 0xff, 0x2d, 0x00,
			 0x2e, 0xff, 0x2f, 0x00, 0x30, 0x20, 0x31, 0x06,
			 0x32, 0x0c, 0x34, 0x0f, 0x37, 0xfe, 0x38, 0x00,
			 0x39, 0x63, 0x3a, 0x10, 0x3b, 0x10, 0x47, 0x00,
			 0x49, 0xe5, 0x4b, 0x00, 0x50, 0xc0, 0x52, 0x20,
			 0x54, 0x5a, 0x55, 0x5b, 0x56, 0x40, 0x57, 0x70,
			 0x5c, 0x50, 0x5d, 0x00, 0x62, 0x17, 0x63, 0x2f,
			 0x64, 0x6f, 0x68, 0x00, 0x69, 0x89, 0x6a, 0x00,
			 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00,
			 0x70, 0x00, 0x71, 0x00, 0x75, 0x00, 0x76, 0x30,
			 0x77, 0x01, 0xaf, 0x00, 0xb0, 0xa0, 0xb2, 0x3d,
			 0xb3, 0x25, 0xb4, 0x8b, 0xb5, 0x4b, 0xb6, 0x3f,
			 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xfc, 0xba, 0x00,
			 0xbb, 0x00, 0xbc, 0x00, 0xd0, 0x30, 0xe4, 0x84,
			 0xf0, 0x48, 0xf1, 0x19, 0xf2, 0x5a, 0xf3, 0x8e,
			 0xf4, 0x2d, 0xf5, 0x07, 0xf6, 0x5a, 0xf7, 0xba,
			 0xf8, 0xd7 };

static u8 c_table[]={ 0x58, 0x8a, 0x7b, 0x59, 0x8c, 0x7b, 0x5a, 0x8e, 0x5b,
		      0x5b, 0x90, 0x5b, 0x5c, 0x92, 0x5b, 0x5d, 0x94, 0x5b,
		      0x5e, 0x96, 0x5b, 0x5f, 0x98, 0x3b, 0x60, 0x9a, 0x3b,
		      0x61, 0x9c, 0x3b, 0x62, 0x9e, 0x3b, 0x63, 0xa0, 0x3b,
		      0x64, 0xa2, 0x1b, 0x65, 0xa4, 0x1b, 0x66, 0xa6, 0x1b,
		      0x67, 0xa8, 0x1b, 0x68, 0xaa, 0x1b, 0x69, 0xac, 0x1b,
		      0x6a, 0xae, 0x1b, 0x6b, 0xb0, 0x1b, 0x6c, 0xb2, 0x1b,
		      0x6d, 0xb4, 0xfb, 0x6e, 0xb6, 0xfb, 0x6f, 0xb8, 0xfb,
		      0x70, 0xba, 0xfb, 0x71, 0xbc, 0xdb, 0x72, 0xbe, 0xdb,
		      0x73, 0xc0, 0xdb, 0x74, 0xc2, 0xdb, 0x75, 0xc4, 0xdb,
		      0x76, 0xc6, 0xdb, 0x77, 0xc8, 0xbb, 0x78, 0xca, 0xbb,
		      0x79, 0xcc, 0xbb, 0x7a, 0xce, 0xbb, 0x7b, 0xd0, 0xbb,
		      0x7c, 0xd2, 0xbb, 0x7d, 0xd4, 0xbb, 0x7e, 0xd6, 0xbb,
		      0x7f, 0xd8, 0xbb, 0x80, 0xda, 0x9b, 0x81, 0xdc, 0x9b,
		      0x82, 0xde, 0x9b, 0x83, 0xe0, 0x9b, 0x84, 0xe2, 0x9b,
		      0x85, 0xe4, 0x9b, 0x86, 0xe6, 0x9b, 0x87, 0xe8, 0x9b,
		      0x88, 0xea, 0x9b, 0x89, 0xec, 0x9b };

int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data) {
	switch(cmd) {
	case ISDB_T_CMD_INIT:
		s921_isdb_init(dev);
		break;
	case ISDB_T_CMD_SET_PARAM:
		s921_isdb_set_parameters(dev, data);
		break;
	case ISDB_T_CMD_TUNE:
		s921_isdb_tune(dev, data);
		break;
	case ISDB_T_CMD_GET_STATUS:
		s921_isdb_get_status(dev, data);
		break;
	default:
		printk("unhandled command\n");
		return -EINVAL;
	}
	return 0;
}

static int s921_isdb_init(struct s921_isdb_t *dev) {
	unsigned int i;
	unsigned int ret;
	printk("isdb_init\n");
	for (i = 0; i < sizeof(init_table); i+=2) {
		ret = dev->i2c_write(dev->priv_dev, init_table[i], init_table[i+1]);
		if (ret != 0) {
			printk("i2c write failed\n");
			return ret;
		}
	}
	return 0;
}

static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params) {

	int ret;
	/* auto is sufficient for now, lateron this should be reflected in an extra interface */



	ret = dev->i2c_write(dev->priv_dev, 0xb0, 0xa0); //mod_b2);
	ret = dev->i2c_write(dev->priv_dev, 0xb2, 0x3d); //mod_b2);

	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xb3, 0x25); //mod_b3);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xb4, 0x8b); //mod_b4);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xb5, 0x4b); //mod_b5);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xb6, 0x3f); //mod_b6);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xb7, 0x3f); //mod_b7);
	if (ret < 0)
		return -EINVAL;

	return E_OK;
}

static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params) {

	int ret;
	int index;

	index = (params->frequency - 473143000)/6000000;

	if (index > 48) {
		return -EINVAL;
	}

	dev->i2c_write(dev->priv_dev, 0x47, 0x60);

	ret = dev->i2c_write(dev->priv_dev, 0x68, 0x00);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0x69, 0x89);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf0, 0x48);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf1, 0x19);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf2, c_table[index*3]);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf3, c_table[index*3+1]);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf4, c_table[index*3+2]);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf5, 0xae);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf6, 0xb7);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf7, 0xba);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0xf8, 0xd7);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0x68, 0x0a);
	if (ret < 0)
		return -EINVAL;

	ret = dev->i2c_write(dev->priv_dev, 0x69, 0x09);
	if (ret < 0)
		return -EINVAL;

	dev->i2c_write(dev->priv_dev, 0x01, 0x40);
	return 0;
}

static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data) {
	unsigned int *ret = (unsigned int*)data;
	u8 ifagc_dt;
	u8 rfagc_dt;

	mdelay(10);
	ifagc_dt = dev->i2c_read(dev->priv_dev, 0x81);
	rfagc_dt = dev->i2c_read(dev->priv_dev, 0x82);
	if (rfagc_dt == 0x40) {
		*ret = 1;
	}
	return 0;
}
Loading