xref: /openbmc/linux/drivers/media/i2c/vpx3220.c (revision aaeb31c0)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
6cb7a01acSMauro Carvalho Chehab  */
7cb7a01acSMauro Carvalho Chehab 
8cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
9cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
10cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
137c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
16cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
17cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
18cb7a01acSMauro Carvalho Chehab 
19cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
20cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Laurent Pinchart");
21cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
22cb7a01acSMauro Carvalho Chehab 
23cb7a01acSMauro Carvalho Chehab static int debug;
24cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0);
25cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
26cb7a01acSMauro Carvalho Chehab 
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab #define VPX_TIMEOUT_COUNT  10
29cb7a01acSMauro Carvalho Chehab 
30cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
31cb7a01acSMauro Carvalho Chehab 
32cb7a01acSMauro Carvalho Chehab struct vpx3220 {
33cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
34cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
35cb7a01acSMauro Carvalho Chehab 	unsigned char reg[255];
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab 	v4l2_std_id norm;
38cb7a01acSMauro Carvalho Chehab 	int input;
39cb7a01acSMauro Carvalho Chehab 	int enable;
40cb7a01acSMauro Carvalho Chehab };
41cb7a01acSMauro Carvalho Chehab 
to_vpx3220(struct v4l2_subdev * sd)42cb7a01acSMauro Carvalho Chehab static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
43cb7a01acSMauro Carvalho Chehab {
44cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct vpx3220, sd);
45cb7a01acSMauro Carvalho Chehab }
46cb7a01acSMauro Carvalho Chehab 
to_sd(struct v4l2_ctrl * ctrl)47cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
48cb7a01acSMauro Carvalho Chehab {
49cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
50cb7a01acSMauro Carvalho Chehab }
51cb7a01acSMauro Carvalho Chehab 
52cb7a01acSMauro Carvalho Chehab static char *inputs[] = { "internal", "composite", "svideo" };
53cb7a01acSMauro Carvalho Chehab 
54cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
55cb7a01acSMauro Carvalho Chehab 
vpx3220_write(struct v4l2_subdev * sd,u8 reg,u8 value)56cb7a01acSMauro Carvalho Chehab static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
57cb7a01acSMauro Carvalho Chehab {
58cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
59cb7a01acSMauro Carvalho Chehab 	struct vpx3220 *decoder = i2c_get_clientdata(client);
60cb7a01acSMauro Carvalho Chehab 
61cb7a01acSMauro Carvalho Chehab 	decoder->reg[reg] = value;
62cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
63cb7a01acSMauro Carvalho Chehab }
64cb7a01acSMauro Carvalho Chehab 
vpx3220_read(struct v4l2_subdev * sd,u8 reg)65cb7a01acSMauro Carvalho Chehab static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
66cb7a01acSMauro Carvalho Chehab {
67cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
68cb7a01acSMauro Carvalho Chehab 
69cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
70cb7a01acSMauro Carvalho Chehab }
71cb7a01acSMauro Carvalho Chehab 
vpx3220_fp_status(struct v4l2_subdev * sd)72cb7a01acSMauro Carvalho Chehab static int vpx3220_fp_status(struct v4l2_subdev *sd)
73cb7a01acSMauro Carvalho Chehab {
74cb7a01acSMauro Carvalho Chehab 	unsigned char status;
75cb7a01acSMauro Carvalho Chehab 	unsigned int i;
76cb7a01acSMauro Carvalho Chehab 
77cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
78cb7a01acSMauro Carvalho Chehab 		status = vpx3220_read(sd, 0x29);
79cb7a01acSMauro Carvalho Chehab 
80cb7a01acSMauro Carvalho Chehab 		if (!(status & 4))
81cb7a01acSMauro Carvalho Chehab 			return 0;
82cb7a01acSMauro Carvalho Chehab 
83cb7a01acSMauro Carvalho Chehab 		udelay(10);
84cb7a01acSMauro Carvalho Chehab 
85cb7a01acSMauro Carvalho Chehab 		if (need_resched())
86cb7a01acSMauro Carvalho Chehab 			cond_resched();
87cb7a01acSMauro Carvalho Chehab 	}
88cb7a01acSMauro Carvalho Chehab 
89cb7a01acSMauro Carvalho Chehab 	return -1;
90cb7a01acSMauro Carvalho Chehab }
91cb7a01acSMauro Carvalho Chehab 
vpx3220_fp_write(struct v4l2_subdev * sd,u8 fpaddr,u16 data)92cb7a01acSMauro Carvalho Chehab static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
93cb7a01acSMauro Carvalho Chehab {
94cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
95cb7a01acSMauro Carvalho Chehab 
96cb7a01acSMauro Carvalho Chehab 	/* Write the 16-bit address to the FPWR register */
97cb7a01acSMauro Carvalho Chehab 	if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
98cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
99cb7a01acSMauro Carvalho Chehab 		return -1;
100cb7a01acSMauro Carvalho Chehab 	}
101cb7a01acSMauro Carvalho Chehab 
102cb7a01acSMauro Carvalho Chehab 	if (vpx3220_fp_status(sd) < 0)
103cb7a01acSMauro Carvalho Chehab 		return -1;
104cb7a01acSMauro Carvalho Chehab 
105cb7a01acSMauro Carvalho Chehab 	/* Write the 16-bit data to the FPDAT register */
106cb7a01acSMauro Carvalho Chehab 	if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
107cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
108cb7a01acSMauro Carvalho Chehab 		return -1;
109cb7a01acSMauro Carvalho Chehab 	}
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab 	return 0;
112cb7a01acSMauro Carvalho Chehab }
113cb7a01acSMauro Carvalho Chehab 
vpx3220_fp_read(struct v4l2_subdev * sd,u16 fpaddr)11439de7d95SDan Carpenter static int vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
115cb7a01acSMauro Carvalho Chehab {
116cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
117cb7a01acSMauro Carvalho Chehab 	s16 data;
118cb7a01acSMauro Carvalho Chehab 
119cb7a01acSMauro Carvalho Chehab 	/* Write the 16-bit address to the FPRD register */
120cb7a01acSMauro Carvalho Chehab 	if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
121cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
122cb7a01acSMauro Carvalho Chehab 		return -1;
123cb7a01acSMauro Carvalho Chehab 	}
124cb7a01acSMauro Carvalho Chehab 
125cb7a01acSMauro Carvalho Chehab 	if (vpx3220_fp_status(sd) < 0)
126cb7a01acSMauro Carvalho Chehab 		return -1;
127cb7a01acSMauro Carvalho Chehab 
128cb7a01acSMauro Carvalho Chehab 	/* Read the 16-bit data from the FPDAT register */
129cb7a01acSMauro Carvalho Chehab 	data = i2c_smbus_read_word_data(client, 0x28);
130cb7a01acSMauro Carvalho Chehab 	if (data == -1) {
131cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
132cb7a01acSMauro Carvalho Chehab 		return -1;
133cb7a01acSMauro Carvalho Chehab 	}
134cb7a01acSMauro Carvalho Chehab 
135cb7a01acSMauro Carvalho Chehab 	return swab16(data);
136cb7a01acSMauro Carvalho Chehab }
137cb7a01acSMauro Carvalho Chehab 
vpx3220_write_block(struct v4l2_subdev * sd,const u8 * data,unsigned int len)138cb7a01acSMauro Carvalho Chehab static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
139cb7a01acSMauro Carvalho Chehab {
140cb7a01acSMauro Carvalho Chehab 	u8 reg;
141cb7a01acSMauro Carvalho Chehab 	int ret = -1;
142cb7a01acSMauro Carvalho Chehab 
143cb7a01acSMauro Carvalho Chehab 	while (len >= 2) {
144cb7a01acSMauro Carvalho Chehab 		reg = *data++;
145cb7a01acSMauro Carvalho Chehab 		ret = vpx3220_write(sd, reg, *data++);
146cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
147cb7a01acSMauro Carvalho Chehab 			break;
148cb7a01acSMauro Carvalho Chehab 		len -= 2;
149cb7a01acSMauro Carvalho Chehab 	}
150cb7a01acSMauro Carvalho Chehab 
151cb7a01acSMauro Carvalho Chehab 	return ret;
152cb7a01acSMauro Carvalho Chehab }
153cb7a01acSMauro Carvalho Chehab 
vpx3220_write_fp_block(struct v4l2_subdev * sd,const u16 * data,unsigned int len)154cb7a01acSMauro Carvalho Chehab static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
155cb7a01acSMauro Carvalho Chehab 		const u16 *data, unsigned int len)
156cb7a01acSMauro Carvalho Chehab {
157cb7a01acSMauro Carvalho Chehab 	u8 reg;
158cb7a01acSMauro Carvalho Chehab 	int ret = 0;
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab 	while (len > 1) {
161cb7a01acSMauro Carvalho Chehab 		reg = *data++;
162cb7a01acSMauro Carvalho Chehab 		ret |= vpx3220_fp_write(sd, reg, *data++);
163cb7a01acSMauro Carvalho Chehab 		len -= 2;
164cb7a01acSMauro Carvalho Chehab 	}
165cb7a01acSMauro Carvalho Chehab 
166cb7a01acSMauro Carvalho Chehab 	return ret;
167cb7a01acSMauro Carvalho Chehab }
168cb7a01acSMauro Carvalho Chehab 
169cb7a01acSMauro Carvalho Chehab /* ---------------------------------------------------------------------- */
170cb7a01acSMauro Carvalho Chehab 
171cb7a01acSMauro Carvalho Chehab static const unsigned short init_ntsc[] = {
172cb7a01acSMauro Carvalho Chehab 	0x1c, 0x00,		/* NTSC tint angle */
173cb7a01acSMauro Carvalho Chehab 	0x88, 17,		/* Window 1 vertical */
174cb7a01acSMauro Carvalho Chehab 	0x89, 240,		/* Vertical lines in */
175cb7a01acSMauro Carvalho Chehab 	0x8a, 240,		/* Vertical lines out */
176cb7a01acSMauro Carvalho Chehab 	0x8b, 000,		/* Horizontal begin */
177cb7a01acSMauro Carvalho Chehab 	0x8c, 640,		/* Horizontal length */
178cb7a01acSMauro Carvalho Chehab 	0x8d, 640,		/* Number of pixels */
179cb7a01acSMauro Carvalho Chehab 	0x8f, 0xc00,		/* Disable window 2 */
180cb7a01acSMauro Carvalho Chehab 	0xf0, 0x73,		/* 13.5 MHz transport, Forced
181cb7a01acSMauro Carvalho Chehab 				 * mode, latch windows */
182cb7a01acSMauro Carvalho Chehab 	0xf2, 0x13,		/* NTSC M, composite input */
183cb7a01acSMauro Carvalho Chehab 	0xe7, 0x1e1,		/* Enable vertical standard
184cb7a01acSMauro Carvalho Chehab 				 * locking @ 240 lines */
185cb7a01acSMauro Carvalho Chehab };
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab static const unsigned short init_pal[] = {
188cb7a01acSMauro Carvalho Chehab 	0x88, 23,		/* Window 1 vertical begin */
189cb7a01acSMauro Carvalho Chehab 	0x89, 288,		/* Vertical lines in (16 lines
190cb7a01acSMauro Carvalho Chehab 				 * skipped by the VFE) */
191cb7a01acSMauro Carvalho Chehab 	0x8a, 288,		/* Vertical lines out (16 lines
192cb7a01acSMauro Carvalho Chehab 				 * skipped by the VFE) */
193cb7a01acSMauro Carvalho Chehab 	0x8b, 16,		/* Horizontal begin */
194cb7a01acSMauro Carvalho Chehab 	0x8c, 768,		/* Horizontal length */
195cb7a01acSMauro Carvalho Chehab 	0x8d, 784,		/* Number of pixels
196cb7a01acSMauro Carvalho Chehab 				 * Must be >= Horizontal begin + Horizontal length */
197cb7a01acSMauro Carvalho Chehab 	0x8f, 0xc00,		/* Disable window 2 */
198cb7a01acSMauro Carvalho Chehab 	0xf0, 0x77,		/* 13.5 MHz transport, Forced
199cb7a01acSMauro Carvalho Chehab 				 * mode, latch windows */
200cb7a01acSMauro Carvalho Chehab 	0xf2, 0x3d1,		/* PAL B,G,H,I, composite input */
201cb7a01acSMauro Carvalho Chehab 	0xe7, 0x241,		/* PAL/SECAM set to 288 lines */
202cb7a01acSMauro Carvalho Chehab };
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab static const unsigned short init_secam[] = {
205cb7a01acSMauro Carvalho Chehab 	0x88, 23,		/* Window 1 vertical begin */
206cb7a01acSMauro Carvalho Chehab 	0x89, 288,		/* Vertical lines in (16 lines
207cb7a01acSMauro Carvalho Chehab 				 * skipped by the VFE) */
208cb7a01acSMauro Carvalho Chehab 	0x8a, 288,		/* Vertical lines out (16 lines
209cb7a01acSMauro Carvalho Chehab 				 * skipped by the VFE) */
210cb7a01acSMauro Carvalho Chehab 	0x8b, 16,		/* Horizontal begin */
211cb7a01acSMauro Carvalho Chehab 	0x8c, 768,		/* Horizontal length */
212cb7a01acSMauro Carvalho Chehab 	0x8d, 784,		/* Number of pixels
213cb7a01acSMauro Carvalho Chehab 				 * Must be >= Horizontal begin + Horizontal length */
214cb7a01acSMauro Carvalho Chehab 	0x8f, 0xc00,		/* Disable window 2 */
215cb7a01acSMauro Carvalho Chehab 	0xf0, 0x77,		/* 13.5 MHz transport, Forced
216cb7a01acSMauro Carvalho Chehab 				 * mode, latch windows */
217cb7a01acSMauro Carvalho Chehab 	0xf2, 0x3d5,		/* SECAM, composite input */
218cb7a01acSMauro Carvalho Chehab 	0xe7, 0x241,		/* PAL/SECAM set to 288 lines */
219cb7a01acSMauro Carvalho Chehab };
220cb7a01acSMauro Carvalho Chehab 
221cb7a01acSMauro Carvalho Chehab static const unsigned char init_common[] = {
222cb7a01acSMauro Carvalho Chehab 	0xf2, 0x00,		/* Disable all outputs */
223cb7a01acSMauro Carvalho Chehab 	0x33, 0x0d,		/* Luma : VIN2, Chroma : CIN
224cb7a01acSMauro Carvalho Chehab 				 * (clamp off) */
225cb7a01acSMauro Carvalho Chehab 	0xd8, 0xa8,		/* HREF/VREF active high, VREF
226cb7a01acSMauro Carvalho Chehab 				 * pulse = 2, Odd/Even flag */
227cb7a01acSMauro Carvalho Chehab 	0x20, 0x03,		/* IF compensation 0dB/oct */
228cb7a01acSMauro Carvalho Chehab 	0xe0, 0xff,		/* Open up all comparators */
229cb7a01acSMauro Carvalho Chehab 	0xe1, 0x00,
230cb7a01acSMauro Carvalho Chehab 	0xe2, 0x7f,
231cb7a01acSMauro Carvalho Chehab 	0xe3, 0x80,
232cb7a01acSMauro Carvalho Chehab 	0xe4, 0x7f,
233cb7a01acSMauro Carvalho Chehab 	0xe5, 0x80,
234cb7a01acSMauro Carvalho Chehab 	0xe6, 0x00,		/* Brightness set to 0 */
235cb7a01acSMauro Carvalho Chehab 	0xe7, 0xe0,		/* Contrast to 1.0, noise shaping
236cb7a01acSMauro Carvalho Chehab 				 * 10 to 8 2-bit error diffusion */
237cb7a01acSMauro Carvalho Chehab 	0xe8, 0xf8,		/* YUV422, CbCr binary offset,
238cb7a01acSMauro Carvalho Chehab 				 * ... (p.32) */
239cb7a01acSMauro Carvalho Chehab 	0xea, 0x18,		/* LLC2 connected, output FIFO
240cb7a01acSMauro Carvalho Chehab 				 * reset with VACTintern */
241cb7a01acSMauro Carvalho Chehab 	0xf0, 0x8a,		/* Half full level to 10, bus
242cb7a01acSMauro Carvalho Chehab 				 * shuffler [7:0, 23:16, 15:8] */
243cb7a01acSMauro Carvalho Chehab 	0xf1, 0x18,		/* Single clock, sync mode, no
244cb7a01acSMauro Carvalho Chehab 				 * FE delay, no HLEN counter */
245cb7a01acSMauro Carvalho Chehab 	0xf8, 0x12,		/* Port A, PIXCLK, HF# & FE#
246cb7a01acSMauro Carvalho Chehab 				 * strength to 2 */
247cb7a01acSMauro Carvalho Chehab 	0xf9, 0x24,		/* Port B, HREF, VREF, PREF &
248cb7a01acSMauro Carvalho Chehab 				 * ALPHA strength to 4 */
249cb7a01acSMauro Carvalho Chehab };
250cb7a01acSMauro Carvalho Chehab 
251cb7a01acSMauro Carvalho Chehab static const unsigned short init_fp[] = {
252cb7a01acSMauro Carvalho Chehab 	0x59, 0,
253cb7a01acSMauro Carvalho Chehab 	0xa0, 2070,		/* ACC reference */
254cb7a01acSMauro Carvalho Chehab 	0xa3, 0,
255cb7a01acSMauro Carvalho Chehab 	0xa4, 0,
256cb7a01acSMauro Carvalho Chehab 	0xa8, 30,
257cb7a01acSMauro Carvalho Chehab 	0xb2, 768,
258cb7a01acSMauro Carvalho Chehab 	0xbe, 27,
259cb7a01acSMauro Carvalho Chehab 	0x58, 0,
260cb7a01acSMauro Carvalho Chehab 	0x26, 0,
261cb7a01acSMauro Carvalho Chehab 	0x4b, 0x298,		/* PLL gain */
262cb7a01acSMauro Carvalho Chehab };
263cb7a01acSMauro Carvalho Chehab 
264cb7a01acSMauro Carvalho Chehab 
vpx3220_init(struct v4l2_subdev * sd,u32 val)265cb7a01acSMauro Carvalho Chehab static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
266cb7a01acSMauro Carvalho Chehab {
267cb7a01acSMauro Carvalho Chehab 	struct vpx3220 *decoder = to_vpx3220(sd);
268cb7a01acSMauro Carvalho Chehab 
269cb7a01acSMauro Carvalho Chehab 	vpx3220_write_block(sd, init_common, sizeof(init_common));
270cb7a01acSMauro Carvalho Chehab 	vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
271cb7a01acSMauro Carvalho Chehab 	if (decoder->norm & V4L2_STD_NTSC)
272cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
273cb7a01acSMauro Carvalho Chehab 	else if (decoder->norm & V4L2_STD_PAL)
274cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
275cb7a01acSMauro Carvalho Chehab 	else if (decoder->norm & V4L2_STD_SECAM)
276cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
277cb7a01acSMauro Carvalho Chehab 	else
278cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
279cb7a01acSMauro Carvalho Chehab 	return 0;
280cb7a01acSMauro Carvalho Chehab }
281cb7a01acSMauro Carvalho Chehab 
vpx3220_status(struct v4l2_subdev * sd,u32 * pstatus,v4l2_std_id * pstd)282cb7a01acSMauro Carvalho Chehab static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
283cb7a01acSMauro Carvalho Chehab {
284cb7a01acSMauro Carvalho Chehab 	int res = V4L2_IN_ST_NO_SIGNAL, status;
28532cb3b09SHans Verkuil 	v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
286cb7a01acSMauro Carvalho Chehab 
287cb7a01acSMauro Carvalho Chehab 	status = vpx3220_fp_read(sd, 0x0f3);
288cb7a01acSMauro Carvalho Chehab 
289cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
290cb7a01acSMauro Carvalho Chehab 
291cb7a01acSMauro Carvalho Chehab 	if (status < 0)
292cb7a01acSMauro Carvalho Chehab 		return status;
293cb7a01acSMauro Carvalho Chehab 
294cb7a01acSMauro Carvalho Chehab 	if ((status & 0x20) == 0) {
295cb7a01acSMauro Carvalho Chehab 		res = 0;
296cb7a01acSMauro Carvalho Chehab 
297cb7a01acSMauro Carvalho Chehab 		switch (status & 0x18) {
298cb7a01acSMauro Carvalho Chehab 		case 0x00:
299cb7a01acSMauro Carvalho Chehab 		case 0x10:
300cb7a01acSMauro Carvalho Chehab 		case 0x14:
301cb7a01acSMauro Carvalho Chehab 		case 0x18:
30232cb3b09SHans Verkuil 			std &= V4L2_STD_PAL;
303cb7a01acSMauro Carvalho Chehab 			break;
304cb7a01acSMauro Carvalho Chehab 
305cb7a01acSMauro Carvalho Chehab 		case 0x08:
30632cb3b09SHans Verkuil 			std &= V4L2_STD_SECAM;
307cb7a01acSMauro Carvalho Chehab 			break;
308cb7a01acSMauro Carvalho Chehab 
309cb7a01acSMauro Carvalho Chehab 		case 0x04:
310cb7a01acSMauro Carvalho Chehab 		case 0x0c:
311cb7a01acSMauro Carvalho Chehab 		case 0x1c:
31232cb3b09SHans Verkuil 			std &= V4L2_STD_NTSC;
313cb7a01acSMauro Carvalho Chehab 			break;
314cb7a01acSMauro Carvalho Chehab 		}
31532cb3b09SHans Verkuil 	} else {
31632cb3b09SHans Verkuil 		std = V4L2_STD_UNKNOWN;
317cb7a01acSMauro Carvalho Chehab 	}
318cb7a01acSMauro Carvalho Chehab 	if (pstd)
319cb7a01acSMauro Carvalho Chehab 		*pstd = std;
320cb7a01acSMauro Carvalho Chehab 	if (pstatus)
321cb7a01acSMauro Carvalho Chehab 		*pstatus = res;
322cb7a01acSMauro Carvalho Chehab 	return 0;
323cb7a01acSMauro Carvalho Chehab }
324cb7a01acSMauro Carvalho Chehab 
vpx3220_querystd(struct v4l2_subdev * sd,v4l2_std_id * std)325cb7a01acSMauro Carvalho Chehab static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
326cb7a01acSMauro Carvalho Chehab {
327cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "querystd\n");
328cb7a01acSMauro Carvalho Chehab 	return vpx3220_status(sd, NULL, std);
329cb7a01acSMauro Carvalho Chehab }
330cb7a01acSMauro Carvalho Chehab 
vpx3220_g_input_status(struct v4l2_subdev * sd,u32 * status)331cb7a01acSMauro Carvalho Chehab static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
332cb7a01acSMauro Carvalho Chehab {
333cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "g_input_status\n");
334cb7a01acSMauro Carvalho Chehab 	return vpx3220_status(sd, status, NULL);
335cb7a01acSMauro Carvalho Chehab }
336cb7a01acSMauro Carvalho Chehab 
vpx3220_s_std(struct v4l2_subdev * sd,v4l2_std_id std)337cb7a01acSMauro Carvalho Chehab static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
338cb7a01acSMauro Carvalho Chehab {
339cb7a01acSMauro Carvalho Chehab 	struct vpx3220 *decoder = to_vpx3220(sd);
340cb7a01acSMauro Carvalho Chehab 	int temp_input;
341cb7a01acSMauro Carvalho Chehab 
342cb7a01acSMauro Carvalho Chehab 	/* Here we back up the input selection because it gets
343cb7a01acSMauro Carvalho Chehab 	   overwritten when we fill the registers with the
344cb7a01acSMauro Carvalho Chehab 	   chosen video norm */
345cb7a01acSMauro Carvalho Chehab 	temp_input = vpx3220_fp_read(sd, 0xf2);
346cb7a01acSMauro Carvalho Chehab 
347cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
348cb7a01acSMauro Carvalho Chehab 	if (std & V4L2_STD_NTSC) {
349cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
350cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
351cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_PAL) {
352cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
353cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
354cb7a01acSMauro Carvalho Chehab 	} else if (std & V4L2_STD_SECAM) {
355cb7a01acSMauro Carvalho Chehab 		vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
356cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
357cb7a01acSMauro Carvalho Chehab 	} else {
358cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
359cb7a01acSMauro Carvalho Chehab 	}
360cb7a01acSMauro Carvalho Chehab 
361cb7a01acSMauro Carvalho Chehab 	decoder->norm = std;
362cb7a01acSMauro Carvalho Chehab 
363cb7a01acSMauro Carvalho Chehab 	/* And here we set the backed up video input again */
364cb7a01acSMauro Carvalho Chehab 	vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
365cb7a01acSMauro Carvalho Chehab 	udelay(10);
366cb7a01acSMauro Carvalho Chehab 	return 0;
367cb7a01acSMauro Carvalho Chehab }
368cb7a01acSMauro Carvalho Chehab 
vpx3220_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)369cb7a01acSMauro Carvalho Chehab static int vpx3220_s_routing(struct v4l2_subdev *sd,
370cb7a01acSMauro Carvalho Chehab 			     u32 input, u32 output, u32 config)
371cb7a01acSMauro Carvalho Chehab {
372cb7a01acSMauro Carvalho Chehab 	int data;
373cb7a01acSMauro Carvalho Chehab 
374cb7a01acSMauro Carvalho Chehab 	/* RJ:   input = 0: ST8 (PCTV) input
375cb7a01acSMauro Carvalho Chehab 		 input = 1: COMPOSITE  input
376cb7a01acSMauro Carvalho Chehab 		 input = 2: SVHS       input  */
377cb7a01acSMauro Carvalho Chehab 
3789ecb6718SColin Ian King 	static const int input_vals[3][2] = {
379cb7a01acSMauro Carvalho Chehab 		{0x0c, 0},
380cb7a01acSMauro Carvalho Chehab 		{0x0d, 0},
381cb7a01acSMauro Carvalho Chehab 		{0x0e, 1}
382cb7a01acSMauro Carvalho Chehab 	};
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab 	if (input > 2)
385cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
386cb7a01acSMauro Carvalho Chehab 
387cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
388cb7a01acSMauro Carvalho Chehab 
389cb7a01acSMauro Carvalho Chehab 	vpx3220_write(sd, 0x33, input_vals[input][0]);
390cb7a01acSMauro Carvalho Chehab 
391cb7a01acSMauro Carvalho Chehab 	data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
392cb7a01acSMauro Carvalho Chehab 	if (data < 0)
393cb7a01acSMauro Carvalho Chehab 		return data;
394cb7a01acSMauro Carvalho Chehab 	/* 0x0010 is required to latch the setting */
395cb7a01acSMauro Carvalho Chehab 	vpx3220_fp_write(sd, 0xf2,
396cb7a01acSMauro Carvalho Chehab 			data | (input_vals[input][1] << 5) | 0x0010);
397cb7a01acSMauro Carvalho Chehab 
398cb7a01acSMauro Carvalho Chehab 	udelay(10);
399cb7a01acSMauro Carvalho Chehab 	return 0;
400cb7a01acSMauro Carvalho Chehab }
401cb7a01acSMauro Carvalho Chehab 
vpx3220_s_stream(struct v4l2_subdev * sd,int enable)402cb7a01acSMauro Carvalho Chehab static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
403cb7a01acSMauro Carvalho Chehab {
404cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
405cb7a01acSMauro Carvalho Chehab 
406cb7a01acSMauro Carvalho Chehab 	vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
407cb7a01acSMauro Carvalho Chehab 	return 0;
408cb7a01acSMauro Carvalho Chehab }
409cb7a01acSMauro Carvalho Chehab 
vpx3220_s_ctrl(struct v4l2_ctrl * ctrl)410cb7a01acSMauro Carvalho Chehab static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
411cb7a01acSMauro Carvalho Chehab {
412cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
413cb7a01acSMauro Carvalho Chehab 
414cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
415cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
416cb7a01acSMauro Carvalho Chehab 		vpx3220_write(sd, 0xe6, ctrl->val);
417cb7a01acSMauro Carvalho Chehab 		return 0;
418cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
419cb7a01acSMauro Carvalho Chehab 		/* Bit 7 and 8 is for noise shaping */
420cb7a01acSMauro Carvalho Chehab 		vpx3220_write(sd, 0xe7, ctrl->val + 192);
421cb7a01acSMauro Carvalho Chehab 		return 0;
422cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
423cb7a01acSMauro Carvalho Chehab 		vpx3220_fp_write(sd, 0xa0, ctrl->val);
424cb7a01acSMauro Carvalho Chehab 		return 0;
425cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
426cb7a01acSMauro Carvalho Chehab 		vpx3220_fp_write(sd, 0x1c, ctrl->val);
427cb7a01acSMauro Carvalho Chehab 		return 0;
428cb7a01acSMauro Carvalho Chehab 	}
429cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
430cb7a01acSMauro Carvalho Chehab }
431cb7a01acSMauro Carvalho Chehab 
432cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
433cb7a01acSMauro Carvalho Chehab 
434cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
435cb7a01acSMauro Carvalho Chehab 	.s_ctrl = vpx3220_s_ctrl,
436cb7a01acSMauro Carvalho Chehab };
437cb7a01acSMauro Carvalho Chehab 
438cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
439cb7a01acSMauro Carvalho Chehab 	.init = vpx3220_init,
440cb7a01acSMauro Carvalho Chehab };
441cb7a01acSMauro Carvalho Chehab 
442cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
4438774bed9SLaurent Pinchart 	.s_std = vpx3220_s_std,
444cb7a01acSMauro Carvalho Chehab 	.s_routing = vpx3220_s_routing,
445cb7a01acSMauro Carvalho Chehab 	.s_stream = vpx3220_s_stream,
446cb7a01acSMauro Carvalho Chehab 	.querystd = vpx3220_querystd,
447cb7a01acSMauro Carvalho Chehab 	.g_input_status = vpx3220_g_input_status,
448cb7a01acSMauro Carvalho Chehab };
449cb7a01acSMauro Carvalho Chehab 
450cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops vpx3220_ops = {
451cb7a01acSMauro Carvalho Chehab 	.core = &vpx3220_core_ops,
452cb7a01acSMauro Carvalho Chehab 	.video = &vpx3220_video_ops,
453cb7a01acSMauro Carvalho Chehab };
454cb7a01acSMauro Carvalho Chehab 
455cb7a01acSMauro Carvalho Chehab /* -----------------------------------------------------------------------
456cb7a01acSMauro Carvalho Chehab  * Client management code
457cb7a01acSMauro Carvalho Chehab  */
458cb7a01acSMauro Carvalho Chehab 
vpx3220_probe(struct i2c_client * client)459b7a45a22SUwe Kleine-König static int vpx3220_probe(struct i2c_client *client)
460cb7a01acSMauro Carvalho Chehab {
461cb7a01acSMauro Carvalho Chehab 	struct vpx3220 *decoder;
462cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
463cb7a01acSMauro Carvalho Chehab 	const char *name = NULL;
464cb7a01acSMauro Carvalho Chehab 	u8 ver;
465cb7a01acSMauro Carvalho Chehab 	u16 pn;
466cb7a01acSMauro Carvalho Chehab 
467cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
468cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter,
469cb7a01acSMauro Carvalho Chehab 		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
470cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
471cb7a01acSMauro Carvalho Chehab 
472c02b211dSLaurent Pinchart 	decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
473cb7a01acSMauro Carvalho Chehab 	if (decoder == NULL)
474cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
475cb7a01acSMauro Carvalho Chehab 	sd = &decoder->sd;
476cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
477cb7a01acSMauro Carvalho Chehab 	decoder->norm = V4L2_STD_PAL;
478cb7a01acSMauro Carvalho Chehab 	decoder->input = 0;
479cb7a01acSMauro Carvalho Chehab 	decoder->enable = 1;
480cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&decoder->hdl, 4);
481cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
482cb7a01acSMauro Carvalho Chehab 		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
483cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
484cb7a01acSMauro Carvalho Chehab 		V4L2_CID_CONTRAST, 0, 63, 1, 32);
485cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
486cb7a01acSMauro Carvalho Chehab 		V4L2_CID_SATURATION, 0, 4095, 1, 2048);
487cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
488cb7a01acSMauro Carvalho Chehab 		V4L2_CID_HUE, -512, 511, 1, 0);
489cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &decoder->hdl;
490cb7a01acSMauro Carvalho Chehab 	if (decoder->hdl.error) {
491cb7a01acSMauro Carvalho Chehab 		int err = decoder->hdl.error;
492cb7a01acSMauro Carvalho Chehab 
493cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&decoder->hdl);
494cb7a01acSMauro Carvalho Chehab 		return err;
495cb7a01acSMauro Carvalho Chehab 	}
496cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_setup(&decoder->hdl);
497cb7a01acSMauro Carvalho Chehab 
498cb7a01acSMauro Carvalho Chehab 	ver = i2c_smbus_read_byte_data(client, 0x00);
499cb7a01acSMauro Carvalho Chehab 	pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
500cb7a01acSMauro Carvalho Chehab 		i2c_smbus_read_byte_data(client, 0x01);
501cb7a01acSMauro Carvalho Chehab 	if (ver == 0xec) {
502cb7a01acSMauro Carvalho Chehab 		switch (pn) {
503cb7a01acSMauro Carvalho Chehab 		case 0x4680:
504cb7a01acSMauro Carvalho Chehab 			name = "vpx3220a";
505cb7a01acSMauro Carvalho Chehab 			break;
506cb7a01acSMauro Carvalho Chehab 		case 0x4260:
507cb7a01acSMauro Carvalho Chehab 			name = "vpx3216b";
508cb7a01acSMauro Carvalho Chehab 			break;
509cb7a01acSMauro Carvalho Chehab 		case 0x4280:
510cb7a01acSMauro Carvalho Chehab 			name = "vpx3214c";
511cb7a01acSMauro Carvalho Chehab 			break;
512cb7a01acSMauro Carvalho Chehab 		}
513cb7a01acSMauro Carvalho Chehab 	}
514cb7a01acSMauro Carvalho Chehab 	if (name)
515cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
516cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
517cb7a01acSMauro Carvalho Chehab 	else
518cb7a01acSMauro Carvalho Chehab 		v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
519cb7a01acSMauro Carvalho Chehab 			ver, pn, client->addr << 1, client->adapter->name);
520cb7a01acSMauro Carvalho Chehab 
521cb7a01acSMauro Carvalho Chehab 	vpx3220_write_block(sd, init_common, sizeof(init_common));
522cb7a01acSMauro Carvalho Chehab 	vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
523cb7a01acSMauro Carvalho Chehab 	/* Default to PAL */
524cb7a01acSMauro Carvalho Chehab 	vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
525cb7a01acSMauro Carvalho Chehab 	return 0;
526cb7a01acSMauro Carvalho Chehab }
527cb7a01acSMauro Carvalho Chehab 
vpx3220_remove(struct i2c_client * client)528ed5c2f5fSUwe Kleine-König static void vpx3220_remove(struct i2c_client *client)
529cb7a01acSMauro Carvalho Chehab {
530cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
531cb7a01acSMauro Carvalho Chehab 	struct vpx3220 *decoder = to_vpx3220(sd);
532cb7a01acSMauro Carvalho Chehab 
533cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
534cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&decoder->hdl);
535cb7a01acSMauro Carvalho Chehab }
536cb7a01acSMauro Carvalho Chehab 
537cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id vpx3220_id[] = {
538cb7a01acSMauro Carvalho Chehab 	{ "vpx3220a", 0 },
539cb7a01acSMauro Carvalho Chehab 	{ "vpx3216b", 0 },
540cb7a01acSMauro Carvalho Chehab 	{ "vpx3214c", 0 },
541cb7a01acSMauro Carvalho Chehab 	{ }
542cb7a01acSMauro Carvalho Chehab };
543cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, vpx3220_id);
544cb7a01acSMauro Carvalho Chehab 
545cb7a01acSMauro Carvalho Chehab static struct i2c_driver vpx3220_driver = {
546cb7a01acSMauro Carvalho Chehab 	.driver = {
547cb7a01acSMauro Carvalho Chehab 		.name	= "vpx3220",
548cb7a01acSMauro Carvalho Chehab 	},
549*aaeb31c0SUwe Kleine-König 	.probe		= vpx3220_probe,
550cb7a01acSMauro Carvalho Chehab 	.remove		= vpx3220_remove,
551cb7a01acSMauro Carvalho Chehab 	.id_table	= vpx3220_id,
552cb7a01acSMauro Carvalho Chehab };
553cb7a01acSMauro Carvalho Chehab 
554cb7a01acSMauro Carvalho Chehab module_i2c_driver(vpx3220_driver);
555