diff options
author | Michel Ludwig <michel.ludwig@gmail.com> | 2007-07-18 09:29:10 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:01:03 -0500 |
commit | 701672eb7387390bff43f0c979026dd317836626 (patch) | |
tree | 8b4023f5b5b8c14aabee2e753302c0ccfd01a21a /drivers/media | |
parent | c2622e5f8ec0c53794fa33488477f07250c3e452 (diff) |
V4L/DVB (6425): Add experimental DVB frontend tuner interface to xc2028/3028
Signed-off-by: Michel Ludwig <michel.ludwig@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/tuner-xc2028.c | 225 | ||||
-rw-r--r-- | drivers/media/video/tuner-xc2028.h | 5 |
2 files changed, 191 insertions, 39 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 4b33143788d6..2bb695490ab2 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c | |||
@@ -1,6 +1,8 @@ | |||
1 | /* tuner-xc2028 | 1 | /* tuner-xc2028 |
2 | * | 2 | * |
3 | * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) | 3 | * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) |
4 | * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) | ||
5 | * - frontend interface | ||
4 | * This code is placed under the terms of the GNU General Public License v2 | 6 | * This code is placed under the terms of the GNU General Public License v2 |
5 | */ | 7 | */ |
6 | 8 | ||
@@ -9,19 +11,38 @@ | |||
9 | #include <linux/firmware.h> | 11 | #include <linux/firmware.h> |
10 | #include <linux/videodev.h> | 12 | #include <linux/videodev.h> |
11 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <media/tuner.h> | ||
12 | #include "tuner-driver.h" | 15 | #include "tuner-driver.h" |
13 | #include "tuner-xc2028.h" | 16 | #include "tuner-xc2028.h" |
14 | 17 | ||
18 | #include <linux/dvb/frontend.h> | ||
19 | #include "dvb_frontend.h" | ||
20 | |||
21 | /* digital TV standards */ | ||
22 | #define V4L2_STD_DTV_6MHZ ((v4l2_std_id)0x04000000) | ||
23 | #define V4L2_STD_DTV_7MHZ ((v4l2_std_id)0x08000000) | ||
24 | #define V4L2_STD_DTV_8MHZ ((v4l2_std_id)0x10000000) | ||
25 | |||
15 | /* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */ | 26 | /* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */ |
16 | static const char *firmware_6M = "tm6000_xc3028_DTV_6M.fw"; | 27 | static const char *firmware_6M = "tm6000_xc3028_DTV_6M.fw"; |
17 | static const char *firmware_8M = "tm6000_xc3028_78M.fw"; | 28 | // static const char *firmware_78M = "tm6000_xc3028_78M.fw"; |
29 | static const char *firmware_7M = "tm6000_xc3028_7M.fw"; | ||
30 | static const char *firmware_8M = "tm6000_xc3028_8M.fw"; | ||
31 | static const char *firmware_B = "tm6000_xc3028_B_PAL.fw"; | ||
18 | static const char *firmware_DK = "tm6000_xc3028_DK_PAL_MTS.fw"; | 32 | static const char *firmware_DK = "tm6000_xc3028_DK_PAL_MTS.fw"; |
19 | static const char *firmware_MN = "tm6000_xc3028_MN_BTSC.fw"; | 33 | static const char *firmware_MN = "tm6000_xc3028_MN_BTSC.fw"; |
34 | static const char *firmware_INIT0 = "tm6000_xc3028_INIT0.fw"; | ||
35 | static const char *firmware_8MHZ_INIT0 = "tm6000_xc3028_8MHZ_INIT0.fw"; | ||
20 | 36 | ||
21 | struct xc2028_data { | 37 | struct xc2028_data { |
22 | v4l2_std_id firm_type; /* video stds supported by current firmware */ | 38 | v4l2_std_id firm_type; /* video stds supported |
23 | int bandwidth; /* Firmware bandwidth: 6M, 7M or 8M */ | 39 | by current firmware */ |
24 | int need_load_generic; /* The generic firmware were loaded? */ | 40 | fe_bandwidth_t bandwidth; /* Firmware bandwidth: |
41 | 6M, 7M or 8M */ | ||
42 | int need_load_generic; /* The generic firmware | ||
43 | were loaded? */ | ||
44 | enum tuner_mode mode; | ||
45 | struct i2c_client *i2c_client; | ||
25 | }; | 46 | }; |
26 | 47 | ||
27 | #define i2c_send(rc,c,buf,size) \ | 48 | #define i2c_send(rc,c,buf,size) \ |
@@ -80,7 +101,7 @@ static int load_firmware (struct i2c_client *c, const char *name) | |||
80 | int len=0, rc=0; | 101 | int len=0, rc=0; |
81 | static const char firmware_ver[] = "tm6000/xcv v1"; | 102 | static const char firmware_ver[] = "tm6000/xcv v1"; |
82 | 103 | ||
83 | tuner_info("Loading firmware %s\n", name); | 104 | tuner_info("xc2028: Loading firmware %s\n", name); |
84 | rc = request_firmware(&fw, name, &c->dev); | 105 | rc = request_firmware(&fw, name, &c->dev); |
85 | if (rc < 0) { | 106 | if (rc < 0) { |
86 | tuner_info("Error %d while requesting firmware\n", rc); | 107 | tuner_info("Error %d while requesting firmware\n", rc); |
@@ -154,24 +175,39 @@ err: | |||
154 | return rc; | 175 | return rc; |
155 | } | 176 | } |
156 | 177 | ||
157 | static int check_firmware(struct i2c_client *c) | 178 | static int check_firmware(struct i2c_client *c, enum tuner_mode new_mode, |
179 | fe_bandwidth_t bandwidth) | ||
158 | { | 180 | { |
159 | int rc, version; | 181 | int rc, version; |
160 | struct tuner *t = i2c_get_clientdata(c); | 182 | struct tuner *t = i2c_get_clientdata(c); |
161 | struct xc2028_data *xc2028 = t->priv; | 183 | struct xc2028_data *xc2028 = t->priv; |
162 | const char *name; | 184 | const char *name; |
185 | int change_digital_bandwidth; | ||
163 | 186 | ||
164 | if (!t->tuner_callback) { | 187 | if (!t->tuner_callback) { |
165 | printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n"); | 188 | printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n"); |
166 | return -EINVAL; | 189 | return -EINVAL; |
167 | } | 190 | } |
168 | 191 | ||
192 | printk(KERN_INFO "xc2028: I am in mode %u and I should switch to mode %i\n", | ||
193 | xc2028->mode, new_mode); | ||
194 | |||
195 | /* first of all, determine whether we have switched the mode */ | ||
196 | if(new_mode != xc2028->mode) { | ||
197 | xc2028->mode = new_mode; | ||
198 | xc2028->need_load_generic = 1; | ||
199 | } | ||
200 | |||
201 | change_digital_bandwidth = (xc2028->mode == T_DIGITAL_TV | ||
202 | && bandwidth != xc2028->bandwidth) ? 1 : 0; | ||
203 | tuner_info("xc2028: old bandwidth %u, new bandwidth %u\n", xc2028->bandwidth, | ||
204 | bandwidth); | ||
205 | |||
169 | if (xc2028->need_load_generic) { | 206 | if (xc2028->need_load_generic) { |
170 | if (xc2028->bandwidth==6) | 207 | if (xc2028->bandwidth==6) |
171 | name = firmware_6M; | 208 | name = firmware_INIT0; |
172 | else | 209 | else |
173 | name = firmware_8M; | 210 | name = firmware_8MHZ_INIT0; |
174 | |||
175 | /* Reset is needed before loading firmware */ | 211 | /* Reset is needed before loading firmware */ |
176 | rc = t->tuner_callback(c->adapter->algo_data, | 212 | rc = t->tuner_callback(c->adapter->algo_data, |
177 | XC2028_TUNER_RESET, 0); | 213 | XC2028_TUNER_RESET, 0); |
@@ -184,37 +220,54 @@ static int check_firmware(struct i2c_client *c) | |||
184 | 220 | ||
185 | xc2028->need_load_generic=0; | 221 | xc2028->need_load_generic=0; |
186 | xc2028->firm_type=0; | 222 | xc2028->firm_type=0; |
223 | if(xc2028->mode == T_DIGITAL_TV) { | ||
224 | change_digital_bandwidth=1; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | tuner_info("xc2028: I should change bandwidth %u\n", | ||
229 | change_digital_bandwidth); | ||
230 | |||
231 | if (change_digital_bandwidth) { | ||
232 | switch(bandwidth) { | ||
233 | case BANDWIDTH_8_MHZ: | ||
234 | t->std = V4L2_STD_DTV_8MHZ; | ||
235 | break; | ||
236 | |||
237 | case BANDWIDTH_7_MHZ: | ||
238 | t->std = V4L2_STD_DTV_7MHZ; | ||
239 | break; | ||
240 | |||
241 | case BANDWIDTH_6_MHZ: | ||
242 | t->std = V4L2_STD_DTV_6MHZ; | ||
243 | break; | ||
244 | |||
245 | default: | ||
246 | tuner_info("error: bandwidth not supported.\n"); | ||
247 | }; | ||
248 | xc2028->bandwidth = bandwidth; | ||
187 | } | 249 | } |
188 | 250 | ||
189 | if (xc2028->firm_type & t->std) | 251 | if (xc2028->firm_type & t->std) |
190 | return 0; | 252 | return 0; |
191 | 253 | ||
192 | send_seq (c, {0x12, 0x39}); | ||
193 | send_seq (c, {0x0c, 0x80, 0xf0, 0xf7, 0x3e, 0x75, 0xc1, 0x8a, 0xe4}); | ||
194 | send_seq (c, {0x0c, 0x02, 0x00}); | ||
195 | send_seq (c, {0x05, 0x0f, 0xee, 0xaa, 0x5f, 0xea, 0x90}); | ||
196 | send_seq (c, {0x06, 0x00, 0x0a, 0x4d, 0x8c, 0xf2, 0xd8, 0xcf, 0x30}); | ||
197 | send_seq (c, {0x06, 0x79, 0x9f}); | ||
198 | send_seq (c, {0x0b, 0x0d, 0xa4, 0x6c}); | ||
199 | send_seq (c, {0x0a, 0x01, 0x67, 0x24, 0x40, 0x08, 0xc3, 0x20, 0x10}); | ||
200 | send_seq (c, {0x0a, 0x64, 0x3c, 0xfa, 0xf7, 0xe1, 0x0c, 0x2c}); | ||
201 | send_seq (c, {0x09, 0x0b}); | ||
202 | send_seq (c, {0x10, 0x13}); | ||
203 | send_seq (c, {0x16, 0x12}); | ||
204 | send_seq (c, {0x1f, 0x02}); | ||
205 | send_seq (c, {0x21, 0x02}); | ||
206 | send_seq (c, {0x01, 0x02}); | ||
207 | send_seq (c, {0x2b, 0x10}); | ||
208 | send_seq (c, {0x02, 0x02}); | ||
209 | send_seq (c, {0x02, 0x03}); | ||
210 | send_seq (c, {0x00, 0x8c}); | ||
211 | 254 | ||
212 | if (t->std & V4L2_STD_MN) | 255 | if (t->std & V4L2_STD_MN) |
213 | name=firmware_MN; | 256 | name=firmware_MN; |
257 | else if (t->std & V4L2_STD_DTV_6MHZ) | ||
258 | name=firmware_6M; | ||
259 | else if (t->std & V4L2_STD_DTV_7MHZ) | ||
260 | name=firmware_7M; | ||
261 | else if (t->std & V4L2_STD_DTV_8MHZ) | ||
262 | name=firmware_8M; | ||
263 | else if (t->std & V4L2_STD_PAL_B) | ||
264 | name=firmware_B; | ||
214 | else | 265 | else |
215 | name=firmware_DK; | 266 | name=firmware_DK; |
216 | 267 | ||
217 | rc = load_firmware(c,name); | 268 | tuner_info("xc2028: loading firmware named %s.\n", name); |
269 | |||
270 | rc = load_firmware(c, name); | ||
218 | if (rc<0) | 271 | if (rc<0) |
219 | return rc; | 272 | return rc; |
220 | 273 | ||
@@ -231,8 +284,7 @@ static int xc2028_signal(struct i2c_client *c) | |||
231 | { | 284 | { |
232 | int lock, signal; | 285 | int lock, signal; |
233 | 286 | ||
234 | if (check_firmware(c)<0) | 287 | printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); |
235 | return 0; | ||
236 | 288 | ||
237 | lock = xc2028_get_reg(c, 0x2); | 289 | lock = xc2028_get_reg(c, 0x2); |
238 | if (lock<=0) | 290 | if (lock<=0) |
@@ -250,16 +302,37 @@ static int xc2028_signal(struct i2c_client *c) | |||
250 | 302 | ||
251 | #define DIV 15625 | 303 | #define DIV 15625 |
252 | 304 | ||
253 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) | 305 | static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */, |
306 | enum tuner_mode new_mode, fe_bandwidth_t bandwidth) | ||
254 | { | 307 | { |
255 | int rc; | 308 | int rc; |
256 | unsigned char buf[5]; | 309 | unsigned char buf[5]; |
257 | struct tuner *t = i2c_get_clientdata(c); | 310 | struct tuner *t = i2c_get_clientdata(c); |
258 | unsigned long div = (freq*62500l+DIV/2)/DIV; | 311 | u32 div, offset = 0; |
259 | 312 | ||
260 | if (check_firmware(c)<0) | 313 | printk("xc3028: should set frequency %d kHz)\n", freq / 1000); |
314 | |||
315 | if (check_firmware(c, new_mode, bandwidth)<0) | ||
261 | return; | 316 | return; |
262 | 317 | ||
318 | if(new_mode == T_DIGITAL_TV) { | ||
319 | switch(bandwidth) { | ||
320 | case BANDWIDTH_8_MHZ: | ||
321 | offset = 2750000; | ||
322 | break; | ||
323 | |||
324 | case BANDWIDTH_7_MHZ: | ||
325 | offset = 2750000; | ||
326 | break; | ||
327 | |||
328 | case BANDWIDTH_6_MHZ: | ||
329 | default: | ||
330 | printk(KERN_ERR "xc2028: bandwidth not implemented!\n"); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | div = (freq - offset + DIV/2)/DIV; | ||
335 | |||
263 | /* Reset GPIO 1 */ | 336 | /* Reset GPIO 1 */ |
264 | if (t->tuner_callback) { | 337 | if (t->tuner_callback) { |
265 | rc = t->tuner_callback( c->adapter->algo_data, | 338 | rc = t->tuner_callback( c->adapter->algo_data, |
@@ -281,6 +354,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) | |||
281 | msleep(10); | 354 | msleep(10); |
282 | // send_seq(c, {0x00, 0x00, 0x10, 0xd0, 0x00}); | 355 | // send_seq(c, {0x00, 0x00, 0x10, 0xd0, 0x00}); |
283 | // msleep(100); | 356 | // msleep(100); |
357 | |||
284 | buf[0]= 0xff & (div>>24); | 358 | buf[0]= 0xff & (div>>24); |
285 | buf[1]= 0xff & (div>>16); | 359 | buf[1]= 0xff & (div>>16); |
286 | buf[2]= 0xff & (div>>8); | 360 | buf[2]= 0xff & (div>>8); |
@@ -299,6 +373,14 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) | |||
299 | } | 373 | } |
300 | 374 | ||
301 | 375 | ||
376 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) | ||
377 | { | ||
378 | printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); | ||
379 | |||
380 | generic_set_tv_freq(c, freq * 62500l, T_ANALOG_TV, | ||
381 | BANDWIDTH_8_MHZ /* unimportant */); | ||
382 | } | ||
383 | |||
302 | static void xc2028_release(struct i2c_client *c) | 384 | static void xc2028_release(struct i2c_client *c) |
303 | { | 385 | { |
304 | struct tuner *t = i2c_get_clientdata(c); | 386 | struct tuner *t = i2c_get_clientdata(c); |
@@ -324,6 +406,8 @@ int xc2028_tuner_init(struct i2c_client *c) | |||
324 | int prd_id = xc2028_get_reg(c, 0x8); | 406 | int prd_id = xc2028_get_reg(c, 0x8); |
325 | struct xc2028_data *xc2028; | 407 | struct xc2028_data *xc2028; |
326 | 408 | ||
409 | tuner_info("Xcv2028/3028 init called!\n"); | ||
410 | |||
327 | if (init) { | 411 | if (init) { |
328 | printk (KERN_ERR "Module already initialized!\n"); | 412 | printk (KERN_ERR "Module already initialized!\n"); |
329 | return 0; | 413 | return 0; |
@@ -335,12 +419,9 @@ int xc2028_tuner_init(struct i2c_client *c) | |||
335 | return -ENOMEM; | 419 | return -ENOMEM; |
336 | t->priv = xc2028; | 420 | t->priv = xc2028; |
337 | 421 | ||
338 | #ifdef HACK | 422 | xc2028->bandwidth=BANDWIDTH_6_MHZ; |
339 | xc2028->firm_type=1; | ||
340 | xc2028->bandwidth=6; | ||
341 | #endif | ||
342 | xc2028->bandwidth=6; | ||
343 | xc2028->need_load_generic=1; | 423 | xc2028->need_load_generic=1; |
424 | xc2028->mode = T_UNINITIALIZED; | ||
344 | 425 | ||
345 | /* FIXME: Check where t->priv will be freed */ | 426 | /* FIXME: Check where t->priv will be freed */ |
346 | 427 | ||
@@ -360,3 +441,69 @@ int xc2028_tuner_init(struct i2c_client *c) | |||
360 | 441 | ||
361 | return 0; | 442 | return 0; |
362 | } | 443 | } |
444 | |||
445 | static int xc3028_set_params(struct dvb_frontend *fe, | ||
446 | struct dvb_frontend_parameters *p) | ||
447 | { | ||
448 | struct i2c_client *c = fe->tuner_priv; | ||
449 | |||
450 | printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); | ||
451 | |||
452 | generic_set_tv_freq(c, p->frequency, T_DIGITAL_TV, | ||
453 | p->u.ofdm.bandwidth); | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int xc3028_dvb_release(struct dvb_frontend *fe) | ||
459 | { | ||
460 | printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); | ||
461 | |||
462 | fe->tuner_priv = NULL; | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | static int xc3028_dvb_init(struct dvb_frontend *fe) | ||
468 | { | ||
469 | printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__); | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static const struct dvb_tuner_ops xc3028_dvb_tuner_ops = { | ||
475 | .info = { | ||
476 | .name = "Xceive XC3028", | ||
477 | .frequency_min = 42000000, | ||
478 | .frequency_max = 864000000, | ||
479 | .frequency_step = 50000, | ||
480 | }, | ||
481 | |||
482 | .release = xc3028_dvb_release, | ||
483 | .init = xc3028_dvb_init, | ||
484 | |||
485 | // int (*sleep)(struct dvb_frontend *fe); | ||
486 | |||
487 | /** This is for simple PLLs - set all parameters in one go. */ | ||
488 | .set_params = xc3028_set_params, | ||
489 | |||
490 | /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ | ||
491 | // int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); | ||
492 | |||
493 | // int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); | ||
494 | // int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); | ||
495 | |||
496 | // int (*get_status)(struct dvb_frontend *fe, u32 *status); | ||
497 | }; | ||
498 | |||
499 | int xc2028_attach(struct i2c_client *c, struct dvb_frontend *fe) | ||
500 | { | ||
501 | fe->tuner_priv = c; | ||
502 | |||
503 | memcpy(&fe->ops.tuner_ops, &xc3028_dvb_tuner_ops, sizeof(fe->ops.tuner_ops)); | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | EXPORT_SYMBOL(xc2028_attach); | ||
509 | |||
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h index 7c540c3871bf..34ff4cba131a 100644 --- a/drivers/media/video/tuner-xc2028.h +++ b/drivers/media/video/tuner-xc2028.h | |||
@@ -7,3 +7,8 @@ | |||
7 | /* xc2028 commands for callback */ | 7 | /* xc2028 commands for callback */ |
8 | #define XC2028_TUNER_RESET 0 | 8 | #define XC2028_TUNER_RESET 0 |
9 | #define XC2028_RESET_CLK 1 | 9 | #define XC2028_RESET_CLK 1 |
10 | |||
11 | struct dvb_frontend; | ||
12 | struct i2c_client; | ||
13 | |||
14 | int xc2028_attach(struct i2c_client *c, struct dvb_frontend *fe); | ||