linux/drivers/media/i2c/tw2804.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005-2006 Micronas USA Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License (Version 2) as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program; if not, write to the Free Software Foundation,
  15 * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  16 */
  17
  18#include <linux/module.h>
  19#include <linux/init.h>
  20#include <linux/i2c.h>
  21#include <linux/videodev2.h>
  22#include <linux/ioctl.h>
  23#include <linux/slab.h>
  24#include <media/v4l2-subdev.h>
  25#include <media/v4l2-device.h>
  26#include <media/v4l2-chip-ident.h>
  27#include <media/v4l2-ctrls.h>
  28
  29#define TW2804_REG_AUTOGAIN             0x02
  30#define TW2804_REG_HUE                  0x0f
  31#define TW2804_REG_SATURATION           0x10
  32#define TW2804_REG_CONTRAST             0x11
  33#define TW2804_REG_BRIGHTNESS           0x12
  34#define TW2804_REG_COLOR_KILLER         0x14
  35#define TW2804_REG_GAIN                 0x3c
  36#define TW2804_REG_CHROMA_GAIN          0x3d
  37#define TW2804_REG_BLUE_BALANCE         0x3e
  38#define TW2804_REG_RED_BALANCE          0x3f
  39
  40struct tw2804 {
  41        struct v4l2_subdev sd;
  42        struct v4l2_ctrl_handler hdl;
  43        u8 channel:2;
  44        u8 input:1;
  45        int norm;
  46};
  47
  48static const u8 global_registers[] = {
  49        0x39, 0x00,
  50        0x3a, 0xff,
  51        0x3b, 0x84,
  52        0x3c, 0x80,
  53        0x3d, 0x80,
  54        0x3e, 0x82,
  55        0x3f, 0x82,
  56        0x78, 0x00,
  57        0xff, 0xff, /* Terminator (reg 0xff does not exist) */
  58};
  59
  60static const u8 channel_registers[] = {
  61        0x01, 0xc4,
  62        0x02, 0xa5,
  63        0x03, 0x20,
  64        0x04, 0xd0,
  65        0x05, 0x20,
  66        0x06, 0xd0,
  67        0x07, 0x88,
  68        0x08, 0x20,
  69        0x09, 0x07,
  70        0x0a, 0xf0,
  71        0x0b, 0x07,
  72        0x0c, 0xf0,
  73        0x0d, 0x40,
  74        0x0e, 0xd2,
  75        0x0f, 0x80,
  76        0x10, 0x80,
  77        0x11, 0x80,
  78        0x12, 0x80,
  79        0x13, 0x1f,
  80        0x14, 0x00,
  81        0x15, 0x00,
  82        0x16, 0x00,
  83        0x17, 0x00,
  84        0x18, 0xff,
  85        0x19, 0xff,
  86        0x1a, 0xff,
  87        0x1b, 0xff,
  88        0x1c, 0xff,
  89        0x1d, 0xff,
  90        0x1e, 0xff,
  91        0x1f, 0xff,
  92        0x20, 0x07,
  93        0x21, 0x07,
  94        0x22, 0x00,
  95        0x23, 0x91,
  96        0x24, 0x51,
  97        0x25, 0x03,
  98        0x26, 0x00,
  99        0x27, 0x00,
 100        0x28, 0x00,
 101        0x29, 0x00,
 102        0x2a, 0x00,
 103        0x2b, 0x00,
 104        0x2c, 0x00,
 105        0x2d, 0x00,
 106        0x2e, 0x00,
 107        0x2f, 0x00,
 108        0x30, 0x00,
 109        0x31, 0x00,
 110        0x32, 0x00,
 111        0x33, 0x00,
 112        0x34, 0x00,
 113        0x35, 0x00,
 114        0x36, 0x00,
 115        0x37, 0x00,
 116        0xff, 0xff, /* Terminator (reg 0xff does not exist) */
 117};
 118
 119static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel)
 120{
 121        return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
 122}
 123
 124static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel)
 125{
 126        int ret;
 127        int i;
 128
 129        for (i = 0; regs[i] != 0xff; i += 2) {
 130                ret = i2c_smbus_write_byte_data(client,
 131                                regs[i] | (channel << 6), regs[i + 1]);
 132                if (ret < 0)
 133                        return ret;
 134        }
 135        return 0;
 136}
 137
 138static int read_reg(struct i2c_client *client, u8 reg, u8 channel)
 139{
 140        return i2c_smbus_read_byte_data(client, (reg) | (channel << 6));
 141}
 142
 143static inline struct tw2804 *to_state(struct v4l2_subdev *sd)
 144{
 145        return container_of(sd, struct tw2804, sd);
 146}
 147
 148static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl)
 149{
 150        return container_of(ctrl->handler, struct tw2804, hdl);
 151}
 152
 153static int tw2804_log_status(struct v4l2_subdev *sd)
 154{
 155        struct tw2804 *state = to_state(sd);
 156
 157        v4l2_info(sd, "Standard: %s\n",
 158                        state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz");
 159        v4l2_info(sd, "Channel: %d\n", state->channel);
 160        v4l2_info(sd, "Input: %d\n", state->input);
 161        return v4l2_ctrl_subdev_log_status(sd);
 162}
 163
 164/*
 165 * These volatile controls are needed because all four channels share
 166 * these controls. So a change made to them through one channel would
 167 * require another channel to be updated.
 168 *
 169 * Normally this would have been done in a different way, but since the one
 170 * board that uses this driver sees this single chip as if it was on four
 171 * different i2c adapters (each adapter belonging to a separate instance of
 172 * the same USB driver) there is no reliable method that I have found to let
 173 * the instances know about each other.
 174 *
 175 * So implementing these global registers as volatile is the best we can do.
 176 */
 177static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 178{
 179        struct tw2804 *state = to_state_from_ctrl(ctrl);
 180        struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
 181
 182        switch (ctrl->id) {
 183        case V4L2_CID_GAIN:
 184                ctrl->val = read_reg(client, TW2804_REG_GAIN, 0);
 185                return 0;
 186
 187        case V4L2_CID_CHROMA_GAIN:
 188                ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0);
 189                return 0;
 190
 191        case V4L2_CID_BLUE_BALANCE:
 192                ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0);
 193                return 0;
 194
 195        case V4L2_CID_RED_BALANCE:
 196                ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0);
 197                return 0;
 198        }
 199        return 0;
 200}
 201
 202static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl)
 203{
 204        struct tw2804 *state = to_state_from_ctrl(ctrl);
 205        struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
 206        int addr;
 207        int reg;
 208
 209        switch (ctrl->id) {
 210        case V4L2_CID_AUTOGAIN:
 211                addr = TW2804_REG_AUTOGAIN;
 212                reg = read_reg(client, addr, state->channel);
 213                if (reg < 0)
 214                        return reg;
 215                if (ctrl->val == 0)
 216                        reg &= ~(1 << 7);
 217                else
 218                        reg |= 1 << 7;
 219                return write_reg(client, addr, reg, state->channel);
 220
 221        case V4L2_CID_COLOR_KILLER:
 222                addr = TW2804_REG_COLOR_KILLER;
 223                reg = read_reg(client, addr, state->channel);
 224                if (reg < 0)
 225                        return reg;
 226                reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03);
 227                return write_reg(client, addr, reg, state->channel);
 228
 229        case V4L2_CID_GAIN:
 230                return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0);
 231
 232        case V4L2_CID_CHROMA_GAIN:
 233                return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0);
 234
 235        case V4L2_CID_BLUE_BALANCE:
 236                return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0);
 237
 238        case V4L2_CID_RED_BALANCE:
 239                return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0);
 240
 241        case V4L2_CID_BRIGHTNESS:
 242                return write_reg(client, TW2804_REG_BRIGHTNESS,
 243                                ctrl->val, state->channel);
 244
 245        case V4L2_CID_CONTRAST:
 246                return write_reg(client, TW2804_REG_CONTRAST,
 247                                ctrl->val, state->channel);
 248
 249        case V4L2_CID_SATURATION:
 250                return write_reg(client, TW2804_REG_SATURATION,
 251                                ctrl->val, state->channel);
 252
 253        case V4L2_CID_HUE:
 254                return write_reg(client, TW2804_REG_HUE,
 255                                ctrl->val, state->channel);
 256
 257        default:
 258                break;
 259        }
 260        return -EINVAL;
 261}
 262
 263static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
 264{
 265        struct tw2804 *dec = to_state(sd);
 266        struct i2c_client *client = v4l2_get_subdevdata(sd);
 267        bool is_60hz = norm & V4L2_STD_525_60;
 268        u8 regs[] = {
 269                0x01, is_60hz ? 0xc4 : 0x84,
 270                0x09, is_60hz ? 0x07 : 0x04,
 271                0x0a, is_60hz ? 0xf0 : 0x20,
 272                0x0b, is_60hz ? 0x07 : 0x04,
 273                0x0c, is_60hz ? 0xf0 : 0x20,
 274                0x0d, is_60hz ? 0x40 : 0x4a,
 275                0x16, is_60hz ? 0x00 : 0x40,
 276                0x17, is_60hz ? 0x00 : 0x40,
 277                0x20, is_60hz ? 0x07 : 0x0f,
 278                0x21, is_60hz ? 0x07 : 0x0f,
 279                0xff, 0xff,
 280        };
 281
 282        write_regs(client, regs, dec->channel);
 283        dec->norm = norm;
 284        return 0;
 285}
 286
 287static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
 288        u32 config)
 289{
 290        struct tw2804 *dec = to_state(sd);
 291        struct i2c_client *client = v4l2_get_subdevdata(sd);
 292        int reg;
 293
 294        if (config && config - 1 != dec->channel) {
 295                if (config > 4) {
 296                        dev_err(&client->dev,
 297                                "channel %d is not between 1 and 4!\n", config);
 298                        return -EINVAL;
 299                }
 300                dec->channel = config - 1;
 301                dev_dbg(&client->dev, "initializing TW2804 channel %d\n",
 302                        dec->channel);
 303                if (dec->channel == 0 &&
 304                                write_regs(client, global_registers, 0) < 0) {
 305                        dev_err(&client->dev,
 306                                "error initializing TW2804 global registers\n");
 307                        return -EIO;
 308                }
 309                if (write_regs(client, channel_registers, dec->channel) < 0) {
 310                        dev_err(&client->dev,
 311                                "error initializing TW2804 channel %d\n",
 312                                dec->channel);
 313                        return -EIO;
 314                }
 315        }
 316
 317        if (input > 1)
 318                return -EINVAL;
 319
 320        if (input == dec->input)
 321                return 0;
 322
 323        reg = read_reg(client, 0x22, dec->channel);
 324
 325        if (reg >= 0) {
 326                if (input == 0)
 327                        reg &= ~(1 << 2);
 328                else
 329                        reg |= 1 << 2;
 330                reg = write_reg(client, 0x22, reg, dec->channel);
 331        }
 332
 333        if (reg >= 0)
 334                dec->input = input;
 335        else
 336                return reg;
 337        return 0;
 338}
 339
 340static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
 341        .g_volatile_ctrl = tw2804_g_volatile_ctrl,
 342        .s_ctrl = tw2804_s_ctrl,
 343};
 344
 345static const struct v4l2_subdev_video_ops tw2804_video_ops = {
 346        .s_routing = tw2804_s_video_routing,
 347};
 348
 349static const struct v4l2_subdev_core_ops tw2804_core_ops = {
 350        .log_status = tw2804_log_status,
 351        .s_std = tw2804_s_std,
 352};
 353
 354static const struct v4l2_subdev_ops tw2804_ops = {
 355        .core = &tw2804_core_ops,
 356        .video = &tw2804_video_ops,
 357};
 358
 359static int tw2804_probe(struct i2c_client *client,
 360                            const struct i2c_device_id *id)
 361{
 362        struct i2c_adapter *adapter = client->adapter;
 363        struct tw2804 *state;
 364        struct v4l2_subdev *sd;
 365        struct v4l2_ctrl *ctrl;
 366        int err;
 367
 368        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 369                return -ENODEV;
 370
 371        state = kzalloc(sizeof(struct tw2804), GFP_KERNEL);
 372
 373        if (state == NULL)
 374                return -ENOMEM;
 375        sd = &state->sd;
 376        v4l2_i2c_subdev_init(sd, client, &tw2804_ops);
 377        state->channel = -1;
 378        state->norm = V4L2_STD_NTSC;
 379
 380        v4l2_ctrl_handler_init(&state->hdl, 10);
 381        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 382                                V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
 383        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 384                                V4L2_CID_CONTRAST, 0, 255, 1, 128);
 385        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 386                                V4L2_CID_SATURATION, 0, 255, 1, 128);
 387        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 388                                V4L2_CID_HUE, 0, 255, 1, 128);
 389        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 390                                V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
 391        v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 392                                V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
 393        ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 394                                V4L2_CID_GAIN, 0, 255, 1, 128);
 395        if (ctrl)
 396                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 397        ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 398                                V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128);
 399        if (ctrl)
 400                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 401        ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 402                                V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122);
 403        if (ctrl)
 404                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 405        ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
 406                                V4L2_CID_RED_BALANCE, 0, 255, 1, 122);
 407        if (ctrl)
 408                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 409        sd->ctrl_handler = &state->hdl;
 410        err = state->hdl.error;
 411        if (err) {
 412                v4l2_ctrl_handler_free(&state->hdl);
 413                kfree(state);
 414                return err;
 415        }
 416
 417        v4l_info(client, "chip found @ 0x%02x (%s)\n",
 418                        client->addr << 1, client->adapter->name);
 419
 420        return 0;
 421}
 422
 423static int tw2804_remove(struct i2c_client *client)
 424{
 425        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 426        struct tw2804 *state = to_state(sd);
 427
 428        v4l2_device_unregister_subdev(sd);
 429        v4l2_ctrl_handler_free(&state->hdl);
 430        kfree(state);
 431        return 0;
 432}
 433
 434static const struct i2c_device_id tw2804_id[] = {
 435        { "tw2804", 0 },
 436        { }
 437};
 438MODULE_DEVICE_TABLE(i2c, tw2804_id);
 439
 440static struct i2c_driver tw2804_driver = {
 441        .driver = {
 442                .name   = "tw2804",
 443        },
 444        .probe          = tw2804_probe,
 445        .remove         = tw2804_remove,
 446        .id_table       = tw2804_id,
 447};
 448
 449module_i2c_driver(tw2804_driver);
 450
 451MODULE_LICENSE("GPL v2");
 452MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver");
 453MODULE_AUTHOR("Micronas USA Inc");
 454