Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/radio/radio-tea5764.c
15111 views
1
/*
2
* driver/media/radio/radio-tea5764.c
3
*
4
* Driver for TEA5764 radio chip for linux 2.6.
5
* This driver is for TEA5764 chip from NXP, used in EZX phones from Motorola.
6
* The I2C protocol is used for communicate with chip.
7
*
8
* Based in radio-tea5761.c Copyright (C) 2005 Nokia Corporation
9
*
10
* Copyright (c) 2008 Fabio Belavenuto <[email protected]>
11
*
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
16
*
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
21
*
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
*
26
* History:
27
* 2008-12-06 Fabio Belavenuto <[email protected]>
28
* initial code
29
*
30
* TODO:
31
* add platform_data support for IRQs platform dependencies
32
* add RDS support
33
*/
34
#include <linux/kernel.h>
35
#include <linux/slab.h>
36
#include <linux/module.h>
37
#include <linux/init.h> /* Initdata */
38
#include <linux/videodev2.h> /* kernel radio structs */
39
#include <linux/i2c.h> /* I2C */
40
#include <media/v4l2-common.h>
41
#include <media/v4l2-ioctl.h>
42
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
43
44
#define DRIVER_VERSION "v0.01"
45
#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
46
47
#define DRIVER_AUTHOR "Fabio Belavenuto <[email protected]>"
48
#define DRIVER_DESC "A driver for the TEA5764 radio chip for EZX Phones."
49
50
#define PINFO(format, ...)\
51
printk(KERN_INFO KBUILD_MODNAME ": "\
52
DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
53
#define PWARN(format, ...)\
54
printk(KERN_WARNING KBUILD_MODNAME ": "\
55
DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
56
# define PDEBUG(format, ...)\
57
printk(KERN_DEBUG KBUILD_MODNAME ": "\
58
DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
59
60
/* Frequency limits in MHz -- these are European values. For Japanese
61
devices, that would be 76000 and 91000. */
62
#define FREQ_MIN 87500
63
#define FREQ_MAX 108000
64
#define FREQ_MUL 16
65
66
/* TEA5764 registers */
67
#define TEA5764_MANID 0x002b
68
#define TEA5764_CHIPID 0x5764
69
70
#define TEA5764_INTREG_BLMSK 0x0001
71
#define TEA5764_INTREG_FRRMSK 0x0002
72
#define TEA5764_INTREG_LEVMSK 0x0008
73
#define TEA5764_INTREG_IFMSK 0x0010
74
#define TEA5764_INTREG_BLMFLAG 0x0100
75
#define TEA5764_INTREG_FRRFLAG 0x0200
76
#define TEA5764_INTREG_LEVFLAG 0x0800
77
#define TEA5764_INTREG_IFFLAG 0x1000
78
79
#define TEA5764_FRQSET_SUD 0x8000
80
#define TEA5764_FRQSET_SM 0x4000
81
82
#define TEA5764_TNCTRL_PUPD1 0x8000
83
#define TEA5764_TNCTRL_PUPD0 0x4000
84
#define TEA5764_TNCTRL_BLIM 0x2000
85
#define TEA5764_TNCTRL_SWPM 0x1000
86
#define TEA5764_TNCTRL_IFCTC 0x0800
87
#define TEA5764_TNCTRL_AFM 0x0400
88
#define TEA5764_TNCTRL_SMUTE 0x0200
89
#define TEA5764_TNCTRL_SNC 0x0100
90
#define TEA5764_TNCTRL_MU 0x0080
91
#define TEA5764_TNCTRL_SSL1 0x0040
92
#define TEA5764_TNCTRL_SSL0 0x0020
93
#define TEA5764_TNCTRL_HLSI 0x0010
94
#define TEA5764_TNCTRL_MST 0x0008
95
#define TEA5764_TNCTRL_SWP 0x0004
96
#define TEA5764_TNCTRL_DTC 0x0002
97
#define TEA5764_TNCTRL_AHLSI 0x0001
98
99
#define TEA5764_TUNCHK_LEVEL(x) (((x) & 0x00F0) >> 4)
100
#define TEA5764_TUNCHK_IFCNT(x) (((x) & 0xFE00) >> 9)
101
#define TEA5764_TUNCHK_TUNTO 0x0100
102
#define TEA5764_TUNCHK_LD 0x0008
103
#define TEA5764_TUNCHK_STEREO 0x0004
104
105
#define TEA5764_TESTREG_TRIGFR 0x0800
106
107
struct tea5764_regs {
108
u16 intreg; /* INTFLAG & INTMSK */
109
u16 frqset; /* FRQSETMSB & FRQSETLSB */
110
u16 tnctrl; /* TNCTRL1 & TNCTRL2 */
111
u16 frqchk; /* FRQCHKMSB & FRQCHKLSB */
112
u16 tunchk; /* IFCHK & LEVCHK */
113
u16 testreg; /* TESTBITS & TESTMODE */
114
u16 rdsstat; /* RDSSTAT1 & RDSSTAT2 */
115
u16 rdslb; /* RDSLBMSB & RDSLBLSB */
116
u16 rdspb; /* RDSPBMSB & RDSPBLSB */
117
u16 rdsbc; /* RDSBBC & RDSGBC */
118
u16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */
119
u16 rdsbbl; /* PAUSEDET & RDSBBL */
120
u16 manid; /* MANID1 & MANID2 */
121
u16 chipid; /* CHIPID1 & CHIPID2 */
122
} __attribute__ ((packed));
123
124
struct tea5764_write_regs {
125
u8 intreg; /* INTMSK */
126
u16 frqset; /* FRQSETMSB & FRQSETLSB */
127
u16 tnctrl; /* TNCTRL1 & TNCTRL2 */
128
u16 testreg; /* TESTBITS & TESTMODE */
129
u16 rdsctrl; /* RDSCTRL1 & RDSCTRL2 */
130
u16 rdsbbl; /* PAUSEDET & RDSBBL */
131
} __attribute__ ((packed));
132
133
#ifndef RADIO_TEA5764_XTAL
134
#define RADIO_TEA5764_XTAL 1
135
#endif
136
137
static int radio_nr = -1;
138
static int use_xtal = RADIO_TEA5764_XTAL;
139
140
struct tea5764_device {
141
struct i2c_client *i2c_client;
142
struct video_device *videodev;
143
struct tea5764_regs regs;
144
struct mutex mutex;
145
};
146
147
/* I2C code related */
148
int tea5764_i2c_read(struct tea5764_device *radio)
149
{
150
int i;
151
u16 *p = (u16 *) &radio->regs;
152
153
struct i2c_msg msgs[1] = {
154
{ radio->i2c_client->addr, I2C_M_RD, sizeof(radio->regs),
155
(void *)&radio->regs },
156
};
157
if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
158
return -EIO;
159
for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++)
160
p[i] = __be16_to_cpu(p[i]);
161
162
return 0;
163
}
164
165
int tea5764_i2c_write(struct tea5764_device *radio)
166
{
167
struct tea5764_write_regs wr;
168
struct tea5764_regs *r = &radio->regs;
169
struct i2c_msg msgs[1] = {
170
{ radio->i2c_client->addr, 0, sizeof(wr), (void *) &wr },
171
};
172
wr.intreg = r->intreg & 0xff;
173
wr.frqset = __cpu_to_be16(r->frqset);
174
wr.tnctrl = __cpu_to_be16(r->tnctrl);
175
wr.testreg = __cpu_to_be16(r->testreg);
176
wr.rdsctrl = __cpu_to_be16(r->rdsctrl);
177
wr.rdsbbl = __cpu_to_be16(r->rdsbbl);
178
if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
179
return -EIO;
180
return 0;
181
}
182
183
/* V4L2 code related */
184
static struct v4l2_queryctrl radio_qctrl[] = {
185
{
186
.id = V4L2_CID_AUDIO_MUTE,
187
.name = "Mute",
188
.minimum = 0,
189
.maximum = 1,
190
.default_value = 1,
191
.type = V4L2_CTRL_TYPE_BOOLEAN,
192
}
193
};
194
195
static void tea5764_power_up(struct tea5764_device *radio)
196
{
197
struct tea5764_regs *r = &radio->regs;
198
199
if (!(r->tnctrl & TEA5764_TNCTRL_PUPD0)) {
200
r->tnctrl &= ~(TEA5764_TNCTRL_AFM | TEA5764_TNCTRL_MU |
201
TEA5764_TNCTRL_HLSI);
202
if (!use_xtal)
203
r->testreg |= TEA5764_TESTREG_TRIGFR;
204
else
205
r->testreg &= ~TEA5764_TESTREG_TRIGFR;
206
207
r->tnctrl |= TEA5764_TNCTRL_PUPD0;
208
tea5764_i2c_write(radio);
209
}
210
}
211
212
static void tea5764_power_down(struct tea5764_device *radio)
213
{
214
struct tea5764_regs *r = &radio->regs;
215
216
if (r->tnctrl & TEA5764_TNCTRL_PUPD0) {
217
r->tnctrl &= ~TEA5764_TNCTRL_PUPD0;
218
tea5764_i2c_write(radio);
219
}
220
}
221
222
static void tea5764_set_freq(struct tea5764_device *radio, int freq)
223
{
224
struct tea5764_regs *r = &radio->regs;
225
226
/* formula: (freq [+ or -] 225000) / 8192 */
227
if (r->tnctrl & TEA5764_TNCTRL_HLSI)
228
r->frqset = (freq + 225000) / 8192;
229
else
230
r->frqset = (freq - 225000) / 8192;
231
}
232
233
static int tea5764_get_freq(struct tea5764_device *radio)
234
{
235
struct tea5764_regs *r = &radio->regs;
236
237
if (r->tnctrl & TEA5764_TNCTRL_HLSI)
238
return (r->frqchk * 8192) - 225000;
239
else
240
return (r->frqchk * 8192) + 225000;
241
}
242
243
/* tune an frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
244
static void tea5764_tune(struct tea5764_device *radio, int freq)
245
{
246
tea5764_set_freq(radio, freq);
247
if (tea5764_i2c_write(radio))
248
PWARN("Could not set frequency!");
249
}
250
251
static void tea5764_set_audout_mode(struct tea5764_device *radio, int audmode)
252
{
253
struct tea5764_regs *r = &radio->regs;
254
int tnctrl = r->tnctrl;
255
256
if (audmode == V4L2_TUNER_MODE_MONO)
257
r->tnctrl |= TEA5764_TNCTRL_MST;
258
else
259
r->tnctrl &= ~TEA5764_TNCTRL_MST;
260
if (tnctrl != r->tnctrl)
261
tea5764_i2c_write(radio);
262
}
263
264
static int tea5764_get_audout_mode(struct tea5764_device *radio)
265
{
266
struct tea5764_regs *r = &radio->regs;
267
268
if (r->tnctrl & TEA5764_TNCTRL_MST)
269
return V4L2_TUNER_MODE_MONO;
270
else
271
return V4L2_TUNER_MODE_STEREO;
272
}
273
274
static void tea5764_mute(struct tea5764_device *radio, int on)
275
{
276
struct tea5764_regs *r = &radio->regs;
277
int tnctrl = r->tnctrl;
278
279
if (on)
280
r->tnctrl |= TEA5764_TNCTRL_MU;
281
else
282
r->tnctrl &= ~TEA5764_TNCTRL_MU;
283
if (tnctrl != r->tnctrl)
284
tea5764_i2c_write(radio);
285
}
286
287
static int tea5764_is_muted(struct tea5764_device *radio)
288
{
289
return radio->regs.tnctrl & TEA5764_TNCTRL_MU;
290
}
291
292
/* V4L2 vidioc */
293
static int vidioc_querycap(struct file *file, void *priv,
294
struct v4l2_capability *v)
295
{
296
struct tea5764_device *radio = video_drvdata(file);
297
struct video_device *dev = radio->videodev;
298
299
strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver));
300
strlcpy(v->card, dev->name, sizeof(v->card));
301
snprintf(v->bus_info, sizeof(v->bus_info),
302
"I2C:%s", dev_name(&dev->dev));
303
v->version = RADIO_VERSION;
304
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
305
return 0;
306
}
307
308
static int vidioc_g_tuner(struct file *file, void *priv,
309
struct v4l2_tuner *v)
310
{
311
struct tea5764_device *radio = video_drvdata(file);
312
struct tea5764_regs *r = &radio->regs;
313
314
if (v->index > 0)
315
return -EINVAL;
316
317
memset(v, 0, sizeof(*v));
318
strcpy(v->name, "FM");
319
v->type = V4L2_TUNER_RADIO;
320
tea5764_i2c_read(radio);
321
v->rangelow = FREQ_MIN * FREQ_MUL;
322
v->rangehigh = FREQ_MAX * FREQ_MUL;
323
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
324
if (r->tunchk & TEA5764_TUNCHK_STEREO)
325
v->rxsubchans = V4L2_TUNER_SUB_STEREO;
326
else
327
v->rxsubchans = V4L2_TUNER_SUB_MONO;
328
v->audmode = tea5764_get_audout_mode(radio);
329
v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf;
330
v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk);
331
332
return 0;
333
}
334
335
static int vidioc_s_tuner(struct file *file, void *priv,
336
struct v4l2_tuner *v)
337
{
338
struct tea5764_device *radio = video_drvdata(file);
339
340
if (v->index > 0)
341
return -EINVAL;
342
343
tea5764_set_audout_mode(radio, v->audmode);
344
return 0;
345
}
346
347
static int vidioc_s_frequency(struct file *file, void *priv,
348
struct v4l2_frequency *f)
349
{
350
struct tea5764_device *radio = video_drvdata(file);
351
352
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
353
return -EINVAL;
354
if (f->frequency == 0) {
355
/* We special case this as a power down control. */
356
tea5764_power_down(radio);
357
}
358
if (f->frequency < (FREQ_MIN * FREQ_MUL))
359
return -EINVAL;
360
if (f->frequency > (FREQ_MAX * FREQ_MUL))
361
return -EINVAL;
362
tea5764_power_up(radio);
363
tea5764_tune(radio, (f->frequency * 125) / 2);
364
return 0;
365
}
366
367
static int vidioc_g_frequency(struct file *file, void *priv,
368
struct v4l2_frequency *f)
369
{
370
struct tea5764_device *radio = video_drvdata(file);
371
struct tea5764_regs *r = &radio->regs;
372
373
if (f->tuner != 0)
374
return -EINVAL;
375
tea5764_i2c_read(radio);
376
memset(f, 0, sizeof(*f));
377
f->type = V4L2_TUNER_RADIO;
378
if (r->tnctrl & TEA5764_TNCTRL_PUPD0)
379
f->frequency = (tea5764_get_freq(radio) * 2) / 125;
380
else
381
f->frequency = 0;
382
383
return 0;
384
}
385
386
static int vidioc_queryctrl(struct file *file, void *priv,
387
struct v4l2_queryctrl *qc)
388
{
389
int i;
390
391
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
392
if (qc->id && qc->id == radio_qctrl[i].id) {
393
memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
394
return 0;
395
}
396
}
397
return -EINVAL;
398
}
399
400
static int vidioc_g_ctrl(struct file *file, void *priv,
401
struct v4l2_control *ctrl)
402
{
403
struct tea5764_device *radio = video_drvdata(file);
404
405
switch (ctrl->id) {
406
case V4L2_CID_AUDIO_MUTE:
407
tea5764_i2c_read(radio);
408
ctrl->value = tea5764_is_muted(radio) ? 1 : 0;
409
return 0;
410
}
411
return -EINVAL;
412
}
413
414
static int vidioc_s_ctrl(struct file *file, void *priv,
415
struct v4l2_control *ctrl)
416
{
417
struct tea5764_device *radio = video_drvdata(file);
418
419
switch (ctrl->id) {
420
case V4L2_CID_AUDIO_MUTE:
421
tea5764_mute(radio, ctrl->value);
422
return 0;
423
}
424
return -EINVAL;
425
}
426
427
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
428
{
429
*i = 0;
430
return 0;
431
}
432
433
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
434
{
435
if (i != 0)
436
return -EINVAL;
437
return 0;
438
}
439
440
static int vidioc_g_audio(struct file *file, void *priv,
441
struct v4l2_audio *a)
442
{
443
if (a->index > 1)
444
return -EINVAL;
445
446
strcpy(a->name, "Radio");
447
a->capability = V4L2_AUDCAP_STEREO;
448
return 0;
449
}
450
451
static int vidioc_s_audio(struct file *file, void *priv,
452
struct v4l2_audio *a)
453
{
454
if (a->index != 0)
455
return -EINVAL;
456
457
return 0;
458
}
459
460
/* File system interface */
461
static const struct v4l2_file_operations tea5764_fops = {
462
.owner = THIS_MODULE,
463
.unlocked_ioctl = video_ioctl2,
464
};
465
466
static const struct v4l2_ioctl_ops tea5764_ioctl_ops = {
467
.vidioc_querycap = vidioc_querycap,
468
.vidioc_g_tuner = vidioc_g_tuner,
469
.vidioc_s_tuner = vidioc_s_tuner,
470
.vidioc_g_audio = vidioc_g_audio,
471
.vidioc_s_audio = vidioc_s_audio,
472
.vidioc_g_input = vidioc_g_input,
473
.vidioc_s_input = vidioc_s_input,
474
.vidioc_g_frequency = vidioc_g_frequency,
475
.vidioc_s_frequency = vidioc_s_frequency,
476
.vidioc_queryctrl = vidioc_queryctrl,
477
.vidioc_g_ctrl = vidioc_g_ctrl,
478
.vidioc_s_ctrl = vidioc_s_ctrl,
479
};
480
481
/* V4L2 interface */
482
static struct video_device tea5764_radio_template = {
483
.name = "TEA5764 FM-Radio",
484
.fops = &tea5764_fops,
485
.ioctl_ops = &tea5764_ioctl_ops,
486
.release = video_device_release,
487
};
488
489
/* I2C probe: check if the device exists and register with v4l if it is */
490
static int __devinit tea5764_i2c_probe(struct i2c_client *client,
491
const struct i2c_device_id *id)
492
{
493
struct tea5764_device *radio;
494
struct tea5764_regs *r;
495
int ret;
496
497
PDEBUG("probe");
498
radio = kzalloc(sizeof(struct tea5764_device), GFP_KERNEL);
499
if (!radio)
500
return -ENOMEM;
501
502
mutex_init(&radio->mutex);
503
radio->i2c_client = client;
504
ret = tea5764_i2c_read(radio);
505
if (ret)
506
goto errfr;
507
r = &radio->regs;
508
PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid);
509
if (r->chipid != TEA5764_CHIPID ||
510
(r->manid & 0x0fff) != TEA5764_MANID) {
511
PWARN("This chip is not a TEA5764!");
512
ret = -EINVAL;
513
goto errfr;
514
}
515
516
radio->videodev = video_device_alloc();
517
if (!(radio->videodev)) {
518
ret = -ENOMEM;
519
goto errfr;
520
}
521
memcpy(radio->videodev, &tea5764_radio_template,
522
sizeof(tea5764_radio_template));
523
524
i2c_set_clientdata(client, radio);
525
video_set_drvdata(radio->videodev, radio);
526
radio->videodev->lock = &radio->mutex;
527
528
/* initialize and power off the chip */
529
tea5764_i2c_read(radio);
530
tea5764_set_audout_mode(radio, V4L2_TUNER_MODE_STEREO);
531
tea5764_mute(radio, 1);
532
tea5764_power_down(radio);
533
534
ret = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
535
if (ret < 0) {
536
PWARN("Could not register video device!");
537
goto errrel;
538
}
539
540
PINFO("registered.");
541
return 0;
542
errrel:
543
video_device_release(radio->videodev);
544
errfr:
545
kfree(radio);
546
return ret;
547
}
548
549
static int __devexit tea5764_i2c_remove(struct i2c_client *client)
550
{
551
struct tea5764_device *radio = i2c_get_clientdata(client);
552
553
PDEBUG("remove");
554
if (radio) {
555
tea5764_power_down(radio);
556
video_unregister_device(radio->videodev);
557
kfree(radio);
558
}
559
return 0;
560
}
561
562
/* I2C subsystem interface */
563
static const struct i2c_device_id tea5764_id[] = {
564
{ "radio-tea5764", 0 },
565
{ } /* Terminating entry */
566
};
567
MODULE_DEVICE_TABLE(i2c, tea5764_id);
568
569
static struct i2c_driver tea5764_i2c_driver = {
570
.driver = {
571
.name = "radio-tea5764",
572
.owner = THIS_MODULE,
573
},
574
.probe = tea5764_i2c_probe,
575
.remove = __devexit_p(tea5764_i2c_remove),
576
.id_table = tea5764_id,
577
};
578
579
/* init the driver */
580
static int __init tea5764_init(void)
581
{
582
int ret = i2c_add_driver(&tea5764_i2c_driver);
583
584
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
585
DRIVER_DESC "\n");
586
return ret;
587
}
588
589
/* cleanup the driver */
590
static void __exit tea5764_exit(void)
591
{
592
i2c_del_driver(&tea5764_i2c_driver);
593
}
594
595
MODULE_AUTHOR(DRIVER_AUTHOR);
596
MODULE_DESCRIPTION(DRIVER_DESC);
597
MODULE_LICENSE("GPL");
598
599
module_param(use_xtal, int, 1);
600
MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
601
module_param(radio_nr, int, 0);
602
MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
603
604
module_init(tea5764_init);
605
module_exit(tea5764_exit);
606
607