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