V4L/DVB (12999): Add a driver for Earthsoft PT1

Add a driver for Earthsoft PT1

Eearthsoft PT1 is a PCI card for Japanese broadcasting with two ISDB-S
and ISDB-T demodulators.

This card has neither MPEG decoder nor conditional access module
onboard. It transmits only compressed and possibly encrypted MPEG data
over the PCI bus, so you need an external software decoder and a
decrypter to watch TV on your computer.

This driver is originally developed by Tomoaki Ishikawa
<tomy@users.sourceforge.jp> by reverse engineering.

[mchehab@redhat.com: renamed isdb_ts to isdbs_ts to use the current standard]
Signed-off-by: HIRANO Takahito <hiranotaka@zng.info>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Mauro Carvalho Chehab 2009-08-23 00:51:22 -03:00
parent 98293ef3e5
commit 3d17fb1be9
9 changed files with 2284 additions and 1 deletions

View file

@ -68,6 +68,10 @@ comment "Supported FireWire (IEEE 1394) Adapters"
depends on DVB_CORE && IEEE1394
source "drivers/media/dvb/firewire/Kconfig"
comment "Supported Earthsoft PT1 Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/pt1/Kconfig"
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"

View file

@ -2,6 +2,6 @@
# Makefile for the kernel multimedia device drivers.
#
obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/
obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ pt1/
obj-$(CONFIG_DVB_FIREDTV) += firewire/

View file

@ -0,0 +1,12 @@
config DVB_PT1
tristate "PT1 cards"
depends on DVB_CORE && PCI && I2C
help
Support for Earthsoft PT1 PCI cards.
Since these cards have no MPEG decoder onboard, they transmit
only compressed MPEG data over the PCI bus, so you need
an external software decoder to watch TV on your computer.
Say Y or M if you own such a device and want to use it.

View file

@ -0,0 +1,5 @@
earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
obj-$(CONFIG_DVB_PT1) += earth-pt1.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends

