root/drivers/media/radio/radio-tea5764.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tea5764_i2c_read
  2. tea5764_i2c_write
  3. tea5764_power_up
  4. tea5764_power_down
  5. tea5764_set_freq
  6. tea5764_get_freq
  7. tea5764_tune
  8. tea5764_set_audout_mode
  9. tea5764_get_audout_mode
  10. tea5764_mute
  11. vidioc_querycap
  12. vidioc_g_tuner
  13. vidioc_s_tuner
  14. vidioc_s_frequency
  15. vidioc_g_frequency
  16. tea5764_s_ctrl
  17. tea5764_i2c_probe
  18. tea5764_i2c_remove

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

/* [<][>][^][v][top][bottom][index][help] */