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