xref: /openbmc/linux/drivers/media/i2c/upd64083.c (revision aaeb31c0)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver
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 #include <linux/module.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/kernel.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
15cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
16b5dcee22SMauro Carvalho Chehab #include <media/i2c/upd64083.h>
17cb7a01acSMauro Carvalho Chehab 
18cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("uPD64083 driver");
19cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
20cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
21cb7a01acSMauro Carvalho Chehab 
22cb7a01acSMauro Carvalho Chehab static bool debug;
23cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
24cb7a01acSMauro Carvalho Chehab 
25cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
26cb7a01acSMauro Carvalho Chehab 
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab enum {
29cb7a01acSMauro Carvalho Chehab 	R00 = 0, R01, R02, R03, R04,
30cb7a01acSMauro Carvalho Chehab 	R05, R06, R07, R08, R09,
31cb7a01acSMauro Carvalho Chehab 	R0A, R0B, R0C, R0D, R0E, R0F,
32cb7a01acSMauro Carvalho Chehab 	R10, R11, R12, R13, R14,
33cb7a01acSMauro Carvalho Chehab 	R15, R16,
34cb7a01acSMauro Carvalho Chehab 	TOT_REGS
35cb7a01acSMauro Carvalho Chehab };
36cb7a01acSMauro Carvalho Chehab 
37cb7a01acSMauro Carvalho Chehab struct upd64083_state {
38cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
39cb7a01acSMauro Carvalho Chehab 	u8 mode;
40cb7a01acSMauro Carvalho Chehab 	u8 ext_y_adc;
41cb7a01acSMauro Carvalho Chehab 	u8 regs[TOT_REGS];
42cb7a01acSMauro Carvalho Chehab };
43cb7a01acSMauro Carvalho Chehab 
to_state(struct v4l2_subdev * sd)44cb7a01acSMauro Carvalho Chehab static inline struct upd64083_state *to_state(struct v4l2_subdev *sd)
45cb7a01acSMauro Carvalho Chehab {
46cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct upd64083_state, sd);
47cb7a01acSMauro Carvalho Chehab }
48cb7a01acSMauro Carvalho Chehab 
49cb7a01acSMauro Carvalho Chehab /* Initial values when used in combination with the
50cb7a01acSMauro Carvalho Chehab    NEC upd64031a ghost reduction chip. */
51cb7a01acSMauro Carvalho Chehab static u8 upd64083_init[] = {
52cb7a01acSMauro Carvalho Chehab 	0x1f, 0x01, 0xa0, 0x2d, 0x29,  /* we use EXCSS=0 */
53cb7a01acSMauro Carvalho Chehab 	0x36, 0xdd, 0x05, 0x56, 0x48,
54cb7a01acSMauro Carvalho Chehab 	0x00, 0x3a, 0xa0, 0x05, 0x08,
55cb7a01acSMauro Carvalho Chehab 	0x44, 0x60, 0x08, 0x52, 0xf8,
56cb7a01acSMauro Carvalho Chehab 	0x53, 0x60, 0x10
57cb7a01acSMauro Carvalho Chehab };
58cb7a01acSMauro Carvalho Chehab 
59cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
60cb7a01acSMauro Carvalho Chehab 
upd64083_write(struct v4l2_subdev * sd,u8 reg,u8 val)61cb7a01acSMauro Carvalho Chehab static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val)
62cb7a01acSMauro Carvalho Chehab {
63cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
64cb7a01acSMauro Carvalho Chehab 	u8 buf[2];
65cb7a01acSMauro Carvalho Chehab 
66cb7a01acSMauro Carvalho Chehab 	buf[0] = reg;
67cb7a01acSMauro Carvalho Chehab 	buf[1] = val;
68cb7a01acSMauro Carvalho Chehab 	v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val);
69cb7a01acSMauro Carvalho Chehab 	if (i2c_master_send(client, buf, 2) != 2)
70cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
71cb7a01acSMauro Carvalho Chehab }
72cb7a01acSMauro Carvalho Chehab 
73cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
74cb7a01acSMauro Carvalho Chehab 
75cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
upd64083_read(struct v4l2_subdev * sd,u8 reg)76cb7a01acSMauro Carvalho Chehab static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg)
77cb7a01acSMauro Carvalho Chehab {
78cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
79cb7a01acSMauro Carvalho Chehab 	u8 buf[7];
80cb7a01acSMauro Carvalho Chehab 
81cb7a01acSMauro Carvalho Chehab 	if (reg >= sizeof(buf))
82cb7a01acSMauro Carvalho Chehab 		return 0xff;
83cb7a01acSMauro Carvalho Chehab 	i2c_master_recv(client, buf, sizeof(buf));
84cb7a01acSMauro Carvalho Chehab 	return buf[reg];
85cb7a01acSMauro Carvalho Chehab }
86cb7a01acSMauro Carvalho Chehab #endif
87cb7a01acSMauro Carvalho Chehab 
88cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
89cb7a01acSMauro Carvalho Chehab 
upd64083_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)90cb7a01acSMauro Carvalho Chehab static int upd64083_s_routing(struct v4l2_subdev *sd,
91cb7a01acSMauro Carvalho Chehab 			      u32 input, u32 output, u32 config)
92cb7a01acSMauro Carvalho Chehab {
93cb7a01acSMauro Carvalho Chehab 	struct upd64083_state *state = to_state(sd);
94cb7a01acSMauro Carvalho Chehab 	u8 r00, r02;
95cb7a01acSMauro Carvalho Chehab 
96cb7a01acSMauro Carvalho Chehab 	if (input > 7 || (input & 6) == 6)
97cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
98cb7a01acSMauro Carvalho Chehab 	state->mode = (input & 3) << 6;
99cb7a01acSMauro Carvalho Chehab 	state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3;
100cb7a01acSMauro Carvalho Chehab 	r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
101cb7a01acSMauro Carvalho Chehab 	r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
102cb7a01acSMauro Carvalho Chehab 	upd64083_write(sd, R00, r00);
103cb7a01acSMauro Carvalho Chehab 	upd64083_write(sd, R02, r02);
104cb7a01acSMauro Carvalho Chehab 	return 0;
105cb7a01acSMauro Carvalho Chehab }
106cb7a01acSMauro Carvalho Chehab 
107cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
upd64083_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)108cb7a01acSMauro Carvalho Chehab static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
109cb7a01acSMauro Carvalho Chehab {
110cb7a01acSMauro Carvalho Chehab 	reg->val = upd64083_read(sd, reg->reg & 0xff);
111cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
112cb7a01acSMauro Carvalho Chehab 	return 0;
113cb7a01acSMauro Carvalho Chehab }
114cb7a01acSMauro Carvalho Chehab 
upd64083_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)115977ba3b1SHans Verkuil static int upd64083_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
116cb7a01acSMauro Carvalho Chehab {
117cb7a01acSMauro Carvalho Chehab 	upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
118cb7a01acSMauro Carvalho Chehab 	return 0;
119cb7a01acSMauro Carvalho Chehab }
120cb7a01acSMauro Carvalho Chehab #endif
121cb7a01acSMauro Carvalho Chehab 
upd64083_log_status(struct v4l2_subdev * sd)122cb7a01acSMauro Carvalho Chehab static int upd64083_log_status(struct v4l2_subdev *sd)
123cb7a01acSMauro Carvalho Chehab {
124cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
125cb7a01acSMauro Carvalho Chehab 	u8 buf[7];
126cb7a01acSMauro Carvalho Chehab 
127cb7a01acSMauro Carvalho Chehab 	i2c_master_recv(client, buf, 7);
128cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
129cb7a01acSMauro Carvalho Chehab 		      "SA04=%02x SA05=%02x SA06=%02x\n",
130cb7a01acSMauro Carvalho Chehab 		buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
131cb7a01acSMauro Carvalho Chehab 	return 0;
132cb7a01acSMauro Carvalho Chehab }
133cb7a01acSMauro Carvalho Chehab 
134cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
135cb7a01acSMauro Carvalho Chehab 
136cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops upd64083_core_ops = {
137cb7a01acSMauro Carvalho Chehab 	.log_status = upd64083_log_status,
138cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
139cb7a01acSMauro Carvalho Chehab 	.g_register = upd64083_g_register,
140cb7a01acSMauro Carvalho Chehab 	.s_register = upd64083_s_register,
141cb7a01acSMauro Carvalho Chehab #endif
142cb7a01acSMauro Carvalho Chehab };
143cb7a01acSMauro Carvalho Chehab 
144cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops upd64083_video_ops = {
145cb7a01acSMauro Carvalho Chehab 	.s_routing = upd64083_s_routing,
146cb7a01acSMauro Carvalho Chehab };
147cb7a01acSMauro Carvalho Chehab 
148cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops upd64083_ops = {
149cb7a01acSMauro Carvalho Chehab 	.core = &upd64083_core_ops,
150cb7a01acSMauro Carvalho Chehab 	.video = &upd64083_video_ops,
151cb7a01acSMauro Carvalho Chehab };
152cb7a01acSMauro Carvalho Chehab 
153cb7a01acSMauro Carvalho Chehab /* ------------------------------------------------------------------------ */
154cb7a01acSMauro Carvalho Chehab 
155cb7a01acSMauro Carvalho Chehab /* i2c implementation */
156cb7a01acSMauro Carvalho Chehab 
upd64083_probe(struct i2c_client * client)157bd38d137SUwe Kleine-König static int upd64083_probe(struct i2c_client *client)
158cb7a01acSMauro Carvalho Chehab {
159cb7a01acSMauro Carvalho Chehab 	struct upd64083_state *state;
160cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
161cb7a01acSMauro Carvalho Chehab 	int i;
162cb7a01acSMauro Carvalho Chehab 
163cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
164cb7a01acSMauro Carvalho Chehab 		return -EIO;
165cb7a01acSMauro Carvalho Chehab 
166cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
167cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
168cb7a01acSMauro Carvalho Chehab 
169c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
170cb7a01acSMauro Carvalho Chehab 	if (state == NULL)
171cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
172cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
173cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &upd64083_ops);
174cb7a01acSMauro Carvalho Chehab 	/* Initially assume that a ghost reduction chip is present */
175cb7a01acSMauro Carvalho Chehab 	state->mode = 0;  /* YCS mode */
176cb7a01acSMauro Carvalho Chehab 	state->ext_y_adc = (1 << 5);
177cb7a01acSMauro Carvalho Chehab 	memcpy(state->regs, upd64083_init, TOT_REGS);
178cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < TOT_REGS; i++)
179cb7a01acSMauro Carvalho Chehab 		upd64083_write(sd, i, state->regs[i]);
180cb7a01acSMauro Carvalho Chehab 	return 0;
181cb7a01acSMauro Carvalho Chehab }
182cb7a01acSMauro Carvalho Chehab 
upd64083_remove(struct i2c_client * client)183ed5c2f5fSUwe Kleine-König static void upd64083_remove(struct i2c_client *client)
184cb7a01acSMauro Carvalho Chehab {
185cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
188cb7a01acSMauro Carvalho Chehab }
189cb7a01acSMauro Carvalho Chehab 
190cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
191cb7a01acSMauro Carvalho Chehab 
192cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id upd64083_id[] = {
193cb7a01acSMauro Carvalho Chehab 	{ "upd64083", 0 },
194cb7a01acSMauro Carvalho Chehab 	{ }
195cb7a01acSMauro Carvalho Chehab };
196cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, upd64083_id);
197cb7a01acSMauro Carvalho Chehab 
198cb7a01acSMauro Carvalho Chehab static struct i2c_driver upd64083_driver = {
199cb7a01acSMauro Carvalho Chehab 	.driver = {
200cb7a01acSMauro Carvalho Chehab 		.name	= "upd64083",
201cb7a01acSMauro Carvalho Chehab 	},
202*aaeb31c0SUwe Kleine-König 	.probe		= upd64083_probe,
203cb7a01acSMauro Carvalho Chehab 	.remove		= upd64083_remove,
204cb7a01acSMauro Carvalho Chehab 	.id_table	= upd64083_id,
205cb7a01acSMauro Carvalho Chehab };
206cb7a01acSMauro Carvalho Chehab 
207cb7a01acSMauro Carvalho Chehab module_i2c_driver(upd64083_driver);
208