1056
drivers/media/dvb/pt1/pt1.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,658 @@
/*
* ISDB-S driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "va1j5jf8007s.h"
enum va1j5jf8007s_tune_state {
VA1J5JF8007S_IDLE,
VA1J5JF8007S_SET_FREQUENCY_1,
VA1J5JF8007S_SET_FREQUENCY_2,
VA1J5JF8007S_SET_FREQUENCY_3,
VA1J5JF8007S_CHECK_FREQUENCY,
VA1J5JF8007S_SET_MODULATION,
VA1J5JF8007S_CHECK_MODULATION,
VA1J5JF8007S_SET_TS_ID,
VA1J5JF8007S_CHECK_TS_ID,
VA1J5JF8007S_TRACK,
};
struct va1j5jf8007s_state {
const struct va1j5jf8007s_config *config;
struct i2c_adapter *adap;
struct dvb_frontend fe;
enum va1j5jf8007s_tune_state tune_state;
};
static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
static int
va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
switch (state->tune_state) {
case VA1J5JF8007S_IDLE:
case VA1J5JF8007S_SET_FREQUENCY_1:
case VA1J5JF8007S_SET_FREQUENCY_2:
case VA1J5JF8007S_SET_FREQUENCY_3:
case VA1J5JF8007S_CHECK_FREQUENCY:
*status = 0;
return 0;
case VA1J5JF8007S_SET_MODULATION:
case VA1J5JF8007S_CHECK_MODULATION:
*status |= FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_SET_TS_ID:
case VA1J5JF8007S_CHECK_TS_ID:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
case VA1J5JF8007S_TRACK:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
struct va1j5jf8007s_cb_map {
u32 frequency;
u8 cb;
};
static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
{ 986000, 0xb2 },
{ 1072000, 0xd2 },
{ 1154000, 0xe2 },
{ 1291000, 0x20 },
{ 1447000, 0x40 },
{ 1615000, 0x60 },
{ 1791000, 0x80 },
{ 1972000, 0xa0 },
};
static u8 va1j5jf8007s_lookup_cb(u32 frequency)
{
int i;
const struct va1j5jf8007s_cb_map *map;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
map = &va1j5jf8007s_cb_maps[i];
if (frequency < map->frequency)
return map->cb;
}
return 0xc0;
}
static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
{
u32 frequency;
u16 word;
u8 buf[6];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
word = (frequency + 500) / 1000;
if (frequency < 1072000)
word = (word << 1 & ~0x1f) | (word & 0x0f);
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0x40 | word >> 8;
buf[3] = word;
buf[4] = 0xe0;
buf[5] = va1j5jf8007s_lookup_cb(frequency);
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
{
u8 buf[3];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xe4;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
{
u32 frequency;
u8 buf[4];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xf4;
buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[2], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xfe;
write_buf[1] = 0xc1;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = read_buf[0] & 0x40;
return 0;
}
static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x03;
buf[1] = 0x01;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xc3;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = !(read_buf[0] & 0x10);
return 0;
}
static int
va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
{
u32 ts_id;
u8 buf[3];
struct i2c_msg msg;
ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
if (!ts_id)
return 0;
buf[0] = 0x8f;
buf[1] = ts_id >> 8;
buf[2] = ts_id;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[1], read_buf[2];
struct i2c_msg msgs[2];
u32 ts_id;
ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
if (!ts_id) {
*lock = 1;
return 0;
}
addr = state->config->demod_address;
write_buf[0] = 0xe6;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
return 0;
}
static int
va1j5jf8007s_tune(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params,
unsigned int mode_flags, unsigned int *delay,
fe_status_t *status)
{
struct va1j5jf8007s_state *state;
int ret;
int lock;
state = fe->demodulator_priv;
if (params != NULL)
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
switch (state->tune_state) {
case VA1J5JF8007S_IDLE:
*delay = 3 * HZ;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_1:
ret = va1j5jf8007s_set_frequency_1(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_2:
ret = va1j5jf8007s_set_frequency_2(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
*delay = (HZ + 99) / 100;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_3:
ret = va1j5jf8007s_set_frequency_3(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007S_CHECK_FREQUENCY:
ret = va1j5jf8007s_check_frequency(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 999) / 1000;
*status = 0;
return 0;
}
state->tune_state = VA1J5JF8007S_SET_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_SET_MODULATION:
ret = va1j5jf8007s_set_modulation(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_CHECK_MODULATION:
ret = va1j5jf8007s_check_modulation(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 49) / 50;
*status = FE_HAS_SIGNAL;
return 0;
}
state->tune_state = VA1J5JF8007S_SET_TS_ID;
*delay = 0;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
case VA1J5JF8007S_SET_TS_ID:
ret = va1j5jf8007s_set_ts_id(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
return 0;
case VA1J5JF8007S_CHECK_TS_ID:
ret = va1j5jf8007s_check_ts_id(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 99) / 100;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
}
state->tune_state = VA1J5JF8007S_TRACK;
/* fall through */
case VA1J5JF8007S_TRACK:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
{
u8 buf[4];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xf0;
buf[3] = 0x04;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x17;
buf[1] = sleep ? 0x01 : 0x00;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007s_init_frequency(state);
if (ret < 0)
return ret;
return va1j5jf8007s_set_sleep(state, 1);
}
static int va1j5jf8007s_init(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
state->tune_state = VA1J5JF8007S_IDLE;
return va1j5jf8007s_set_sleep(state, 0);
}
static void va1j5jf8007s_release(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops va1j5jf8007s_ops = {
.info = {
.name = "VA1J5JF8007 ISDB-S",
.type = FE_QPSK,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_stepsize = 1000,
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
.get_frontend_algo = va1j5jf8007s_get_frontend_algo,
.read_status = va1j5jf8007s_read_status,
.tune = va1j5jf8007s_tune,
.sleep = va1j5jf8007s_sleep,
.init = va1j5jf8007s_init,
.release = va1j5jf8007s_release,
};
static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0x07;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
if (read_buf[0] != 0x41)
return -EIO;
return 0;
}
static const u8 va1j5jf8007s_prepare_bufs[][2] = {
{0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
{0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
{0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
{0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
};
static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
{
u8 addr;
u8 buf[2];
struct i2c_msg msg;
int i;
addr = state->config->demod_address;
msg.addr = addr;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_prepare_bufs); i++) {
memcpy(buf, va1j5jf8007s_prepare_bufs[i], sizeof(buf));
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
}
return 0;
}
/* must be called after va1j5jf8007t_attach */
int va1j5jf8007s_prepare(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007s_prepare_1(state);
if (ret < 0)
return ret;
ret = va1j5jf8007s_prepare_2(state);
if (ret < 0)
return ret;
return va1j5jf8007s_init_frequency(state);
}
struct dvb_frontend *
va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
struct i2c_adapter *adap)
{
struct va1j5jf8007s_state *state;
struct dvb_frontend *fe;
u8 buf[2];
struct i2c_msg msg;
state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
if (!state)
return NULL;
state->config = config;
state->adap = adap;
fe = &state->fe;
memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
fe->demodulator_priv = state;
buf[0] = 0x01;
buf[1] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1) {
kfree(state);
return NULL;
}
return fe;
}

View file

@ -0,0 +1,40 @@
/*
* ISDB-S driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 VA1J5JF8007S_H
#define VA1J5JF8007S_H
struct va1j5jf8007s_config {
u8 demod_address;
};
struct i2c_adapter;
struct dvb_frontend *
va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
struct i2c_adapter *adap);
/* must be called after va1j5jf8007t_attach */
int va1j5jf8007s_prepare(struct dvb_frontend *fe);
#endif

View file

@ -0,0 +1,468 @@
/*
* ISDB-T driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "dvb_math.h"
#include "va1j5jf8007t.h"
enum va1j5jf8007t_tune_state {
VA1J5JF8007T_IDLE,
VA1J5JF8007T_SET_FREQUENCY,
VA1J5JF8007T_CHECK_FREQUENCY,
VA1J5JF8007T_SET_MODULATION,
VA1J5JF8007T_CHECK_MODULATION,
VA1J5JF8007T_TRACK,
VA1J5JF8007T_ABORT,
};
struct va1j5jf8007t_state {
const struct va1j5jf8007t_config *config;
struct i2c_adapter *adap;
struct dvb_frontend fe;
enum va1j5jf8007t_tune_state tune_state;
};
static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
static int
va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
switch (state->tune_state) {
case VA1J5JF8007T_IDLE:
case VA1J5JF8007T_SET_FREQUENCY:
case VA1J5JF8007T_CHECK_FREQUENCY:
*status = 0;
return 0;
case VA1J5JF8007T_SET_MODULATION:
case VA1J5JF8007T_CHECK_MODULATION:
case VA1J5JF8007T_ABORT:
*status |= FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_TRACK:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
struct va1j5jf8007t_cb_map {
u32 frequency;
u8 cb;
};
static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = {
{ 90000000, 0x80 },
{ 140000000, 0x81 },
{ 170000000, 0xa1 },
{ 220000000, 0x62 },
{ 330000000, 0xa2 },
{ 402000000, 0xe2 },
{ 450000000, 0x64 },
{ 550000000, 0x84 },
{ 600000000, 0xa4 },
{ 700000000, 0xc4 },
};
static u8 va1j5jf8007t_lookup_cb(u32 frequency)
{
int i;
const struct va1j5jf8007t_cb_map *map;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) {
map = &va1j5jf8007t_cb_maps[i];
if (frequency < map->frequency)
return map->cb;
}
return 0xe4;
}
static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state)
{
u32 frequency;
u16 word;
u8 buf[6];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
word = (frequency + 71428) / 142857 + 399;
buf[0] = 0xfe;
buf[1] = 0xc2;
buf[2] = word >> 8;
buf[3] = word;
buf[4] = 0x80;
buf[5] = va1j5jf8007t_lookup_cb(frequency);
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock)
{
u8 addr;
u8 write_buf[2], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xfe;
write_buf[1] = 0xc3;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = read_buf[0] & 0x40;
return 0;
}
static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x01;
buf[1] = 0x40;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state,
int *lock, int *retry)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0x80;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = !(read_buf[0] & 0x10);
*retry = read_buf[0] & 0x80;
return 0;
}
static int
va1j5jf8007t_tune(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params,
unsigned int mode_flags, unsigned int *delay,
fe_status_t *status)
{
struct va1j5jf8007t_state *state;
int ret;
int lock, retry;
state = fe->demodulator_priv;
if (params != NULL)
state->tune_state = VA1J5JF8007T_SET_FREQUENCY;
switch (state->tune_state) {
case VA1J5JF8007T_IDLE:
*delay = 3 * HZ;
*status = 0;
return 0;
case VA1J5JF8007T_SET_FREQUENCY:
ret = va1j5jf8007t_set_frequency(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007T_CHECK_FREQUENCY:
ret = va1j5jf8007t_check_frequency(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 999) / 1000;
*status = 0;
return 0;
}
state->tune_state = VA1J5JF8007T_SET_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_SET_MODULATION:
ret = va1j5jf8007t_set_modulation(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007T_CHECK_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_CHECK_MODULATION:
ret = va1j5jf8007t_check_modulation(state, &lock, &retry);
if (ret < 0)
return ret;
if (!lock) {
if (!retry) {
state->tune_state = VA1J5JF8007T_ABORT;
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL;
return 0;
}
*delay = (HZ + 999) / 1000;
*status = FE_HAS_SIGNAL;
return 0;
}
state->tune_state = VA1J5JF8007T_TRACK;
/* fall through */
case VA1J5JF8007T_TRACK:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
case VA1J5JF8007T_ABORT:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL;
return 0;
}
BUG();
}
static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state)
{
u8 buf[7];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc2;
buf[2] = 0x01;
buf[3] = 0x8f;
buf[4] = 0xc1;
buf[5] = 0x80;
buf[6] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x03;
buf[1] = sleep ? 0x90 : 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_sleep(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007t_init_frequency(state);
if (ret < 0)
return ret;
return va1j5jf8007t_set_sleep(state, 1);
}
static int va1j5jf8007t_init(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
state->tune_state = VA1J5JF8007T_IDLE;
return va1j5jf8007t_set_sleep(state, 0);
}
static void va1j5jf8007t_release(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops va1j5jf8007t_ops = {
.info = {
.name = "VA1J5JF8007 ISDB-T",
.type = FE_OFDM,
.frequency_min = 90000000,
.frequency_max = 770000000,
.frequency_stepsize = 142857,
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
.get_frontend_algo = va1j5jf8007t_get_frontend_algo,
.read_status = va1j5jf8007t_read_status,
.tune = va1j5jf8007t_tune,
.sleep = va1j5jf8007t_sleep,
.init = va1j5jf8007t_init,
.release = va1j5jf8007t_release,
};
static const u8 va1j5jf8007t_prepare_bufs[][2] = {
{0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2},
{0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00},
{0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03},
{0xef, 0x01}
};
int va1j5jf8007t_prepare(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
u8 buf[2];
struct i2c_msg msg;
int i;
state = fe->demodulator_priv;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_prepare_bufs); i++) {
memcpy(buf, va1j5jf8007t_prepare_bufs[i], sizeof(buf));
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
}
return va1j5jf8007t_init_frequency(state);
}
struct dvb_frontend *
va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
struct i2c_adapter *adap)
{
struct va1j5jf8007t_state *state;
struct dvb_frontend *fe;
u8 buf[2];
struct i2c_msg msg;
state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL);
if (!state)
return NULL;
state->config = config;
state->adap = adap;
fe = &state->fe;
memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops));
fe->demodulator_priv = state;
buf[0] = 0x01;
buf[1] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1) {
kfree(state);
return NULL;
}
return fe;
}

View file

@ -0,0 +1,40 @@
/*
* ISDB-T driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 VA1J5JF8007T_H
#define VA1J5JF8007T_H
struct va1j5jf8007t_config {
u8 demod_address;
};
struct i2c_adapter;
struct dvb_frontend *
va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
struct i2c_adapter *adap);
/* must be called after va1j5jf8007s_attach */
int va1j5jf8007t_prepare(struct dvb_frontend *fe);
#endif