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