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