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