summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
blob: dc60a6a9298038d7f20e788653fee1ddfe7dac32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
 * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices 
 * based on reference design made by DiBcom (http://www.dibcom.fr/)
 *
 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
 *
 * see dvb-dibusb-core.c for more copyright details.
 *
 * This file contains functions for attaching, initializing of an appropriate
 * demodulator/frontend. I2C-stuff is also located here.
 * 
 */
#include "dvb-dibusb.h"

#include <linux/usb.h>

static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, 
		u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
	u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
	/* write only ? */
	int wo = (rbuf == NULL || rlen == 0), 
		len = 2 + wlen + (wo ? 0 : 2);
	
	sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
	sndbuf[1] = (addr & 0xfe) | (wo ? 0 : 1);

	memcpy(&sndbuf[2],wbuf,wlen);
	
	if (!wo) {
		sndbuf[wlen+2] = (rlen >> 8) & 0xff;
		sndbuf[wlen+3] = rlen & 0xff;
	}
	
	return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen);
}

/*
 * I2C master xfer function
 */
static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
	struct usb_dibusb *dib = i2c_get_adapdata(adap);
	int i;

	if (down_interruptible(&dib->i2c_sem) < 0) 
		return -EAGAIN;

	if (num > 2)
		warn("more than 2 i2c messages at a time is not handled yet. TODO.");
	
	for (i = 0; i < num; i++) {
		/* write/read request */
		if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
			if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,
						msg[i+1].buf,msg[i+1].len) < 0)
				break;
			i++;
		} else 
			if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
				break;
	}
	
	up(&dib->i2c_sem);
	return i;	
}

static u32 dibusb_i2c_func(struct i2c_adapter *adapter)
{
	return I2C_FUNC_I2C;
}

static struct i2c_algorithm dibusb_algo = {
	.name			= "DiBcom USB i2c algorithm",
	.id				= I2C_ALGO_BIT,
	.master_xfer	= dibusb_i2c_xfer,
	.functionality	= dibusb_i2c_func,
};

int dibusb_fe_init(struct usb_dibusb* dib)
{
	struct dib3000_config demod_cfg;
	int i;
	
	for (i = 0; i < sizeof(dib->dibdev->parm->demod_i2c_addrs) / sizeof(unsigned char) && 
			dib->dibdev->parm->demod_i2c_addrs[i] != 0; i++) {

		demod_cfg.demod_address = dib->dibdev->parm->demod_i2c_addrs[i];
		demod_cfg.pll_addr = dib->dibdev->parm->pll_addr;
		demod_cfg.pll_set = dib->dibdev->parm->pll_set;
		demod_cfg.pll_init = NULL;

		switch (dib->dibdev->parm->type) {
			case DIBUSB1_1:
			case DIBUSB1_1_AN2235:
				dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
			break;
			case DIBUSB2_0:
				dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
			break;
		}
		if (dib->fe != NULL) {
			info("found demodulator at i2c address 0x%x",demod_cfg.demod_address);
			break;
		}
	}

	if (dib->fe == NULL) {
		err("A frontend driver was not found for device '%s'.",
		       dib->dibdev->name);
		return -ENODEV;
	} else {
		if (dvb_register_frontend(dib->adapter, dib->fe)) {
			err("Frontend registration failed.");
			if (dib->fe->ops->release)
				dib->fe->ops->release(dib->fe);
			dib->fe = NULL;
			return -ENODEV;
		}
	}
	return 0;
}

int dibusb_fe_exit(struct usb_dibusb *dib)
{
	if (dib->fe != NULL)
		dvb_unregister_frontend(dib->fe);
	return 0;
}

int dibusb_i2c_init(struct usb_dibusb *dib)
{
	int ret = 0;

	dib->adapter->priv = dib;

	strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE);
#ifdef I2C_ADAP_CLASS_TV_DIGITAL
	dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
#else
	dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
#endif
	dib->i2c_adap.algo 		= &dibusb_algo;
	dib->i2c_adap.algo_data = NULL;
	dib->i2c_adap.id		= I2C_ALGO_BIT;
	
	i2c_set_adapdata(&dib->i2c_adap, dib);
	
	if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0)
		err("could not add i2c adapter");

	return ret;
}

int dibusb_i2c_exit(struct usb_dibusb *dib)
{
	i2c_del_adapter(&dib->i2c_adap);
	return 0;
}



/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */
int thomson_cable_eu_pll_set(struct dvb_frontend* fe, struct
		dvb_frontend_parameters* params)
{
	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
	u8 buf[4];
	struct i2c_msg msg = { 
		.addr = dib->dibdev->parm->pll_addr, 
		.flags = 0, 
		.buf = buf, 
		.len = sizeof(buf) 
	};
	u32 tfreq = (params->frequency + 36125000) / 62500;
	int vu,p0,p1,p2;

	if (params->frequency > 403250000)
		vu = 1, p2 = 1, p1 = 0, p0 = 1;
	else if (params->frequency > 115750000)
		vu = 0, p2 = 1, p1 = 1, p0 = 0;
	else if (params->frequency > 44250000)
		vu = 0, p2 = 0, p1 = 1, p0 = 1;
	else
		return -EINVAL;

	buf[0] = (tfreq >> 8) & 0x7f;
	buf[1] = tfreq & 0xff;
   	buf[2] = 0x8e;
   	buf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0;

	if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1)
		return -EIO;

	msleep(1);
	return 0;
}

int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend *fe, struct
		dvb_frontend_parameters *params)
{
	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
	u8 buf[4];
	u32 freq = params->frequency;
	u32 tfreq = (freq + 36125000) / 1000000 * 6 + 1;
	u8 TA, T210, R210, ctrl1, cp210, p4321;
	struct i2c_msg msg = { 
		.addr = dib->dibdev->parm->pll_addr, 
		.flags = 0, 
		.buf = buf, 
		.len = sizeof(buf) 
	};

	if (freq > 858000000) {
		err("frequency cannot be larger than 858 MHz.");
		return -EINVAL;
	}
	
	// contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0
	TA = 1;
	T210 = 0;
	R210 = 0x2;
	ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210;

// ********    CHARGE PUMP CONFIG vs RF FREQUENCIES     *****************
	if (freq < 470000000) 
		cp210 = 2;  // VHF Low and High band ch E12 to E4 to E12
	else if (freq < 526000000) 
		cp210 = 4;  // UHF band Ch E21 to E27
	else // if (freq < 862000000) 
		cp210 = 5;  // UHF band ch E28 to E69

//*********************    BW select  *******************************
	if (freq < 153000000) 
		p4321  = 1; // BW selected for VHF low
	else if (freq < 470000000) 
		p4321  = 2; // BW selected for VHF high E5 to E12
	else // if (freq < 862000000) 
		p4321  = 4; // BW selection for UHF E21 to E69

	buf[0] = (tfreq >> 8) & 0xff;
	buf[1] = (tfreq >> 0) & 0xff;
	buf[2] = 0xff & ctrl1;
	buf[3] =  (cp210 << 5) | (p4321);

	if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1)
		return -EIO;

	msleep(1);
	return 0;
}