446 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OKI Semiconductor ML86V7667 video decoder driver4 *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 750#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 0x0F55 56/* Sync separation level register bits */57#define SSEPL_LUMINANCE_ONOFF (1 << 7)58#define SSEPL_LUMINANCE_MASK 0x7F59 60/* Chrominance Control A register bits */61#define CHRCA_MODE_SHIFT 662#define CHRCA_MODE_MASK (1 << 6)63 64/* ACC Loop filter & Chrominance control register bits */65#define ACCC_CHROMA_CR_SHIFT 366#define ACCC_CHROMA_CR_MASK (7 << 3)67#define ACCC_CHROMA_CB_SHIFT 068#define ACCC_CHROMA_CB_MASK (7 << 0)69 70/* ACC Reference level control register bits */71#define ACCRC_CHROMA_MASK 0xfc72#define ACCRC_CHROMA_SHIFT 273 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 else171 *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->type = V4L2_MBUS_BT656;227 cfg->bus.parallel.flags = V4L2_MBUS_MASTER |228 V4L2_MBUS_PCLK_SAMPLE_RISING |229 V4L2_MBUS_DATA_ACTIVE_HIGH;230 231 return 0;232}233 234static int ml86v7667_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)235{236 struct ml86v7667_priv *priv = to_ml86v7667(sd);237 238 *std = priv->std;239 240 return 0;241}242 243static int ml86v7667_s_std(struct v4l2_subdev *sd, v4l2_std_id std)244{245 struct ml86v7667_priv *priv = to_ml86v7667(sd);246 struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);247 int ret;248 u8 mode;249 250 /* PAL/NTSC ITU-R BT.601 input mode */251 mode = std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;252 ret = ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, mode);253 if (ret < 0)254 return ret;255 256 priv->std = std;257 258 return 0;259}260 261#ifdef CONFIG_VIDEO_ADV_DEBUG262static int ml86v7667_g_register(struct v4l2_subdev *sd,263 struct v4l2_dbg_register *reg)264{265 struct i2c_client *client = v4l2_get_subdevdata(sd);266 int ret;267 268 ret = i2c_smbus_read_byte_data(client, (u8)reg->reg);269 if (ret < 0)270 return ret;271 272 reg->val = ret;273 reg->size = sizeof(u8);274 275 return 0;276}277 278static int ml86v7667_s_register(struct v4l2_subdev *sd,279 const struct v4l2_dbg_register *reg)280{281 struct i2c_client *client = v4l2_get_subdevdata(sd);282 283 return i2c_smbus_write_byte_data(client, (u8)reg->reg, (u8)reg->val);284}285#endif286 287static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {288 .s_ctrl = ml86v7667_s_ctrl,289};290 291static const struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {292 .g_std = ml86v7667_g_std,293 .s_std = ml86v7667_s_std,294 .querystd = ml86v7667_querystd,295 .g_input_status = ml86v7667_g_input_status,296};297 298static const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = {299 .enum_mbus_code = ml86v7667_enum_mbus_code,300 .get_fmt = ml86v7667_fill_fmt,301 .set_fmt = ml86v7667_fill_fmt,302 .get_mbus_config = ml86v7667_get_mbus_config,303};304 305static const struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = {306#ifdef CONFIG_VIDEO_ADV_DEBUG307 .g_register = ml86v7667_g_register,308 .s_register = ml86v7667_s_register,309#endif310};311 312static const struct v4l2_subdev_ops ml86v7667_subdev_ops = {313 .core = &ml86v7667_subdev_core_ops,314 .video = &ml86v7667_subdev_video_ops,315 .pad = &ml86v7667_subdev_pad_ops,316};317 318static int ml86v7667_init(struct ml86v7667_priv *priv)319{320 struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);321 int val;322 int ret;323 324 /* BT.656-4 output mode, register mode */325 ret = ml86v7667_mask_set(client, MRA_REG,326 MRA_OUTPUT_MODE_MASK | MRA_REGISTER_MODE,327 MRA_ITUR_BT656 | MRA_REGISTER_MODE);328 329 /* PLL circuit fixed clock, 32MHz */330 ret |= ml86v7667_mask_set(client, PLLR1_REG, PLLR1_FIXED_CLOCK,331 PLLR1_FIXED_CLOCK);332 333 /* ADC2 clamping voltage maximum */334 ret |= ml86v7667_mask_set(client, ADC2_REG, ADC2_CLAMP_VOLTAGE_MASK,335 ADC2_CLAMP_VOLTAGE(7));336 337 /* enable luminance function */338 ret |= ml86v7667_mask_set(client, SSEPL_REG, SSEPL_LUMINANCE_ONOFF,339 SSEPL_LUMINANCE_ONOFF);340 341 /* enable contrast function */342 ret |= ml86v7667_mask_set(client, CLC_REG, CLC_CONTRAST_ONOFF, 0);343 344 /*345 * PAL/NTSC autodetection is enabled after reset,346 * set the autodetected std in manual std mode and347 * disable autodetection348 */349 val = i2c_smbus_read_byte_data(client, STATUS_REG);350 if (val < 0)351 return val;352 353 priv->std = val & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60;354 ret |= ml86v7667_mask_set(client, MRC_REG, MRC_AUTOSELECT, 0);355 356 val = priv->std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;357 ret |= ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, val);358 359 return ret;360}361 362static int ml86v7667_probe(struct i2c_client *client)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 void 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 426static const struct i2c_device_id ml86v7667_id[] = {427 { DRV_NAME },428 {}429};430MODULE_DEVICE_TABLE(i2c, ml86v7667_id);431 432static struct i2c_driver ml86v7667_i2c_driver = {433 .driver = {434 .name = DRV_NAME,435 },436 .probe = ml86v7667_probe,437 .remove = ml86v7667_remove,438 .id_table = ml86v7667_id,439};440 441module_i2c_driver(ml86v7667_i2c_driver);442 443MODULE_DESCRIPTION("OKI Semiconductor ML86V7667 video decoder driver");444MODULE_AUTHOR("Vladimir Barinov");445MODULE_LICENSE("GPL");446