Commit 4d1f413e authored by HIRANO Takahito's avatar HIRANO Takahito Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB: pt1: Support Earthsoft PT2



Support Earthsoft PT2.

Signed-off-by: default avatarHIRANO Takahito <hiranotaka@zng.info>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 2f790884
Loading
Loading
Loading
Loading
+211 −60
Original line number Diff line number Diff line
/*
 * driver for Earthsoft PT1
 * driver for Earthsoft PT1/PT2
 *
 * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
 *
@@ -77,6 +77,10 @@ struct pt1 {
	struct pt1_adapter *adaps[PT1_NR_ADAPS];
	struct pt1_table *tables;
	struct task_struct *kthread;

	struct mutex lock;
	int power;
	int reset;
};

struct pt1_adapter {
@@ -95,6 +99,11 @@ struct pt1_adapter {
	struct dvb_frontend *fe;
	int (*orig_set_voltage)(struct dvb_frontend *fe,
				fe_sec_voltage_t voltage);
	int (*orig_sleep)(struct dvb_frontend *fe);
	int (*orig_init)(struct dvb_frontend *fe);

	fe_sec_voltage_t voltage;
	int sleep;
};

#define pt1_printk(level, pt1, format, arg...)	\
@@ -219,8 +228,10 @@ static int pt1_do_enable_ram(struct pt1 *pt1)
static int pt1_enable_ram(struct pt1 *pt1)
{
	int i, ret;
	int phase;
	schedule_timeout_uninterruptible((HZ + 999) / 1000);
	for (i = 0; i < 10; i++) {
	phase = pt1->pdev->device == 0x211a ? 128 : 166;
	for (i = 0; i < phase; i++) {
		ret = pt1_do_enable_ram(pt1);
		if (ret < 0)
			return ret;
@@ -485,33 +496,47 @@ static int pt1_stop_feed(struct dvb_demux_feed *feed)
}

static void
pt1_set_power(struct pt1 *pt1, int power, int lnb, int reset)
{
	pt1_write_reg(pt1, 1, power | lnb << 1 | !reset << 3);
}

static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
pt1_update_power(struct pt1 *pt1)
{
	int bits;
	int i;
	struct pt1_adapter *adap;
	int lnb;

	adap = container_of(fe->dvb, struct pt1_adapter, adap);
	static const int sleep_bits[] = {
		1 << 4,
		1 << 6 | 1 << 7,
		1 << 5,
		1 << 6 | 1 << 8,
	};

	switch (voltage) {
	bits = pt1->power | !pt1->reset << 3;
	mutex_lock(&pt1->lock);
	for (i = 0; i < PT1_NR_ADAPS; i++) {
		adap = pt1->adaps[i];
		switch (adap->voltage) {
		case SEC_VOLTAGE_13: /* actually 11V */
		lnb = 2;
			bits |= 1 << 1;
			break;
		case SEC_VOLTAGE_18: /* actually 15V */
		lnb = 3;
		break;
	case SEC_VOLTAGE_OFF:
		lnb = 0;
			bits |= 1 << 1 | 1 << 2;
			break;
		default:
		return -EINVAL;
			break;
		}

	pt1_set_power(adap->pt1, 1, lnb, 0);
		/* XXX: The bits should be changed depending on adap->sleep. */
		bits |= sleep_bits[i];
	}
	pt1_write_reg(pt1, 1, bits);
	mutex_unlock(&pt1->lock);
}

static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
	struct pt1_adapter *adap;

	adap = container_of(fe->dvb, struct pt1_adapter, adap);
	adap->voltage = voltage;
	pt1_update_power(adap->pt1);

	if (adap->orig_set_voltage)
		return adap->orig_set_voltage(fe, voltage);
@@ -519,9 +544,37 @@ static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
		return 0;
}

static int pt1_sleep(struct dvb_frontend *fe)
{
	struct pt1_adapter *adap;

	adap = container_of(fe->dvb, struct pt1_adapter, adap);
	adap->sleep = 1;
	pt1_update_power(adap->pt1);

	if (adap->orig_sleep)
		return adap->orig_sleep(fe);
	else
		return 0;
}

static int pt1_wakeup(struct dvb_frontend *fe)
{
	struct pt1_adapter *adap;

	adap = container_of(fe->dvb, struct pt1_adapter, adap);
	adap->sleep = 0;
	pt1_update_power(adap->pt1);
	schedule_timeout_uninterruptible((HZ + 999) / 1000);

	if (adap->orig_init)
		return adap->orig_init(fe);
	else
		return 0;
}

