xref: /openbmc/linux/drivers/media/i2c/cs53l32a.c (revision cb7a01ac)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver.
3cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2005  Martin Vaughan
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Audio source switching for Adaptec AVC-2410 added by Trev Jackson
6cb7a01acSMauro Carvalho Chehab  *
7cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
8cb7a01acSMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
9cb7a01acSMauro Carvalho Chehab  * the Free Software Foundation; either version 2 of the License, or
10cb7a01acSMauro Carvalho Chehab  * (at your option) any later version.
11cb7a01acSMauro Carvalho Chehab  *
12cb7a01acSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
13cb7a01acSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cb7a01acSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
16cb7a01acSMauro Carvalho Chehab  *
17cb7a01acSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
18cb7a01acSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
19cb7a01acSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20cb7a01acSMauro Carvalho Chehab  */
21cb7a01acSMauro Carvalho Chehab 
22cb7a01acSMauro Carvalho Chehab 
23cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
24cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
26cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
27cb7a01acSMauro Carvalho Chehab #include <asm/uaccess.h>
28cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
29cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h>
32cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
35cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Martin Vaughan");
36cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab static bool debug;
39cb7a01acSMauro Carvalho Chehab 
40cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
41cb7a01acSMauro Carvalho Chehab 
42cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
43cb7a01acSMauro Carvalho Chehab 
44cb7a01acSMauro Carvalho Chehab 
45cb7a01acSMauro Carvalho Chehab struct cs53l32a_state {
46cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
47cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
48cb7a01acSMauro Carvalho Chehab };
49cb7a01acSMauro Carvalho Chehab 
50cb7a01acSMauro Carvalho Chehab static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
51cb7a01acSMauro Carvalho Chehab {
52cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct cs53l32a_state, sd);
53cb7a01acSMauro Carvalho Chehab }
54cb7a01acSMauro Carvalho Chehab 
55cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
56cb7a01acSMauro Carvalho Chehab {
57cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
58cb7a01acSMauro Carvalho Chehab }
59cb7a01acSMauro Carvalho Chehab 
60cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
61cb7a01acSMauro Carvalho Chehab 
62cb7a01acSMauro Carvalho Chehab static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
63cb7a01acSMauro Carvalho Chehab {
64cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
65cb7a01acSMauro Carvalho Chehab 
66cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
67cb7a01acSMauro Carvalho Chehab }
68cb7a01acSMauro Carvalho Chehab 
69cb7a01acSMauro Carvalho Chehab static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
70cb7a01acSMauro Carvalho Chehab {
71cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
72cb7a01acSMauro Carvalho Chehab 
73cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
74cb7a01acSMauro Carvalho Chehab }
75cb7a01acSMauro Carvalho Chehab 
76cb7a01acSMauro Carvalho Chehab static int cs53l32a_s_routing(struct v4l2_subdev *sd,
77cb7a01acSMauro Carvalho Chehab 			      u32 input, u32 output, u32 config)
78cb7a01acSMauro Carvalho Chehab {
79cb7a01acSMauro Carvalho Chehab 	/* There are 2 physical inputs, but the second input can be
80cb7a01acSMauro Carvalho Chehab 	   placed in two modes, the first mode bypasses the PGA (gain),
81cb7a01acSMauro Carvalho Chehab 	   the second goes through the PGA. Hence there are three
82cb7a01acSMauro Carvalho Chehab 	   possible inputs to choose from. */
83cb7a01acSMauro Carvalho Chehab 	if (input > 2) {
84cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "Invalid input %d.\n", input);
85cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
86cb7a01acSMauro Carvalho Chehab 	}
87cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x01, 0x01 + (input << 4));
88cb7a01acSMauro Carvalho Chehab 	return 0;
89cb7a01acSMauro Carvalho Chehab }
90cb7a01acSMauro Carvalho Chehab 
91cb7a01acSMauro Carvalho Chehab static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
92cb7a01acSMauro Carvalho Chehab {
93cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
94cb7a01acSMauro Carvalho Chehab 
95cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
96cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_MUTE:
97cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
98cb7a01acSMauro Carvalho Chehab 		return 0;
99cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
100cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x04, (u8)ctrl->val);
101cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x05, (u8)ctrl->val);
102cb7a01acSMauro Carvalho Chehab 		return 0;
103cb7a01acSMauro Carvalho Chehab 	}
104cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
105cb7a01acSMauro Carvalho Chehab }
106cb7a01acSMauro Carvalho Chehab 
107cb7a01acSMauro Carvalho Chehab static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
108cb7a01acSMauro Carvalho Chehab {
109cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
110cb7a01acSMauro Carvalho Chehab 
111cb7a01acSMauro Carvalho Chehab 	return v4l2_chip_ident_i2c_client(client,
112cb7a01acSMauro Carvalho Chehab 			chip, V4L2_IDENT_CS53l32A, 0);
113cb7a01acSMauro Carvalho Chehab }
114cb7a01acSMauro Carvalho Chehab 
115cb7a01acSMauro Carvalho Chehab static int cs53l32a_log_status(struct v4l2_subdev *sd)
116cb7a01acSMauro Carvalho Chehab {
117cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state = to_state(sd);
118cb7a01acSMauro Carvalho Chehab 	u8 v = cs53l32a_read(sd, 0x01);
119cb7a01acSMauro Carvalho Chehab 
120cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Input:  %d\n", (v >> 4) & 3);
121cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
122cb7a01acSMauro Carvalho Chehab 	return 0;
123cb7a01acSMauro Carvalho Chehab }
124cb7a01acSMauro Carvalho Chehab 
125cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
126cb7a01acSMauro Carvalho Chehab 
127cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
128cb7a01acSMauro Carvalho Chehab 	.s_ctrl = cs53l32a_s_ctrl,
129cb7a01acSMauro Carvalho Chehab };
130cb7a01acSMauro Carvalho Chehab 
131cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
132cb7a01acSMauro Carvalho Chehab 	.log_status = cs53l32a_log_status,
133cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = cs53l32a_g_chip_ident,
134cb7a01acSMauro Carvalho Chehab 	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
135cb7a01acSMauro Carvalho Chehab 	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
136cb7a01acSMauro Carvalho Chehab 	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
137cb7a01acSMauro Carvalho Chehab 	.g_ctrl = v4l2_subdev_g_ctrl,
138cb7a01acSMauro Carvalho Chehab 	.s_ctrl = v4l2_subdev_s_ctrl,
139cb7a01acSMauro Carvalho Chehab 	.queryctrl = v4l2_subdev_queryctrl,
140cb7a01acSMauro Carvalho Chehab 	.querymenu = v4l2_subdev_querymenu,
141cb7a01acSMauro Carvalho Chehab };
142cb7a01acSMauro Carvalho Chehab 
143cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
144cb7a01acSMauro Carvalho Chehab 	.s_routing = cs53l32a_s_routing,
145cb7a01acSMauro Carvalho Chehab };
146cb7a01acSMauro Carvalho Chehab 
147cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops cs53l32a_ops = {
148cb7a01acSMauro Carvalho Chehab 	.core = &cs53l32a_core_ops,
149cb7a01acSMauro Carvalho Chehab 	.audio = &cs53l32a_audio_ops,
150cb7a01acSMauro Carvalho Chehab };
151cb7a01acSMauro Carvalho Chehab 
152cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
153cb7a01acSMauro Carvalho Chehab 
154cb7a01acSMauro Carvalho Chehab /* i2c implementation */
155cb7a01acSMauro Carvalho Chehab 
156cb7a01acSMauro Carvalho Chehab /*
157cb7a01acSMauro Carvalho Chehab  * Generic i2c probe
158cb7a01acSMauro Carvalho Chehab  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
159cb7a01acSMauro Carvalho Chehab  */
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab static int cs53l32a_probe(struct i2c_client *client,
162cb7a01acSMauro Carvalho Chehab 			  const struct i2c_device_id *id)
163cb7a01acSMauro Carvalho Chehab {
164cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state;
165cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
166cb7a01acSMauro Carvalho Chehab 	int i;
167cb7a01acSMauro Carvalho Chehab 
168cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
169cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
170cb7a01acSMauro Carvalho Chehab 		return -EIO;
171cb7a01acSMauro Carvalho Chehab 
172cb7a01acSMauro Carvalho Chehab 	if (!id)
173cb7a01acSMauro Carvalho Chehab 		strlcpy(client->name, "cs53l32a", sizeof(client->name));
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
176cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
177cb7a01acSMauro Carvalho Chehab 
178cb7a01acSMauro Carvalho Chehab 	state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
179cb7a01acSMauro Carvalho Chehab 	if (state == NULL)
180cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
181cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
182cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
183cb7a01acSMauro Carvalho Chehab 
184cb7a01acSMauro Carvalho Chehab 	for (i = 1; i <= 7; i++) {
185cb7a01acSMauro Carvalho Chehab 		u8 v = cs53l32a_read(sd, i);
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
188cb7a01acSMauro Carvalho Chehab 	}
189cb7a01acSMauro Carvalho Chehab 
190cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->hdl, 2);
191cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
192cb7a01acSMauro Carvalho Chehab 			V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
193cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
194cb7a01acSMauro Carvalho Chehab 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
195cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &state->hdl;
196cb7a01acSMauro Carvalho Chehab 	if (state->hdl.error) {
197cb7a01acSMauro Carvalho Chehab 		int err = state->hdl.error;
198cb7a01acSMauro Carvalho Chehab 
199cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->hdl);
200cb7a01acSMauro Carvalho Chehab 		kfree(state);
201cb7a01acSMauro Carvalho Chehab 		return err;
202cb7a01acSMauro Carvalho Chehab 	}
203cb7a01acSMauro Carvalho Chehab 
204cb7a01acSMauro Carvalho Chehab 	/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
205cb7a01acSMauro Carvalho Chehab 
206cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x01, 0x21);
207cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x02, 0x29);
208cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x03, 0x30);
209cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x04, 0x00);
210cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x05, 0x00);
211cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x06, 0x00);
212cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x07, 0x00);
213cb7a01acSMauro Carvalho Chehab 
214cb7a01acSMauro Carvalho Chehab 	/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
215cb7a01acSMauro Carvalho Chehab 
216cb7a01acSMauro Carvalho Chehab 	for (i = 1; i <= 7; i++) {
217cb7a01acSMauro Carvalho Chehab 		u8 v = cs53l32a_read(sd, i);
218cb7a01acSMauro Carvalho Chehab 
219cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
220cb7a01acSMauro Carvalho Chehab 	}
221cb7a01acSMauro Carvalho Chehab 	return 0;
222cb7a01acSMauro Carvalho Chehab }
223cb7a01acSMauro Carvalho Chehab 
224cb7a01acSMauro Carvalho Chehab static int cs53l32a_remove(struct i2c_client *client)
225cb7a01acSMauro Carvalho Chehab {
226cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
227cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state = to_state(sd);
228cb7a01acSMauro Carvalho Chehab 
229cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
230cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->hdl);
231cb7a01acSMauro Carvalho Chehab 	kfree(state);
232cb7a01acSMauro Carvalho Chehab 	return 0;
233cb7a01acSMauro Carvalho Chehab }
234cb7a01acSMauro Carvalho Chehab 
235cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id cs53l32a_id[] = {
236cb7a01acSMauro Carvalho Chehab 	{ "cs53l32a", 0 },
237cb7a01acSMauro Carvalho Chehab 	{ }
238cb7a01acSMauro Carvalho Chehab };
239cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
240cb7a01acSMauro Carvalho Chehab 
241cb7a01acSMauro Carvalho Chehab static struct i2c_driver cs53l32a_driver = {
242cb7a01acSMauro Carvalho Chehab 	.driver = {
243cb7a01acSMauro Carvalho Chehab 		.owner	= THIS_MODULE,
244cb7a01acSMauro Carvalho Chehab 		.name	= "cs53l32a",
245cb7a01acSMauro Carvalho Chehab 	},
246cb7a01acSMauro Carvalho Chehab 	.probe		= cs53l32a_probe,
247cb7a01acSMauro Carvalho Chehab 	.remove		= cs53l32a_remove,
248cb7a01acSMauro Carvalho Chehab 	.id_table	= cs53l32a_id,
249cb7a01acSMauro Carvalho Chehab };
250cb7a01acSMauro Carvalho Chehab 
251cb7a01acSMauro Carvalho Chehab module_i2c_driver(cs53l32a_driver);
252