/*
 * Infineon TUA 9001 silicon tuner driver
 *
 * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
 *
 *    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 "fc2580.h"
#include "fc2580_priv.h"

static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug");

/* write single register */
static int fc2580_write_reg(struct fc2580_priv *priv, u8 reg, u8 val)
{
	u8 buf[2] = {reg, val};
	struct i2c_msg msg[1] = {
		{
			.addr = priv->cfg->i2c_address,
			.flags = 0,
			.len = 2,
			.buf = buf
		},
	};

	if (i2c_transfer(priv->i2c, msg, 1) != 1) {
		warn("I2C write failed reg:%02x", reg);
		return -EREMOTEIO;
	}
	return 0;
}

/* read single register */
static int fc2580_read_reg(struct fc2580_priv *priv, u8 reg, u8 *val)
{
	struct i2c_msg msg[2] = {
		{
			.addr = priv->cfg->i2c_address,
			.flags = 0,
			.len = 1,
			.buf = &reg
		}, {
			.addr = priv->cfg->i2c_address,
			.flags = I2C_M_RD,
			.len = 1,
			.buf = val
		}
	};

	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
		warn("I2C read failed reg:%02x", reg);
		return -EREMOTEIO;
	}
	return 0;
}

static int fc2580_release(struct dvb_frontend *fe)
{
	deb_info("%s:\n", __func__);
	kfree(fe->tuner_priv);
	fe->tuner_priv = NULL;
	return 0;
}


static int fc2580_set_filter(struct dvb_frontend *fe)
{
	u8 cal_mon, i;
	int ret = 0;
	struct fc2580_priv *priv = fe->tuner_priv;
	struct regdesc data1[] = {
		{0x36, 0x18},
		{0x37, 0x36},
		{0x39, 0x80},
		{0x2e, 0x09},
	};

	deb_info("%s:\n", __func__);

	for (i = 0; i < ARRAY_SIZE(data1); i++) {
		ret = fc2580_write_reg(priv, data1[i].reg, data1[i].val);
		if (ret)
			break;
	}

	
	for(i=0; i<5; i++) {

		deb_info("%s: LOOOP\n", __func__);

		msleep(10);
		ret = fc2580_read_reg(priv, 0x2f, &cal_mon);
		if( (cal_mon & 0xc0) != 0xc0) {
			ret = fc2580_write_reg(priv, 0x2e, 0x01);
			ret = fc2580_write_reg(priv, 0x2e, 0x09);
		}
		else
			break;
	}

	ret = fc2580_write_reg(priv, 0x2e, 0x01);

	return ret;
}

static int fc2580_init(struct dvb_frontend *fe)
{
	struct fc2580_priv *priv = fe->tuner_priv;
	int ret = 0;
	u8 i;

	struct regdesc data1[] = {
		{0x00, 0x00},
		{0x12, 0x86},
		{0x14, 0x5c},
		{0x16, 0x3c},
		{0x1f, 0xd2},
		{0x09, 0xd7},
		{0x0b, 0xd5},
		{0x0c, 0x32},
		{0x0e, 0x43},
		{0x21, 0x0a},
		{0x22, 0x82},
		{0x45, 0x10},
		{0x4c, 0x00},
		{0x3f, 0x88},
		{0x02, 0x0e},
		{0x58, 0x14},
// fc2580_set_filter()
//		{0x36, 0x18},
//		{0x37, 0x36},
//		{0x39, 0x80},
//		{0x2e, 0x09},
	};
	struct regdesc data2[] = {

// rd 0x2f < 0xcd
//000573:  OUT: 000002 ms 030124 ms 40 00 ac 00 10 06 01 00 >>>  2f
//000574:  OUT: 000002 ms 030126 ms c0 00 ac 00 00 06 01 00 <<<  cd
		{0x2e, 0x01},
		{0x2e, 0x09},
	};
	deb_info("%s:\n", __func__);

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */


	for (i = 0; i < ARRAY_SIZE(data1); i++) {
		ret = fc2580_write_reg(priv, data1[i].reg, data1[i].val);
		if (ret)
			break;
	}

	msleep(10);
/*
	for (i = 0; i < ARRAY_SIZE(data2); i++) {
		ret = fc2580_write_reg(priv, data2[i].reg, data2[i].val);
		if (ret)
			break;
	}
*/

	ret = fc2580_set_filter(fe);

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */

	if (ret)
		deb_info("%s: failed:%d\n", __func__, ret);

	return ret;
}

static int fc2580_set_params(struct dvb_frontend *fe,
	struct dvb_frontend_parameters *params)
{
	struct fc2580_priv *priv = fe->tuner_priv;
	int ret;
	u16 val;
	u8 i;

