linux/drivers/media/i2c/sony-btf-mpx.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005-2006 Micronas USA Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License (Version 2) as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program; if not, write to the Free Software Foundation,
  15 * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  16 */
  17
  18#include <linux/module.h>
  19#include <linux/init.h>
  20#include <linux/i2c.h>
  21#include <linux/videodev2.h>
  22#include <media/tuner.h>
  23#include <media/v4l2-common.h>
  24#include <media/v4l2-ioctl.h>
  25#include <media/v4l2-device.h>
  26#include <linux/slab.h>
  27
  28MODULE_DESCRIPTION("sony-btf-mpx driver");
  29MODULE_LICENSE("GPL v2");
  30
  31static int debug;
  32module_param(debug, int, 0644);
  33MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on");
  34
  35/* #define MPX_DEBUG */
  36
  37/*
  38 * Note:
  39 *
  40 * AS(IF/MPX) pin:      LOW      HIGH/OPEN
  41 * IF/MPX address:   0x42/0x40   0x43/0x44
  42 */
  43
  44
  45static int force_mpx_mode = -1;
  46module_param(force_mpx_mode, int, 0644);
  47
  48struct sony_btf_mpx {
  49        struct v4l2_subdev sd;
  50        int mpxmode;
  51        u32 audmode;
  52};
  53
  54static inline struct sony_btf_mpx *to_state(struct v4l2_subdev *sd)
  55{
  56        return container_of(sd, struct sony_btf_mpx, sd);
  57}
  58
  59static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
  60{
  61        u8 buffer[5];
  62        struct i2c_msg msg;
  63
  64        buffer[0] = dev;
  65        buffer[1] = addr >> 8;
  66        buffer[2] = addr & 0xff;
  67        buffer[3] = val >> 8;
  68        buffer[4] = val & 0xff;
  69        msg.addr = client->addr;
  70        msg.flags = 0;
  71        msg.len = 5;
  72        msg.buf = buffer;
  73        i2c_transfer(client->adapter, &msg, 1);
  74        return 0;
  75}
  76
  77/*
  78 * MPX register values for the BTF-PG472Z:
  79 *
  80 *                                 FM_     NICAM_  SCART_
  81 *          MODUS  SOURCE    ACB   PRESCAL PRESCAL PRESCAL SYSTEM  VOLUME
  82 *         10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
  83 *         ---------------------------------------------------------------
  84 * Auto     1003    0020    0100    2603    5000    XXXX    0001    7500
  85 *
  86 * B/G
  87 *  Mono    1003    0020    0100    2603    5000    XXXX    0003    7500
  88 *  A2      1003    0020    0100    2601    5000    XXXX    0003    7500
  89 *  NICAM   1003    0120    0100    2603    5000    XXXX    0008    7500
  90 *
  91 * I
  92 *  Mono    1003    0020    0100    2603    7900    XXXX    000A    7500
  93 *  NICAM   1003    0120    0100    2603    7900    XXXX    000A    7500
  94 *
  95 * D/K
  96 *  Mono    1003    0020    0100    2603    5000    XXXX    0004    7500
  97 *  A2-1    1003    0020    0100    2601    5000    XXXX    0004    7500
  98 *  A2-2    1003    0020    0100    2601    5000    XXXX    0005    7500
  99 *  A2-3    1003    0020    0100    2601    5000    XXXX    0007    7500
 100 *  NICAM   1003    0120    0100    2603    5000    XXXX    000B    7500
 101 *
 102 * L/L'
 103 *  Mono    0003    0200    0100    7C03    5000    2200    0009    7500
 104 *  NICAM   0003    0120    0100    7C03    5000    XXXX    0009    7500
 105 *
 106 * M
 107 *  Mono    1003    0200    0100    2B03    5000    2B00    0002    7500
 108 *
 109 * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
 110 *
 111 * Bilingual selection in A2/NICAM:
 112 *
 113 *         High byte of SOURCE     Left chan   Right chan
 114 *                 0x01              MAIN         SUB
 115 *                 0x03              MAIN         MAIN
 116 *                 0x04              SUB          SUB
 117 *
 118 * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
 119 * 0x00 (all other bands).  Force mono in A2 with FMONO_A2:
 120 *
 121 *                      FMONO_A2
 122 *                      10/0022
 123 *                      --------
 124 *     Forced mono ON     07F0
 125 *     Forced mono OFF    0190
 126 */
 127
 128static const struct {
 129        enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
 130        u16 modus;
 131        u16 source;
 132        u16 acb;
 133        u16 fm_prescale;
 134        u16 nicam_prescale;
 135        u16 scart_prescale;
 136        u16 system;
 137        u16 volume;
 138} mpx_audio_modes[] = {
 139        /* Auto */      { AUD_MONO,     0x1003, 0x0020, 0x0100, 0x2603,
 140                                        0x5000, 0x0000, 0x0001, 0x7500 },
 141        /* B/G Mono */  { AUD_MONO,     0x1003, 0x0020, 0x0100, 0x2603,
 142                                        0x5000, 0x0000, 0x0003, 0x7500 },
 143        /* B/G A2 */    { AUD_A2,       0x1003, 0x0020, 0x0100, 0x2601,
 144                                        0x5000, 0x0000, 0x0003, 0x7500 },
 145        /* B/G NICAM */ { AUD_NICAM,    0x1003, 0x0120, 0x0100, 0x2603,
 146                                        0x5000, 0x0000, 0x0008, 0x7500 },
 147        /* I Mono */    { AUD_MONO,     0x1003, 0x0020, 0x0100, 0x2603,
 148                                        0x7900, 0x0000, 0x000A, 0x7500 },
 149        /* I NICAM */   { AUD_NICAM,    0x1003, 0x0120, 0x0100, 0x2603,
 150                                        0x7900, 0x0000, 0x000A, 0x7500 },
 151        /* D/K Mono */  { AUD_MONO,     0x1003, 0x0020, 0x0100, 0x2603,
 152                                        0x5000, 0x0000, 0x0004, 0x7500 },
 153        /* D/K A2-1 */  { AUD_A2,       0x1003, 0x0020, 0x0100, 0x2601,
 154                                        0x5000, 0x0000, 0x0004, 0x7500 },
 155        /* D/K A2-2 */  { AUD_A2,       0x1003, 0x0020, 0x0100, 0x2601,
 156                                        0x5000, 0x0000, 0x0005, 0x7500 },
 157        /* D/K A2-3 */  { AUD_A2,       0x1003, 0x0020, 0x0100, 0x2601,
 158                                        0x5000, 0x0000, 0x0007, 0x7500 },
 159        /* D/K NICAM */ { AUD_NICAM,    0x1003, 0x0120, 0x0100, 0x2603,
 160                                        0x5000, 0x0000, 0x000B, 0x7500 },
 161        /* L/L' Mono */ { AUD_MONO,     0x0003, 0x0200, 0x0100, 0x7C03,
 162                                        0x5000, 0x2200, 0x0009, 0x7500 },
 163        /* L/L' NICAM */{ AUD_NICAM_L,  0x0003, 0x0120, 0x0100, 0x7C03,
 164                                        0x5000, 0x0000, 0x0009, 0x7500 },
 165};
 166
 167#define MPX_NUM_MODES   ARRAY_SIZE(mpx_audio_modes)
 168
 169static int mpx_setup(struct sony_btf_mpx *t)
 170{
 171        struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
 172        u16 source = 0;
 173        u8 buffer[3];
 174        struct i2c_msg msg;
 175        int mode = t->mpxmode;
 176
 177        /* reset MPX */
 178        buffer[0] = 0x00;
 179        buffer[1] = 0x80;
 180        buffer[2] = 0x00;
 181        msg.addr = client->addr;
 182        msg.flags = 0;
 183        msg.len = 3;
 184        msg.buf = buffer;
 185        i2c_transfer(client->adapter, &msg, 1);
 186        buffer[1] = 0x00;
 187        i2c_transfer(client->adapter, &msg, 1);
 188
 189        if (t->audmode != V4L2_TUNER_MODE_MONO)
 190                mode++;
 191
 192        if (mpx_audio_modes[mode].audio_mode != AUD_MONO) {
 193                switch (t->audmode) {
 194                case V4L2_TUNER_MODE_MONO:
 195                        switch (mpx_audio_modes[mode].audio_mode) {
 196                        case AUD_A2:
 197                                source = mpx_audio_modes[mode].source;
 198                                break;
 199                        case AUD_NICAM:
 200                                source = 0x0000;
 201                                break;
 202                        case AUD_NICAM_L:
 203                                source = 0x0200;
 204                                break;
 205                        default:
 206                                break;
 207                        }
 208                        break;
 209                case V4L2_TUNER_MODE_STEREO:
 210                        source = mpx_audio_modes[mode].source;
 211                        break;
 212                case V4L2_TUNER_MODE_LANG1:
 213                        source = 0x0300;
 214                        break;
 215                case V4L2_TUNER_MODE_LANG2:
 216                        source = 0x0400;
 217                        break;
 218                }
 219                source |= mpx_audio_modes[mode].source & 0x00ff;
 220        } else
 221                source = mpx_audio_modes[mode].source;
 222
 223        mpx_write(client, 0x10, 0x0030, mpx_audio_modes[mode].modus);
 224        mpx_write(client, 0x12, 0x0008, source);
 225        mpx_write(client, 0x12, 0x0013, mpx_audio_modes[mode].acb);
 226        mpx_write(client, 0x12, 0x000e,
 227                        mpx_audio_modes[mode].fm_prescale);
 228        mpx_write(client, 0x12, 0x0010,
 229                        mpx_audio_modes[mode].nicam_prescale);
 230        mpx_write(client, 0x12, 0x000d,
 231                        mpx_audio_modes[mode].scart_prescale);
 232        mpx_write(client, 0x10, 0x0020, mpx_audio_modes[mode].system);
 233        mpx_write(client, 0x12, 0x0000, mpx_audio_modes[mode].volume);
 234        if (mpx_audio_modes[mode].audio_mode == AUD_A2)
 235                mpx_write(client, 0x10, 0x0022,
 236                        t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);
 237
 238#ifdef MPX_DEBUG
 239        {
 240                u8 buf1[3], buf2[2];
 241                struct i2c_msg msgs[2];
 242
 243                v4l2_info(client,
 244                        "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n",
 245                        mpx_audio_modes[mode].modus,
 246                        source,
 247                        mpx_audio_modes[mode].acb,
 248                        mpx_audio_modes[mode].fm_prescale,
 249                        mpx_audio_modes[mode].nicam_prescale,
 250                        mpx_audio_modes[mode].scart_prescale,
 251                        mpx_audio_modes[mode].system,
 252                        mpx_audio_modes[mode].volume);
 253                buf1[0] = 0x11;
 254                buf1[1] = 0x00;
 255                buf1[2] = 0x7e;
 256                msgs[0].addr = client->addr;
 257                msgs[0].flags = 0;
 258                msgs[0].len = 3;
 259                msgs[0].buf = buf1;
 260                msgs[1].addr = client->addr;
 261                msgs[1].flags = I2C_M_RD;
 262                msgs[1].len = 2;
 263                msgs[1].buf = buf2;
 264                i2c_transfer(client->adapter, msgs, 2);
 265                v4l2_info(client, "MPX system: %02x%02x\n",
 266                                buf2[0], buf2[1]);
 267                buf1[0] = 0x11;
 268                buf1[1] = 0x02;
 269                buf1[2] = 0x00;
 270                i2c_transfer(client->adapter, msgs, 2);
 271                v4l2_info(client, "MPX status: %02x%02x\n",
 272                                buf2[0], buf2[1]);
 273        }
 274#endif
 275        return 0;
 276}
 277
 278
 279static int sony_btf_mpx_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 280{
 281        struct sony_btf_mpx *t = to_state(sd);
 282        int default_mpx_mode = 0;
 283
 284        if (std & V4L2_STD_PAL_BG)
 285                default_mpx_mode = 1;
 286        else if (std & V4L2_STD_PAL_I)
 287                default_mpx_mode = 4;
 288        else if (std & V4L2_STD_PAL_DK)
 289                default_mpx_mode = 6;
 290        else if (std & V4L2_STD_SECAM_L)
 291                default_mpx_mode = 11;
 292
 293        if (default_mpx_mode != t->mpxmode) {
 294                t->mpxmode = default_mpx_mode;
 295                mpx_setup(t);
 296        }
 297        return 0;
 298}
 299
 300static int sony_btf_mpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 301{
 302        struct sony_btf_mpx *t = to_state(sd);
 303
 304        vt->capability = V4L2_TUNER_CAP_NORM |
 305                V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
 306                V4L2_TUNER_CAP_LANG2;
 307        vt->rxsubchans = V4L2_TUNER_SUB_MONO |
 308                V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
 309                V4L2_TUNER_SUB_LANG2;
 310        vt->audmode = t->audmode;
 311        return 0;
 312}
 313
 314static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
 315{
 316        struct sony_btf_mpx *t = to_state(sd);
 317
 318        if (vt->type != V4L2_TUNER_ANALOG_TV)
 319                return -EINVAL;
 320
 321        if (vt->audmode != t->audmode) {
 322                t->audmode = vt->audmode;
 323                mpx_setup(t);
 324        }
 325        return 0;
 326}
 327
 328/* --------------------------------------------------------------------------*/
 329
 330static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = {
 331        .s_std = sony_btf_mpx_s_std,
 332};
 333
 334static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = {
 335        .s_tuner = sony_btf_mpx_s_tuner,
 336        .g_tuner = sony_btf_mpx_g_tuner,
 337};
 338
 339static const struct v4l2_subdev_ops sony_btf_mpx_ops = {
 340        .core = &sony_btf_mpx_core_ops,
 341        .tuner = &sony_btf_mpx_tuner_ops,
 342};
 343
 344/* --------------------------------------------------------------------------*/
 345
 346static int sony_btf_mpx_probe(struct i2c_client *client,
 347                                const struct i2c_device_id *id)
 348{
 349        struct sony_btf_mpx *t;
 350        struct v4l2_subdev *sd;
 351
 352        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
 353                return -ENODEV;
 354
 355        v4l_info(client, "chip found @ 0x%x (%s)\n",
 356                        client->addr << 1, client->adapter->name);
 357
 358        t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
 359        if (t == NULL)
 360                return -ENOMEM;
 361
 362        sd = &t->sd;
 363        v4l2_i2c_subdev_init(sd, client, &sony_btf_mpx_ops);
 364
 365        /* Initialize sony_btf_mpx */
 366        t->mpxmode = 0;
 367        t->audmode = V4L2_TUNER_MODE_STEREO;
 368
 369        return 0;
 370}
 371
 372static int sony_btf_mpx_remove(struct i2c_client *client)
 373{
 374        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 375
 376        v4l2_device_unregister_subdev(sd);
 377
 378        return 0;
 379}
 380
 381/* ----------------------------------------------------------------------- */
 382
 383static const struct i2c_device_id sony_btf_mpx_id[] = {
 384        { "sony-btf-mpx", 0 },
 385        { }
 386};
 387MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id);
 388
 389static struct i2c_driver sony_btf_mpx_driver = {
 390        .driver = {
 391                .owner  = THIS_MODULE,
 392                .name   = "sony-btf-mpx",
 393        },
 394        .probe = sony_btf_mpx_probe,
 395        .remove = sony_btf_mpx_remove,
 396        .id_table = sony_btf_mpx_id,
 397};
 398module_i2c_driver(sony_btf_mpx_driver);
 399