static void pt1_free_adapter(struct pt1_adapter *adap)
{
	dvb_unregister_frontend(adap->fe);
	dvb_net_release(&adap->net);
	adap->demux.dmx.close(&adap->demux.dmx);
	dvb_dmxdev_release(&adap->dmxdev);
@@ -534,7 +587,7 @@ static void pt1_free_adapter(struct pt1_adapter *adap)
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);

static struct pt1_adapter *
pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe)
pt1_alloc_adapter(struct pt1 *pt1)
{
	struct pt1_adapter *adap;
	void *buf;
@@ -551,8 +604,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe)

	adap->pt1 = pt1;

	adap->orig_set_voltage = fe->ops.set_voltage;
	fe->ops.set_voltage = pt1_set_voltage;
	adap->voltage = SEC_VOLTAGE_OFF;
	adap->sleep = 1;

	buf = (u8 *)__get_free_page(GFP_KERNEL);
	if (!buf) {
@@ -593,17 +646,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe)

	dvb_net_init(dvb_adap, &adap->net, &demux->dmx);

	ret = dvb_register_frontend(dvb_adap, fe);
	if (ret < 0)
		goto err_net_release;
	adap->fe = fe;

	return adap;

err_net_release:
	dvb_net_release(&adap->net);
	adap->demux.dmx.close(&adap->demux.dmx);
	dvb_dmxdev_release(&adap->dmxdev);
err_dmx_release:
	dvb_dmx_release(demux);
err_unregister_adapter:
@@ -623,6 +667,62 @@ static void pt1_cleanup_adapters(struct pt1 *pt1)
		pt1_free_adapter(pt1->adaps[i]);
}

static int pt1_init_adapters(struct pt1 *pt1)
{
	int i;
	struct pt1_adapter *adap;
	int ret;

	for (i = 0; i < PT1_NR_ADAPS; i++) {
		adap = pt1_alloc_adapter(pt1);
		if (IS_ERR(adap)) {
			ret = PTR_ERR(adap);
			goto err;
		}

		adap->index = i;
		pt1->adaps[i] = adap;
	}
	return 0;

err:
	while (i--)
		pt1_free_adapter(pt1->adaps[i]);

	return ret;
}

static void pt1_cleanup_frontend(struct pt1_adapter *adap)
{
	dvb_unregister_frontend(adap->fe);
}

static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe)
{
	int ret;

	adap->orig_set_voltage = fe->ops.set_voltage;
	adap->orig_sleep = fe->ops.sleep;
	adap->orig_init = fe->ops.init;
	fe->ops.set_voltage = pt1_set_voltage;
	fe->ops.sleep = pt1_sleep;
	fe->ops.init = pt1_wakeup;

	ret = dvb_register_frontend(&adap->adap, fe);
	if (ret < 0)
		return ret;

	adap->fe = fe;
	return 0;
}

static void pt1_cleanup_frontends(struct pt1 *pt1)
{
	int i;
	for (i = 0; i < PT1_NR_ADAPS; i++)
		pt1_cleanup_frontend(pt1->adaps[i]);
}

struct pt1_config {
	struct va1j5jf8007s_config va1j5jf8007s_config;
	struct va1j5jf8007t_config va1j5jf8007t_config;
@@ -630,29 +730,63 @@ struct pt1_config {

static const struct pt1_config pt1_configs[2] = {
	{
		{ .demod_address = 0x1b },
		{ .demod_address = 0x1a },
		{
			.demod_address = 0x1b,
			.frequency = VA1J5JF8007S_20MHZ,
		},
		{
			.demod_address = 0x1a,
			.frequency = VA1J5JF8007T_20MHZ,
		},
	}, {
		{ .demod_address = 0x19 },
		{ .demod_address = 0x18 },
		{
			.demod_address = 0x19,
			.frequency = VA1J5JF8007S_20MHZ,
		},
		{
			.demod_address = 0x18,
			.frequency = VA1J5JF8007T_20MHZ,
		},
	},
};

static int pt1_init_adapters(struct pt1 *pt1)
static const struct pt1_config pt2_configs[2] = {
	{
		{
			.demod_address = 0x1b,
			.frequency = VA1J5JF8007S_25MHZ,
		},
		{
			.demod_address = 0x1a,
			.frequency = VA1J5JF8007T_25MHZ,
		},
	}, {
		{
			.demod_address = 0x19,
			.frequency = VA1J5JF8007S_25MHZ,
		},
		{
			.demod_address = 0x18,
			.frequency = VA1J5JF8007T_25MHZ,
		},
	},
};

static int pt1_init_frontends(struct pt1 *pt1)
{
	int i, j;
	struct i2c_adapter *i2c_adap;
	const struct pt1_config *config;
	const struct pt1_config *configs, *config;
	struct dvb_frontend *fe[4];
	struct pt1_adapter *adap;
	int ret;

	i = 0;
	j = 0;

	i2c_adap = &pt1->i2c_adap;
	configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs;
	do {
		config = &pt1_configs[i / 2];
		config = &configs[i / 2];

		fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config,
					    i2c_adap);
@@ -681,11 +815,9 @@ static int pt1_init_adapters(struct pt1 *pt1)
	} while (i < 4);

	do {
		adap = pt1_alloc_adapter(pt1, fe[j]);
		if (IS_ERR(adap))
		ret = pt1_init_frontend(pt1->adaps[j], fe[j]);
		if (ret < 0)
			goto err;
		adap->index = j;
		pt1->adaps[j] = adap;
	} while (++j < 4);

	return 0;
@@ -695,7 +827,7 @@ err:
		fe[i]->ops.release(fe[i]);

	while (j--)
		pt1_free_adapter(pt1->adaps[j]);
		dvb_unregister_frontend(fe[j]);

	return ret;
}
@@ -890,9 +1022,12 @@ static void __devexit pt1_remove(struct pci_dev *pdev)

