linux/drivers/media/i2c/vpx3220.c
<<
>>
Prefs
   1/*
   2 * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
   3 *
   4 * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/init.h>
  19#include <linux/delay.h>
  20#include <linux/types.h>
  21#include <linux/slab.h>
  22#include <linux/uaccess.h>
  23#include <linux/i2c.h>
  24#include <linux/videodev2.h>
  25#include <media/v4l2-device.h>
  26#include <media/v4l2-ctrls.h>
  27
  28MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
  29MODULE_AUTHOR("Laurent Pinchart");
  30MODULE_LICENSE("GPL");
  31
  32static int debug;
  33module_param(debug, int, 0);
  34MODULE_PARM_DESC(debug, "Debug level (0-1)");
  35
  36
  37#define VPX_TIMEOUT_COUNT  10
  38
  39/* ----------------------------------------------------------------------- */
  40
  41struct vpx3220 {
  42        struct v4l2_subdev sd;
  43        struct v4l2_ctrl_handler hdl;
  44        unsigned char reg[255];
  45
  46        v4l2_std_id norm;
  47        int input;
  48        int enable;
  49};
  50
  51static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
  52{
  53        return container_of(sd, struct vpx3220, sd);
  54}
  55
  56static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
  57{
  58        return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
  59}
  60
  61static char *inputs[] = { "internal", "composite", "svideo" };
  62
  63/* ----------------------------------------------------------------------- */
  64
  65static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
  66{
  67        struct i2c_client *client = v4l2_get_subdevdata(sd);
  68        struct vpx3220 *decoder = i2c_get_clientdata(client);
  69
  70        decoder->reg[reg] = value;
  71        return i2c_smbus_write_byte_data(client, reg, value);
  72}
  73
  74static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
  75{
  76        struct i2c_client *client = v4l2_get_subdevdata(sd);
  77
  78        return i2c_smbus_read_byte_data(client, reg);
  79}
  80
  81static int vpx3220_fp_status(struct v4l2_subdev *sd)
  82{
  83        unsigned char status;
  84        unsigned int i;
  85
  86        for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
  87                status = vpx3220_read(sd, 0x29);
  88
  89                if (!(status & 4))
  90                        return 0;
  91
  92                udelay(10);
  93
  94                if (need_resched())
  95                        cond_resched();
  96        }
  97
  98        return -1;
  99}
 100
 101static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
 102{
 103        struct i2c_client *client = v4l2_get_subdevdata(sd);
 104
 105        /* Write the 16-bit address to the FPWR register */
 106        if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
 107                v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
 108                return -1;
 109        }
 110
 111        if (vpx3220_fp_status(sd) < 0)
 112                return -1;
 113
 114        /* Write the 16-bit data to the FPDAT register */
 115        if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
 116                v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
 117                return -1;
 118        }
 119
 120        return 0;
 121}
 122
 123static int vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
 124{
 125        struct i2c_client *client = v4l2_get_subdevdata(sd);
 126        s16 data;
 127
 128        /* Write the 16-bit address to the FPRD register */
 129        if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
 130                v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
 131                return -1;
 132        }
 133
 134        if (vpx3220_fp_status(sd) < 0)
 135                return -1;
 136
 137        /* Read the 16-bit data from the FPDAT register */
 138        data = i2c_smbus_read_word_data(client, 0x28);
 139        if (data == -1) {
 140                v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
 141                return -1;
 142        }
 143
 144        return swab16(data);
 145}
 146
 147static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
 148{
 149        u8 reg;
 150        int ret = -1;
 151
 152        while (len >= 2) {
 153                reg = *data++;
 154                ret = vpx3220_write(sd, reg, *data++);
 155                if (ret < 0)
 156                        break;
 157                len -= 2;
 158        }
 159
 160        return ret;
 161}
 162
 163static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
 164                const u16 *data, unsigned int len)
 165{
 166        u8 reg;
 167        int ret = 0;
 168
 169        while (len > 1) {
 170                reg = *data++;
 171                ret |= vpx3220_fp_write(sd, reg, *data++);
 172                len -= 2;
 173        }
 174
 175        return ret;
 176}
 177
 178/* ---------------------------------------------------------------------- */
 179
 180static const unsigned short init_ntsc[] = {
 181        0x1c, 0x00,             /* NTSC tint angle */
 182        0x88, 17,               /* Window 1 vertical */
 183        0x89, 240,              /* Vertical lines in */
 184        0x8a, 240,              /* Vertical lines out */
 185        0x8b, 000,              /* Horizontal begin */
 186        0x8c, 640,              /* Horizontal length */
 187        0x8d, 640,              /* Number of pixels */
 188        0x8f, 0xc00,            /* Disable window 2 */
 189        0xf0, 0x73,             /* 13.5 MHz transport, Forced
 190                                 * mode, latch windows */
 191        0xf2, 0x13,             /* NTSC M, composite input */
 192        0xe7, 0x1e1,            /* Enable vertical standard
 193                                 * locking @ 240 lines */
 194};
 195
 196static const unsigned short init_pal[] = {
 197        0x88, 23,               /* Window 1 vertical begin */
 198        0x89, 288,              /* Vertical lines in (16 lines
 199                                 * skipped by the VFE) */
 200        0x8a, 288,              /* Vertical lines out (16 lines
 201                                 * skipped by the VFE) */
 202        0x8b, 16,               /* Horizontal begin */
 203        0x8c, 768,              /* Horizontal length */
 204        0x8d, 784,              /* Number of pixels
 205                                 * Must be >= Horizontal begin + Horizontal length */
 206        0x8f, 0xc00,            /* Disable window 2 */
 207        0xf0, 0x77,             /* 13.5 MHz transport, Forced
 208                                 * mode, latch windows */
 209        0xf2, 0x3d1,            /* PAL B,G,H,I, composite input */
 210        0xe7, 0x241,            /* PAL/SECAM set to 288 lines */
 211};
 212
 213static const unsigned short init_secam[] = {
 214        0x88, 23,               /* Window 1 vertical begin */
 215        0x89, 288,              /* Vertical lines in (16 lines
 216                                 * skipped by the VFE) */
 217        0x8a, 288,              /* Vertical lines out (16 lines
 218                                 * skipped by the VFE) */
 219        0x8b, 16,               /* Horizontal begin */
 220        0x8c, 768,              /* Horizontal length */
 221        0x8d, 784,              /* Number of pixels
 222                                 * Must be >= Horizontal begin + Horizontal length */
 223        0x8f, 0xc00,            /* Disable window 2 */
 224        0xf0, 0x77,             /* 13.5 MHz transport, Forced
 225                                 * mode, latch windows */
 226        0xf2, 0x3d5,            /* SECAM, composite input */
 227        0xe7, 0x241,            /* PAL/SECAM set to 288 lines */
 228};
 229
 230static const unsigned char init_common[] = {
 231        0xf2, 0x00,             /* Disable all outputs */
 232        0x33, 0x0d,             /* Luma : VIN2, Chroma : CIN
 233                                 * (clamp off) */
 234        0xd8, 0xa8,             /* HREF/VREF active high, VREF
 235                                 * pulse = 2, Odd/Even flag */
 236        0x20, 0x03,             /* IF compensation 0dB/oct */
 237        0xe0, 0xff,             /* Open up all comparators */
 238        0xe1, 0x00,
 239        0xe2, 0x7f,
 240        0xe3, 0x80,
 241        0xe4, 0x7f,
 242        0xe5, 0x80,
 243        0xe6, 0x00,             /* Brightness set to 0 */
 244        0xe7, 0xe0,             /* Contrast to 1.0, noise shaping
 245                                 * 10 to 8 2-bit error diffusion */
 246        0xe8, 0xf8,             /* YUV422, CbCr binary offset,
 247                                 * ... (p.32) */
 248        0xea, 0x18,             /* LLC2 connected, output FIFO
 249                                 * reset with VACTintern */
 250        0xf0, 0x8a,             /* Half full level to 10, bus
 251                                 * shuffler [7:0, 23:16, 15:8] */
 252        0xf1, 0x18,             /* Single clock, sync mode, no
 253                                 * FE delay, no HLEN counter */
 254        0xf8, 0x12,             /* Port A, PIXCLK, HF# & FE#
 255                                 * strength to 2 */
 256        0xf9, 0x24,             /* Port B, HREF, VREF, PREF &
 257                                 * ALPHA strength to 4 */
 258};
 259
 260static const unsigned short init_fp[] = {
 261        0x59, 0,
 262        0xa0, 2070,             /* ACC reference */
 263        0xa3, 0,
 264        0xa4, 0,
 265        0xa8, 30,
 266        0xb2, 768,
 267        0xbe, 27,
 268        0x58, 0,
 269        0x26, 0,
 270        0x4b, 0x298,            /* PLL gain */
 271};
 272
 273
 274static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
 275{
 276        struct vpx3220 *decoder = to_vpx3220(sd);
 277
 278        vpx3220_write_block(sd, init_common, sizeof(init_common));
 279        vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
 280        if (decoder->norm & V4L2_STD_NTSC)
 281                vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
 282        else if (decoder->norm & V4L2_STD_PAL)
 283                vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
 284        else if (decoder->norm & V4L2_STD_SECAM)
 285                vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
 286        else
 287                vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
 288        return 0;
 289}
 290
 291static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
 292{
 293        int res = V4L2_IN_ST_NO_SIGNAL, status;
 294        v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
 295
 296        status = vpx3220_fp_read(sd, 0x0f3);
 297
 298        v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
 299
 300        if (status < 0)
 301                return status;
 302
 303        if ((status & 0x20) == 0) {
 304                res = 0;
 305
 306                switch (status & 0x18) {
 307                case 0x00:
 308                case 0x10:
 309                case 0x14:
 310                case 0x18:
 311                        std &= V4L2_STD_PAL;
 312                        break;
 313
 314                case 0x08:
 315                        std &= V4L2_STD_SECAM;
 316                        break;
 317
 318                case 0x04:
 319                case 0x0c:
 320                case 0x1c:
 321                        std &= V4L2_STD_NTSC;
 322                        break;
 323                }
 324        } else {
 325                std = V4L2_STD_UNKNOWN;
 326        }
 327        if (pstd)
 328                *pstd = std;
 329        if (pstatus)
 330                *pstatus = res;
 331        return 0;
 332}
 333
 334static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
 335{
 336        v4l2_dbg(1, debug, sd, "querystd\n");
 337        return vpx3220_status(sd, NULL, std);
 338}
 339
 340static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
 341{
 342        v4l2_dbg(1, debug, sd, "g_input_status\n");
 343        return vpx3220_status(sd, status, NULL);
 344}
 345
 346static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 347{
 348        struct vpx3220 *decoder = to_vpx3220(sd);
 349        int temp_input;
 350
 351        /* Here we back up the input selection because it gets
 352           overwritten when we fill the registers with the
 353           chosen video norm */
 354        temp_input = vpx3220_fp_read(sd, 0xf2);
 355
 356        v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
 357        if (std & V4L2_STD_NTSC) {
 358                vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
 359                v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
 360        } else if (std & V4L2_STD_PAL) {
 361                vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
 362                v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
 363        } else if (std & V4L2_STD_SECAM) {
 364                vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
 365                v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
 366        } else {
 367                return -EINVAL;
 368        }
 369
 370        decoder->norm = std;
 371
 372        /* And here we set the backed up video input again */
 373        vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
 374        udelay(10);
 375        return 0;
 376}
 377
 378static int vpx3220_s_routing(struct v4l2_subdev *sd,
 379                             u32 input, u32 output, u32 config)
 380{
 381        int data;
 382
 383        /* RJ:   input = 0: ST8 (PCTV) input
 384                 input = 1: COMPOSITE  input
 385                 input = 2: SVHS       input  */
 386
 387        const int input_vals[3][2] = {
 388                {0x0c, 0},
 389                {0x0d, 0},
 390                {0x0e, 1}
 391        };
 392
 393        if (input > 2)
 394                return -EINVAL;
 395
 396        v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
 397
 398        vpx3220_write(sd, 0x33, input_vals[input][0]);
 399
 400        data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
 401        if (data < 0)
 402                return data;
 403        /* 0x0010 is required to latch the setting */
 404        vpx3220_fp_write(sd, 0xf2,
 405                        data | (input_vals[input][1] << 5) | 0x0010);
 406
 407        udelay(10);
 408        return 0;
 409}
 410
 411static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
 412{
 413        v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
 414
 415        vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
 416        return 0;
 417}
 418
 419static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
 420{
 421        struct v4l2_subdev *sd = to_sd(ctrl);
 422
 423        switch (ctrl->id) {
 424        case V4L2_CID_BRIGHTNESS:
 425                vpx3220_write(sd, 0xe6, ctrl->val);
 426                return 0;
 427        case V4L2_CID_CONTRAST:
 428                /* Bit 7 and 8 is for noise shaping */
 429                vpx3220_write(sd, 0xe7, ctrl->val + 192);
 430                return 0;
 431        case V4L2_CID_SATURATION:
 432                vpx3220_fp_write(sd, 0xa0, ctrl->val);
 433                return 0;
 434        case V4L2_CID_HUE:
 435                vpx3220_fp_write(sd, 0x1c, ctrl->val);
 436                return 0;
 437        }
 438        return -EINVAL;
 439}
 440
 441/* ----------------------------------------------------------------------- */
 442
 443static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
 444        .s_ctrl = vpx3220_s_ctrl,
 445};
 446
 447static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
 448        .init = vpx3220_init,
 449};
 450
 451static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
 452        .s_std = vpx3220_s_std,
 453        .s_routing = vpx3220_s_routing,
 454        .s_stream = vpx3220_s_stream,
 455        .querystd = vpx3220_querystd,
 456        .g_input_status = vpx3220_g_input_status,
 457};
 458
 459static const struct v4l2_subdev_ops vpx3220_ops = {
 460        .core = &vpx3220_core_ops,
 461        .video = &vpx3220_video_ops,
 462};
 463
 464/* -----------------------------------------------------------------------
 465 * Client management code
 466 */
 467
 468static int vpx3220_probe(struct i2c_client *client,
 469                        const struct i2c_device_id *id)
 470{
 471        struct vpx3220 *decoder;
 472        struct v4l2_subdev *sd;
 473        const char *name = NULL;
 474        u8 ver;
 475        u16 pn;
 476
 477        /* Check if the adapter supports the needed features */
 478        if (!i2c_check_functionality(client->adapter,
 479                I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
 480                return -ENODEV;
 481
 482        decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
 483        if (decoder == NULL)
 484                return -ENOMEM;
 485        sd = &decoder->sd;
 486        v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
 487        decoder->norm = V4L2_STD_PAL;
 488        decoder->input = 0;
 489        decoder->enable = 1;
 490        v4l2_ctrl_handler_init(&decoder->hdl, 4);
 491        v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
 492                V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
 493        v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
 494                V4L2_CID_CONTRAST, 0, 63, 1, 32);
 495        v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
 496                V4L2_CID_SATURATION, 0, 4095, 1, 2048);
 497        v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
 498                V4L2_CID_HUE, -512, 511, 1, 0);
 499        sd->ctrl_handler = &decoder->hdl;
 500        if (decoder->hdl.error) {
 501                int err = decoder->hdl.error;
 502
 503                v4l2_ctrl_handler_free(&decoder->hdl);
 504                return err;
 505        }
 506        v4l2_ctrl_handler_setup(&decoder->hdl);
 507
 508        ver = i2c_smbus_read_byte_data(client, 0x00);
 509        pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
 510                i2c_smbus_read_byte_data(client, 0x01);
 511        if (ver == 0xec) {
 512                switch (pn) {
 513                case 0x4680:
 514                        name = "vpx3220a";
 515                        break;
 516                case 0x4260:
 517                        name = "vpx3216b";
 518                        break;
 519                case 0x4280:
 520                        name = "vpx3214c";
 521                        break;
 522                }
 523        }
 524        if (name)
 525                v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
 526                        client->addr << 1, client->adapter->name);
 527        else
 528                v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
 529                        ver, pn, client->addr << 1, client->adapter->name);
 530
 531        vpx3220_write_block(sd, init_common, sizeof(init_common));
 532        vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
 533        /* Default to PAL */
 534        vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
 535        return 0;
 536}
 537
 538static int vpx3220_remove(struct i2c_client *client)
 539{
 540        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 541        struct vpx3220 *decoder = to_vpx3220(sd);
 542
 543        v4l2_device_unregister_subdev(sd);
 544        v4l2_ctrl_handler_free(&decoder->hdl);
 545
 546        return 0;
 547}
 548
 549static const struct i2c_device_id vpx3220_id[] = {
 550        { "vpx3220a", 0 },
 551        { "vpx3216b", 0 },
 552        { "vpx3214c", 0 },
 553        { }
 554};
 555MODULE_DEVICE_TABLE(i2c, vpx3220_id);
 556
 557static struct i2c_driver vpx3220_driver = {
 558        .driver = {
 559                .name   = "vpx3220",
 560        },
 561        .probe          = vpx3220_probe,
 562        .remove         = vpx3220_remove,
 563        .id_table       = vpx3220_id,
 564};
 565
 566module_i2c_driver(vpx3220_driver);
 567