linux/drivers/media/usb/dvb-usb/pctv452e.c
Thomas Gleixner 2874c5fd28 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152
Based on 1 normalized pattern(s):

  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

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 3029 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:32 -07:00

1099 lines
25 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCTV 452e DVB driver
*
* Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net>
*
* TT connect S2-3650-CI Common Interface support, MAC readout
* Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at>
*/
/* dvb usb framework */
#define DVB_USB_LOG_PREFIX "pctv452e"
#include "dvb-usb.h"
/* Demodulator */
#include "stb0899_drv.h"
#include "stb0899_reg.h"
#include "stb0899_cfg.h"
/* Tuner */
#include "stb6100.h"
#include "stb6100_cfg.h"
/* FE Power */
#include "lnbp22.h"
#include <media/dvb_ca_en50221.h>
#include "ttpci-eeprom.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define ISOC_INTERFACE_ALTERNATIVE 3
#define SYNC_BYTE_OUT 0xaa
#define SYNC_BYTE_IN 0x55
/* guessed: (copied from ttusb-budget) */
#define PCTV_CMD_RESET 0x15
/* command to poll IR receiver */
#define PCTV_CMD_IR 0x1b
/* command to send I2C */
#define PCTV_CMD_I2C 0x31
#define I2C_ADDR_STB0899 (0xd0 >> 1)
#define I2C_ADDR_STB6100 (0xc0 >> 1)
#define I2C_ADDR_LNBP22 (0x10 >> 1)
#define I2C_ADDR_24C16 (0xa0 >> 1)
#define I2C_ADDR_24C64 (0xa2 >> 1)
/* pctv452e sends us this amount of data for each issued usb-command */
#define PCTV_ANSWER_LEN 64
/* Wait up to 1000ms for device */
#define PCTV_TIMEOUT 1000
#define PCTV_LED_GPIO STB0899_GPIO01
#define PCTV_LED_GREEN 0x82
#define PCTV_LED_ORANGE 0x02
#define ci_dbg(format, arg...) \
do { \
if (0) \
printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
": " format "\n" , ## arg); \
} while (0)
enum {
TT3650_CMD_CI_TEST = 0x40,
TT3650_CMD_CI_RD_CTRL,
TT3650_CMD_CI_WR_CTRL,
TT3650_CMD_CI_RD_ATTR,
TT3650_CMD_CI_WR_ATTR,
TT3650_CMD_CI_RESET,
TT3650_CMD_CI_SET_VIDEO_PORT
};
static struct stb0899_postproc pctv45e_postproc[] = {
{ PCTV_LED_GPIO, STB0899_GPIOPULLUP },
{ 0, 0 }
};
/*
* stores all private variables for communication with the PCTV452e DVB-S2
*/
struct pctv452e_state {
struct dvb_ca_en50221 ca;
struct mutex ca_mutex;
u8 c; /* transaction counter, wraps around... */
u8 initialized; /* set to 1 if 0x15 has been sent */
u16 last_rc_key;
};
static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
unsigned int write_len, unsigned int read_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
u8 *buf;
u8 id;
unsigned int rlen;
int ret;
if (!data || (write_len > 64 - 4) || (read_len > 64 - 4)) {
err("%s: transfer data invalid", __func__);
return -EIO;
}
buf = kmalloc(64, GFP_KERNEL);
if (!buf)
return -ENOMEM;
id = state->c++;
buf[0] = SYNC_BYTE_OUT;
buf[1] = id;
buf[2] = cmd;
buf[3] = write_len;
memcpy(buf + 4, data, write_len);
rlen = (read_len > 0) ? 64 : 0;
ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
buf, rlen, /* delay_ms */ 0);
if (0 != ret)
goto failed;
ret = -EIO;
if (SYNC_BYTE_IN != buf[0] || id != buf[1])
goto failed;
memcpy(data, buf + 4, read_len);
kfree(buf);
return 0;
failed:
err("CI error %d; %02X %02X %02X -> %*ph.",
ret, SYNC_BYTE_OUT, id, cmd, 3, buf);
kfree(buf);
return ret;
}
static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca,
u8 cmd, u8 *data, unsigned int write_len,
unsigned int read_len)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
int ret;
mutex_lock(&state->ca_mutex);
ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
mutex_unlock(&state->ca_mutex);
return ret;
}
static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
int slot, int address)
{
u8 buf[3];
int ret;
if (0 != slot)
return -EINVAL;
buf[0] = (address >> 8) & 0x0F;
buf[1] = address;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
ci_dbg("%s %04x -> %d 0x%02x",
__func__, address, ret, buf[2]);
if (ret < 0)
return ret;
return buf[2];
}
static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
int slot, int address, u8 value)
{
u8 buf[3];
ci_dbg("%s %d 0x%04x 0x%02x",
__func__, slot, address, value);
if (0 != slot)
return -EINVAL;
buf[0] = (address >> 8) & 0x0F;
buf[1] = address;
buf[2] = value;
return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
}
static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca,
int slot,
u8 address)
{
u8 buf[2];
int ret;
if (0 != slot)
return -EINVAL;
buf[0] = address & 3;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
ci_dbg("%s 0x%02x -> %d 0x%02x",
__func__, address, ret, buf[1]);
if (ret < 0)
return ret;
return buf[1];
}
static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca,
int slot,
u8 address,
u8 value)
{
u8 buf[2];
ci_dbg("%s %d 0x%02x 0x%02x",
__func__, slot, address, value);
if (0 != slot)
return -EINVAL;
buf[0] = address;
buf[1] = value;
return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
}
static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca,
int slot,
int enable)
{
u8 buf[1];
int ret;
ci_dbg("%s %d %d", __func__, slot, enable);
if (0 != slot)
return -EINVAL;
enable = !!enable;
buf[0] = enable;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
if (ret < 0)
return ret;
if (enable != buf[0]) {
err("CI not %sabled.", enable ? "en" : "dis");
return -EIO;
}
return 0;
}
static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
{
return tt3650_ci_set_video_port(ca, slot, /* enable */ 0);
}
static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
{
return tt3650_ci_set_video_port(ca, slot, /* enable */ 1);
}
static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
{
struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
u8 buf[1];
int ret;
ci_dbg("%s %d", __func__, slot);
if (0 != slot)
return -EINVAL;
buf[0] = 0;
mutex_lock(&state->ca_mutex);
ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
if (0 != ret)
goto failed;
msleep(500);
buf[0] = 1;
ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
if (0 != ret)
goto failed;
msleep(500);
buf[0] = 0; /* FTA */
ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
failed:
mutex_unlock(&state->ca_mutex);
return ret;
}
static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca,
int slot,
int open)
{
u8 buf[1];
int ret;
if (0 != slot)
return -EINVAL;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
if (0 != ret)
return ret;
if (1 == buf[0])
return DVB_CA_EN50221_POLL_CAM_PRESENT |
DVB_CA_EN50221_POLL_CAM_READY;
return 0;
}
static void tt3650_ci_uninit(struct dvb_usb_device *d)
{
struct pctv452e_state *state;
ci_dbg("%s", __func__);
if (NULL == d)
return;
state = (struct pctv452e_state *)d->priv;
if (NULL == state)
return;
if (NULL == state->ca.data)
return;
/* Error ignored. */
tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
dvb_ca_en50221_release(&state->ca);
memset(&state->ca, 0, sizeof(state->ca));
}
static int tt3650_ci_init(struct dvb_usb_adapter *a)
{
struct dvb_usb_device *d = a->dev;
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
int ret;
ci_dbg("%s", __func__);
mutex_init(&state->ca_mutex);
state->ca.owner = THIS_MODULE;
state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
state->ca.read_cam_control = tt3650_ci_read_cam_control;
state->ca.write_cam_control = tt3650_ci_write_cam_control;
state->ca.slot_reset = tt3650_ci_slot_reset;
state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
state->ca.data = d;
ret = dvb_ca_en50221_init(&a->dvb_adap,
&state->ca,
/* flags */ 0,
/* n_slots */ 1);
if (0 != ret) {
err("Cannot initialize CI: Error %d.", ret);
memset(&state->ca, 0, sizeof(state->ca));
return ret;
}
info("CI initialized.");
return 0;
}
#define CMD_BUFFER_SIZE 0x28
static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
const u8 *snd_buf, u8 snd_len,
u8 *rcv_buf, u8 rcv_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
u8 *buf;
u8 id;
int ret;
buf = kmalloc(64, GFP_KERNEL);
if (!buf)
return -ENOMEM;
id = state->c++;
ret = -EINVAL;
if (snd_len > 64 - 7 || rcv_len > 64 - 7)
goto failed;
buf[0] = SYNC_BYTE_OUT;
buf[1] = id;
buf[2] = PCTV_CMD_I2C;
buf[3] = snd_len + 3;
buf[4] = addr << 1;
buf[5] = snd_len;
buf[6] = rcv_len;
memcpy(buf + 7, snd_buf, snd_len);
ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
buf, /* rcv_len */ 64,
/* delay_ms */ 0);
if (ret < 0)
goto failed;
/* TT USB protocol error. */
ret = -EIO;
if (SYNC_BYTE_IN != buf[0] || id != buf[1])
goto failed;
/* I2C device didn't respond as expected. */
ret = -EREMOTEIO;
if (buf[5] < snd_len || buf[6] < rcv_len)
goto failed;
memcpy(rcv_buf, buf + 7, rcv_len);
kfree(buf);
return rcv_len;
failed:
err("I2C error %d; %02X %02X %02X %02X %02X -> %*ph",
ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
7, buf);
kfree(buf);
return ret;
}
static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adapter);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
int ret;
if (msg[i].flags & I2C_M_RD) {
addr = msg[i].addr;
snd_buf = NULL;
snd_len = 0;
rcv_buf = msg[i].buf;
rcv_len = msg[i].len;
} else {
addr = msg[i].addr;
snd_buf = msg[i].buf;
snd_len = msg[i].len;
rcv_buf = NULL;
rcv_len = 0;
}
ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf,
rcv_len);
if (ret < rcv_len)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 pctv452e_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
u8 *b0, *rx;
int ret;
info("%s: %d\n", __func__, i);
if (!i)
return 0;
if (state->initialized)
return 0;
b0 = kmalloc(5 + PCTV_ANSWER_LEN, GFP_KERNEL);
if (!b0)
return -ENOMEM;
rx = b0 + 5;
/* hmm where should this should go? */
ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
if (ret != 0)
info("%s: Warning set interface returned: %d\n",
__func__, ret);
/* this is a one-time initialization, don't know where to put */
b0[0] = 0xaa;
b0[1] = state->c++;
b0[2] = PCTV_CMD_RESET;
b0[3] = 1;
b0[4] = 0;
/* reset board */
ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
goto ret;
b0[1] = state->c++;
b0[4] = 1;
/* reset board (again?) */
ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
goto ret;
state->initialized = 1;
ret:
kfree(b0);
return ret;
}
static int pctv452e_rc_query(struct dvb_usb_device *d)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
u8 *b, *rx;
int ret, i;
u8 id;
b = kmalloc(CMD_BUFFER_SIZE + PCTV_ANSWER_LEN, GFP_KERNEL);
if (!b)
return -ENOMEM;
rx = b + CMD_BUFFER_SIZE;
id = state->c++;
/* prepare command header */
b[0] = SYNC_BYTE_OUT;
b[1] = id;
b[2] = PCTV_CMD_IR;
b[3] = 0;
/* send ir request */
ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
if (ret != 0)
goto ret;
if (debug > 3) {
info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx);
for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
info(" %02x", rx[i+3]);
info("\n");
}
if ((rx[3] == 9) && (rx[12] & 0x01)) {
/* got a "press" event */
state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]);
if (debug > 2)
info("%s: cmd=0x%02x sys=0x%02x\n",
__func__, rx[6], rx[7]);
rc_keydown(d->rc_dev, RC_PROTO_RC5, state->last_rc_key, 0);
} else if (state->last_rc_key) {
rc_keyup(d->rc_dev);
state->last_rc_key = 0;
}
ret:
kfree(b);
return ret;
}
static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
{
const u8 mem_addr[] = { 0x1f, 0xcc };
u8 encoded_mac[20];
int ret;
ret = -EAGAIN;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
goto failed;
ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16,
mem_addr + 1, /* snd_len */ 1,
encoded_mac, /* rcv_len */ 20);
if (-EREMOTEIO == ret)
/* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a
byte write if /WC is low. */
ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64,
mem_addr, 2,
encoded_mac, 20);
mutex_unlock(&d->i2c_mutex);
if (20 != ret)
goto failed;
ret = ttpci_eeprom_decode_mac(mac, encoded_mac);
if (0 != ret)
goto failed;
return 0;
failed:
eth_zero_addr(mac);
return ret;
}
static const struct stb0899_s1_reg pctv452e_init_dev[] = {
{ STB0899_DISCNTRL1, 0x26 },
{ STB0899_DISCNTRL2, 0x80 },
{ STB0899_DISRX_ST0, 0x04 },
{ STB0899_DISRX_ST1, 0x20 },
{ STB0899_DISPARITY, 0x00 },
{ STB0899_DISFIFO, 0x00 },
{ STB0899_DISF22, 0x99 },
{ STB0899_DISF22RX, 0x85 }, /* 0xa8 */
{ STB0899_ACRPRESC, 0x11 },
{ STB0899_ACRDIV1, 0x0a },
{ STB0899_ACRDIV2, 0x05 },
{ STB0899_DACR1 , 0x00 },
{ STB0899_DACR2 , 0x00 },
{ STB0899_OUTCFG, 0x00 },
{ STB0899_MODECFG, 0x00 }, /* Inversion */
{ STB0899_IRQMSK_3, 0xf3 },
{ STB0899_IRQMSK_2, 0xfc },
{ STB0899_IRQMSK_1, 0xff },
{ STB0899_IRQMSK_0, 0xff },
{ STB0899_I2CCFG, 0x88 },
{ STB0899_I2CRPT, 0x58 },
{ STB0899_GPIO00CFG, 0x82 },
{ STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */
{ STB0899_GPIO02CFG, 0x82 },
{ STB0899_GPIO03CFG, 0x82 },
{ STB0899_GPIO04CFG, 0x82 },
{ STB0899_GPIO05CFG, 0x82 },
{ STB0899_GPIO06CFG, 0x82 },
{ STB0899_GPIO07CFG, 0x82 },
{ STB0899_GPIO08CFG, 0x82 },
{ STB0899_GPIO09CFG, 0x82 },
{ STB0899_GPIO10CFG, 0x82 },
{ STB0899_GPIO11CFG, 0x82 },
{ STB0899_GPIO12CFG, 0x82 },
{ STB0899_GPIO13CFG, 0x82 },
{ STB0899_GPIO14CFG, 0x82 },
{ STB0899_GPIO15CFG, 0x82 },
{ STB0899_GPIO16CFG, 0x82 },
{ STB0899_GPIO17CFG, 0x82 },
{ STB0899_GPIO18CFG, 0x82 },
{ STB0899_GPIO19CFG, 0x82 },
{ STB0899_GPIO20CFG, 0x82 },
{ STB0899_SDATCFG, 0xb8 },
{ STB0899_SCLTCFG, 0xba },
{ STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */
{ STB0899_GPIO22, 0x82 },
{ STB0899_GPIO21, 0x91 },
{ STB0899_DIRCLKCFG, 0x82 },
{ STB0899_CLKOUT27CFG, 0x7e },
{ STB0899_STDBYCFG, 0x82 },
{ STB0899_CS0CFG, 0x82 },
{ STB0899_CS1CFG, 0x82 },
{ STB0899_DISEQCOCFG, 0x20 },
{ STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */
{ STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */
{ STB0899_FILTCTRL, 0x00 },
{ STB0899_SYSCTRL, 0x00 },
{ STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */
{ STB0899_STOPCLK2, 0x00 },
{ STB0899_INTBUFCTRL, 0x0a },
{ STB0899_AGC2I1, 0x00 },
{ STB0899_AGC2I2, 0x00 },
{ STB0899_AGCIQIN, 0x00 },
{ STB0899_TSTRES, 0x40 }, /* rjkm */
{ 0xffff, 0xff },
};
static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = {
{ STB0899_DEMOD, 0x00 },
{ STB0899_RCOMPC, 0xc9 },
{ STB0899_AGC1CN, 0x01 },
{ STB0899_AGC1REF, 0x10 },
{ STB0899_RTC, 0x23 },
{ STB0899_TMGCFG, 0x4e },
{ STB0899_AGC2REF, 0x34 },
{ STB0899_TLSR, 0x84 },
{ STB0899_CFD, 0xf7 },
{ STB0899_ACLC, 0x87 },
{ STB0899_BCLC, 0x94 },
{ STB0899_EQON, 0x41 },
{ STB0899_LDT, 0xf1 },
{ STB0899_LDT2, 0xe3 },
{ STB0899_EQUALREF, 0xb4 },
{ STB0899_TMGRAMP, 0x10 },
{ STB0899_TMGTHD, 0x30 },
{ STB0899_IDCCOMP, 0xfd },
{ STB0899_QDCCOMP, 0xff },
{ STB0899_POWERI, 0x0c },
{ STB0899_POWERQ, 0x0f },
{ STB0899_RCOMP, 0x6c },
{ STB0899_AGCIQIN, 0x80 },
{ STB0899_AGC2I1, 0x06 },
{ STB0899_AGC2I2, 0x00 },
{ STB0899_TLIR, 0x30 },
{ STB0899_RTF, 0x7f },
{ STB0899_DSTATUS, 0x00 },
{ STB0899_LDI, 0xbc },
{ STB0899_CFRM, 0xea },
{ STB0899_CFRL, 0x31 },
{ STB0899_NIRM, 0x2b },
{ STB0899_NIRL, 0x80 },
{ STB0899_ISYMB, 0x1d },
{ STB0899_QSYMB, 0xa6 },
{ STB0899_SFRH, 0x2f },
{ STB0899_SFRM, 0x68 },
{ STB0899_SFRL, 0x40 },
{ STB0899_SFRUPH, 0x2f },
{ STB0899_SFRUPM, 0x68 },
{ STB0899_SFRUPL, 0x40 },
{ STB0899_EQUAI1, 0x02 },
{ STB0899_EQUAQ1, 0xff },
{ STB0899_EQUAI2, 0x04 },
{ STB0899_EQUAQ2, 0x05 },
{ STB0899_EQUAI3, 0x02 },
{ STB0899_EQUAQ3, 0xfd },
{ STB0899_EQUAI4, 0x03 },
{ STB0899_EQUAQ4, 0x07 },
{ STB0899_EQUAI5, 0x08 },
{ STB0899_EQUAQ5, 0xf5 },
{ STB0899_DSTATUS2, 0x00 },
{ STB0899_VSTATUS, 0x00 },
{ STB0899_VERROR, 0x86 },
{ STB0899_IQSWAP, 0x2a },
{ STB0899_ECNT1M, 0x00 },
{ STB0899_ECNT1L, 0x00 },
{ STB0899_ECNT2M, 0x00 },
{ STB0899_ECNT2L, 0x00 },
{ STB0899_ECNT3M, 0x0a },
{ STB0899_ECNT3L, 0xad },
{ STB0899_FECAUTO1, 0x06 },
{ STB0899_FECM, 0x01 },
{ STB0899_VTH12, 0xb0 },
{ STB0899_VTH23, 0x7a },
{ STB0899_VTH34, 0x58 },
{ STB0899_VTH56, 0x38 },
{ STB0899_VTH67, 0x34 },
{ STB0899_VTH78, 0x24 },
{ STB0899_PRVIT, 0xff },
{ STB0899_VITSYNC, 0x19 },
{ STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
{ STB0899_TSULC, 0x42 },
{ STB0899_RSLLC, 0x41 },
{ STB0899_TSLPL, 0x12 },
{ STB0899_TSCFGH, 0x0c },
{ STB0899_TSCFGM, 0x00 },
{ STB0899_TSCFGL, 0x00 },
{ STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */
{ STB0899_RSSYNCDEL, 0x00 },
{ STB0899_TSINHDELH, 0x02 },
{ STB0899_TSINHDELM, 0x00 },
{ STB0899_TSINHDELL, 0x00 },
{ STB0899_TSLLSTKM, 0x1b },
{ STB0899_TSLLSTKL, 0xb3 },
{ STB0899_TSULSTKM, 0x00 },
{ STB0899_TSULSTKL, 0x00 },
{ STB0899_PCKLENUL, 0xbc },
{ STB0899_PCKLENLL, 0xcc },
{ STB0899_RSPCKLEN, 0xbd },
{ STB0899_TSSTATUS, 0x90 },
{ STB0899_ERRCTRL1, 0xb6 },
{ STB0899_ERRCTRL2, 0x95 },
{ STB0899_ERRCTRL3, 0x8d },
{ STB0899_DMONMSK1, 0x27 },
{ STB0899_DMONMSK0, 0x03 },
{ STB0899_DEMAPVIT, 0x5c },
{ STB0899_PLPARM, 0x19 },
{ STB0899_PDELCTRL, 0x48 },
{ STB0899_PDELCTRL2, 0x00 },
{ STB0899_BBHCTRL1, 0x00 },
{ STB0899_BBHCTRL2, 0x00 },
{ STB0899_HYSTTHRESH, 0x77 },
{ STB0899_MATCSTM, 0x00 },
{ STB0899_MATCSTL, 0x00 },
{ STB0899_UPLCSTM, 0x00 },
{ STB0899_UPLCSTL, 0x00 },
{ STB0899_DFLCSTM, 0x00 },
{ STB0899_DFLCSTL, 0x00 },
{ STB0899_SYNCCST, 0x00 },
{ STB0899_SYNCDCSTM, 0x00 },
{ STB0899_SYNCDCSTL, 0x00 },
{ STB0899_ISI_ENTRY, 0x00 },
{ STB0899_ISI_BIT_EN, 0x00 },
{ STB0899_MATSTRM, 0xf0 },
{ STB0899_MATSTRL, 0x02 },
{ STB0899_UPLSTRM, 0x45 },
{ STB0899_UPLSTRL, 0x60 },
{ STB0899_DFLSTRM, 0xe3 },
{ STB0899_DFLSTRL, 0x00 },
{ STB0899_SYNCSTR, 0x47 },
{ STB0899_SYNCDSTRM, 0x05 },
{ STB0899_SYNCDSTRL, 0x18 },
{ STB0899_CFGPDELSTATUS1, 0x19 },
{ STB0899_CFGPDELSTATUS2, 0x2b },
{ STB0899_BBFERRORM, 0x00 },
{ STB0899_BBFERRORL, 0x01 },
{ STB0899_UPKTERRORM, 0x00 },
{ STB0899_UPKTERRORL, 0x00 },
{ 0xffff, 0xff },
};
static struct stb0899_config stb0899_config = {
.init_dev = pctv452e_init_dev,
.init_s2_demod = stb0899_s2_init_2,
.init_s1_demod = pctv452e_init_s1_demod,
.init_s2_fec = stb0899_s2_init_4,
.init_tst = stb0899_s1_init_5,
.demod_address = I2C_ADDR_STB0899, /* I2C Address */
.block_sync_mode = STB0899_SYNC_FORCED, /* ? */
.xtal_freq = 27000000, /* Assume Hz ? */
.inversion = IQ_SWAP_ON,
.lo_clk = 76500000,
.hi_clk = 99000000,
.ts_output_mode = 0, /* Use parallel mode */
.clock_polarity = 0,
.data_clk_parity = 0,
.fec_mode = 0,
.esno_ave = STB0899_DVBS2_ESNO_AVE,
.esno_quant = STB0899_DVBS2_ESNO_QUANT,
.avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
.avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
.miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
.uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
.uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
.uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
.sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
.btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
.btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
.crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
.ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
.tuner_get_frequency = stb6100_get_frequency,
.tuner_set_frequency = stb6100_set_frequency,
.tuner_set_bandwidth = stb6100_set_bandwidth,
.tuner_get_bandwidth = stb6100_get_bandwidth,
.tuner_set_rfsiggain = NULL,
/* helper for switching LED green/orange */
.postproc = pctv45e_postproc
};
static struct stb6100_config stb6100_config = {
.tuner_address = I2C_ADDR_STB6100,
.refclock = 27000000
};
static struct i2c_algorithm pctv452e_i2c_algo = {
.master_xfer = pctv452e_i2c_xfer,
.functionality = pctv452e_i2c_func
};
static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
{
struct usb_device_id *id;
a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config,
&a->dev->i2c_adap);
if (!a->fe_adap[0].fe)
return -ENODEV;
/*
* dvb_frontend will call dvb_detach for both stb0899_detach
* and stb0899_release but we only do dvb_attach(stb0899_attach).
* Increment the module refcount instead.
*/
symbol_get(stb0899_attach);
if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
&a->dev->i2c_adap)) == NULL)
err("Cannot attach lnbp22\n");
id = a->dev->desc->warm_ids[0];
if (USB_VID_TECHNOTREND == id->idVendor
&& USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct)
/* Error ignored. */
tt3650_ci_init(a);
return 0;
}
static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
{
if (!a->fe_adap[0].fe)
return -ENODEV;
if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
&a->dev->i2c_adap) == NULL) {
err("%s failed\n", __func__);
return -ENODEV;
}
return 0;
}
static struct usb_device_id pctv452e_usb_table[] = {
{USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)},
{USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)},
{USB_DEVICE(USB_VID_TECHNOTREND,
USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)},
{}
};
MODULE_DEVICE_TABLE(usb, pctv452e_usb_table);
static struct dvb_usb_device_properties pctv452e_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = sizeof(struct pctv452e_state),
.power_ctrl = pctv452e_power_ctrl,
.rc.core = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.allowed_protos = RC_PROTO_BIT_RC5,
.rc_query = pctv452e_rc_query,
.rc_interval = 100,
},
.num_adapters = 1,
.adapter = {{
.num_frontends = 1,
.fe = {{
.frontend_attach = pctv452e_frontend_attach,
.tuner_attach = pctv452e_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_ISOC,
.count = 4,
.endpoint = 0x02,
.u = {
.isoc = {
.framesperurb = 4,
.framesize = 940,
.interval = 1
}
}
},
} },
} },
.i2c_algo = &pctv452e_i2c_algo,
.generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */
.num_device_descs = 1,
.devices = {
{ .name = "PCTV HDTV USB",
.cold_ids = { NULL, NULL }, /* this is a warm only device */
.warm_ids = { &pctv452e_usb_table[0], NULL }
},
{ NULL },
}
};
static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = sizeof(struct pctv452e_state),
.power_ctrl = pctv452e_power_ctrl,
.read_mac_address = pctv452e_read_mac_address,
.rc.core = {
.rc_codes = RC_MAP_TT_1500,
.allowed_protos = RC_PROTO_BIT_RC5,
.rc_query = pctv452e_rc_query,
.rc_interval = 100,
},
.num_adapters = 1,
.adapter = {{
.num_frontends = 1,
.fe = {{
.frontend_attach = pctv452e_frontend_attach,
.tuner_attach = pctv452e_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_ISOC,
.count = 4,
.endpoint = 0x02,
.u = {
.isoc = {
.framesperurb = 64,
.framesize = 940,
.interval = 1
}
}
},
} },
} },
.i2c_algo = &pctv452e_i2c_algo,
.generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/
.num_device_descs = 2,
.devices = {
{ .name = "Technotrend TT Connect S2-3600",
.cold_ids = { NULL, NULL }, /* this is a warm only device */
.warm_ids = { &pctv452e_usb_table[1], NULL }
},
{ .name = "Technotrend TT Connect S2-3650-CI",
.cold_ids = { NULL, NULL },
.warm_ids = { &pctv452e_usb_table[2], NULL }
},
{ NULL },
}
};
static void pctv452e_usb_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
tt3650_ci_uninit(d);
dvb_usb_device_exit(intf);
}
static int pctv452e_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (0 == dvb_usb_device_init(intf, &pctv452e_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties,
THIS_MODULE, NULL, adapter_nr))
return 0;
return -ENODEV;
}
static struct usb_driver pctv452e_usb_driver = {
.name = "pctv452e",
.probe = pctv452e_usb_probe,
.disconnect = pctv452e_usb_disconnect,
.id_table = pctv452e_usb_table,
};
module_usb_driver(pctv452e_usb_driver);
MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>");
MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>");
MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>");
MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver");
MODULE_LICENSE("GPL");