	struct regdesc data1[] = {
		{0x25, 0xf0},
		{0x27, 0x77},
		{0x28, 0x53},
		{0x29, 0x60},
		{0x30, 0x09},
		{0x50, 0x8c},
		{0x53, 0x50},
		{0x5f, 0x15},
		{0x61, 0x03},
		{0x62, 0x03},
		{0x67, 0x03},
		{0x68, 0x05},
		{0x69, 0x0c},
		{0x6a, 0x0e},
		{0x63, 0x15},
		{0x6b, 0x0b},
		{0x6c, 0x0c},
		{0x6d, 0x78},
		{0x6e, 0x32},
		{0x6f, 0x14},
// fc2580_set_filter()
//		{0x36, 0x18},
//		{0x37, 0x36},
//		{0x39, 0x80},
//		{0x2e, 0x09},
	};


	struct regdesc data2[] = {
// rd 0x2f < 0xcd
//000573:  OUT: 000002 ms 030124 ms 40 00 ac 00 10 06 01 00 >>>  2f
//000574:  OUT: 000002 ms 030126 ms c0 00 ac 00 00 06 01 00 <<<  cd
//		{0x2e, 0x01},
		{0x02, 0x0e},
		{0x18, 0x02},
		{0x1a, 0x88},
		{0x1b, 0x00},
		{0x1c, 0x57},
		{0x2d, 0x9f},
		{0x36, 0x18},
		{0x37, 0x36},
		{0x39, 0x80},
		{0x2e, 0x09},
	};
// rd 0x2f < 0xcd
//000573:  OUT: 000002 ms 030124 ms 40 00 ac 00 10 06 01 00 >>>  2f
//000574:  OUT: 000002 ms 030126 ms c0 00 ac 00 00 06 01 00 <<<  cd

	struct regdesc data3[] = {
		{0x2e, 0x01},
	};
	deb_info("%s:\n", __func__);

	deb_info("%s: freq:%d bw:%d freq tuner:%d val:%d\n", __func__,
		params->frequency, params->u.ofdm.bandwidth, priv->frequency,
		val);

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */

	for (i = 0; i < ARRAY_SIZE(data1); i++) {
		ret = fc2580_write_reg(priv, data1[i].reg, data1[i].val);
		if (ret)
			break;
	}

	ret = fc2580_set_filter(fe);
	msleep(10);

	for (i = 0; i < ARRAY_SIZE(data2); i++) {
		ret = fc2580_write_reg(priv, data2[i].reg, data2[i].val);
		if (ret)
			break;
	}

	msleep(10);

	for (i = 0; i < ARRAY_SIZE(data3); i++) {
		ret = fc2580_write_reg(priv, data3[i].reg, data3[i].val);
		if (ret)
			break;
	}

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */

	if (ret)
		deb_info("%s: failed:%d\n", __func__, ret);

	return ret;
}

static int fc2580_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
	struct fc2580_priv *priv = fe->tuner_priv;
	*frequency = priv->frequency;
	return 0;
}

static const struct dvb_tuner_ops fc2580_tuner_ops = {
	.info = {
		.name           = "FCI FC2580",

		.frequency_min  = 170000000,
		.frequency_max  = 860000000,
		.frequency_step = 0,
	},

	.release       = fc2580_release,
	.init          = fc2580_init,

	.set_params    = fc2580_set_params,

//	.get_frequency = fc2580_get_frequency,
};

struct dvb_frontend * fc2580_attach(struct dvb_frontend *fe,
	struct i2c_adapter *i2c, struct fc2580_config *cfg)
{
	int ret;
	u8 tmp;
	struct fc2580_priv *priv = NULL;

	priv = kzalloc(sizeof(struct fc2580_priv), GFP_KERNEL);
	if (priv == NULL)
		return NULL;

	priv->cfg = cfg;
	priv->i2c = i2c;

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */

	/* check if the tuner is there */
	ret = fc2580_read_reg(priv, 0x01, &tmp);

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */

	if (ret || tmp != 0x56) {
		kfree(priv);
		return NULL;
	}

	printk(KERN_INFO "FCI FC2580 successfully identified.\n");
	info("FCI FC2580 successfully identified.");

	memcpy(&fe->ops.tuner_ops, &fc2580_tuner_ops,
		sizeof(struct dvb_tuner_ops));

	fe->tuner_priv = priv;
	return fe;
}
EXPORT_SYMBOL(fc2580_attach);

MODULE_DESCRIPTION("FCI FC2580 silicon tuner driver");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_LICENSE("GPL");
