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