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