diff options
author | Michael Krufky <mkrufky@linuxtv.org> | 2007-08-27 20:24:27 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-10-09 21:07:40 -0400 |
commit | 8d0936ed15f509c32e8f81849be3a1cee80e2225 (patch) | |
tree | 59eb2b0fe8a46d174d007b19a4401471f4aa759a /drivers/media/video/tea5767.c | |
parent | 7ab10bf72add23f0badf98ead92f58e34e14d35a (diff) |
V4L/DVB (6132): tea5767: convert from tuner sub-driver into dvb_frontend module
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Acked-by: Hans Verkuil <hverkuil@xs4all.nl>
Acked-by: Mike Isely <isely@pobox.com>
Acked-by: Steven Toth <stoth@hauppauge.com>
Acked-by: Patrick Boettcher <pb@linuxtv.org>
Acked-by: Jarod Wilson <jwilson@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/tea5767.c')
-rw-r--r-- | drivers/media/video/tea5767.c | 149 |
1 files changed, 97 insertions, 52 deletions
diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c index 5a08ec2f99fb..cf908d853195 100644 --- a/drivers/media/video/tea5767.c +++ b/drivers/media/video/tea5767.c | |||
@@ -13,15 +13,19 @@ | |||
13 | #include <linux/i2c.h> | 13 | #include <linux/i2c.h> |
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/videodev.h> | 15 | #include <linux/videodev.h> |
16 | #include "tuner-driver.h" | 16 | #include "tuner-i2c.h" |
17 | #include "tea5767.h" | ||
17 | 18 | ||
18 | #define PREFIX "TEA5767 " | 19 | static int debug = 0; |
20 | module_param(debug, int, 0644); | ||
21 | MODULE_PARM_DESC(debug, "enable verbose debug messages"); | ||
19 | 22 | ||
20 | /* from tuner-core.c */ | 23 | #define PREFIX "tea5767 " |
21 | extern int tuner_debug; | ||
22 | 24 | ||
23 | struct tea5767_priv { | 25 | struct tea5767_priv { |
24 | struct tuner_i2c_props i2c_props; | 26 | struct tuner_i2c_props i2c_props; |
27 | |||
28 | u32 frequency; | ||
25 | }; | 29 | }; |
26 | 30 | ||
27 | /*****************************************************************************/ | 31 | /*****************************************************************************/ |
@@ -133,11 +137,6 @@ enum tea5767_xtal_freq { | |||
133 | 137 | ||
134 | /*****************************************************************************/ | 138 | /*****************************************************************************/ |
135 | 139 | ||
136 | static void set_tv_freq(struct tuner *t, unsigned int freq) | ||
137 | { | ||
138 | tuner_warn("This tuner doesn't support TV freq.\n"); | ||
139 | } | ||
140 | |||
141 | static void tea5767_status_dump(unsigned char *buffer) | 140 | static void tea5767_status_dump(unsigned char *buffer) |
142 | { | 141 | { |
143 | unsigned int div, frq; | 142 | unsigned int div, frq; |
@@ -192,14 +191,16 @@ static void tea5767_status_dump(unsigned char *buffer) | |||
192 | } | 191 | } |
193 | 192 | ||
194 | /* Freq should be specifyed at 62.5 Hz */ | 193 | /* Freq should be specifyed at 62.5 Hz */ |
195 | static void set_radio_freq(struct tuner *t, unsigned int frq) | 194 | static int set_radio_freq(struct dvb_frontend *fe, |
195 | struct analog_parameters *params) | ||
196 | { | 196 | { |
197 | struct tea5767_priv *priv = t->priv; | 197 | struct tea5767_priv *priv = fe->tuner_priv; |
198 | unsigned int frq = params->frequency; | ||
198 | unsigned char buffer[5]; | 199 | unsigned char buffer[5]; |
199 | unsigned div; | 200 | unsigned div; |
200 | int rc; | 201 | int rc; |
201 | 202 | ||
202 | tuner_dbg (PREFIX "radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); | 203 | tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); |
203 | 204 | ||
204 | /* Rounds freq to next decimal value - for 62.5 KHz step */ | 205 | /* Rounds freq to next decimal value - for 62.5 KHz step */ |
205 | /* frq = 20*(frq/16)+radio_frq[frq%16]; */ | 206 | /* frq = 20*(frq/16)+radio_frq[frq%16]; */ |
@@ -209,7 +210,7 @@ static void set_radio_freq(struct tuner *t, unsigned int frq) | |||
209 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND; | 210 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND; |
210 | buffer[4] = 0; | 211 | buffer[4] = 0; |
211 | 212 | ||
212 | if (t->audmode == V4L2_TUNER_MODE_MONO) { | 213 | if (params->audmode == V4L2_TUNER_MODE_MONO) { |
213 | tuner_dbg("TEA5767 set to mono\n"); | 214 | tuner_dbg("TEA5767 set to mono\n"); |
214 | buffer[2] |= TEA5767_MONO; | 215 | buffer[2] |= TEA5767_MONO; |
215 | } else { | 216 | } else { |
@@ -219,26 +220,26 @@ static void set_radio_freq(struct tuner *t, unsigned int frq) | |||
219 | /* Should be replaced */ | 220 | /* Should be replaced */ |
220 | switch (TEA5767_HIGH_LO_32768) { | 221 | switch (TEA5767_HIGH_LO_32768) { |
221 | case TEA5767_HIGH_LO_13MHz: | 222 | case TEA5767_HIGH_LO_13MHz: |
222 | tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 13 MHz\n"); | 223 | tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); |
223 | buffer[2] |= TEA5767_HIGH_LO_INJECT; | 224 | buffer[2] |= TEA5767_HIGH_LO_INJECT; |
224 | buffer[4] |= TEA5767_PLLREF_ENABLE; | 225 | buffer[4] |= TEA5767_PLLREF_ENABLE; |
225 | div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; | 226 | div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; |
226 | break; | 227 | break; |
227 | case TEA5767_LOW_LO_13MHz: | 228 | case TEA5767_LOW_LO_13MHz: |
228 | tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 13 MHz\n"); | 229 | tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); |
229 | 230 | ||
230 | buffer[4] |= TEA5767_PLLREF_ENABLE; | 231 | buffer[4] |= TEA5767_PLLREF_ENABLE; |
231 | div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; | 232 | div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; |
232 | break; | 233 | break; |
233 | case TEA5767_LOW_LO_32768: | 234 | case TEA5767_LOW_LO_32768: |
234 | tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 32,768 MHz\n"); | 235 | tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); |
235 | buffer[3] |= TEA5767_XTAL_32768; | 236 | buffer[3] |= TEA5767_XTAL_32768; |
236 | /* const 700=4000*175 Khz - to adjust freq to right value */ | 237 | /* const 700=4000*175 Khz - to adjust freq to right value */ |
237 | div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; | 238 | div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; |
238 | break; | 239 | break; |
239 | case TEA5767_HIGH_LO_32768: | 240 | case TEA5767_HIGH_LO_32768: |
240 | default: | 241 | default: |
241 | tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 32,768 MHz\n"); | 242 | tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); |
242 | 243 | ||
243 | buffer[2] |= TEA5767_HIGH_LO_INJECT; | 244 | buffer[2] |= TEA5767_HIGH_LO_INJECT; |
244 | buffer[3] |= TEA5767_XTAL_32768; | 245 | buffer[3] |= TEA5767_XTAL_32768; |
@@ -251,19 +252,23 @@ static void set_radio_freq(struct tuner *t, unsigned int frq) | |||
251 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) | 252 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) |
252 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 253 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); |
253 | 254 | ||
254 | if (tuner_debug) { | 255 | if (debug) { |
255 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) | 256 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) |
256 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 257 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); |
257 | else | 258 | else |
258 | tea5767_status_dump(buffer); | 259 | tea5767_status_dump(buffer); |
259 | } | 260 | } |
261 | |||
262 | priv->frequency = frq * 125 / 2; | ||
263 | |||
264 | return 0; | ||
260 | } | 265 | } |
261 | 266 | ||
262 | static int tea5767_signal(struct tuner *t) | 267 | static int tea5767_signal(struct dvb_frontend *fe) |
263 | { | 268 | { |
264 | unsigned char buffer[5]; | 269 | unsigned char buffer[5]; |
265 | int rc; | 270 | int rc; |
266 | struct tea5767_priv *priv = t->priv; | 271 | struct tea5767_priv *priv = fe->tuner_priv; |
267 | 272 | ||
268 | memset(buffer, 0, sizeof(buffer)); | 273 | memset(buffer, 0, sizeof(buffer)); |
269 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) | 274 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) |
@@ -272,11 +277,11 @@ static int tea5767_signal(struct tuner *t) | |||
272 | return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); | 277 | return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); |
273 | } | 278 | } |
274 | 279 | ||
275 | static int tea5767_stereo(struct tuner *t) | 280 | static int tea5767_stereo(struct dvb_frontend *fe) |
276 | { | 281 | { |
277 | unsigned char buffer[5]; | 282 | unsigned char buffer[5]; |
278 | int rc; | 283 | int rc; |
279 | struct tea5767_priv *priv = t->priv; | 284 | struct tea5767_priv *priv = fe->tuner_priv; |
280 | 285 | ||
281 | memset(buffer, 0, sizeof(buffer)); | 286 | memset(buffer, 0, sizeof(buffer)); |
282 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) | 287 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) |
@@ -284,15 +289,31 @@ static int tea5767_stereo(struct tuner *t) | |||
284 | 289 | ||
285 | rc = buffer[2] & TEA5767_STEREO_MASK; | 290 | rc = buffer[2] & TEA5767_STEREO_MASK; |
286 | 291 | ||
287 | tuner_dbg("TEA5767 radio ST GET = %02x\n", rc); | 292 | tuner_dbg("radio ST GET = %02x\n", rc); |
288 | 293 | ||
289 | return ((buffer[2] & TEA5767_STEREO_MASK) ? V4L2_TUNER_SUB_STEREO : 0); | 294 | return ((buffer[2] & TEA5767_STEREO_MASK) ? V4L2_TUNER_SUB_STEREO : 0); |
290 | } | 295 | } |
291 | 296 | ||
292 | static void tea5767_standby(struct tuner *t) | 297 | static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) |
298 | { | ||
299 | struct tea5767_priv *priv = fe->tuner_priv; | ||
300 | int signal = tea5767_signal(fe); | ||
301 | *status = 0; | ||
302 | |||
303 | if (signal) | ||
304 | *status = TUNER_STATUS_LOCKED; | ||
305 | if (tea5767_stereo(fe)) | ||
306 | *status |= TUNER_STATUS_STEREO; | ||
307 | |||
308 | tuner_dbg("tea5767: Signal strength: %d\n", signal); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static int tea5767_standby(struct dvb_frontend *fe) | ||
293 | { | 314 | { |
294 | unsigned char buffer[5]; | 315 | unsigned char buffer[5]; |
295 | struct tea5767_priv *priv = t->priv; | 316 | struct tea5767_priv *priv = fe->tuner_priv; |
296 | unsigned div, rc; | 317 | unsigned div, rc; |
297 | 318 | ||
298 | div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ | 319 | div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ |
@@ -305,23 +326,25 @@ static void tea5767_standby(struct tuner *t) | |||
305 | 326 | ||
306 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) | 327 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) |
307 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 328 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); |
329 | |||
330 | return 0; | ||
308 | } | 331 | } |
309 | 332 | ||
310 | int tea5767_autodetection(struct tuner *t) | 333 | int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) |
311 | { | 334 | { |
312 | struct tuner_i2c_props i2c = { .adap = t->i2c.adapter, .addr = t->i2c.addr }; | 335 | struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; |
313 | unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | 336 | unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
314 | int rc; | 337 | int rc; |
315 | 338 | ||
316 | if ((rc = tuner_i2c_xfer_send(&i2c, buffer, 7))< 5) { | 339 | if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { |
317 | tuner_warn("It is not a TEA5767. Received %i bytes.\n", rc); | 340 | printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); |
318 | return EINVAL; | 341 | return EINVAL; |
319 | } | 342 | } |
320 | 343 | ||
321 | /* If all bytes are the same then it's a TV tuner and not a tea5767 */ | 344 | /* If all bytes are the same then it's a TV tuner and not a tea5767 */ |
322 | if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && | 345 | if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && |
323 | buffer[0] == buffer[3] && buffer[0] == buffer[4]) { | 346 | buffer[0] == buffer[3] && buffer[0] == buffer[4]) { |
324 | tuner_warn("All bytes are equal. It is not a TEA5767\n"); | 347 | printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); |
325 | return EINVAL; | 348 | return EINVAL; |
326 | } | 349 | } |
327 | 350 | ||
@@ -331,51 +354,73 @@ int tea5767_autodetection(struct tuner *t) | |||
331 | * Byte 5: bit 7:0 : == 0 | 354 | * Byte 5: bit 7:0 : == 0 |
332 | */ | 355 | */ |
333 | if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { | 356 | if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { |
334 | tuner_warn("Chip ID is not zero. It is not a TEA5767\n"); | 357 | printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); |
335 | return EINVAL; | 358 | return EINVAL; |
336 | } | 359 | } |
337 | 360 | ||
338 | /* It seems that tea5767 returns 0xff after the 5th byte */ | 361 | /* It seems that tea5767 returns 0xff after the 5th byte */ |
339 | if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { | 362 | if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { |
340 | tuner_warn("Returned more than 5 bytes. It is not a TEA5767\n"); | 363 | printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n"); |
341 | return EINVAL; | 364 | return EINVAL; |
342 | } | 365 | } |
343 | 366 | ||
344 | tuner_warn("TEA5767 detected.\n"); | 367 | printk(KERN_WARNING "TEA5767 detected.\n"); |
345 | return 0; | 368 | return 0; |
346 | } | 369 | } |
347 | 370 | ||
348 | static void tea5767_release(struct tuner *t) | 371 | static int tea5767_release(struct dvb_frontend *fe) |
349 | { | 372 | { |
350 | kfree(t->priv); | 373 | kfree(fe->tuner_priv); |
351 | t->priv = NULL; | 374 | fe->tuner_priv = NULL; |
375 | |||
376 | return 0; | ||
352 | } | 377 | } |
353 | 378 | ||
354 | static struct tuner_operations tea5767_tuner_ops = { | 379 | static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) |
355 | .set_tv_freq = set_tv_freq, | 380 | { |
356 | .set_radio_freq = set_radio_freq, | 381 | struct tea5767_priv *priv = fe->tuner_priv; |
357 | .has_signal = tea5767_signal, | 382 | *frequency = priv->frequency; |
358 | .is_stereo = tea5767_stereo, | 383 | return 0; |
359 | .standby = tea5767_standby, | 384 | } |
360 | .release = tea5767_release, | 385 | |
386 | static struct dvb_tuner_ops tea5767_tuner_ops = { | ||
387 | .info = { | ||
388 | .name = "tea5767", // Philips TEA5767HN FM Radio | ||
389 | }, | ||
390 | |||
391 | .set_analog_params = set_radio_freq, | ||
392 | .sleep = tea5767_standby, | ||
393 | .release = tea5767_release, | ||
394 | .get_frequency = tea5767_get_frequency, | ||
395 | .get_status = tea5767_get_status, | ||
361 | }; | 396 | }; |
362 | 397 | ||
363 | int tea5767_tuner_init(struct tuner *t) | 398 | struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, |
399 | struct i2c_adapter* i2c_adap, | ||
400 | u8 i2c_addr) | ||
364 | { | 401 | { |
365 | struct tea5767_priv *priv = NULL; | 402 | struct tea5767_priv *priv = NULL; |
366 | 403 | ||
367 | priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); | 404 | priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); |
368 | if (priv == NULL) | 405 | if (priv == NULL) |
369 | return -ENOMEM; | 406 | return NULL; |
370 | t->priv = priv; | 407 | fe->tuner_priv = priv; |
371 | 408 | ||
372 | priv->i2c_props.addr = t->i2c.addr; | 409 | priv->i2c_props.addr = i2c_addr; |
373 | priv->i2c_props.adap = t->i2c.adapter; | 410 | priv->i2c_props.adap = i2c_adap; |
374 | 411 | ||
375 | tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio"); | 412 | memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, |
376 | strlcpy(t->i2c.name, "tea5767", sizeof(t->i2c.name)); | 413 | sizeof(struct dvb_tuner_ops)); |
377 | 414 | ||
378 | memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations)); | 415 | tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); |
379 | 416 | ||
380 | return (0); | 417 | return fe; |
381 | } | 418 | } |
419 | |||
420 | |||
421 | EXPORT_SYMBOL_GPL(tea5767_attach); | ||
422 | EXPORT_SYMBOL_GPL(tea5767_autodetection); | ||
423 | |||
424 | MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); | ||
425 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); | ||
426 | MODULE_LICENSE("GPL"); | ||