xref: /openbmc/linux/drivers/media/i2c/upd64031a.c (revision aaeb31c0)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * 2003 by T.Adachi <tadachi@tadachi-net.com>
6cb7a01acSMauro Carvalho Chehab  * 2003 by Takeru KOMORIYA <komoriya@paken.org>
7cb7a01acSMauro Carvalho Chehab  * 2006 by Hans Verkuil <hverkuil@xs4all.nl>
8cb7a01acSMauro Carvalho Chehab  */
9cb7a01acSMauro Carvalho Chehab 
10cb7a01acSMauro Carvalho Chehab 
11cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
16cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
17b5dcee22SMauro Carvalho Chehab #include <media/i2c/upd64031a.h>
18cb7a01acSMauro Carvalho Chehab 
19cb7a01acSMauro Carvalho Chehab /* --------------------- read registers functions define -------------------- */
20cb7a01acSMauro Carvalho Chehab 
21cb7a01acSMauro Carvalho Chehab /* bit masks */
22cb7a01acSMauro Carvalho Chehab #define GR_MODE_MASK              0xc0
23cb7a01acSMauro Carvalho Chehab #define DIRECT_3DYCS_CONNECT_MASK 0xc0
24cb7a01acSMauro Carvalho Chehab #define SYNC_CIRCUIT_MASK         0xa0
25cb7a01acSMauro Carvalho Chehab 
26cb7a01acSMauro Carvalho Chehab /* -------------------------------------------------------------------------- */
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("uPD64031A driver");
29cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
30cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
31cb7a01acSMauro Carvalho Chehab 
32cb7a01acSMauro Carvalho Chehab static int debug;
33cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0644);
34cb7a01acSMauro Carvalho Chehab 
35cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab enum {
39cb7a01acSMauro Carvalho Chehab 	R00 = 0, R01, R02, R03, R04,
40cb7a01acSMauro Carvalho Chehab 	R05, R06, R07, R08, R09,
41cb7a01acSMauro Carvalho Chehab 	R0A, R0B, R0C, R0D, R0E, R0F,
42cb7a01acSMauro Carvalho Chehab 	/* unused registers
43cb7a01acSMauro Carvalho Chehab 	 R10, R11, R12, R13, R14,
44cb7a01acSMauro Carvalho Chehab 	 R15, R16, R17,
45cb7a01acSMauro Carvalho Chehab 	 */
46cb7a01acSMauro Carvalho Chehab 	TOT_REGS
47cb7a01acSMauro Carvalho Chehab };
48cb7a01acSMauro Carvalho Chehab 
49cb7a01acSMauro Carvalho Chehab struct upd64031a_state {
50cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
51cb7a01acSMauro Carvalho Chehab 	u8 regs[TOT_REGS];
52cb7a01acSMauro Carvalho Chehab 	u8 gr_mode;
53cb7a01acSMauro Carvalho Chehab 	u8 direct_3dycs_connect;
54cb7a01acSMauro Carvalho Chehab 	u8 ext_comp_sync;
55cb7a01acSMauro Carvalho Chehab 	u8 ext_vert_sync;
56cb7a01acSMauro Carvalho Chehab };
57cb7a01acSMauro Carvalho Chehab 
to_state(struct v4l2_subdev * sd)58cb7a01acSMauro Carvalho Chehab static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd)
59cb7a01acSMauro Carvalho Chehab {
60cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct upd64031a_state, sd);
61cb7a01acSMauro Carvalho Chehab }
62cb7a01acSMauro Carvalho Chehab 
63cb7a01acSMauro Carvalho Chehab static u8 upd64031a_init[] = {
64cb7a01acSMauro Carvalho Chehab 	0x00, 0xb8, 0x48, 0xd2, 0xe6,
65cb7a01acSMauro Carvalho Chehab 	0x03, 0x10, 0x0b, 0xaf, 0x7f,
66cb7a01acSMauro Carvalho Chehab 	0x00, 0x00, 0x1d, 0x5e, 0x00,
67cb7a01acSMauro Carvalho Chehab 	0xd0
68cb7a01acSMauro Carvalho Chehab };
69cb7a01acSMauro Carvalho Chehab 
70cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
71cb7a01acSMauro Carvalho Chehab 
upd64031a_read(struct v4l2_subdev * sd,u8 reg)72cb7a01acSMauro Carvalho Chehab static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg)
73cb7a01acSMauro Carvalho Chehab {
74cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
75cb7a01acSMauro Carvalho Chehab 	u8 buf[2];
76cb7a01acSMauro Carvalho Chehab 
77cb7a01acSMauro Carvalho Chehab 	if (reg >= sizeof(buf))
78cb7a01acSMauro Carvalho Chehab 		return 0xff;
79cb7a01acSMauro Carvalho Chehab 	i2c_master_recv(client, buf, 2);
80cb7a01acSMauro Carvalho Chehab 	return buf[reg];
81cb7a01acSMauro Carvalho Chehab }
82cb7a01acSMauro Carvalho Chehab 
83cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
84cb7a01acSMauro Carvalho Chehab 
upd64031a_write(struct v4l2_subdev * sd,u8 reg,u8 val)85cb7a01acSMauro Carvalho Chehab static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)
86cb7a01acSMauro Carvalho Chehab {
87cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
88cb7a01acSMauro Carvalho Chehab 	u8 buf[2];
89cb7a01acSMauro Carvalho Chehab 
90cb7a01acSMauro Carvalho Chehab 	buf[0] = reg;
91cb7a01acSMauro Carvalho Chehab 	buf[1] = val;
92cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val);
93cb7a01acSMauro Carvalho Chehab 	if (i2c_master_send(client, buf, 2) != 2)
94cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
95cb7a01acSMauro Carvalho Chehab }
96cb7a01acSMauro Carvalho Chehab 
97cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
98cb7a01acSMauro Carvalho Chehab 
99cb7a01acSMauro Carvalho Chehab /* The input changed due to new input or channel changed */
upd64031a_s_frequency(struct v4l2_subdev * sd,const struct v4l2_frequency * freq)100b530a447SHans Verkuil static int upd64031a_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)
101cb7a01acSMauro Carvalho Chehab {
102cb7a01acSMauro Carvalho Chehab 	struct upd64031a_state *state = to_state(sd);
103cb7a01acSMauro Carvalho Chehab 	u8 reg = state->regs[R00];
104cb7a01acSMauro Carvalho Chehab 
105cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "changed input or channel\n");
106cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, R00, reg | 0x10);
107cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, R00, reg & ~0x10);
108cb7a01acSMauro Carvalho Chehab 	return 0;
109cb7a01acSMauro Carvalho Chehab }
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
112cb7a01acSMauro Carvalho Chehab 
upd64031a_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)113cb7a01acSMauro Carvalho Chehab static int upd64031a_s_routing(struct v4l2_subdev *sd,
114cb7a01acSMauro Carvalho Chehab 			       u32 input, u32 output, u32 config)
115cb7a01acSMauro Carvalho Chehab {
116cb7a01acSMauro Carvalho Chehab 	struct upd64031a_state *state = to_state(sd);
117cb7a01acSMauro Carvalho Chehab 	u8 r00, r05, r08;
118cb7a01acSMauro Carvalho Chehab 
119cb7a01acSMauro Carvalho Chehab 	state->gr_mode = (input & 3) << 6;
120cb7a01acSMauro Carvalho Chehab 	state->direct_3dycs_connect = (input & 0xc) << 4;
121cb7a01acSMauro Carvalho Chehab 	state->ext_comp_sync =
122cb7a01acSMauro Carvalho Chehab 		(input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
123cb7a01acSMauro Carvalho Chehab 	state->ext_vert_sync =
124cb7a01acSMauro Carvalho Chehab 		(input & UPD64031A_VERTICAL_EXTERNAL) << 2;
125cb7a01acSMauro Carvalho Chehab 	r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
126cb7a01acSMauro Carvalho Chehab 	r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
127cb7a01acSMauro Carvalho Chehab 		state->ext_comp_sync | state->ext_vert_sync;
128cb7a01acSMauro Carvalho Chehab 	r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
129cb7a01acSMauro Carvalho Chehab 		state->direct_3dycs_connect;
130cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, R00, r00);
131cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, R05, r05);
132cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, R08, r08);
133cb7a01acSMauro Carvalho Chehab 	return upd64031a_s_frequency(sd, NULL);
134cb7a01acSMauro Carvalho Chehab }
135cb7a01acSMauro Carvalho Chehab 
upd64031a_log_status(struct v4l2_subdev * sd)136cb7a01acSMauro Carvalho Chehab static int upd64031a_log_status(struct v4l2_subdev *sd)
137cb7a01acSMauro Carvalho Chehab {
138cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
139cb7a01acSMauro Carvalho Chehab 			upd64031a_read(sd, 0), upd64031a_read(sd, 1));
140cb7a01acSMauro Carvalho Chehab 	return 0;
141cb7a01acSMauro Carvalho Chehab }
142cb7a01acSMauro Carvalho Chehab 
143cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
upd64031a_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)144cb7a01acSMauro Carvalho Chehab static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
145cb7a01acSMauro Carvalho Chehab {
146cb7a01acSMauro Carvalho Chehab 	reg->val = upd64031a_read(sd, reg->reg & 0xff);
147cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
148cb7a01acSMauro Carvalho Chehab 	return 0;
149cb7a01acSMauro Carvalho Chehab }
150cb7a01acSMauro Carvalho Chehab 
upd64031a_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)151977ba3b1SHans Verkuil static int upd64031a_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
152cb7a01acSMauro Carvalho Chehab {
153cb7a01acSMauro Carvalho Chehab 	upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
154cb7a01acSMauro Carvalho Chehab 	return 0;
155cb7a01acSMauro Carvalho Chehab }
156cb7a01acSMauro Carvalho Chehab #endif
157cb7a01acSMauro Carvalho Chehab 
158cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
161cb7a01acSMauro Carvalho Chehab 	.log_status = upd64031a_log_status,
162cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
163cb7a01acSMauro Carvalho Chehab 	.g_register = upd64031a_g_register,
164cb7a01acSMauro Carvalho Chehab 	.s_register = upd64031a_s_register,
165cb7a01acSMauro Carvalho Chehab #endif
166cb7a01acSMauro Carvalho Chehab };
167cb7a01acSMauro Carvalho Chehab 
168cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = {
169cb7a01acSMauro Carvalho Chehab 	.s_frequency = upd64031a_s_frequency,
170cb7a01acSMauro Carvalho Chehab };
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops upd64031a_video_ops = {
173cb7a01acSMauro Carvalho Chehab 	.s_routing = upd64031a_s_routing,
174cb7a01acSMauro Carvalho Chehab };
175cb7a01acSMauro Carvalho Chehab 
176cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops upd64031a_ops = {
177cb7a01acSMauro Carvalho Chehab 	.core = &upd64031a_core_ops,
178cb7a01acSMauro Carvalho Chehab 	.tuner = &upd64031a_tuner_ops,
179cb7a01acSMauro Carvalho Chehab 	.video = &upd64031a_video_ops,
180cb7a01acSMauro Carvalho Chehab };
181cb7a01acSMauro Carvalho Chehab 
182cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
183cb7a01acSMauro Carvalho Chehab 
184cb7a01acSMauro Carvalho Chehab /* i2c implementation */
185cb7a01acSMauro Carvalho Chehab 
upd64031a_probe(struct i2c_client * client)186b75ac196SUwe Kleine-König static int upd64031a_probe(struct i2c_client *client)
187cb7a01acSMauro Carvalho Chehab {
188cb7a01acSMauro Carvalho Chehab 	struct upd64031a_state *state;
189cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
190cb7a01acSMauro Carvalho Chehab 	int i;
191cb7a01acSMauro Carvalho Chehab 
192cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
193cb7a01acSMauro Carvalho Chehab 		return -EIO;
194cb7a01acSMauro Carvalho Chehab 
195cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
196cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
197cb7a01acSMauro Carvalho Chehab 
198c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
199cb7a01acSMauro Carvalho Chehab 	if (state == NULL)
200cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
201cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
202cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &upd64031a_ops);
203cb7a01acSMauro Carvalho Chehab 	memcpy(state->regs, upd64031a_init, sizeof(state->regs));
204cb7a01acSMauro Carvalho Chehab 	state->gr_mode = UPD64031A_GR_ON << 6;
205cb7a01acSMauro Carvalho Chehab 	state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
206cb7a01acSMauro Carvalho Chehab 	state->ext_comp_sync = state->ext_vert_sync = 0;
207cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < TOT_REGS; i++)
208cb7a01acSMauro Carvalho Chehab 		upd64031a_write(sd, i, state->regs[i]);
209cb7a01acSMauro Carvalho Chehab 	return 0;
210cb7a01acSMauro Carvalho Chehab }
211cb7a01acSMauro Carvalho Chehab 
upd64031a_remove(struct i2c_client * client)212ed5c2f5fSUwe Kleine-König static void upd64031a_remove(struct i2c_client *client)
213cb7a01acSMauro Carvalho Chehab {
214cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
215cb7a01acSMauro Carvalho Chehab 
216cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
217cb7a01acSMauro Carvalho Chehab }
218cb7a01acSMauro Carvalho Chehab 
219cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
220cb7a01acSMauro Carvalho Chehab 
221cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id upd64031a_id[] = {
222cb7a01acSMauro Carvalho Chehab 	{ "upd64031a", 0 },
223cb7a01acSMauro Carvalho Chehab 	{ }
224cb7a01acSMauro Carvalho Chehab };
225cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, upd64031a_id);
226cb7a01acSMauro Carvalho Chehab 
227cb7a01acSMauro Carvalho Chehab static struct i2c_driver upd64031a_driver = {
228cb7a01acSMauro Carvalho Chehab 	.driver = {
229cb7a01acSMauro Carvalho Chehab 		.name	= "upd64031a",
230cb7a01acSMauro Carvalho Chehab 	},
231*aaeb31c0SUwe Kleine-König 	.probe		= upd64031a_probe,
232cb7a01acSMauro Carvalho Chehab 	.remove		= upd64031a_remove,
233cb7a01acSMauro Carvalho Chehab 	.id_table	= upd64031a_id,
234cb7a01acSMauro Carvalho Chehab };
235cb7a01acSMauro Carvalho Chehab 
236cb7a01acSMauro Carvalho Chehab module_i2c_driver(upd64031a_driver);
237