![Mauro Carvalho Chehab](/assets/img/avatar_default.png)
The Linux stack is short; we need to be able to count the number of bytes used at stack on each function. So, we don't like to use variable-length arrays, as complained by smatch: drivers/media/dvb-frontends/horus3a.c:57:19: warning: Variable length array is used. The max usecase of the driver seems to be 10 bytes + 1 for the register. So, let's be safe and allocate 11 bytes for the write buffer. This should be enough to cover all cases. If not, let's print an error message. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
549 lines
15 KiB
C
549 lines
15 KiB
C
/*
|
|
* ascot2e.c
|
|
*
|
|
* Sony Ascot3E DVB-T/T2/C/C2 tuner driver
|
|
*
|
|
* Copyright 2012 Sony Corporation
|
|
* Copyright (C) 2014 NetUP Inc.
|
|
* Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
|
|
* Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dvb/frontend.h>
|
|
#include <linux/types.h>
|
|
#include "ascot2e.h"
|
|
#include "dvb_frontend.h"
|
|
|
|
#define MAX_WRITE_REGSIZE 10
|
|
|
|
enum ascot2e_state {
|
|
STATE_UNKNOWN,
|
|
STATE_SLEEP,
|
|
STATE_ACTIVE
|
|
};
|
|
|
|
struct ascot2e_priv {
|
|
u32 frequency;
|
|
u8 i2c_address;
|
|
struct i2c_adapter *i2c;
|
|
enum ascot2e_state state;
|
|
void *set_tuner_data;
|
|
int (*set_tuner)(void *, int);
|
|
};
|
|
|
|
enum ascot2e_tv_system_t {
|
|
ASCOT2E_DTV_DVBT_5,
|
|
ASCOT2E_DTV_DVBT_6,
|
|
ASCOT2E_DTV_DVBT_7,
|
|
ASCOT2E_DTV_DVBT_8,
|
|
ASCOT2E_DTV_DVBT2_1_7,
|
|
ASCOT2E_DTV_DVBT2_5,
|
|
ASCOT2E_DTV_DVBT2_6,
|
|
ASCOT2E_DTV_DVBT2_7,
|
|
ASCOT2E_DTV_DVBT2_8,
|
|
ASCOT2E_DTV_DVBC_6,
|
|
ASCOT2E_DTV_DVBC_8,
|
|
ASCOT2E_DTV_DVBC2_6,
|
|
ASCOT2E_DTV_DVBC2_8,
|
|
ASCOT2E_DTV_UNKNOWN
|
|
};
|
|
|
|
struct ascot2e_band_sett {
|
|
u8 if_out_sel;
|
|
u8 agc_sel;
|
|
u8 mix_oll;
|
|
u8 rf_gain;
|
|
u8 if_bpf_gc;
|
|
u8 fif_offset;
|
|
u8 bw_offset;
|
|
u8 bw;
|
|
u8 rf_oldet;
|
|
u8 if_bpf_f0;
|
|
};
|
|
|
|
#define ASCOT2E_AUTO 0xff
|
|
#define ASCOT2E_OFFSET(ofs) ((u8)(ofs) & 0x1F)
|
|
#define ASCOT2E_BW_6 0x00
|
|
#define ASCOT2E_BW_7 0x01
|
|
#define ASCOT2E_BW_8 0x02
|
|
#define ASCOT2E_BW_1_7 0x03
|
|
|
|
static struct ascot2e_band_sett ascot2e_sett[] = {
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
|
|
ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
|
|
ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6, 0x09, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
|
|
ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8, 0x09, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
|
|
ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6, 0x09, 0x00 },
|
|
{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
|
|
ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2), ASCOT2E_BW_8, 0x09, 0x00 }
|
|
};
|
|
|
|
static void ascot2e_i2c_debug(struct ascot2e_priv *priv,
|
|
u8 reg, u8 write, const u8 *data, u32 len)
|
|
{
|
|
dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n",
|
|
(write == 0 ? "read" : "write"), reg, len);
|
|
print_hex_dump_bytes("ascot2e: I2C data: ",
|
|
DUMP_PREFIX_OFFSET, data, len);
|
|
}
|
|
|
|
static int ascot2e_write_regs(struct ascot2e_priv *priv,
|
|
u8 reg, const u8 *data, u32 len)
|
|
{
|
|
int ret;
|
|
u8 buf[MAX_WRITE_REGSIZE + 1];
|
|
struct i2c_msg msg[1] = {
|
|
{
|
|
.addr = priv->i2c_address,
|
|
.flags = 0,
|
|
.len = len + 1,
|
|
.buf = buf,
|
|
}
|
|
};
|
|
|
|
if (len + 1 >= sizeof(buf)) {
|
|
dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
|
|
reg, len + 1);
|
|
return -E2BIG;
|
|
}
|
|
|
|
ascot2e_i2c_debug(priv, reg, 1, data, len);
|
|
buf[0] = reg;
|
|
memcpy(&buf[1], data, len);
|
|
ret = i2c_transfer(priv->i2c, msg, 1);
|
|
if (ret >= 0 && ret != 1)
|
|
ret = -EREMOTEIO;
|
|
if (ret < 0) {
|
|
dev_warn(&priv->i2c->dev,
|
|
"%s: i2c wr failed=%d reg=%02x len=%d\n",
|
|
KBUILD_MODNAME, ret, reg, len);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val)
|
|
{
|
|
return ascot2e_write_regs(priv, reg, &val, 1);
|
|
}
|
|
|
|
static int ascot2e_read_regs(struct ascot2e_priv *priv,
|
|
u8 reg, u8 *val, u32 len)
|
|
{
|
|
int ret;
|
|
struct i2c_msg msg[2] = {
|
|
{
|
|
.addr = priv->i2c_address,
|
|
.flags = 0,
|
|
.len = 1,
|
|
.buf = ®,
|
|
}, {
|
|
.addr = priv->i2c_address,
|
|
.flags = I2C_M_RD,
|
|
.len = len,
|
|
.buf = val,
|
|
}
|
|
};
|
|
|
|
ret = i2c_transfer(priv->i2c, &msg[0], 1);
|
|
if (ret >= 0 && ret != 1)
|
|
ret = -EREMOTEIO;
|
|
if (ret < 0) {
|
|
dev_warn(&priv->i2c->dev,
|
|
"%s: I2C rw failed=%d addr=%02x reg=%02x\n",
|
|
KBUILD_MODNAME, ret, priv->i2c_address, reg);
|
|
return ret;
|
|
}
|
|
ret = i2c_transfer(priv->i2c, &msg[1], 1);
|
|
if (ret >= 0 && ret != 1)
|
|
ret = -EREMOTEIO;
|
|
if (ret < 0) {
|
|
dev_warn(&priv->i2c->dev,
|
|
"%s: i2c rd failed=%d addr=%02x reg=%02x\n",
|
|
KBUILD_MODNAME, ret, priv->i2c_address, reg);
|
|
return ret;
|
|
}
|
|
ascot2e_i2c_debug(priv, reg, 0, val, len);
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val)
|
|
{
|
|
return ascot2e_read_regs(priv, reg, val, 1);
|
|
}
|
|
|
|
static int ascot2e_set_reg_bits(struct ascot2e_priv *priv,
|
|
u8 reg, u8 data, u8 mask)
|
|
{
|
|
int res;
|
|
u8 rdata;
|
|
|
|
if (mask != 0xff) {
|
|
res = ascot2e_read_reg(priv, reg, &rdata);
|
|
if (res != 0)
|
|
return res;
|
|
data = ((data & mask) | (rdata & (mask ^ 0xFF)));
|
|
}
|
|
return ascot2e_write_reg(priv, reg, data);
|
|
}
|
|
|
|
static int ascot2e_enter_power_save(struct ascot2e_priv *priv)
|
|
{
|
|
u8 data[2];
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
|
|
if (priv->state == STATE_SLEEP)
|
|
return 0;
|
|
data[0] = 0x00;
|
|
data[1] = 0x04;
|
|
ascot2e_write_regs(priv, 0x14, data, 2);
|
|
ascot2e_write_reg(priv, 0x50, 0x01);
|
|
priv->state = STATE_SLEEP;
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_leave_power_save(struct ascot2e_priv *priv)
|
|
{
|
|
u8 data[2] = { 0xFB, 0x0F };
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
|
|
if (priv->state == STATE_ACTIVE)
|
|
return 0;
|
|
ascot2e_write_regs(priv, 0x14, data, 2);
|
|
ascot2e_write_reg(priv, 0x50, 0x00);
|
|
priv->state = STATE_ACTIVE;
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_init(struct dvb_frontend *fe)
|
|
{
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
|
|
return ascot2e_leave_power_save(priv);
|
|
}
|
|
|
|
static int ascot2e_release(struct dvb_frontend *fe)
|
|
{
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
|
|
kfree(fe->tuner_priv);
|
|
fe->tuner_priv = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_sleep(struct dvb_frontend *fe)
|
|
{
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
|
|
ascot2e_enter_power_save(priv);
|
|
return 0;
|
|
}
|
|
|
|
static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe)
|
|
{
|
|
enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN;
|
|
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
if (p->delivery_system == SYS_DVBT) {
|
|
if (p->bandwidth_hz <= 5000000)
|
|
system = ASCOT2E_DTV_DVBT_5;
|
|
else if (p->bandwidth_hz <= 6000000)
|
|
system = ASCOT2E_DTV_DVBT_6;
|
|
else if (p->bandwidth_hz <= 7000000)
|
|
system = ASCOT2E_DTV_DVBT_7;
|
|
else if (p->bandwidth_hz <= 8000000)
|
|
system = ASCOT2E_DTV_DVBT_8;
|
|
else {
|
|
system = ASCOT2E_DTV_DVBT_8;
|
|
p->bandwidth_hz = 8000000;
|
|
}
|
|
} else if (p->delivery_system == SYS_DVBT2) {
|
|
if (p->bandwidth_hz <= 5000000)
|
|
system = ASCOT2E_DTV_DVBT2_5;
|
|
else if (p->bandwidth_hz <= 6000000)
|
|
system = ASCOT2E_DTV_DVBT2_6;
|
|
else if (p->bandwidth_hz <= 7000000)
|
|
system = ASCOT2E_DTV_DVBT2_7;
|
|
else if (p->bandwidth_hz <= 8000000)
|
|
system = ASCOT2E_DTV_DVBT2_8;
|
|
else {
|
|
system = ASCOT2E_DTV_DVBT2_8;
|
|
p->bandwidth_hz = 8000000;
|
|
}
|
|
} else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
|
|
if (p->bandwidth_hz <= 6000000)
|
|
system = ASCOT2E_DTV_DVBC_6;
|
|
else if (p->bandwidth_hz <= 8000000)
|
|
system = ASCOT2E_DTV_DVBC_8;
|
|
}
|
|
dev_dbg(&priv->i2c->dev,
|
|
"%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n",
|
|
__func__, (int)system, p->delivery_system, p->bandwidth_hz);
|
|
return system;
|
|
}
|
|
|
|
static int ascot2e_set_params(struct dvb_frontend *fe)
|
|
{
|
|
u8 data[10];
|
|
u32 frequency;
|
|
enum ascot2e_tv_system_t tv_system;
|
|
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
|
|
__func__, p->frequency / 1000);
|
|
tv_system = ascot2e_get_tv_system(fe);
|
|
|
|
if (tv_system == ASCOT2E_DTV_UNKNOWN) {
|
|
dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
if (priv->set_tuner)
|
|
priv->set_tuner(priv->set_tuner_data, 1);
|
|
frequency = roundup(p->frequency / 1000, 25);
|
|
if (priv->state == STATE_SLEEP)
|
|
ascot2e_leave_power_save(priv);
|
|
|
|
/* IF_OUT_SEL / AGC_SEL setting */
|
|
data[0] = 0x00;
|
|
if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) {
|
|
/* AGC pin setting from parameter table */
|
|
data[0] |= (u8)(
|
|
(ascot2e_sett[tv_system].agc_sel & 0x03) << 3);
|
|
}
|
|
if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) {
|
|
/* IFOUT pin setting from parameter table */
|
|
data[0] |= (u8)(
|
|
(ascot2e_sett[tv_system].if_out_sel & 0x01) << 2);
|
|
}
|
|
/* Set bit[4:2] only */
|
|
ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c);
|
|
/* 0x06 - 0x0F */
|
|
/* REF_R setting (0x06) */
|
|
if (tv_system == ASCOT2E_DTV_DVBC_6 ||
|
|
tv_system == ASCOT2E_DTV_DVBC_8) {
|
|
/* xtal, xtal*2 */
|
|
data[0] = (frequency > 500000) ? 16 : 32;
|
|
} else {
|
|
/* xtal/8, xtal/4 */
|
|
data[0] = (frequency > 500000) ? 2 : 4;
|
|
}
|
|
/* XOSC_SEL=100uA */
|
|
data[1] = 0x04;
|
|
/* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */
|
|
if (tv_system == ASCOT2E_DTV_DVBC_6 ||
|
|
tv_system == ASCOT2E_DTV_DVBC_8) {
|
|
data[2] = 18;
|
|
data[3] = 120;
|
|
data[4] = 20;
|
|
} else {
|
|
data[2] = 48;
|
|
data[3] = 10;
|
|
data[4] = 30;
|
|
}
|
|
/* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */
|
|
if (tv_system == ASCOT2E_DTV_DVBC_6 ||
|
|
tv_system == ASCOT2E_DTV_DVBC_8)
|
|
data[5] = (frequency > 500000) ? 0x08 : 0x0c;
|
|
else
|
|
data[5] = (frequency > 500000) ? 0x30 : 0x38;
|
|
/* Set MIX_OLL (0x0C) value from parameter table */
|
|
data[6] = ascot2e_sett[tv_system].mix_oll;
|
|
/* Set RF_GAIN (0x0D) setting from parameter table */
|
|
if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) {
|
|
/* RF_GAIN auto control enable */
|
|
ascot2e_write_reg(priv, 0x4E, 0x01);
|
|
/* RF_GAIN Default value */
|
|
data[7] = 0x00;
|
|
} else {
|
|
/* RF_GAIN auto control disable */
|
|
ascot2e_write_reg(priv, 0x4E, 0x00);
|
|
data[7] = ascot2e_sett[tv_system].rf_gain;
|
|
}
|
|
/* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */
|
|
data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) |
|
|
(ascot2e_sett[tv_system].if_bpf_gc & 0x07));
|
|
/* Set BW_OFFSET (0x0F) value from parameter table */
|
|
data[9] = ascot2e_sett[tv_system].bw_offset;
|
|
ascot2e_write_regs(priv, 0x06, data, 10);
|
|
/*
|
|
* 0x45 - 0x47
|
|
* LNA optimization setting
|
|
* RF_LNA_DIST1-5, RF_LNA_CM
|
|
*/
|
|
if (tv_system == ASCOT2E_DTV_DVBC_6 ||
|
|
tv_system == ASCOT2E_DTV_DVBC_8) {
|
|
data[0] = 0x0F;
|
|
data[1] = 0x00;
|
|
data[2] = 0x01;
|
|
} else {
|
|
data[0] = 0x0F;
|
|
data[1] = 0x00;
|
|
data[2] = 0x03;
|
|
}
|
|
ascot2e_write_regs(priv, 0x45, data, 3);
|
|
/* 0x49 - 0x4A
|
|
Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */
|
|
data[0] = ascot2e_sett[tv_system].rf_oldet;
|
|
/* Set IF_BPF_F0 value from parameter table */
|
|
data[1] = ascot2e_sett[tv_system].if_bpf_f0;
|
|
ascot2e_write_regs(priv, 0x49, data, 2);
|
|
/*
|
|
* Tune now
|
|
* RFAGC fast mode / RFAGC auto control enable
|
|
* (set bit[7], bit[5:4] only)
|
|
* vco_cal = 1, set MIX_OL_CPU_EN
|
|
*/
|
|
ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0);
|
|
/* Logic wake up, CPU wake up */
|
|
data[0] = 0xc4;
|
|
data[1] = 0x40;
|
|
ascot2e_write_regs(priv, 0x03, data, 2);
|
|
/* 0x10 - 0x14 */
|
|
data[0] = (u8)(frequency & 0xFF); /* 0x10: FRF_L */
|
|
data[1] = (u8)((frequency >> 8) & 0xFF); /* 0x11: FRF_M */
|
|
data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */
|
|
/* 0x12: BW (bit[5:4]) */
|
|
data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4);
|
|
data[3] = 0xFF; /* 0x13: VCO calibration enable */
|
|
data[4] = 0xFF; /* 0x14: Analog block enable */
|
|
/* Tune (Burst write) */
|
|
ascot2e_write_regs(priv, 0x10, data, 5);
|
|
msleep(50);
|
|
/* CPU deep sleep */
|
|
ascot2e_write_reg(priv, 0x04, 0x00);
|
|
/* Logic sleep */
|
|
ascot2e_write_reg(priv, 0x03, 0xC0);
|
|
/* RFAGC normal mode (set bit[5:4] only) */
|
|
ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30);
|
|
priv->frequency = frequency;
|
|
return 0;
|
|
}
|
|
|
|
static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
|
{
|
|
struct ascot2e_priv *priv = fe->tuner_priv;
|
|
|
|
*frequency = priv->frequency * 1000;
|
|
return 0;
|
|
}
|
|
|
|
static struct dvb_tuner_ops ascot2e_tuner_ops = {
|
|
.info = {
|
|
.name = "Sony ASCOT2E",
|
|
.frequency_min = 1000000,
|
|
.frequency_max = 1200000000,
|
|
.frequency_step = 25000,
|
|
},
|
|
.init = ascot2e_init,
|
|
.release = ascot2e_release,
|
|
.sleep = ascot2e_sleep,
|
|
.set_params = ascot2e_set_params,
|
|
.get_frequency = ascot2e_get_frequency,
|
|
};
|
|
|
|
struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
|
|
const struct ascot2e_config *config,
|
|
struct i2c_adapter *i2c)
|
|
{
|
|
u8 data[4];
|
|
struct ascot2e_priv *priv = NULL;
|
|
|
|
priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL);
|
|
if (priv == NULL)
|
|
return NULL;
|
|
priv->i2c_address = (config->i2c_address >> 1);
|
|
priv->i2c = i2c;
|
|
priv->set_tuner_data = config->set_tuner_priv;
|
|
priv->set_tuner = config->set_tuner_callback;
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
|
|
/* 16 MHz xTal frequency */
|
|
data[0] = 16;
|
|
/* VCO current setting */
|
|
data[1] = 0x06;
|
|
/* Logic wake up, CPU boot */
|
|
data[2] = 0xC4;
|
|
data[3] = 0x40;
|
|
ascot2e_write_regs(priv, 0x01, data, 4);
|
|
/* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */
|
|
data[0] = 0x10;
|
|
data[1] = 0x3F;
|
|
data[2] = 0x25;
|
|
ascot2e_write_regs(priv, 0x22, data, 3);
|
|
/* PLL mode setting */
|
|
ascot2e_write_reg(priv, 0x28, 0x1e);
|
|
/* RSSI setting */
|
|
ascot2e_write_reg(priv, 0x59, 0x04);
|
|
/* TODO check CPU HW error state here */
|
|
msleep(80);
|
|
/* Xtal oscillator current control setting */
|
|
ascot2e_write_reg(priv, 0x4c, 0x01);
|
|
/* XOSC_SEL=100uA */
|
|
ascot2e_write_reg(priv, 0x07, 0x04);
|
|
/* CPU deep sleep */
|
|
ascot2e_write_reg(priv, 0x04, 0x00);
|
|
/* Logic sleep */
|
|
ascot2e_write_reg(priv, 0x03, 0xc0);
|
|
/* Power save setting */
|
|
data[0] = 0x00;
|
|
data[1] = 0x04;
|
|
ascot2e_write_regs(priv, 0x14, data, 2);
|
|
ascot2e_write_reg(priv, 0x50, 0x01);
|
|
priv->state = STATE_SLEEP;
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 0);
|
|
|
|
memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops,
|
|
sizeof(struct dvb_tuner_ops));
|
|
fe->tuner_priv = priv;
|
|
dev_info(&priv->i2c->dev,
|
|
"Sony ASCOT2E attached on addr=%x at I2C adapter %p\n",
|
|
priv->i2c_address, priv->i2c);
|
|
return fe;
|
|
}
|
|
EXPORT_SYMBOL(ascot2e_attach);
|
|
|
|
MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver");
|
|
MODULE_AUTHOR("info@netup.ru");
|
|
MODULE_LICENSE("GPL");
|