	kthread_stop(pt1->kthread);
	pt1_cleanup_tables(pt1);
	pt1_cleanup_adapters(pt1);
	pt1_cleanup_frontends(pt1);
	pt1_disable_ram(pt1);
	pt1_set_power(pt1, 0, 0, 1);
	pt1->power = 0;
	pt1->reset = 1;
	pt1_update_power(pt1);
	pt1_cleanup_adapters(pt1);
	i2c_del_adapter(&pt1->i2c_adap);
	pci_set_drvdata(pdev, NULL);
	kfree(pt1);
@@ -936,10 +1071,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
		goto err_pci_iounmap;
	}

	mutex_init(&pt1->lock);
	pt1->pdev = pdev;
	pt1->regs = regs;
	pci_set_drvdata(pdev, pt1);

	ret = pt1_init_adapters(pt1);
	if (ret < 0)
		goto err_kfree;

	mutex_init(&pt1->lock);

	pt1->power = 0;
	pt1->reset = 1;
	pt1_update_power(pt1);

	i2c_adap = &pt1->i2c_adap;
	i2c_adap->class = I2C_CLASS_TV_DIGITAL;
	i2c_adap->algo = &pt1_i2c_algo;
@@ -948,9 +1094,7 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
	i2c_set_adapdata(i2c_adap, pt1);
	ret = i2c_add_adapter(i2c_adap);
	if (ret < 0)
		goto err_kfree;

	pt1_set_power(pt1, 0, 0, 1);
		goto err_pt1_cleanup_adapters;

	pt1_i2c_init(pt1);
	pt1_i2c_wait(pt1);
