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