18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OKI Semiconductor ML86V7667 video decoder driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Vladimir Barinov <source@cogentembedded.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2013 Cogent Embedded, Inc. 78c2ecf20Sopenharmony_ci * Copyright (C) 2013 Renesas Solutions Corp. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DRV_NAME "ml86v7667" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Subaddresses */ 238c2ecf20Sopenharmony_ci#define MRA_REG 0x00 /* Mode Register A */ 248c2ecf20Sopenharmony_ci#define MRC_REG 0x02 /* Mode Register C */ 258c2ecf20Sopenharmony_ci#define LUMC_REG 0x0C /* Luminance Control */ 268c2ecf20Sopenharmony_ci#define CLC_REG 0x10 /* Contrast level control */ 278c2ecf20Sopenharmony_ci#define SSEPL_REG 0x11 /* Sync separation level */ 288c2ecf20Sopenharmony_ci#define CHRCA_REG 0x12 /* Chrominance Control A */ 298c2ecf20Sopenharmony_ci#define ACCC_REG 0x14 /* ACC Loop filter & Chrominance control */ 308c2ecf20Sopenharmony_ci#define ACCRC_REG 0x15 /* ACC Reference level control */ 318c2ecf20Sopenharmony_ci#define HUE_REG 0x16 /* Hue control */ 328c2ecf20Sopenharmony_ci#define ADC2_REG 0x1F /* ADC Register 2 */ 338c2ecf20Sopenharmony_ci#define PLLR1_REG 0x20 /* PLL Register 1 */ 348c2ecf20Sopenharmony_ci#define STATUS_REG 0x2C /* STATUS Register */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Mode Register A register bits */ 378c2ecf20Sopenharmony_ci#define MRA_OUTPUT_MODE_MASK (3 << 6) 388c2ecf20Sopenharmony_ci#define MRA_ITUR_BT601 (1 << 6) 398c2ecf20Sopenharmony_ci#define MRA_ITUR_BT656 (0 << 6) 408c2ecf20Sopenharmony_ci#define MRA_INPUT_MODE_MASK (7 << 3) 418c2ecf20Sopenharmony_ci#define MRA_PAL_BT601 (4 << 3) 428c2ecf20Sopenharmony_ci#define MRA_NTSC_BT601 (0 << 3) 438c2ecf20Sopenharmony_ci#define MRA_REGISTER_MODE (1 << 0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Mode Register C register bits */ 468c2ecf20Sopenharmony_ci#define MRC_AUTOSELECT (1 << 7) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Luminance Control register bits */ 498c2ecf20Sopenharmony_ci#define LUMC_ONOFF_SHIFT 7 508c2ecf20Sopenharmony_ci#define LUMC_ONOFF_MASK (1 << 7) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Contrast level control register bits */ 538c2ecf20Sopenharmony_ci#define CLC_CONTRAST_ONOFF (1 << 7) 548c2ecf20Sopenharmony_ci#define CLC_CONTRAST_MASK 0x0F 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Sync separation level register bits */ 578c2ecf20Sopenharmony_ci#define SSEPL_LUMINANCE_ONOFF (1 << 7) 588c2ecf20Sopenharmony_ci#define SSEPL_LUMINANCE_MASK 0x7F 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Chrominance Control A register bits */ 618c2ecf20Sopenharmony_ci#define CHRCA_MODE_SHIFT 6 628c2ecf20Sopenharmony_ci#define CHRCA_MODE_MASK (1 << 6) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* ACC Loop filter & Chrominance control register bits */ 658c2ecf20Sopenharmony_ci#define ACCC_CHROMA_CR_SHIFT 3 668c2ecf20Sopenharmony_ci#define ACCC_CHROMA_CR_MASK (7 << 3) 678c2ecf20Sopenharmony_ci#define ACCC_CHROMA_CB_SHIFT 0 688c2ecf20Sopenharmony_ci#define ACCC_CHROMA_CB_MASK (7 << 0) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* ACC Reference level control register bits */ 718c2ecf20Sopenharmony_ci#define ACCRC_CHROMA_MASK 0xfc 728c2ecf20Sopenharmony_ci#define ACCRC_CHROMA_SHIFT 2 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* ADC Register 2 register bits */ 758c2ecf20Sopenharmony_ci#define ADC2_CLAMP_VOLTAGE_MASK (7 << 1) 768c2ecf20Sopenharmony_ci#define ADC2_CLAMP_VOLTAGE(n) ((n & 7) << 1) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* PLL Register 1 register bits */ 798c2ecf20Sopenharmony_ci#define PLLR1_FIXED_CLOCK (1 << 7) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* STATUS Register register bits */ 828c2ecf20Sopenharmony_ci#define STATUS_HLOCK_DETECT (1 << 3) 838c2ecf20Sopenharmony_ci#define STATUS_NTSCPAL (1 << 2) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct ml86v7667_priv { 868c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 878c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 888c2ecf20Sopenharmony_ci v4l2_std_id std; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline struct ml86v7667_priv *to_ml86v7667(struct v4l2_subdev *subdev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci return container_of(subdev, struct ml86v7667_priv, sd); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct ml86v7667_priv, hdl)->sd; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int ml86v7667_mask_set(struct i2c_client *client, const u8 reg, 1028c2ecf20Sopenharmony_ci const u8 mask, const u8 data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int val = i2c_smbus_read_byte_data(client, reg); 1058c2ecf20Sopenharmony_ci if (val < 0) 1068c2ecf20Sopenharmony_ci return val; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci val = (val & ~mask) | (data & mask); 1098c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, reg, val); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int ml86v7667_s_ctrl(struct v4l2_ctrl *ctrl) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 1158c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1168c2ecf20Sopenharmony_ci int ret = -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci switch (ctrl->id) { 1198c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 1208c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, SSEPL_REG, 1218c2ecf20Sopenharmony_ci SSEPL_LUMINANCE_MASK, ctrl->val); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 1248c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, CLC_REG, 1258c2ecf20Sopenharmony_ci CLC_CONTRAST_MASK, ctrl->val); 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case V4L2_CID_CHROMA_GAIN: 1288c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, ACCRC_REG, ACCRC_CHROMA_MASK, 1298c2ecf20Sopenharmony_ci ctrl->val << ACCRC_CHROMA_SHIFT); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case V4L2_CID_HUE: 1328c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, HUE_REG, ~0, ctrl->val); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case V4L2_CID_RED_BALANCE: 1358c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, ACCC_REG, 1368c2ecf20Sopenharmony_ci ACCC_CHROMA_CR_MASK, 1378c2ecf20Sopenharmony_ci ctrl->val << ACCC_CHROMA_CR_SHIFT); 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 1408c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, ACCC_REG, 1418c2ecf20Sopenharmony_ci ACCC_CHROMA_CB_MASK, 1428c2ecf20Sopenharmony_ci ctrl->val << ACCC_CHROMA_CB_SHIFT); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 1458c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, LUMC_REG, 1468c2ecf20Sopenharmony_ci LUMC_ONOFF_MASK, 1478c2ecf20Sopenharmony_ci ctrl->val << LUMC_ONOFF_SHIFT); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case V4L2_CID_COLOR_KILLER: 1508c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, CHRCA_REG, 1518c2ecf20Sopenharmony_ci CHRCA_MODE_MASK, 1528c2ecf20Sopenharmony_ci ctrl->val << CHRCA_MODE_SHIFT); 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int ml86v7667_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1628c2ecf20Sopenharmony_ci int status; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, STATUS_REG); 1658c2ecf20Sopenharmony_ci if (status < 0) 1668c2ecf20Sopenharmony_ci return status; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (status & STATUS_HLOCK_DETECT) 1698c2ecf20Sopenharmony_ci *std &= status & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60; 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci *std = V4L2_STD_UNKNOWN; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int ml86v7667_g_input_status(struct v4l2_subdev *sd, u32 *status) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1798c2ecf20Sopenharmony_ci int status_reg; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci status_reg = i2c_smbus_read_byte_data(client, STATUS_REG); 1828c2ecf20Sopenharmony_ci if (status_reg < 0) 1838c2ecf20Sopenharmony_ci return status_reg; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci *status = status_reg & STATUS_HLOCK_DETECT ? 0 : V4L2_IN_ST_NO_SIGNAL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int ml86v7667_enum_mbus_code(struct v4l2_subdev *sd, 1918c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1928c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (code->pad || code->index > 0) 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_YUYV8_2X8; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int ml86v7667_fill_fmt(struct v4l2_subdev *sd, 2038c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2048c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ml86v7667_priv *priv = to_ml86v7667(sd); 2078c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt = &format->format; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (format->pad) 2108c2ecf20Sopenharmony_ci return -EINVAL; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; 2138c2ecf20Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; 2148c2ecf20Sopenharmony_ci /* The top field is always transferred first by the chip */ 2158c2ecf20Sopenharmony_ci fmt->field = V4L2_FIELD_INTERLACED_TB; 2168c2ecf20Sopenharmony_ci fmt->width = 720; 2178c2ecf20Sopenharmony_ci fmt->height = priv->std & V4L2_STD_525_60 ? 480 : 576; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int ml86v7667_get_mbus_config(struct v4l2_subdev *sd, 2238c2ecf20Sopenharmony_ci unsigned int pad, 2248c2ecf20Sopenharmony_ci struct v4l2_mbus_config *cfg) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | 2278c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_HIGH; 2288c2ecf20Sopenharmony_ci cfg->type = V4L2_MBUS_BT656; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int ml86v7667_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct ml86v7667_priv *priv = to_ml86v7667(sd); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci *std = priv->std; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int ml86v7667_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct ml86v7667_priv *priv = to_ml86v7667(sd); 2458c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci u8 mode; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* PAL/NTSC ITU-R BT.601 input mode */ 2508c2ecf20Sopenharmony_ci mode = std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601; 2518c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, mode); 2528c2ecf20Sopenharmony_ci if (ret < 0) 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci priv->std = std; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 2618c2ecf20Sopenharmony_cistatic int ml86v7667_g_register(struct v4l2_subdev *sd, 2628c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, (u8)reg->reg); 2688c2ecf20Sopenharmony_ci if (ret < 0) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci reg->val = ret; 2728c2ecf20Sopenharmony_ci reg->size = sizeof(u8); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int ml86v7667_s_register(struct v4l2_subdev *sd, 2788c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, (u8)reg->reg, (u8)reg->val); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci#endif 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = { 2878c2ecf20Sopenharmony_ci .s_ctrl = ml86v7667_s_ctrl, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { 2918c2ecf20Sopenharmony_ci .g_std = ml86v7667_g_std, 2928c2ecf20Sopenharmony_ci .s_std = ml86v7667_s_std, 2938c2ecf20Sopenharmony_ci .querystd = ml86v7667_querystd, 2948c2ecf20Sopenharmony_ci .g_input_status = ml86v7667_g_input_status, 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = { 2988c2ecf20Sopenharmony_ci .enum_mbus_code = ml86v7667_enum_mbus_code, 2998c2ecf20Sopenharmony_ci .get_fmt = ml86v7667_fill_fmt, 3008c2ecf20Sopenharmony_ci .set_fmt = ml86v7667_fill_fmt, 3018c2ecf20Sopenharmony_ci .get_mbus_config = ml86v7667_get_mbus_config, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { 3058c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 3068c2ecf20Sopenharmony_ci .g_register = ml86v7667_g_register, 3078c2ecf20Sopenharmony_ci .s_register = ml86v7667_s_register, 3088c2ecf20Sopenharmony_ci#endif 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ml86v7667_subdev_ops = { 3128c2ecf20Sopenharmony_ci .core = &ml86v7667_subdev_core_ops, 3138c2ecf20Sopenharmony_ci .video = &ml86v7667_subdev_video_ops, 3148c2ecf20Sopenharmony_ci .pad = &ml86v7667_subdev_pad_ops, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int ml86v7667_init(struct ml86v7667_priv *priv) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); 3208c2ecf20Sopenharmony_ci int val; 3218c2ecf20Sopenharmony_ci int ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* BT.656-4 output mode, register mode */ 3248c2ecf20Sopenharmony_ci ret = ml86v7667_mask_set(client, MRA_REG, 3258c2ecf20Sopenharmony_ci MRA_OUTPUT_MODE_MASK | MRA_REGISTER_MODE, 3268c2ecf20Sopenharmony_ci MRA_ITUR_BT656 | MRA_REGISTER_MODE); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* PLL circuit fixed clock, 32MHz */ 3298c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, PLLR1_REG, PLLR1_FIXED_CLOCK, 3308c2ecf20Sopenharmony_ci PLLR1_FIXED_CLOCK); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* ADC2 clamping voltage maximum */ 3338c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, ADC2_REG, ADC2_CLAMP_VOLTAGE_MASK, 3348c2ecf20Sopenharmony_ci ADC2_CLAMP_VOLTAGE(7)); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* enable luminance function */ 3378c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, SSEPL_REG, SSEPL_LUMINANCE_ONOFF, 3388c2ecf20Sopenharmony_ci SSEPL_LUMINANCE_ONOFF); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* enable contrast function */ 3418c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, CLC_REG, CLC_CONTRAST_ONOFF, 0); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* 3448c2ecf20Sopenharmony_ci * PAL/NTSC autodetection is enabled after reset, 3458c2ecf20Sopenharmony_ci * set the autodetected std in manual std mode and 3468c2ecf20Sopenharmony_ci * disable autodetection 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, STATUS_REG); 3498c2ecf20Sopenharmony_ci if (val < 0) 3508c2ecf20Sopenharmony_ci return val; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci priv->std = val & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60; 3538c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, MRC_REG, MRC_AUTOSELECT, 0); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci val = priv->std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601; 3568c2ecf20Sopenharmony_ci ret |= ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, val); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int ml86v7667_probe(struct i2c_client *client, 3628c2ecf20Sopenharmony_ci const struct i2c_device_id *did) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct ml86v7667_priv *priv; 3658c2ecf20Sopenharmony_ci int ret; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 3688c2ecf20Sopenharmony_ci return -EIO; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 3718c2ecf20Sopenharmony_ci if (!priv) 3728c2ecf20Sopenharmony_ci return -ENOMEM; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&priv->sd, client, &ml86v7667_subdev_ops); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&priv->hdl, 8); 3778c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3788c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, -64, 63, 1, 0); 3798c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3808c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, -8, 7, 1, 0); 3818c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3828c2ecf20Sopenharmony_ci V4L2_CID_CHROMA_GAIN, -32, 31, 1, 0); 3838c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3848c2ecf20Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 3858c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3868c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, -4, 3, 1, 0); 3878c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3888c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, -4, 3, 1, 0); 3898c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3908c2ecf20Sopenharmony_ci V4L2_CID_SHARPNESS, 0, 1, 1, 0); 3918c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops, 3928c2ecf20Sopenharmony_ci V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); 3938c2ecf20Sopenharmony_ci priv->sd.ctrl_handler = &priv->hdl; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ret = priv->hdl.error; 3968c2ecf20Sopenharmony_ci if (ret) 3978c2ecf20Sopenharmony_ci goto cleanup; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&priv->hdl); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ret = ml86v7667_init(priv); 4028c2ecf20Sopenharmony_ci if (ret) 4038c2ecf20Sopenharmony_ci goto cleanup; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci v4l_info(client, "chip found @ 0x%02x (%s)\n", 4068c2ecf20Sopenharmony_ci client->addr, client->adapter->name); 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cicleanup: 4108c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 4118c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(&priv->sd); 4128c2ecf20Sopenharmony_ci v4l_err(client, "failed to probe @ 0x%02x (%s)\n", 4138c2ecf20Sopenharmony_ci client->addr, client->adapter->name); 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int ml86v7667_remove(struct i2c_client *client) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 4208c2ecf20Sopenharmony_ci struct ml86v7667_priv *priv = to_ml86v7667(sd); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 4238c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(&priv->sd); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic const struct i2c_device_id ml86v7667_id[] = { 4298c2ecf20Sopenharmony_ci {DRV_NAME, 0}, 4308c2ecf20Sopenharmony_ci {}, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ml86v7667_id); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic struct i2c_driver ml86v7667_i2c_driver = { 4358c2ecf20Sopenharmony_ci .driver = { 4368c2ecf20Sopenharmony_ci .name = DRV_NAME, 4378c2ecf20Sopenharmony_ci }, 4388c2ecf20Sopenharmony_ci .probe = ml86v7667_probe, 4398c2ecf20Sopenharmony_ci .remove = ml86v7667_remove, 4408c2ecf20Sopenharmony_ci .id_table = ml86v7667_id, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cimodule_i2c_driver(ml86v7667_i2c_driver); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OKI Semiconductor ML86V7667 video decoder driver"); 4468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vladimir Barinov"); 4478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 448