diff options
Diffstat (limited to 'drivers/media/video/tea5767.c')
-rw-r--r-- | drivers/media/video/tea5767.c | 203 |
1 files changed, 142 insertions, 61 deletions
diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c index 4985d47a508f..71df419df7bc 100644 --- a/drivers/media/video/tea5767.c +++ b/drivers/media/video/tea5767.c | |||
@@ -11,14 +11,22 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/i2c.h> | 13 | #include <linux/i2c.h> |
14 | #include <linux/videodev.h> | ||
15 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
16 | #include "tuner-driver.h" | 15 | #include <linux/videodev.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; | 24 | |
25 | struct tea5767_priv { | ||
26 | struct tuner_i2c_props i2c_props; | ||
27 | |||
28 | u32 frequency; | ||
29 | }; | ||
22 | 30 | ||
23 | /*****************************************************************************/ | 31 | /*****************************************************************************/ |
24 | 32 | ||
@@ -129,13 +137,6 @@ enum tea5767_xtal_freq { | |||
129 | 137 | ||
130 | /*****************************************************************************/ | 138 | /*****************************************************************************/ |
131 | 139 | ||
132 | static void set_tv_freq(struct i2c_client *c, unsigned int freq) | ||
133 | { | ||
134 | struct tuner *t = i2c_get_clientdata(c); | ||
135 | |||
136 | tuner_warn("This tuner doesn't support TV freq.\n"); | ||
137 | } | ||
138 | |||
139 | static void tea5767_status_dump(unsigned char *buffer) | 140 | static void tea5767_status_dump(unsigned char *buffer) |
140 | { | 141 | { |
141 | unsigned int div, frq; | 142 | unsigned int div, frq; |
@@ -190,14 +191,16 @@ static void tea5767_status_dump(unsigned char *buffer) | |||
190 | } | 191 | } |
191 | 192 | ||
192 | /* Freq should be specifyed at 62.5 Hz */ | 193 | /* Freq should be specifyed at 62.5 Hz */ |
193 | static void set_radio_freq(struct i2c_client *c, unsigned int frq) | 194 | static int set_radio_freq(struct dvb_frontend *fe, |
195 | struct analog_parameters *params) | ||
194 | { | 196 | { |
195 | struct tuner *t = i2c_get_clientdata(c); | 197 | struct tea5767_priv *priv = fe->tuner_priv; |
198 | unsigned int frq = params->frequency; | ||
196 | unsigned char buffer[5]; | 199 | unsigned char buffer[5]; |
197 | unsigned div; | 200 | unsigned div; |
198 | int rc; | 201 | int rc; |
199 | 202 | ||
200 | 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); |
201 | 204 | ||
202 | /* Rounds freq to next decimal value - for 62.5 KHz step */ | 205 | /* Rounds freq to next decimal value - for 62.5 KHz step */ |
203 | /* frq = 20*(frq/16)+radio_frq[frq%16]; */ | 206 | /* frq = 20*(frq/16)+radio_frq[frq%16]; */ |
@@ -207,7 +210,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int frq) | |||
207 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND; | 210 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND; |
208 | buffer[4] = 0; | 211 | buffer[4] = 0; |
209 | 212 | ||
210 | if (t->audmode == V4L2_TUNER_MODE_MONO) { | 213 | if (params->audmode == V4L2_TUNER_MODE_MONO) { |
211 | tuner_dbg("TEA5767 set to mono\n"); | 214 | tuner_dbg("TEA5767 set to mono\n"); |
212 | buffer[2] |= TEA5767_MONO; | 215 | buffer[2] |= TEA5767_MONO; |
213 | } else { | 216 | } else { |
@@ -217,26 +220,26 @@ static void set_radio_freq(struct i2c_client *c, unsigned int frq) | |||
217 | /* Should be replaced */ | 220 | /* Should be replaced */ |
218 | switch (TEA5767_HIGH_LO_32768) { | 221 | switch (TEA5767_HIGH_LO_32768) { |
219 | case TEA5767_HIGH_LO_13MHz: | 222 | case TEA5767_HIGH_LO_13MHz: |
220 | tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 13 MHz\n"); | 223 | tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); |
221 | buffer[2] |= TEA5767_HIGH_LO_INJECT; | 224 | buffer[2] |= TEA5767_HIGH_LO_INJECT; |
222 | buffer[4] |= TEA5767_PLLREF_ENABLE; | 225 | buffer[4] |= TEA5767_PLLREF_ENABLE; |
223 | div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; | 226 | div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; |
224 | break; | 227 | break; |
225 | case TEA5767_LOW_LO_13MHz: | 228 | case TEA5767_LOW_LO_13MHz: |
226 | tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 13 MHz\n"); | 229 | tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); |
227 | 230 | ||
228 | buffer[4] |= TEA5767_PLLREF_ENABLE; | 231 | buffer[4] |= TEA5767_PLLREF_ENABLE; |
229 | div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; | 232 | div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; |
230 | break; | 233 | break; |
231 | case TEA5767_LOW_LO_32768: | 234 | case TEA5767_LOW_LO_32768: |
232 | tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 32,768 MHz\n"); | 235 | tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); |
233 | buffer[3] |= TEA5767_XTAL_32768; | 236 | buffer[3] |= TEA5767_XTAL_32768; |
234 | /* const 700=4000*175 Khz - to adjust freq to right value */ | 237 | /* const 700=4000*175 Khz - to adjust freq to right value */ |
235 | div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; | 238 | div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; |
236 | break; | 239 | break; |
237 | case TEA5767_HIGH_LO_32768: | 240 | case TEA5767_HIGH_LO_32768: |
238 | default: | 241 | default: |
239 | tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 32,768 MHz\n"); | 242 | tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); |
240 | 243 | ||
241 | buffer[2] |= TEA5767_HIGH_LO_INJECT; | 244 | buffer[2] |= TEA5767_HIGH_LO_INJECT; |
242 | buffer[3] |= TEA5767_XTAL_32768; | 245 | buffer[3] |= TEA5767_XTAL_32768; |
@@ -246,51 +249,89 @@ static void set_radio_freq(struct i2c_client *c, unsigned int frq) | |||
246 | buffer[0] = (div >> 8) & 0x3f; | 249 | buffer[0] = (div >> 8) & 0x3f; |
247 | buffer[1] = div & 0xff; | 250 | buffer[1] = div & 0xff; |
248 | 251 | ||
249 | if (5 != (rc = i2c_master_send(c, buffer, 5))) | 252 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) |
250 | 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); |
251 | 254 | ||
252 | if (tuner_debug) { | 255 | if (debug) { |
253 | if (5 != (rc = i2c_master_recv(c, buffer, 5))) | 256 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) |
254 | 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); |
255 | else | 258 | else |
256 | tea5767_status_dump(buffer); | 259 | tea5767_status_dump(buffer); |
257 | } | 260 | } |
261 | |||
262 | priv->frequency = frq * 125 / 2; | ||
263 | |||
264 | return 0; | ||
258 | } | 265 | } |
259 | 266 | ||
260 | static int tea5767_signal(struct i2c_client *c) | 267 | static int tea5767_read_status(struct dvb_frontend *fe, char *buffer) |
261 | { | 268 | { |
262 | unsigned char buffer[5]; | 269 | struct tea5767_priv *priv = fe->tuner_priv; |
263 | int rc; | 270 | int rc; |
264 | struct tuner *t = i2c_get_clientdata(c); | ||
265 | 271 | ||
266 | memset(buffer, 0, sizeof(buffer)); | 272 | memset(buffer, 0, 5); |
267 | if (5 != (rc = i2c_master_recv(c, buffer, 5))) | 273 | if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { |
268 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 274 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); |
275 | return -EREMOTEIO; | ||
276 | } | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) | ||
282 | { | ||
283 | struct tea5767_priv *priv = fe->tuner_priv; | ||
284 | |||
285 | int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); | ||
286 | |||
287 | tuner_dbg("Signal strength: %d\n", signal); | ||
288 | |||
289 | return signal; | ||
290 | } | ||
291 | |||
292 | static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) | ||
293 | { | ||
294 | struct tea5767_priv *priv = fe->tuner_priv; | ||
295 | |||
296 | int stereo = buffer[2] & TEA5767_STEREO_MASK; | ||
297 | |||
298 | tuner_dbg("Radio ST GET = %02x\n", stereo); | ||
269 | 299 | ||
270 | return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); | 300 | return (stereo ? V4L2_TUNER_SUB_STEREO : 0); |
271 | } | 301 | } |
272 | 302 | ||
273 | static int tea5767_stereo(struct i2c_client *c) | 303 | static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) |
274 | { | 304 | { |
275 | unsigned char buffer[5]; | 305 | unsigned char buffer[5]; |
276 | int rc; | ||
277 | struct tuner *t = i2c_get_clientdata(c); | ||
278 | 306 | ||
279 | memset(buffer, 0, sizeof(buffer)); | 307 | *status = 0; |
280 | if (5 != (rc = i2c_master_recv(c, buffer, 5))) | 308 | |
281 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 309 | if (0 == tea5767_read_status(fe, buffer)) { |
310 | if (tea5767_signal(fe, buffer)) | ||
311 | *status = TUNER_STATUS_LOCKED; | ||
312 | if (tea5767_stereo(fe, buffer)) | ||
313 | *status |= TUNER_STATUS_STEREO; | ||
314 | } | ||
315 | |||
316 | return 0; | ||
317 | } | ||
282 | 318 | ||
283 | rc = buffer[2] & TEA5767_STEREO_MASK; | 319 | static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) |
320 | { | ||
321 | unsigned char buffer[5]; | ||
284 | 322 | ||
285 | tuner_dbg("TEA5767 radio ST GET = %02x\n", rc); | 323 | *strength = 0; |
286 | 324 | ||
287 | return ((buffer[2] & TEA5767_STEREO_MASK) ? V4L2_TUNER_SUB_STEREO : 0); | 325 | if (0 == tea5767_read_status(fe, buffer)) |
326 | *strength = tea5767_signal(fe, buffer); | ||
327 | |||
328 | return 0; | ||
288 | } | 329 | } |
289 | 330 | ||
290 | static void tea5767_standby(struct i2c_client *c) | 331 | static int tea5767_standby(struct dvb_frontend *fe) |
291 | { | 332 | { |
292 | unsigned char buffer[5]; | 333 | unsigned char buffer[5]; |
293 | struct tuner *t = i2c_get_clientdata(c); | 334 | struct tea5767_priv *priv = fe->tuner_priv; |
294 | unsigned div, rc; | 335 | unsigned div, rc; |
295 | 336 | ||
296 | div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ | 337 | div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ |
@@ -301,25 +342,27 @@ static void tea5767_standby(struct i2c_client *c) | |||
301 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; | 342 | TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; |
302 | buffer[4] = 0; | 343 | buffer[4] = 0; |
303 | 344 | ||
304 | if (5 != (rc = i2c_master_send(c, buffer, 5))) | 345 | if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) |
305 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); | 346 | tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); |
347 | |||
348 | return 0; | ||
306 | } | 349 | } |
307 | 350 | ||
308 | int tea5767_autodetection(struct i2c_client *c) | 351 | int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) |
309 | { | 352 | { |
353 | struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; | ||
310 | unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | 354 | unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
311 | int rc; | 355 | int rc; |
312 | struct tuner *t = i2c_get_clientdata(c); | ||
313 | 356 | ||
314 | if ((rc = i2c_master_recv(c, buffer, 7))< 5) { | 357 | if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { |
315 | tuner_warn("It is not a TEA5767. Received %i bytes.\n", rc); | 358 | printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); |
316 | return EINVAL; | 359 | return EINVAL; |
317 | } | 360 | } |
318 | 361 | ||
319 | /* If all bytes are the same then it's a TV tuner and not a tea5767 */ | 362 | /* If all bytes are the same then it's a TV tuner and not a tea5767 */ |
320 | if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && | 363 | if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && |
321 | buffer[0] == buffer[3] && buffer[0] == buffer[4]) { | 364 | buffer[0] == buffer[3] && buffer[0] == buffer[4]) { |
322 | tuner_warn("All bytes are equal. It is not a TEA5767\n"); | 365 | printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); |
323 | return EINVAL; | 366 | return EINVAL; |
324 | } | 367 | } |
325 | 368 | ||
@@ -329,36 +372,74 @@ int tea5767_autodetection(struct i2c_client *c) | |||
329 | * Byte 5: bit 7:0 : == 0 | 372 | * Byte 5: bit 7:0 : == 0 |
330 | */ | 373 | */ |
331 | if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { | 374 | if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { |
332 | tuner_warn("Chip ID is not zero. It is not a TEA5767\n"); | 375 | printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); |
333 | return EINVAL; | 376 | return EINVAL; |
334 | } | 377 | } |
335 | 378 | ||
336 | /* It seems that tea5767 returns 0xff after the 5th byte */ | 379 | /* It seems that tea5767 returns 0xff after the 5th byte */ |
337 | if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { | 380 | if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { |
338 | tuner_warn("Returned more than 5 bytes. It is not a TEA5767\n"); | 381 | printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n"); |
339 | return EINVAL; | 382 | return EINVAL; |
340 | } | 383 | } |
341 | 384 | ||
342 | tuner_warn("TEA5767 detected.\n"); | 385 | printk(KERN_WARNING "TEA5767 detected.\n"); |
343 | return 0; | 386 | return 0; |
344 | } | 387 | } |
345 | 388 | ||
346 | static struct tuner_operations tea5767_tuner_ops = { | 389 | static int tea5767_release(struct dvb_frontend *fe) |
347 | .set_tv_freq = set_tv_freq, | 390 | { |
348 | .set_radio_freq = set_radio_freq, | 391 | kfree(fe->tuner_priv); |
349 | .has_signal = tea5767_signal, | 392 | fe->tuner_priv = NULL; |
350 | .is_stereo = tea5767_stereo, | 393 | |
351 | .standby = tea5767_standby, | 394 | return 0; |
395 | } | ||
396 | |||
397 | static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
398 | { | ||
399 | struct tea5767_priv *priv = fe->tuner_priv; | ||
400 | *frequency = priv->frequency; | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static struct dvb_tuner_ops tea5767_tuner_ops = { | ||
405 | .info = { | ||
406 | .name = "tea5767", // Philips TEA5767HN FM Radio | ||
407 | }, | ||
408 | |||
409 | .set_analog_params = set_radio_freq, | ||
410 | .sleep = tea5767_standby, | ||
411 | .release = tea5767_release, | ||
412 | .get_frequency = tea5767_get_frequency, | ||
413 | .get_status = tea5767_get_status, | ||
414 | .get_rf_strength = tea5767_get_rf_strength, | ||
352 | }; | 415 | }; |
353 | 416 | ||
354 | int tea5767_tuner_init(struct i2c_client *c) | 417 | struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, |
418 | struct i2c_adapter* i2c_adap, | ||
419 | u8 i2c_addr) | ||
355 | { | 420 | { |
356 | struct tuner *t = i2c_get_clientdata(c); | 421 | struct tea5767_priv *priv = NULL; |
422 | |||
423 | priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); | ||
424 | if (priv == NULL) | ||
425 | return NULL; | ||
426 | fe->tuner_priv = priv; | ||
427 | |||
428 | priv->i2c_props.addr = i2c_addr; | ||
429 | priv->i2c_props.adap = i2c_adap; | ||
357 | 430 | ||
358 | tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio"); | 431 | memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, |
359 | strlcpy(c->name, "tea5767", sizeof(c->name)); | 432 | sizeof(struct dvb_tuner_ops)); |
360 | 433 | ||
361 | memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations)); | 434 | tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); |
362 | 435 | ||
363 | return (0); | 436 | return fe; |
364 | } | 437 | } |
438 | |||
439 | |||
440 | EXPORT_SYMBOL_GPL(tea5767_attach); | ||
441 | EXPORT_SYMBOL_GPL(tea5767_autodetection); | ||
442 | |||
443 | MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); | ||
444 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); | ||
445 | MODULE_LICENSE("GPL"); | ||