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

[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
...@@ -497,7 +497,7 @@ comment "ISDB-T (terrestrial) frontends" ...@@ -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
......
...@@ -5,7 +5,6 @@ ...@@ -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
......
/*
* 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");
/*
* 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 */
/*
* 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;
}
#ifndef _S921_CORE_H
#define _S921_CORE_H
//#define u8 unsigned int
//#define u32 unsigned int
//#define EINVAL -1
#define E_OK 0
struct s921_isdb_t {
void *priv_dev;
int (*i2c_write)(void *dev, u8 reg, u8 val);
int (*i2c_read)(void *dev, u8 reg);
};
#define ISDB_T_CMD_INIT 0
#define ISDB_T_CMD_SET_PARAM 1
#define ISDB_T_CMD_TUNE 2
#define ISDB_T_CMD_GET_STATUS 3
struct s921_isdb_t_tune_params {
u32 frequency;
};
struct s921_isdb_t_status {
};
struct s921_isdb_t_transmission_mode_params {
u8 mode;
u8 layer_a_mode;
#define ISDB_T_LA_MODE_1 0
#define ISDB_T_LA_MODE_2 1
#define ISDB_T_LA_MODE_3 2
u8 layer_a_carrier_modulation;
#define ISDB_T_LA_CM_DQPSK 0
#define ISDB_T_LA_CM_QPSK 1
#define ISDB_T_LA_CM_16QAM 2
#define ISDB_T_LA_CM_64QAM 3
#define ISDB_T_LA_CM_NOLAYER 4
u8 layer_a_code_rate;
#define ISDB_T_LA_CR_1_2 0
#define ISDB_T_LA_CR_2_3 1
#define ISDB_T_LA_CR_3_4 2
#define ISDB_T_LA_CR_5_6 4
#define ISDB_T_LA_CR_7_8 8
#define ISDB_T_LA_CR_NOLAYER 16
u8 layer_a_time_interleave;
#define ISDB_T_LA_TI_0 0
#define ISDB_T_LA_TI_1 1
#define ISDB_T_LA_TI_2 2
#define ISDB_T_LA_TI_4 4
#define ISDB_T_LA_TI_8 8
#define ISDB_T_LA_TI_16 16
#define ISDB_T_LA_TI_32 32
u8 layer_a_nseg;
u8 layer_b_mode;
#define ISDB_T_LB_MODE_1 0
#define ISDB_T_LB_MODE_2 1
#define ISDB_T_LB_MODE_3 2
u8 layer_b_carrier_modulation;
#define ISDB_T_LB_CM_DQPSK 0
#define ISDB_T_LB_CM_QPSK 1
#define ISDB_T_LB_CM_16QAM 2
#define ISDB_T_LB_CM_64QAM 3
#define ISDB_T_LB_CM_NOLAYER 4
u8 layer_b_code_rate;
#define ISDB_T_LB_CR_1_2 0
#define ISDB_T_LB_CR_2_3 1
#define ISDB_T_LB_CR_3_4 2
#define ISDB_T_LB_CR_5_6 4
#define ISDB_T_LB_CR_7_8 8
#define ISDB_T_LB_CR_NOLAYER 16
u8 layer_b_time_interleave;
#define ISDB_T_LB_TI_0 0
#define ISDB_T_LB_TI_1 1
#define ISDB_T_LB_TI_2 2
#define ISDB_T_LB_TI_4 4
#define ISDB_T_LB_TI_8 8
#define ISDB_T_LB_TI_16 16
#define ISDB_T_LB_TI_32 32
u8 layer_b_nseg;
u8 layer_c_mode;
#define ISDB_T_LC_MODE_1 0
#define ISDB_T_LC_MODE_2 1
#define ISDB_T_LC_MODE_3 2
u8 layer_c_carrier_modulation;
#define ISDB_T_LC_CM_DQPSK 0
#define ISDB_T_LC_CM_QPSK 1
#define ISDB_T_LC_CM_16QAM 2
#define ISDB_T_LC_CM_64QAM 3
#define ISDB_T_LC_CM_NOLAYER 4
u8 layer_c_code_rate;
#define ISDB_T_LC_CR_1_2 0
#define ISDB_T_LC_CR_2_3 1
#define ISDB_T_LC_CR_3_4 2
#define ISDB_T_LC_CR_5_6 4
#define ISDB_T_LC_CR_7_8 8
#define ISDB_T_LC_CR_NOLAYER 16
u8 layer_c_time_interleave;
#define ISDB_T_LC_TI_0 0
#define ISDB_T_LC_TI_1 1
#define ISDB_T_LC_TI_2 2
#define ISDB_T_LC_TI_4 4
#define ISDB_T_LC_TI_8 8
#define ISDB_T_LC_TI_16 16
#define ISDB_T_LC_TI_32 32
u8 layer_c_nseg;
};
int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
#endif
/*
* Driver for Sharp s921 driver
*
* Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
*
* All rights reserved.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "dvb_frontend.h"
#include "s921_module.h"
#include "s921_core.h"
static unsigned int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"s921 debugging (default off)");
#define dprintk(fmt, args...) if (debug) do {\
printk("s921 debug: " fmt, ##args); } while (0)
struct s921_state
{
struct dvb_frontend frontend;
fe_modulation_t current_modulation;
__u32 snr;
__u32 current_frequency;
__u8 addr;
struct s921_isdb_t dev;
struct i2c_adapter *i2c;
};
static int s921_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
struct s921_isdb_t_transmission_mode_params params;
struct s921_isdb_t_tune_params tune_params;
tune_params.frequency = param->frequency;
s921_isdb_cmd(&state->dev, ISDB_T_CMD_SET_PARAM, &params);
s921_isdb_cmd(&state->dev, ISDB_T_CMD_TUNE, &tune_params);
mdelay(100);
return 0;
}
static int s921_init(struct dvb_frontend *fe) {
printk("s921 init\n");
return 0;
}
static int s921_sleep(struct dvb_frontend *fe) {
printk("s921 sleep\n");
return 0;
}
static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
unsigned int ret;
mdelay(5);
s921_isdb_cmd(&state->dev, ISDB_T_CMD_GET_STATUS, &ret);
*status = 0;
printk("status: %02x\n", ret);
if (ret == 1) {
*status |= FE_HAS_CARRIER;
*status |= FE_HAS_VITERBI;
*status |= FE_HAS_LOCK;
*status |= FE_HAS_SYNC;
*status |= FE_HAS_SIGNAL;
}
return 0;
}
static int s921_read_ber(struct dvb_frontend *fe, __u32 *ber)
{
dprintk("read ber\n");
return 0;
}
static int s921_read_snr(struct dvb_frontend *fe, __u16 *snr)
{
dprintk("read snr\n");
return 0;
}
static int s921_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
{
dprintk("read ucblocks\n");
return 0;
}
static void s921_release(struct dvb_frontend *fe)
{
struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops demod_s921={
.info = {
.name = "SHARP S921",
.type = FE_OFDM,
.frequency_min = 473143000,
.frequency_max = 767143000,
.frequency_stepsize = 6000000,
.frequency_tolerance = 0,
.caps = 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_HIERARCHY_AUTO | FE_CAN_RECOVER |
FE_CAN_MUTE_TS
},
.init = s921_init,
.sleep = s921_sleep,
.set_frontend = s921_set_parameters,
.read_snr = s921_read_snr,
.read_ber = s921_read_ber,
.read_status = s921_read_status,
.read_ucblocks = s921_read_ucblocks,
.release = s921_release,
};
static int s921_write(void *dev, u8 reg, u8 val) {
struct s921_state *state = dev;
char buf[2]={reg,val};
int err;
struct i2c_msg i2cmsgs = {
.addr = state->addr,
.flags = 0,
.len = 2,
.buf = buf
};
if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
printk("%s i2c_transfer error %d\n", __func__, err);
if (err < 0)
return err;
else
return -EREMOTEIO;
}
return 0;
}
static int s921_read(void *dev, u8 reg) {
struct s921_state *state = dev;
u8 b1;
int ret;
struct i2c_msg msg[2] = { { .addr = state->addr,
.flags = 0,
.buf = &reg, .len = 1 },
{ .addr = state->addr,
.flags = I2C_M_RD,
.buf = &b1, .len = 1 } };
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2)
return ret;
return b1;
}
struct dvb_frontend* s921_attach(const struct s921_config *config,
struct i2c_adapter *i2c)
{
struct s921_state *state;
state = kzalloc(sizeof(struct s921_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->addr = config->i2c_address;
state->i2c = i2c;
state->dev.i2c_write = &s921_write;
state->dev.i2c_read = &s921_read;
state->dev.priv_dev = state;
s921_isdb_cmd(&state->dev, ISDB_T_CMD_INIT, NULL);
memcpy(&state->frontend.ops, &demod_s921, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
}
EXPORT_SYMBOL_GPL(s921_attach);
MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
MODULE_DESCRIPTION("Sharp S921 ISDB-T 1Seg");
MODULE_LICENSE("GPL");
/*
* Driver for DVB-T s921 demodulator
*
* Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
*/
#ifndef S921_MODULE_H
#define S921_MODULE_H
#include <linux/dvb/frontend.h>
#include "s921_core.h"
int s921_isdb_init(struct s921_isdb_t *dev);
int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
struct s921_config
{
/* demodulator's I2C address */
u8 i2c_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);
#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;
}
#endif /* CONFIG_DVB_S921 */
#endif /* S921_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment