Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[linux-dvb] Re: Terratec Cinergy 1200 DVB-C Card supported?



Robert Schlabbach schrieb:
From: "Robert Schlabbach" <robert_s@gmx.net>

(for reference, the VES1820 has ID 0x79/0x7A, the VES1820X has id 0x7C).

Oops, that got cut short a little. The known IDs are:

0x79 = VES1820  (first version)  I2C addresses: 0x08-0x0F
0x7A = VES1820- (second version) I2C addresses: 0x08-0x0F
0x7B = VES1820X (revision 1)     I2C addresses: 0x08-0x0B
0x7C = TDA10021 (revision 1)     I2C addresses: 0x0C/0x0D

So the current probing code would detect all versions. It didn't detect the
TDA10021 due to the different register addressing used compared to the
older versions, that's why you need to remove the 0x00 from the I2C message
buffers.
I've done the steps you mentioned in your last post (renamed and delete 0x00 from the named methods) with one exception, in i2c-id.h exists one define (I2C_DRIVERID_VES1820) for VES1820 but don't for tda10021. So i used the same id for tda10021. Is this correct?

Oh, I just noticed another thing: The TDA10021 has a different I2C address
than the VES1820s, it can be 0x0C or 0x0D. Interestingly enough, the old
code only probed two addresses, although the VES1820 allowed up to 8
different ones...

Anyway, change the probe_demod_addr() function as follows:

{.addr = 0x08,.flags = I2C_M_RD,.buf = &id,.len = 1}

-> change 0x08 to 0x0C

msg[0].addr = msg[1].addr = 0x09;

-> change 0x09 to 0x0D

I've changed this too but nothing happens after loading modules with modified cvs version of insmod.sh with added tda10021.ko and removed usb modules: dvb-ttusb-budget.ko, ttusb_dec.ko, dvb-dibusb.ko
************* syslos *************
Sep 25 22:33:31 nias kernel: Linux video capture interface: v1.00
Sep 25 22:33:31 nias kernel: /home/nias/apps/dvb-kernel-cvs/dvb-kernel/build-2.6/dib3000mb.c: did not found a DiBCom 3000-MB.
Sep 25 22:33:31 nias kernel: mt312_read: ret == -38
Sep 25 22:33:31 nias kernel: saa7146: register extension 'dvb'.
Sep 25 22:33:31 nias kernel: saa7146: register extension 'budget dvb'.
Sep 25 22:33:31 nias kernel: saa7146: register extension 'budget_ci dvb'.
Sep 25 22:33:31 nias kernel: saa7146: register extension 'budget dvb /w video in'.
Sep 25 22:38:01 nias CRON[7235]: No module specific data is present
**********************************

For the above tests, i don't used my modified budget-av.c where i changed the id from 0x1154 to 0x1156 for the MAKE_EXTENSION_PCI(cin1200,...) call.

If i'm also using the modified version of budget-av.c the following happens.
************* syslos *************
Sep 25 22:39:00 nias kernel: Linux video capture interface: v1.00
Sep 25 22:39:00 nias kernel: /home/nias/apps/dvb-kernel-cvs/dvb-kernel/build-2.6/dib3000mb.c: did not found a DiBCom 3000-MB.
Sep 25 22:39:00 nias kernel: mt312_read: ret == -38
Sep 25 22:39:00 nias kernel: saa7146: register extension 'dvb'.
Sep 25 22:39:00 nias kernel: saa7146: register extension 'budget dvb'.
Sep 25 22:39:00 nias kernel: saa7146: register extension 'budget_ci dvb'.
Sep 25 22:39:00 nias kernel: saa7146: register extension 'budget dvb /w video in'.
Sep 25 22:39:00 nias kernel: ACPI: PCI interrupt 0000:00:0f.0[A] -> GSI 18 (level, low) -> IRQ 193
Sep 25 22:39:00 nias kernel: saa7146: found saa7146 @ mem f8de4000 (revision 1, irq 193) (0x153b,0x1156).
Sep 25 22:39:00 nias kernel: DVB: registering new adapter (TerraTec Cinergy 1200 DVB-S).
Sep 25 22:39:01 nias CRON[7411]: No module specific data is present
Sep 25 22:39:02 nias kernel: /home/nias/apps/dvb-kernel-cvs/dvb-kernel/build-2.6/dib3000mb.c: did not found a DiBCom 3000-MB.
Sep 25 22:39:03 nias kernel: cx22702: Detected Hauppauge Nova-T DVB-T - PLL Thomson DTT759x
Sep 25 22:39:03 nias kernel: cx22702_validate_eeprom eeprom content is not valid
Sep 25 22:39:03 nias kernel: mt312_read: ret == -121
Sep 25 22:39:03 nias kernel: adapter failed MAC signature check
Sep 25 22:39:03 nias kernel: encoded MAC from EEPROM was ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff
Sep 25 22:39:04 nias kernel: KNC1-0: MAC addr = 00:0a:ac:01:9e:3e
**********************************

But the tda10021 modul does'nt output something to syslog.
I don't understand, why now some DVB-T stuff will be detected.
Which modules from insmod.sh are at least necessary to load?

I've attached the tda10021.c perhaps i've done something wrong.

I will check too if i can open the metal tuner box whitout destroy them.

Markus
/*
    TDA10021  - Single Chip Cable Channel Receiver driver module
               used on the the Siemens DVB-C cards

    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.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.
*/

#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>

#include "dvb_frontend.h"

/* I2C_DRIVERID_TDA10021 is already defined in i2c-id.h */

#if 0
static int debug = 0;
#define dprintk	if (debug) printk
#endif

static int verbose;

struct tda10021_state {
	int pwm;
	u8 reg0;
	int tuner;
	u8 demod_addr;
	struct i2c_adapter *i2c;
	struct dvb_adapter *dvb;
};

/* possible tda10021 adresses */
static u8 addr[] = { 0x61, 0x62 };

#if defined(CONFIG_DBOX2)
#define XIN 69600000UL
#define DISABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
#define ENABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
#define HAS_INVERSION(reg0)		(reg0 & 0x20)
#else				/* PCI cards */
#define XIN 57840000UL
#define DISABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
#define ENABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
#define HAS_INVERSION(reg0)		(!(reg0 & 0x20))
#endif

#define FIN (XIN >> 4)

static struct dvb_frontend_info tda10021_info = {
	.name = "TDA10021 based DVB-C frontend",
	.type = FE_QAM,
	.frequency_stepsize = 62500,
	.frequency_min = 51000000,
	.frequency_max = 858000000,
	.symbol_rate_min = (XIN / 2) / 64,	/* SACLK/64 == (XIN/2)/64 */
	.symbol_rate_max = (XIN / 2) / 4,	/* SACLK/4 */
#if 0
	.frequency_tolerance = ? ? ?,
	.symbol_rate_tolerance = ? ? ?,	/* ppm *//* == 8% (spec p. 5) */
	.notifier_delay = ?,
#endif
	.caps = FE_CAN_QAM_16 |
		FE_CAN_QAM_32 |
		FE_CAN_QAM_64 |
		FE_CAN_QAM_128 |
		FE_CAN_QAM_256 |
		FE_CAN_FEC_AUTO |
		FE_CAN_INVERSION_AUTO,
};

static u8 tda10021_inittab[] = {
	0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A,
	0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
	0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x40
};

static int tda10021_writereg(struct tda10021_state *state, u8 reg, u8 data)
{
	u8 buf[] = { reg, data };
	struct i2c_msg msg = {.addr = state->demod_addr,.flags = 0,.buf = buf,.len = 3 };
	int ret;

	ret = i2c_transfer(state->i2c, &msg, 1);

	if (ret != 1)
		printk("tda10021: %s(): writereg error (reg == 0x%02x,"
			"val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);

	msleep(10);
	return (ret != 1) ? -EREMOTEIO : 0;
}

static u8 tda10021_readreg(struct tda10021_state *state, u8 reg)
{
	u8 b0[] = { reg };
	u8 b1[] = { 0 };
	struct i2c_msg msg[] = {
		{.addr = state->demod_addr,.flags = 0,.buf = b0,.len = 2},
		{.addr = state->demod_addr,.flags = I2C_M_RD,.buf = b1,.len = 1}
	};
	int ret;

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

	if (ret != 2)
		printk("tda10021: %s(): readreg error (reg == 0x%02x,"
		"ret == %i)\n", __FUNCTION__, reg, ret);

	return b1[0];
}

static int tuner_write(struct tda10021_state *state, u8 addr, u8 data[4])
{
	int ret;
	struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = data,.len = 4 };

	ret = i2c_transfer(state->i2c, &msg, 1);

	if (ret != 1)
		printk("tda10021: %s(): i/o error (ret == %i)\n", __FUNCTION__, ret);

	return (ret != 1) ? -EREMOTEIO : 0;
}

/**
 *   set up the downconverter frequency divisor for a
 *   reference clock comparision frequency of 62.5 kHz.
 */
static int tuner_set_tv_freq(struct tda10021_state *state, u32 freq)
{
	u32 div, ifreq;
	static u8 byte3[] = { 0x8e, 0x85 };
	int tuner_type = state->tuner;
	u8 buf[4];

	if (tuner_type == 0xff)	/*  PLL not reachable over i2c ...  */
		return 0;

	if (strstr(state->i2c->name, "Technotrend")
	 || strstr(state->i2c->name, "TT-Budget"))
		ifreq = 35937500;
	else
		ifreq = 36125000;

	div = (freq + ifreq + 31250) / 62500;

	buf[0] = (div >> 8) & 0x7f;
	buf[1] = div & 0xff;
	buf[2] = byte3[tuner_type];

	if (tuner_type == 1) {
		buf[2] |= (div >> 10) & 0x60;
		buf[3] = (freq < 174000000 ? 0x88 : freq < 470000000 ? 0x84 : 0x81);
	} else {
		buf[3] = (freq < 174000000 ? 0xa1 : freq < 454000000 ? 0x92 : 0x34);
	}

	return tuner_write(state, addr[tuner_type], buf);
}

static int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0, fe_spectral_inversion_t inversion)
{
	reg0 |= state->reg0 & 0x62;

	if (INVERSION_ON == inversion)
		ENABLE_INVERSION(reg0);
	else if (INVERSION_OFF == inversion)
		DISABLE_INVERSION(reg0);

	tda10021_writereg(state, 0x00, reg0 & 0xfe);
	tda10021_writereg(state, 0x00, reg0 | 0x01);

	/**
	 *  check lock and toggle inversion bit if required...
	 */
	if (INVERSION_AUTO == inversion && !(tda10021_readreg(state, 0x11) & 0x08)) {
		mdelay(50);
		if (!(tda10021_readreg(state, 0x11) & 0x08)) {
			reg0 ^= 0x20;
			tda10021_writereg(state, 0x00, reg0 & 0xfe);
			tda10021_writereg(state, 0x00, reg0 | 0x01);
		}
	}

	state->reg0 = reg0;

	return 0;
}

static int tda10021_init(struct tda10021_state *state)
{
	int i;

	tda10021_writereg(state, 0, 0);

#if defined(CONFIG_DBOX2)
	tda10021_inittab[2] &= ~0x08;
#endif

	for (i = 0; i < 53; i++)
		tda10021_writereg(state, i, tda10021_inittab[i]);

	tda10021_writereg(state, 0x34, state->pwm);

	return 0;
}

static int tda10021_set_symbolrate(struct tda10021_state *state, u32 symbolrate)
{
	s32 BDR;
	s32 BDRI;
	s16 SFIL = 0;
	u16 NDEC = 0;
	u32 tmp, ratio;

	if (symbolrate > XIN / 2)
		symbolrate = XIN / 2;

	if (symbolrate < 500000)
		symbolrate = 500000;

	if (symbolrate < XIN / 16)
		NDEC = 1;
	if (symbolrate < XIN / 32)
		NDEC = 2;
	if (symbolrate < XIN / 64)
		NDEC = 3;

	if (symbolrate < (u32) (XIN / 12.3))
		SFIL = 1;
	if (symbolrate < (u32) (XIN / 16))
		SFIL = 0;
	if (symbolrate < (u32) (XIN / 24.6))
		SFIL = 1;
	if (symbolrate < (u32) (XIN / 32))
		SFIL = 0;
	if (symbolrate < (u32) (XIN / 49.2))
		SFIL = 1;
	if (symbolrate < (u32) (XIN / 64))
		SFIL = 0;
	if (symbolrate < (u32) (XIN / 98.4))
		SFIL = 1;

	symbolrate <<= NDEC;
	ratio = (symbolrate << 4) / FIN;
	tmp = ((symbolrate << 4) % FIN) << 8;
	ratio = (ratio << 8) + tmp / FIN;
	tmp = (tmp % FIN) << 8;
	ratio = (ratio << 8) + (tmp + FIN / 2) / FIN;

	BDR = ratio;
	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;

	if (BDRI > 0xFF)
		BDRI = 0xFF;

	SFIL = (SFIL << 4) | tda10021_inittab[0x0E];

	NDEC = (NDEC << 6) | tda10021_inittab[0x03];

	tda10021_writereg(state, 0x03, NDEC);
	tda10021_writereg(state, 0x0a, BDR & 0xff);
	tda10021_writereg(state, 0x0b, (BDR >> 8) & 0xff);
	tda10021_writereg(state, 0x0c, (BDR >> 16) & 0x3f);

	tda10021_writereg(state, 0x0d, BDRI);
	tda10021_writereg(state, 0x0e, SFIL);

	return 0;
}

static int tda10021_set_parameters(struct tda10021_state *state, struct dvb_frontend_parameters *p)
{
	static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
	static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
	static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
	static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
	static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
	int real_qam = p->u.qam.modulation - QAM_16;

	if (real_qam < 0 || real_qam > 4)
		return -EINVAL;

	tuner_set_tv_freq(state, p->frequency);
	tda10021_set_symbolrate(state, p->u.qam.symbol_rate);
	tda10021_writereg(state, 0x34, state->pwm);

	tda10021_writereg(state, 0x01, reg0x01[real_qam]);
	tda10021_writereg(state, 0x05, reg0x05[real_qam]);
	tda10021_writereg(state, 0x08, reg0x08[real_qam]);
	tda10021_writereg(state, 0x09, reg0x09[real_qam]);

	tda10021_setup_reg0(state, reg0x00[real_qam], p->inversion);

	/* yes, this speeds things up: userspace reports lock in about 8 ms
	   instead of 500 to 1200 ms after calling FE_SET_FRONTEND. */
	mdelay(50);

	return 0;
}

static int tda10021_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
	struct tda10021_state *state = (struct tda10021_state *) fe->data;

	switch (cmd) {
	case FE_GET_INFO:
		memcpy(arg, &tda10021_info, sizeof(struct dvb_frontend_info));
		break;

	case FE_READ_STATUS:
		{
			fe_status_t *status = (fe_status_t *) arg;
			int sync;

			*status = 0;

			sync = tda10021_readreg(state, 0x11);

			if (sync & 1)
				*status |= FE_HAS_SIGNAL;

			if (sync & 2)
				*status |= FE_HAS_CARRIER;

			if (sync & 2)	/* XXX FIXME! */
				*status |= FE_HAS_VITERBI;

			if (sync & 4)
				*status |= FE_HAS_SYNC;

			if (sync & 8)
				*status |= FE_HAS_LOCK;

			break;
		}

	case FE_READ_BER:
		{
			u32 ber = tda10021_readreg(state, 0x14) |
					(tda10021_readreg(state, 0x15) << 8) |
					((tda10021_readreg(state, 0x16) & 0x0f) << 16);
			*((u32 *) arg) = 10 * ber;
			break;
		}
	case FE_READ_SIGNAL_STRENGTH:
		{
			u8 gain = tda10021_readreg(state, 0x17);
			*((u16 *) arg) = (gain << 8) | gain;
			break;
		}

	case FE_READ_SNR:
		{
			u8 quality = ~tda10021_readreg(state, 0x18);
			*((u16 *) arg) = (quality << 8) | quality;
			break;
		}

	case FE_READ_UNCORRECTED_BLOCKS:
		*((u32 *) arg) = tda10021_readreg(state, 0x13) & 0x7f;
		if (*((u32 *) arg) == 0x7f)
			*((u32 *) arg) = 0xffffffff;
		/* reset uncorrected block counter */
		tda10021_writereg(state, 0x10, tda10021_inittab[0x10] & 0xdf);
		tda10021_writereg(state, 0x10, tda10021_inittab[0x10]);
		break;

	case FE_SET_FRONTEND:
		return tda10021_set_parameters(state, arg);

	case FE_GET_FRONTEND:
		{
			struct dvb_frontend_parameters *p = (struct dvb_frontend_parameters *) arg;
			int sync;
			s8 afc = 0;

			sync = tda10021_readreg(state, 0x11);
			afc = tda10021_readreg(state, 0x19);
			if (verbose) {
				/* AFC only valid when carrier has been recovered */
				printk(sync & 2 ? "tda10021: AFC (%d) %dHz\n" :
					"tda10021: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
			}

			p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
			p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;

			p->u.qam.fec_inner = FEC_NONE;

			p->frequency = ((p->frequency + 31250) / 62500) * 62500;
			if (sync & 2)
				p->frequency -= ((s32) p->u.qam.symbol_rate * afc) >> 10;
			break;
		}
	case FE_SLEEP:
		tda10021_writereg(state, 0x1b, 0x02);	/* pdown ADC */
		tda10021_writereg(state, 0x00, 0x80);	/* standby */
		break;

	case FE_INIT:
		return tda10021_init(state);

	default:
		return -EINVAL;
	}

	return 0;
}

static long probe_tuner(struct i2c_adapter *i2c)
{
	struct i2c_msg msg1 = {.addr = 0x61,.flags = 0,.buf = NULL,.len = 0 };
	struct i2c_msg msg2 = {.addr = 0x62,.flags = 0,.buf = NULL,.len = 0 };
	int type;

	if (i2c_transfer(i2c, &msg1, 1) == 1) {
		type = 0;
		printk("tda10021: setup for tuner spXXXX\n");
	} else if (i2c_transfer(i2c, &msg2, 1) == 1) {
		type = 1;
		printk("tda10021: setup for tuner sp5659c\n");
	} else {
		type = -1;
	}

	return type;
}

static u8 read_pwm(struct i2c_adapter *i2c)
{
	u8 b = 0xff;
	u8 pwm;
	struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
	{.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
	};

	if ((i2c_transfer(i2c, msg, 2) != 2) || (pwm == 0xff))
		pwm = 0x48;

	printk("tda10021: pwm=0x%02x\n", pwm);

	return pwm;
}

static long probe_demod_addr(struct i2c_adapter *i2c)
{
	u8 b[] = { 0x00, 0x1a };
	u8 id;
	struct i2c_msg msg[] = { 
	{.addr = 0x08,.flags = 0,.buf = b,.len = 2},
	{.addr = 0x0c,.flags = I2C_M_RD,.buf = &id,.len = 1}
	};

	if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
		return msg[0].addr;

	msg[0].addr = msg[1].addr = 0x0d;

	if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
		return msg[0].addr;

	return -1;
}

static ssize_t attr_read_pwm(struct device *dev, char *buf)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);
	return sprintf(buf, "0x%02x\n", state->pwm);
}

static ssize_t attr_write_pwm(struct device *dev, const char *buf, size_t count)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);
	unsigned long pwm;
	pwm = simple_strtoul(buf, NULL, 0);
	state->pwm = pwm & 0xff;
	return strlen(buf)+1;
}

static struct device_attribute dev_attr_client_name = {
	.attr	= { .name = "pwm", .mode = S_IRUGO|S_IWUGO, .owner = THIS_MODULE },
	.show	= &attr_read_pwm,
	.store  = &attr_write_pwm,
};

static struct i2c_client client_template;

static int attach_adapter(struct i2c_adapter *adapter)
{
	struct i2c_client *client;
	struct tda10021_state *state;
	long demod_addr;
	int tuner_type;
	int ret;

	demod_addr = probe_demod_addr(adapter);
	if (demod_addr < 0)
		return -ENODEV;

	tuner_type = probe_tuner(adapter);
	if (tuner_type < 0) {
		printk("tda10021: demod found, but unknown tuner type.\n");
		return -ENODEV;
	}

	if ((state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL)) == NULL) {
		return -ENOMEM;
	}

	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
		kfree(state);
		return -ENOMEM;
	}

	memset(state, 0, sizeof(*state));
	state->i2c = adapter;
	state->tuner = tuner_type;
	state->pwm = read_pwm(adapter);
	state->reg0 = tda10021_inittab[0];
	state->demod_addr = demod_addr;

	memcpy(client, &client_template, sizeof(struct i2c_client));
	client->adapter = adapter;
	client->addr = addr[tuner_type];

	i2c_set_clientdata(client, (void *) state);

	ret = i2c_attach_client(client);
	if (ret) {
		kfree(client);
		kfree(state);
		return ret;
	}

	BUG_ON(!state->dvb);

	device_create_file(&client->dev, &dev_attr_client_name);

	ret = dvb_register_frontend(tda10021_ioctl, state->dvb, state, &tda10021_info, THIS_MODULE);
	if (ret) {
		i2c_detach_client(client);
		kfree(client);
		kfree(state);
		return ret;
	}

	return 0;
}

static int detach_client(struct i2c_client *client)
{
	struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);
	dvb_unregister_frontend(tda10021_ioctl, state->dvb);
	device_remove_file(&client->dev, &dev_attr_client_name);
	i2c_detach_client(client);
	BUG_ON(state->dvb);
	kfree(client);
	kfree(state);
	return 0;
}

static int command(struct i2c_client *client, unsigned int cmd, void *arg)
{
	struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);

	switch (cmd) {
	case FE_REGISTER:{
			state->dvb = (struct dvb_adapter *) arg;
			break;
		}
	case FE_UNREGISTER:{
			state->dvb = NULL;
			break;
		}
	default:
		return -EOPNOTSUPP;
	}
	return 0;
}

static struct i2c_driver driver = {
	.owner = THIS_MODULE,
	.name = "tda10021",
	.id = I2C_DRIVERID_VES1820,
	.flags = I2C_DF_NOTIFY,
	.attach_adapter = attach_adapter,
	.detach_client = detach_client,
	.command = command,
};

static struct i2c_client client_template = {
	I2C_DEVNAME("tda10021"),
	.flags = I2C_CLIENT_ALLOW_USE,
	.driver = &driver,
};

static int __init init_tda10021(void)
{
	return i2c_add_driver(&driver);
}

static void __exit exit_tda10021(void)
{
	if (i2c_del_driver(&driver))
		printk("tda10021: driver deregistration failed\n");
}

module_init(init_tda10021);
module_exit(exit_tda10021);

MODULE_PARM(verbose, "i");
MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");

MODULE_DESCRIPTION("TDA10021 DVB-C frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");

Home | Main Index | Thread Index