linux/drivers/media/i2c/ml86v7667.c
<<
>>
Prefs
   1/*
   2 * OKI Semiconductor ML86V7667 video decoder driver
   3 *
   4 * Author: Vladimir Barinov <source@cogentembedded.com>
   5 * Copyright (C) 2013 Cogent Embedded, Inc.
   6 * Copyright (C) 2013 Renesas Solutions Corp.
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/init.h>
  15#include <linux/module.h>
  16#include <linux/i2c.h>
  17#include <linux/slab.h>
  18#include <linux/videodev2.h>
  19#include <media/v4l2-subdev.h>
  20#include <media/v4l2-device.h>
  21#include <media/v4l2-ioctl.h>
  22#include <media/v4l2-ctrls.h>
  23
  24#define DRV_NAME "ml86v7667"
  25
  26/* Subaddresses */
  27#define MRA_REG                 0x00 /* Mode Register A */
  28#define MRC_REG                 0x02 /* Mode Register C */
  29#define LUMC_REG                0x0C /* Luminance Control */
  30#define CLC_REG                 0x10 /* Contrast level control */
  31#define SSEPL_REG               0x11 /* Sync separation level */
  32#define CHRCA_REG               0x12 /* Chrominance Control A */
  33#define ACCC_REG                0x14 /* ACC Loop filter & Chrominance control */
  34#define ACCRC_REG               0x15 /* ACC Reference level control */
  35#define HUE_REG                 0x16 /* Hue control */
  36#define ADC2_REG                0x1F /* ADC Register 2 */
  37#define PLLR1_REG               0x20 /* PLL Register 1 */
  38#define STATUS_REG              0x2C /* STATUS Register */
  39
  40/* Mode Register A register bits */
  41#define MRA_OUTPUT_MODE_MASK    (3 << 6)
  42#define MRA_ITUR_BT601          (1 << 6)
  43#define MRA_ITUR_BT656          (0 << 6)
  44#define MRA_INPUT_MODE_MASK     (7 << 3)
  45#define MRA_PAL_BT601           (4 << 3)
  46#define MRA_NTSC_BT601          (0 << 3)
  47#define MRA_REGISTER_MODE       (1 << 0)
  48
  49/* Mode Register C register bits */
  50#define MRC_AUTOSELECT          (1 << 7)
  51
  52/* Luminance Control register bits */
  53#define LUMC_ONOFF_SHIFT        7
  54#define LUMC_ONOFF_MASK         (1 << 7)
  55
  56/* Contrast level control register bits */
  57#define CLC_CONTRAST_ONOFF      (1 << 7)
  58#define CLC_CONTRAST_MASK       0x0F
  59
  60/* Sync separation level register bits */
  61#define SSEPL_LUMINANCE_ONOFF   (1 << 7)
  62#define SSEPL_LUMINANCE_MASK    0x7F
  63
  64/* Chrominance Control A register bits */
  65#define CHRCA_MODE_SHIFT        6
  66#define CHRCA_MODE_MASK         (1 << 6)
  67
  68/* ACC Loop filter & Chrominance control register bits */
  69#define ACCC_CHROMA_CR_SHIFT    3
  70#define ACCC_CHROMA_CR_MASK     (7 << 3)
  71#define ACCC_CHROMA_CB_SHIFT    0
  72#define ACCC_CHROMA_CB_MASK     (7 << 0)
  73
  74/* ACC Reference level control register bits */
  75#define ACCRC_CHROMA_MASK       0xfc
  76#define ACCRC_CHROMA_SHIFT      2
  77
  78/* ADC Register 2 register bits */
  79#define ADC2_CLAMP_VOLTAGE_MASK (7 << 1)
  80#define ADC2_CLAMP_VOLTAGE(n)   ((n & 7) << 1)
  81
  82/* PLL Register 1 register bits */
  83#define PLLR1_FIXED_CLOCK       (1 << 7)
  84
  85/* STATUS Register register bits */
  86#define STATUS_HLOCK_DETECT     (1 << 3)
  87#define STATUS_NTSCPAL          (1 << 2)
  88
  89struct ml86v7667_priv {
  90        struct v4l2_subdev              sd;
  91        struct v4l2_ctrl_handler        hdl;
  92        v4l2_std_id                     std;
  93};
  94
  95static inline struct ml86v7667_priv *to_ml86v7667(struct v4l2_subdev *subdev)
  96{
  97        return container_of(subdev, struct ml86v7667_priv, sd);
  98}
  99
 100static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
 101{
 102        return &container_of(ctrl->handler, struct ml86v7667_priv, hdl)->sd;
 103}
 104
 105static int ml86v7667_mask_set(struct i2c_client *client, const u8 reg,
 106                              const u8 mask, const u8 data)
 107{
 108        int val = i2c_smbus_read_byte_data(client, reg);
 109        if (val < 0)
 110                return val;
 111
 112        val = (val & ~mask) | (data & mask);
 113        return i2c_smbus_write_byte_data(client, reg, val);
 114}
 115
 116static int ml86v7667_s_ctrl(struct v4l2_ctrl *ctrl)
 117{
 118        struct v4l2_subdev *sd = to_sd(ctrl);
 119        struct i2c_client *client = v4l2_get_subdevdata(sd);
 120        int ret = -EINVAL;
 121
 122        switch (ctrl->id) {
 123        case V4L2_CID_BRIGHTNESS:
 124                ret = ml86v7667_mask_set(client, SSEPL_REG,
 125                                         SSEPL_LUMINANCE_MASK, ctrl->val);
 126                break;
 127        case V4L2_CID_CONTRAST:
 128                ret = ml86v7667_mask_set(client, CLC_REG,
 129                                         CLC_CONTRAST_MASK, ctrl->val);
 130                break;
 131        case V4L2_CID_CHROMA_GAIN:
 132                ret = ml86v7667_mask_set(client, ACCRC_REG, ACCRC_CHROMA_MASK,
 133                                         ctrl->val << ACCRC_CHROMA_SHIFT);
 134                break;
 135        case V4L2_CID_HUE:
 136                ret = ml86v7667_mask_set(client, HUE_REG, ~0, ctrl->val);
 137                break;
 138        case V4L2_CID_RED_BALANCE:
 139                ret = ml86v7667_mask_set(client, ACCC_REG,
 140                                         ACCC_CHROMA_CR_MASK,
 141                                         ctrl->val << ACCC_CHROMA_CR_SHIFT);
 142                break;
 143        case V4L2_CID_BLUE_BALANCE:
 144                ret = ml86v7667_mask_set(client, ACCC_REG,
 145                                         ACCC_CHROMA_CB_MASK,
 146                                         ctrl->val << ACCC_CHROMA_CB_SHIFT);
 147                break;
 148        case V4L2_CID_SHARPNESS:
 149                ret = ml86v7667_mask_set(client, LUMC_REG,
 150                                         LUMC_ONOFF_MASK,
 151                                         ctrl->val << LUMC_ONOFF_SHIFT);
 152                break;
 153        case V4L2_CID_COLOR_KILLER:
 154                ret = ml86v7667_mask_set(client, CHRCA_REG,
 155                                         CHRCA_MODE_MASK,
 156                                         ctrl->val << CHRCA_MODE_SHIFT);
 157                break;
 158        }
 159
 160        return ret;
 161}
 162
 163static int ml86v7667_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
 164{
 165        struct i2c_client *client = v4l2_get_subdevdata(sd);
 166        int status;
 167
 168        status = i2c_smbus_read_byte_data(client, STATUS_REG);
 169        if (status < 0)
 170                return status;
 171
 172        if (status & STATUS_HLOCK_DETECT)
 173                *std &= status & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60;
 174        else
 175                *std = V4L2_STD_UNKNOWN;
 176
 177        return 0;
 178}
 179
 180static int ml86v7667_g_input_status(struct v4l2_subdev *sd, u32 *status)
 181{
 182        struct i2c_client *client = v4l2_get_subdevdata(sd);
 183        int status_reg;
 184
 185        status_reg = i2c_smbus_read_byte_data(client, STATUS_REG);
 186        if (status_reg < 0)
 187                return status_reg;
 188
 189        *status = status_reg & STATUS_HLOCK_DETECT ? 0 : V4L2_IN_ST_NO_SIGNAL;
 190
 191        return 0;
 192}
 193
 194static int ml86v7667_enum_mbus_code(struct v4l2_subdev *sd,
 195                struct v4l2_subdev_pad_config *cfg,
 196                struct v4l2_subdev_mbus_code_enum *code)
 197{
 198        if (code->pad || code->index > 0)
 199                return -EINVAL;
 200
 201        code->code = MEDIA_BUS_FMT_YUYV8_2X8;
 202
 203        return 0;
 204}
 205
 206static int ml86v7667_fill_fmt(struct v4l2_subdev *sd,
 207                struct v4l2_subdev_pad_config *cfg,
 208                struct v4l2_subdev_format *format)
 209{
 210        struct ml86v7667_priv *priv = to_ml86v7667(sd);
 211        struct v4l2_mbus_framefmt *fmt = &format->format;
 212
 213        if (format->pad)
 214                return -EINVAL;
 215
 216        fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
 217        fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
 218        /* The top field is always transferred first by the chip */
 219        fmt->field = V4L2_FIELD_INTERLACED_TB;
 220        fmt->width = 720;
 221        fmt->height = priv->std & V4L2_STD_525_60 ? 480 : 576;
 222
 223        return 0;
 224}
 225
 226static int ml86v7667_g_mbus_config(struct v4l2_subdev *sd,
 227                                   struct v4l2_mbus_config *cfg)
 228{
 229        cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
 230                     V4L2_MBUS_DATA_ACTIVE_HIGH;
 231        cfg->type = V4L2_MBUS_BT656;
 232
 233        return 0;
 234}
 235
 236static int ml86v7667_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
 237{
 238        struct ml86v7667_priv *priv = to_ml86v7667(sd);
 239
 240        *std = priv->std;
 241
 242        return 0;
 243}
 244
 245static int ml86v7667_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 246{
 247        struct ml86v7667_priv *priv = to_ml86v7667(sd);
 248        struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
 249        int ret;
 250        u8 mode;
 251
 252        /* PAL/NTSC ITU-R BT.601 input mode */
 253        mode = std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;
 254        ret = ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, mode);
 255        if (ret < 0)
 256                return ret;
 257
 258        priv->std = std;
 259
 260        return 0;
 261}
 262
 263#ifdef CONFIG_VIDEO_ADV_DEBUG
 264static int ml86v7667_g_register(struct v4l2_subdev *sd,
 265                                struct v4l2_dbg_register *reg)
 266{
 267        struct i2c_client *client = v4l2_get_subdevdata(sd);
 268        int ret;
 269
 270        ret = i2c_smbus_read_byte_data(client, (u8)reg->reg);
 271        if (ret < 0)
 272                return ret;
 273
 274        reg->val = ret;
 275        reg->size = sizeof(u8);
 276
 277        return 0;
 278}
 279
 280static int ml86v7667_s_register(struct v4l2_subdev *sd,
 281                                const struct v4l2_dbg_register *reg)
 282{
 283        struct i2c_client *client = v4l2_get_subdevdata(sd);
 284
 285        return i2c_smbus_write_byte_data(client, (u8)reg->reg, (u8)reg->val);
 286}
 287#endif
 288
 289static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {
 290        .s_ctrl = ml86v7667_s_ctrl,
 291};
 292
 293static const struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
 294        .g_std = ml86v7667_g_std,
 295        .s_std = ml86v7667_s_std,
 296        .querystd = ml86v7667_querystd,
 297        .g_input_status = ml86v7667_g_input_status,
 298        .g_mbus_config = ml86v7667_g_mbus_config,
 299};
 300
 301static const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = {
 302        .enum_mbus_code = ml86v7667_enum_mbus_code,
 303        .get_fmt = ml86v7667_fill_fmt,
 304        .set_fmt = ml86v7667_fill_fmt,
 305};
 306
 307static const struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = {
 308#ifdef CONFIG_VIDEO_ADV_DEBUG
 309        .g_register = ml86v7667_g_register,
 310        .s_register = ml86v7667_s_register,
 311#endif
 312};
 313
 314static const struct v4l2_subdev_ops ml86v7667_subdev_ops = {
 315        .core = &ml86v7667_subdev_core_ops,
 316        .video = &ml86v7667_subdev_video_ops,
 317        .pad = &ml86v7667_subdev_pad_ops,
 318};
 319
 320static int ml86v7667_init(struct ml86v7667_priv *priv)
 321{
 322        struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
 323        int val;
 324        int ret;
 325
 326        /* BT.656-4 output mode, register mode */
 327        ret = ml86v7667_mask_set(client, MRA_REG,
 328                                 MRA_OUTPUT_MODE_MASK | MRA_REGISTER_MODE,
 329                                 MRA_ITUR_BT656 | MRA_REGISTER_MODE);
 330
 331        /* PLL circuit fixed clock, 32MHz */
 332        ret |= ml86v7667_mask_set(client, PLLR1_REG, PLLR1_FIXED_CLOCK,
 333                                  PLLR1_FIXED_CLOCK);
 334
 335        /* ADC2 clamping voltage maximum  */
 336        ret |= ml86v7667_mask_set(client, ADC2_REG, ADC2_CLAMP_VOLTAGE_MASK,
 337                                  ADC2_CLAMP_VOLTAGE(7));
 338
 339        /* enable luminance function */
 340        ret |= ml86v7667_mask_set(client, SSEPL_REG, SSEPL_LUMINANCE_ONOFF,
 341                                  SSEPL_LUMINANCE_ONOFF);
 342
 343        /* enable contrast function */
 344        ret |= ml86v7667_mask_set(client, CLC_REG, CLC_CONTRAST_ONOFF, 0);
 345
 346        /*
 347         * PAL/NTSC autodetection is enabled after reset,
 348         * set the autodetected std in manual std mode and
 349         * disable autodetection
 350         */
 351        val = i2c_smbus_read_byte_data(client, STATUS_REG);
 352        if (val < 0)
 353                return val;
 354
 355        priv->std = val & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60;
 356        ret |= ml86v7667_mask_set(client, MRC_REG, MRC_AUTOSELECT, 0);
 357
 358        val = priv->std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;
 359        ret |= ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, val);
 360
 361        return ret;
 362}
 363
 364static int ml86v7667_probe(struct i2c_client *client,
 365                           const struct i2c_device_id *did)
 366{
 367        struct ml86v7667_priv *priv;
 368        int ret;
 369
 370        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 371                return -EIO;
 372
 373        priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
 374        if (!priv)
 375                return -ENOMEM;
 376
 377        v4l2_i2c_subdev_init(&priv->sd, client, &ml86v7667_subdev_ops);
 378
 379        v4l2_ctrl_handler_init(&priv->hdl, 8);
 380        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 381                          V4L2_CID_BRIGHTNESS, -64, 63, 1, 0);
 382        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 383                          V4L2_CID_CONTRAST, -8, 7, 1, 0);
 384        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 385                          V4L2_CID_CHROMA_GAIN, -32, 31, 1, 0);
 386        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 387                          V4L2_CID_HUE, -128, 127, 1, 0);
 388        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 389                          V4L2_CID_RED_BALANCE, -4, 3, 1, 0);
 390        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 391                          V4L2_CID_BLUE_BALANCE, -4, 3, 1, 0);
 392        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 393                          V4L2_CID_SHARPNESS, 0, 1, 1, 0);
 394        v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
 395                          V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
 396        priv->sd.ctrl_handler = &priv->hdl;
 397
 398        ret = priv->hdl.error;
 399        if (ret)
 400                goto cleanup;
 401
 402        v4l2_ctrl_handler_setup(&priv->hdl);
 403
 404        ret = ml86v7667_init(priv);
 405        if (ret)
 406                goto cleanup;
 407
 408        v4l_info(client, "chip found @ 0x%02x (%s)\n",
 409                 client->addr, client->adapter->name);
 410        return 0;
 411
 412cleanup:
 413        v4l2_ctrl_handler_free(&priv->hdl);
 414        v4l2_device_unregister_subdev(&priv->sd);
 415        v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
 416                client->addr, client->adapter->name);
 417        return ret;
 418}
 419
 420static int ml86v7667_remove(struct i2c_client *client)
 421{
 422        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 423        struct ml86v7667_priv *priv = to_ml86v7667(sd);
 424
 425        v4l2_ctrl_handler_free(&priv->hdl);
 426        v4l2_device_unregister_subdev(&priv->sd);
 427
 428        return 0;
 429}
 430
 431static const struct i2c_device_id ml86v7667_id[] = {
 432        {DRV_NAME, 0},
 433        {},
 434};
 435MODULE_DEVICE_TABLE(i2c, ml86v7667_id);
 436
 437static struct i2c_driver ml86v7667_i2c_driver = {
 438        .driver = {
 439                .name   = DRV_NAME,
 440        },
 441        .probe          = ml86v7667_probe,
 442        .remove         = ml86v7667_remove,
 443        .id_table       = ml86v7667_id,
 444};
 445
 446module_i2c_driver(ml86v7667_i2c_driver);
 447
 448MODULE_DESCRIPTION("OKI Semiconductor ML86V7667 video decoder driver");
 449MODULE_AUTHOR("Vladimir Barinov");
 450MODULE_LICENSE("GPL");
 451