@@ -979,19 +1123,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

	pt1_init_streams(pt1);

	pt1_set_power(pt1, 1, 0, 1);
	pt1->power = 1;
	pt1_update_power(pt1);
	schedule_timeout_uninterruptible((HZ + 49) / 50);

	pt1_set_power(pt1, 1, 0, 0);
	pt1->reset = 0;
	pt1_update_power(pt1);
	schedule_timeout_uninterruptible((HZ + 999) / 1000);

	ret = pt1_init_adapters(pt1);
	ret = pt1_init_frontends(pt1);
	if (ret < 0)
		goto err_pt1_disable_ram;

	ret = pt1_init_tables(pt1);
	if (ret < 0)
		goto err_pt1_cleanup_adapters;
		goto err_pt1_cleanup_frontends;

	kthread = kthread_run(pt1_thread, pt1, "pt1");
	if (IS_ERR(kthread)) {
@@ -1004,11 +1150,15 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

err_pt1_cleanup_tables:
	pt1_cleanup_tables(pt1);
err_pt1_cleanup_adapters:
	pt1_cleanup_adapters(pt1);
err_pt1_cleanup_frontends:
	pt1_cleanup_frontends(pt1);
err_pt1_disable_ram:
	pt1_disable_ram(pt1);
	pt1_set_power(pt1, 0, 0, 1);
	pt1->power = 0;
	pt1->reset = 1;
	pt1_update_power(pt1);
err_pt1_cleanup_adapters:
	pt1_cleanup_adapters(pt1);
err_i2c_del_adapter:
	i2c_del_adapter(i2c_adap);
err_kfree:
@@ -1027,6 +1177,7 @@ err:

static struct pci_device_id pt1_id_table[] = {
	{ PCI_DEVICE(0x10ee, 0x211a) },
	{ PCI_DEVICE(0x10ee, 0x222a) },
	{ },
};
MODULE_DEVICE_TABLE(pci, pt1_id_table);
@@ -1054,5 +1205,5 @@ module_init(pt1_init);
module_exit(pt1_cleanup);

MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>");
MODULE_DESCRIPTION("Earthsoft PT1 Driver");
MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver");
MODULE_LICENSE("GPL");
+27 −5
Original line number Diff line number Diff line
/*
 * ISDB-S driver for VA1J5JF8007
 * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
 *
 * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
 *
@@ -580,7 +580,7 @@ static void va1j5jf8007s_release(struct dvb_frontend *fe)

static struct dvb_frontend_ops va1j5jf8007s_ops = {
	.info = {
		.name = "VA1J5JF8007 ISDB-S",
		.name = "VA1J5JF8007/VA1J5JF8011 ISDB-S",
		.type = FE_QPSK,
		.frequency_min = 950000,
		.frequency_max = 2150000,
@@ -628,28 +628,50 @@ static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
	return 0;
}

static const u8 va1j5jf8007s_prepare_bufs[][2] = {
static const u8 va1j5jf8007s_20mhz_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 const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = {
	{0x04, 0x02}, {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, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
};

static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
{
	const u8 (*bufs)[2];
	int size;
	u8 addr;
	u8 buf[2];
	struct i2c_msg msg;
	int i;

	switch (state->config->frequency) {
	case VA1J5JF8007S_20MHZ:
		bufs = va1j5jf8007s_20mhz_prepare_bufs;
		size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs);
		break;
	case VA1J5JF8007S_25MHZ:
		bufs = va1j5jf8007s_25mhz_prepare_bufs;
		size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs);
		break;
	default:
		return -EINVAL;
	}

	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));
	for (i = 0; i < size; i++) {
		memcpy(buf, bufs[i], sizeof(buf));
		if (i2c_transfer(state->adap, &msg, 1) != 1)
			return -EREMOTEIO;
	}
+7 −1
Original line number Diff line number Diff line
/*
 * ISDB-S driver for VA1J5JF8007
 * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
 *
 * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
 *
@@ -24,8 +24,14 @@
#ifndef VA1J5JF8007S_H
#define VA1J5JF8007S_H

enum va1j5jf8007s_frequency {
	VA1J5JF8007S_20MHZ,
	VA1J5JF8007S_25MHZ,
};

struct va1j5jf8007s_config {
	u8 demod_address;
	enum va1j5jf8007s_frequency frequency;
};

struct i2c_adapter;
+26 −5
Original line number Diff line number Diff line
/*
 * ISDB-T driver for VA1J5JF8007
 * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
 *
 * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
 *
@@ -429,7 +429,7 @@ static void va1j5jf8007t_release(struct dvb_frontend *fe)

static struct dvb_frontend_ops va1j5jf8007t_ops = {
	.info = {
		.name = "VA1J5JF8007 ISDB-T",
		.name = "VA1J5JF8007/VA1J5JF8011 ISDB-T",
		.type = FE_OFDM,
		.frequency_min = 90000000,
		.frequency_max = 770000000,
@@ -448,29 +448,50 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = {
	.release = va1j5jf8007t_release,
};

static const u8 va1j5jf8007t_prepare_bufs[][2] = {
static const u8 va1j5jf8007t_20mhz_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}
};

static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = {
	{0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83},
	{0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c},
	{0x77, 0x03}, {0xef, 0x01}
};

int va1j5jf8007t_prepare(struct dvb_frontend *fe)
{
	struct va1j5jf8007t_state *state;
	const u8 (*bufs)[2];
	int size;
	u8 buf[2];
	struct i2c_msg msg;
	int i;

	state = fe->demodulator_priv;

	switch (state->config->frequency) {
	case VA1J5JF8007T_20MHZ:
		bufs = va1j5jf8007t_20mhz_prepare_bufs;
		size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs);
		break;
	case VA1J5JF8007T_25MHZ:
		bufs = va1j5jf8007t_25mhz_prepare_bufs;
		size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs);
		break;
	default:
		return -EINVAL;
	}

	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));
	for (i = 0; i < size; i++) {
		memcpy(buf, bufs[i], sizeof(buf));
		if (i2c_transfer(state->adap, &msg, 1) != 1)
			return -EREMOTEIO;
	}
+7 −1
Original line number Diff line number Diff line
/*
 * ISDB-T driver for VA1J5JF8007
 * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
 *
 * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
 *
@@ -24,8 +24,14 @@
#ifndef VA1J5JF8007T_H
#define VA1J5JF8007T_H

enum va1j5jf8007t_frequency {
	VA1J5JF8007T_20MHZ,
	VA1J5JF8007T_25MHZ,
};

struct va1j5jf8007t_config {
	u8 demod_address;
	enum va1j5jf8007t_frequency frequency;
};

struct i2c_adapter;