linux/drivers/media/radio/radio-tea5764.c
<<
>>
Prefs
   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 <belavenuto@gmail.com>
  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 * History:
  23 * 2008-12-06   Fabio Belavenuto <belavenuto@gmail.com>
  24 *              initial code
  25 *
  26 * TODO:
  27 *  add platform_data support for IRQs platform dependencies
  28 *  add RDS support
  29 */
  30#include <linux/kernel.h>
  31#include <linux/slab.h>
  32#include <linux/module.h>
  33#include <linux/init.h>                 /* Initdata                     */
  34#include <linux/videodev2.h>            /* kernel radio structs         */
  35#include <linux/i2c.h>                  /* I2C                          */
  36#include <media/v4l2-common.h>
  37#include <media/v4l2-ioctl.h>
  38#include <media/v4l2-device.h>
  39#include <media/v4l2-ctrls.h>
  40#include <media/v4l2-event.h>
  41
  42#define DRIVER_VERSION  "0.0.2"
  43
  44#define DRIVER_AUTHOR   "Fabio Belavenuto <belavenuto@gmail.com>"
  45#define DRIVER_DESC     "A driver for the TEA5764 radio chip for EZX Phones."
  46
  47#define PINFO(format, ...)\
  48        printk(KERN_INFO KBUILD_MODNAME ": "\
  49                DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
  50#define PWARN(format, ...)\
  51        printk(KERN_WARNING KBUILD_MODNAME ": "\
  52                DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
  53# define PDEBUG(format, ...)\
  54        printk(KERN_DEBUG KBUILD_MODNAME ": "\
  55                DRIVER_VERSION ": " format "\n", ## __VA_ARGS__)
  56
  57/* Frequency limits in MHz -- these are European values.  For Japanese
  58devices, that would be 76000 and 91000.  */
  59#define FREQ_MIN  87500U
  60#define FREQ_MAX 108000U
  61#define FREQ_MUL 16
  62
  63/* TEA5764 registers */
  64#define TEA5764_MANID           0x002b
  65#define TEA5764_CHIPID          0x5764
  66
  67#define TEA5764_INTREG_BLMSK    0x0001
  68#define TEA5764_INTREG_FRRMSK   0x0002
  69#define TEA5764_INTREG_LEVMSK   0x0008
  70#define TEA5764_INTREG_IFMSK    0x0010
  71#define TEA5764_INTREG_BLMFLAG  0x0100
  72#define TEA5764_INTREG_FRRFLAG  0x0200
  73#define TEA5764_INTREG_LEVFLAG  0x0800
  74#define TEA5764_INTREG_IFFLAG   0x1000
  75
  76#define TEA5764_FRQSET_SUD      0x8000
  77#define TEA5764_FRQSET_SM       0x4000
  78
  79#define TEA5764_TNCTRL_PUPD1    0x8000
  80#define TEA5764_TNCTRL_PUPD0    0x4000
  81#define TEA5764_TNCTRL_BLIM     0x2000
  82#define TEA5764_TNCTRL_SWPM     0x1000
  83#define TEA5764_TNCTRL_IFCTC    0x0800
  84#define TEA5764_TNCTRL_AFM      0x0400
  85#define TEA5764_TNCTRL_SMUTE    0x0200
  86#define TEA5764_TNCTRL_SNC      0x0100
  87#define TEA5764_TNCTRL_MU       0x0080
  88#define TEA5764_TNCTRL_SSL1     0x0040
  89#define TEA5764_TNCTRL_SSL0     0x0020
  90#define TEA5764_TNCTRL_HLSI     0x0010
  91#define TEA5764_TNCTRL_MST      0x0008
  92#define TEA5764_TNCTRL_SWP      0x0004
  93#define TEA5764_TNCTRL_DTC      0x0002
  94#define TEA5764_TNCTRL_AHLSI    0x0001
  95
  96#define TEA5764_TUNCHK_LEVEL(x) (((x) & 0x00F0) >> 4)
  97#define TEA5764_TUNCHK_IFCNT(x) (((x) & 0xFE00) >> 9)
  98#define TEA5764_TUNCHK_TUNTO    0x0100
  99#define TEA5764_TUNCHK_LD       0x0008
 100#define TEA5764_TUNCHK_STEREO   0x0004
 101
 102#define TEA5764_TESTREG_TRIGFR  0x0800
 103
 104struct tea5764_regs {
 105        u16 intreg;                             /* INTFLAG & INTMSK */
 106        u16 frqset;                             /* FRQSETMSB & FRQSETLSB */
 107        u16 tnctrl;                             /* TNCTRL1 & TNCTRL2 */
 108        u16 frqchk;                             /* FRQCHKMSB & FRQCHKLSB */
 109        u16 tunchk;                             /* IFCHK & LEVCHK */
 110        u16 testreg;                            /* TESTBITS & TESTMODE */
 111        u16 rdsstat;                            /* RDSSTAT1 & RDSSTAT2 */
 112        u16 rdslb;                              /* RDSLBMSB & RDSLBLSB */
 113        u16 rdspb;                              /* RDSPBMSB & RDSPBLSB */
 114        u16 rdsbc;                              /* RDSBBC & RDSGBC */
 115        u16 rdsctrl;                            /* RDSCTRL1 & RDSCTRL2 */
 116        u16 rdsbbl;                             /* PAUSEDET & RDSBBL */
 117        u16 manid;                              /* MANID1 & MANID2 */
 118        u16 chipid;                             /* CHIPID1 & CHIPID2 */
 119} __attribute__ ((packed));
 120
 121struct tea5764_write_regs {
 122        u8 intreg;                              /* INTMSK */
 123        __be16 frqset;                          /* FRQSETMSB & FRQSETLSB */
 124        __be16 tnctrl;                          /* TNCTRL1 & TNCTRL2 */
 125        __be16 testreg;                         /* TESTBITS & TESTMODE */
 126        __be16 rdsctrl;                         /* RDSCTRL1 & RDSCTRL2 */
 127        __be16 rdsbbl;                          /* PAUSEDET & RDSBBL */
 128} __attribute__ ((packed));
 129
 130#ifdef CONFIG_RADIO_TEA5764_XTAL
 131#define RADIO_TEA5764_XTAL 1
 132#else
 133#define RADIO_TEA5764_XTAL 0
 134#endif
 135
 136static int radio_nr = -1;
 137static int use_xtal = RADIO_TEA5764_XTAL;
 138
 139struct tea5764_device {
 140        struct v4l2_device              v4l2_dev;
 141        struct v4l2_ctrl_handler        ctrl_handler;
 142        struct i2c_client               *i2c_client;
 143        struct video_device             vdev;
 144        struct tea5764_regs             regs;
 145        struct mutex                    mutex;
 146};
 147
 148/* I2C code related */
 149static int tea5764_i2c_read(struct tea5764_device *radio)
 150{
 151        int i;
 152        u16 *p = (u16 *) &radio->regs;
 153
 154        struct i2c_msg msgs[1] = {
 155                {       .addr = radio->i2c_client->addr,
 156                        .flags = I2C_M_RD,
 157                        .len = sizeof(radio->regs),
 158                        .buf = (void *)&radio->regs
 159                },
 160        };
 161        if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
 162                return -EIO;
 163        for (i = 0; i < sizeof(struct tea5764_regs) / sizeof(u16); i++)
 164                p[i] = __be16_to_cpu((__force __be16)p[i]);
 165
 166        return 0;
 167}
 168
 169static int tea5764_i2c_write(struct tea5764_device *radio)
 170{
 171        struct tea5764_write_regs wr;
 172        struct tea5764_regs *r = &radio->regs;
 173        struct i2c_msg msgs[1] = {
 174                {
 175                        .addr = radio->i2c_client->addr,
 176                        .len = sizeof(wr),
 177                        .buf = (void *)&wr
 178                },
 179        };
 180        wr.intreg  = r->intreg & 0xff;
 181        wr.frqset  = __cpu_to_be16(r->frqset);
 182        wr.tnctrl  = __cpu_to_be16(r->tnctrl);
 183        wr.testreg = __cpu_to_be16(r->testreg);
 184        wr.rdsctrl = __cpu_to_be16(r->rdsctrl);
 185        wr.rdsbbl  = __cpu_to_be16(r->rdsbbl);
 186        if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
 187                return -EIO;
 188        return 0;
 189}
 190
 191static void tea5764_power_up(struct tea5764_device *radio)
 192{
 193        struct tea5764_regs *r = &radio->regs;
 194
 195        if (!(r->tnctrl & TEA5764_TNCTRL_PUPD0)) {
 196                r->tnctrl &= ~(TEA5764_TNCTRL_AFM | TEA5764_TNCTRL_MU |
 197                               TEA5764_TNCTRL_HLSI);
 198                if (!use_xtal)
 199                        r->testreg |= TEA5764_TESTREG_TRIGFR;
 200                else
 201                        r->testreg &= ~TEA5764_TESTREG_TRIGFR;
 202
 203                r->tnctrl |= TEA5764_TNCTRL_PUPD0;
 204                tea5764_i2c_write(radio);
 205        }
 206}
 207
 208static void tea5764_power_down(struct tea5764_device *radio)
 209{
 210        struct tea5764_regs *r = &radio->regs;
 211
 212        if (r->tnctrl & TEA5764_TNCTRL_PUPD0) {
 213                r->tnctrl &= ~TEA5764_TNCTRL_PUPD0;
 214                tea5764_i2c_write(radio);
 215        }
 216}
 217
 218static void tea5764_set_freq(struct tea5764_device *radio, int freq)
 219{
 220        struct tea5764_regs *r = &radio->regs;
 221
 222        /* formula: (freq [+ or -] 225000) / 8192 */
 223        if (r->tnctrl & TEA5764_TNCTRL_HLSI)
 224                r->frqset = (freq + 225000) / 8192;
 225        else
 226                r->frqset = (freq - 225000) / 8192;
 227}
 228
 229static int tea5764_get_freq(struct tea5764_device *radio)
 230{
 231        struct tea5764_regs *r = &radio->regs;
 232
 233        if (r->tnctrl & TEA5764_TNCTRL_HLSI)
 234                return (r->frqchk * 8192) - 225000;
 235        else
 236                return (r->frqchk * 8192) + 225000;
 237}
 238
 239/* tune an frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
 240static void tea5764_tune(struct tea5764_device *radio, int freq)
 241{
 242        tea5764_set_freq(radio, freq);
 243        if (tea5764_i2c_write(radio))
 244                PWARN("Could not set frequency!");
 245}
 246
 247static void tea5764_set_audout_mode(struct tea5764_device *radio, int audmode)
 248{
 249        struct tea5764_regs *r = &radio->regs;
 250        int tnctrl = r->tnctrl;
 251
 252        if (audmode == V4L2_TUNER_MODE_MONO)
 253                r->tnctrl |= TEA5764_TNCTRL_MST;
 254        else
 255                r->tnctrl &= ~TEA5764_TNCTRL_MST;
 256        if (tnctrl != r->tnctrl)
 257                tea5764_i2c_write(radio);
 258}
 259
 260static int tea5764_get_audout_mode(struct tea5764_device *radio)
 261{
 262        struct tea5764_regs *r = &radio->regs;
 263
 264        if (r->tnctrl & TEA5764_TNCTRL_MST)
 265                return V4L2_TUNER_MODE_MONO;
 266        else
 267                return V4L2_TUNER_MODE_STEREO;
 268}
 269
 270static void tea5764_mute(struct tea5764_device *radio, int on)
 271{
 272        struct tea5764_regs *r = &radio->regs;
 273        int tnctrl = r->tnctrl;
 274
 275        if (on)
 276                r->tnctrl |= TEA5764_TNCTRL_MU;
 277        else
 278                r->tnctrl &= ~TEA5764_TNCTRL_MU;
 279        if (tnctrl != r->tnctrl)
 280                tea5764_i2c_write(radio);
 281}
 282
 283/* V4L2 vidioc */
 284static int vidioc_querycap(struct file *file, void  *priv,
 285                                        struct v4l2_capability *v)
 286{
 287        struct tea5764_device *radio = video_drvdata(file);
 288        struct video_device *dev = &radio->vdev;
 289
 290        strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver));
 291        strlcpy(v->card, dev->name, sizeof(v->card));
 292        snprintf(v->bus_info, sizeof(v->bus_info),
 293                 "I2C:%s", dev_name(&dev->dev));
 294        v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 295        v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
 296        return 0;
 297}
 298
 299static int vidioc_g_tuner(struct file *file, void *priv,
 300                                struct v4l2_tuner *v)
 301{
 302        struct tea5764_device *radio = video_drvdata(file);
 303        struct tea5764_regs *r = &radio->regs;
 304
 305        if (v->index > 0)
 306                return -EINVAL;
 307
 308        strlcpy(v->name, "FM", sizeof(v->name));
 309        v->type = V4L2_TUNER_RADIO;
 310        tea5764_i2c_read(radio);
 311        v->rangelow   = FREQ_MIN * FREQ_MUL;
 312        v->rangehigh  = FREQ_MAX * FREQ_MUL;
 313        v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
 314        if (r->tunchk & TEA5764_TUNCHK_STEREO)
 315                v->rxsubchans = V4L2_TUNER_SUB_STEREO;
 316        else
 317                v->rxsubchans = V4L2_TUNER_SUB_MONO;
 318        v->audmode = tea5764_get_audout_mode(radio);
 319        v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf;
 320        v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk);
 321
 322        return 0;
 323}
 324
 325static int vidioc_s_tuner(struct file *file, void *priv,
 326                                const struct v4l2_tuner *v)
 327{
 328        struct tea5764_device *radio = video_drvdata(file);
 329
 330        if (v->index > 0)
 331                return -EINVAL;
 332
 333        tea5764_set_audout_mode(radio, v->audmode);
 334        return 0;
 335}
 336
 337static int vidioc_s_frequency(struct file *file, void *priv,
 338                                const struct v4l2_frequency *f)
 339{
 340        struct tea5764_device *radio = video_drvdata(file);
 341        unsigned freq = f->frequency;
 342
 343        if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
 344                return -EINVAL;
 345        if (freq == 0) {
 346                /* We special case this as a power down control. */
 347                tea5764_power_down(radio);
 348                /* Yes, that's what is returned in this case. This
 349                   whole special case is non-compliant and should really
 350                   be replaced with something better, but changing this
 351                   might well break code that depends on this behavior.
 352                   So we keep it as-is. */
 353                return -EINVAL;
 354        }
 355        freq = clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
 356        tea5764_power_up(radio);
 357        tea5764_tune(radio, (freq * 125) / 2);
 358        return 0;
 359}
 360
 361static int vidioc_g_frequency(struct file *file, void *priv,
 362                                struct v4l2_frequency *f)
 363{
 364        struct tea5764_device *radio = video_drvdata(file);
 365        struct tea5764_regs *r = &radio->regs;
 366
 367        if (f->tuner != 0)
 368                return -EINVAL;
 369        tea5764_i2c_read(radio);
 370        f->type = V4L2_TUNER_RADIO;
 371        if (r->tnctrl & TEA5764_TNCTRL_PUPD0)
 372                f->frequency = (tea5764_get_freq(radio) * 2) / 125;
 373        else
 374                f->frequency = 0;
 375
 376        return 0;
 377}
 378
 379static int tea5764_s_ctrl(struct v4l2_ctrl *ctrl)
 380{
 381        struct tea5764_device *radio =
 382                container_of(ctrl->handler, struct tea5764_device, ctrl_handler);
 383
 384        switch (ctrl->id) {
 385        case V4L2_CID_AUDIO_MUTE:
 386                tea5764_mute(radio, ctrl->val);
 387                return 0;
 388        }
 389        return -EINVAL;
 390}
 391
 392static const struct v4l2_ctrl_ops tea5764_ctrl_ops = {
 393        .s_ctrl = tea5764_s_ctrl,
 394};
 395
 396/* File system interface */
 397static const struct v4l2_file_operations tea5764_fops = {
 398        .owner          = THIS_MODULE,
 399        .open           = v4l2_fh_open,
 400        .release        = v4l2_fh_release,
 401        .poll           = v4l2_ctrl_poll,
 402        .unlocked_ioctl = video_ioctl2,
 403};
 404
 405static const struct v4l2_ioctl_ops tea5764_ioctl_ops = {
 406        .vidioc_querycap    = vidioc_querycap,
 407        .vidioc_g_tuner     = vidioc_g_tuner,
 408        .vidioc_s_tuner     = vidioc_s_tuner,
 409        .vidioc_g_frequency = vidioc_g_frequency,
 410        .vidioc_s_frequency = vidioc_s_frequency,
 411        .vidioc_log_status  = v4l2_ctrl_log_status,
 412        .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 413        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 414};
 415
 416/* V4L2 interface */
 417static struct video_device tea5764_radio_template = {
 418        .name           = "TEA5764 FM-Radio",
 419        .fops           = &tea5764_fops,
 420        .ioctl_ops      = &tea5764_ioctl_ops,
 421        .release        = video_device_release_empty,
 422};
 423
 424/* I2C probe: check if the device exists and register with v4l if it is */
 425static int tea5764_i2c_probe(struct i2c_client *client,
 426                             const struct i2c_device_id *id)
 427{
 428        struct tea5764_device *radio;
 429        struct v4l2_device *v4l2_dev;
 430        struct v4l2_ctrl_handler *hdl;
 431        struct tea5764_regs *r;
 432        int ret;
 433
 434        PDEBUG("probe");
 435        radio = kzalloc(sizeof(struct tea5764_device), GFP_KERNEL);
 436        if (!radio)
 437                return -ENOMEM;
 438
 439        v4l2_dev = &radio->v4l2_dev;
 440        ret = v4l2_device_register(&client->dev, v4l2_dev);
 441        if (ret < 0) {
 442                v4l2_err(v4l2_dev, "could not register v4l2_device\n");
 443                goto errfr;
 444        }
 445
 446        hdl = &radio->ctrl_handler;
 447        v4l2_ctrl_handler_init(hdl, 1);
 448        v4l2_ctrl_new_std(hdl, &tea5764_ctrl_ops,
 449                        V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
 450        v4l2_dev->ctrl_handler = hdl;
 451        if (hdl->error) {
 452                ret = hdl->error;
 453                v4l2_err(v4l2_dev, "Could not register controls\n");
 454                goto errunreg;
 455        }
 456
 457        mutex_init(&radio->mutex);
 458        radio->i2c_client = client;
 459        ret = tea5764_i2c_read(radio);
 460        if (ret)
 461                goto errunreg;
 462        r = &radio->regs;
 463        PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid);
 464        if (r->chipid != TEA5764_CHIPID ||
 465                (r->manid & 0x0fff) != TEA5764_MANID) {
 466                PWARN("This chip is not a TEA5764!");
 467                ret = -EINVAL;
 468                goto errunreg;
 469        }
 470
 471        radio->vdev = tea5764_radio_template;
 472
 473        i2c_set_clientdata(client, radio);
 474        video_set_drvdata(&radio->vdev, radio);
 475        radio->vdev.lock = &radio->mutex;
 476        radio->vdev.v4l2_dev = v4l2_dev;
 477
 478        /* initialize and power off the chip */
 479        tea5764_i2c_read(radio);
 480        tea5764_set_audout_mode(radio, V4L2_TUNER_MODE_STEREO);
 481        tea5764_mute(radio, 1);
 482        tea5764_power_down(radio);
 483
 484        ret = video_register_device(&radio->vdev, VFL_TYPE_RADIO, radio_nr);
 485        if (ret < 0) {
 486                PWARN("Could not register video device!");
 487                goto errunreg;
 488        }
 489
 490        PINFO("registered.");
 491        return 0;
 492errunreg:
 493        v4l2_ctrl_handler_free(hdl);
 494        v4l2_device_unregister(v4l2_dev);
 495errfr:
 496        kfree(radio);
 497        return ret;
 498}
 499
 500static int tea5764_i2c_remove(struct i2c_client *client)
 501{
 502        struct tea5764_device *radio = i2c_get_clientdata(client);
 503
 504        PDEBUG("remove");
 505        if (radio) {
 506                tea5764_power_down(radio);
 507                video_unregister_device(&radio->vdev);
 508                v4l2_ctrl_handler_free(&radio->ctrl_handler);
 509                v4l2_device_unregister(&radio->v4l2_dev);
 510                kfree(radio);
 511        }
 512        return 0;
 513}
 514
 515/* I2C subsystem interface */
 516static const struct i2c_device_id tea5764_id[] = {
 517        { "radio-tea5764", 0 },
 518        { }                                     /* Terminating entry */
 519};
 520MODULE_DEVICE_TABLE(i2c, tea5764_id);
 521
 522static struct i2c_driver tea5764_i2c_driver = {
 523        .driver = {
 524                .name = "radio-tea5764",
 525        },
 526        .probe = tea5764_i2c_probe,
 527        .remove = tea5764_i2c_remove,
 528        .id_table = tea5764_id,
 529};
 530
 531module_i2c_driver(tea5764_i2c_driver);
 532
 533MODULE_AUTHOR(DRIVER_AUTHOR);
 534MODULE_DESCRIPTION(DRIVER_DESC);
 535MODULE_LICENSE("GPL");
 536MODULE_VERSION(DRIVER_VERSION);
 537
 538module_param(use_xtal, int, 0);
 539MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
 540module_param(radio_nr, int, 0);
 541MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
 542