xref: /openbmc/linux/drivers/media/i2c/cs53l32a.c (revision aaeb31c0)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver.
4cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2005  Martin Vaughan
5cb7a01acSMauro Carvalho Chehab  *
6cb7a01acSMauro Carvalho Chehab  * Audio source switching for Adaptec AVC-2410 added by Trev Jackson
7cb7a01acSMauro Carvalho Chehab  */
8cb7a01acSMauro Carvalho Chehab 
9cb7a01acSMauro Carvalho Chehab 
10cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
147c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
17cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
18cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
19cb7a01acSMauro Carvalho Chehab 
20cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
21cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Martin Vaughan");
22cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
23cb7a01acSMauro Carvalho Chehab 
24cb7a01acSMauro Carvalho Chehab static bool debug;
25cb7a01acSMauro Carvalho Chehab 
26cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
29cb7a01acSMauro Carvalho Chehab 
30cb7a01acSMauro Carvalho Chehab 
31cb7a01acSMauro Carvalho Chehab struct cs53l32a_state {
32cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
33cb7a01acSMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
34cb7a01acSMauro Carvalho Chehab };
35cb7a01acSMauro Carvalho Chehab 
to_state(struct v4l2_subdev * sd)36cb7a01acSMauro Carvalho Chehab static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
37cb7a01acSMauro Carvalho Chehab {
38cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct cs53l32a_state, sd);
39cb7a01acSMauro Carvalho Chehab }
40cb7a01acSMauro Carvalho Chehab 
to_sd(struct v4l2_ctrl * ctrl)41cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
42cb7a01acSMauro Carvalho Chehab {
43cb7a01acSMauro Carvalho Chehab 	return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
44cb7a01acSMauro Carvalho Chehab }
45cb7a01acSMauro Carvalho Chehab 
46cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
47cb7a01acSMauro Carvalho Chehab 
cs53l32a_write(struct v4l2_subdev * sd,u8 reg,u8 value)48cb7a01acSMauro Carvalho Chehab static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
49cb7a01acSMauro Carvalho Chehab {
50cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
51cb7a01acSMauro Carvalho Chehab 
52cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_write_byte_data(client, reg, value);
53cb7a01acSMauro Carvalho Chehab }
54cb7a01acSMauro Carvalho Chehab 
cs53l32a_read(struct v4l2_subdev * sd,u8 reg)55cb7a01acSMauro Carvalho Chehab static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
56cb7a01acSMauro Carvalho Chehab {
57cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
58cb7a01acSMauro Carvalho Chehab 
59cb7a01acSMauro Carvalho Chehab 	return i2c_smbus_read_byte_data(client, reg);
60cb7a01acSMauro Carvalho Chehab }
61cb7a01acSMauro Carvalho Chehab 
cs53l32a_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)62cb7a01acSMauro Carvalho Chehab static int cs53l32a_s_routing(struct v4l2_subdev *sd,
63cb7a01acSMauro Carvalho Chehab 			      u32 input, u32 output, u32 config)
64cb7a01acSMauro Carvalho Chehab {
65cb7a01acSMauro Carvalho Chehab 	/* There are 2 physical inputs, but the second input can be
66cb7a01acSMauro Carvalho Chehab 	   placed in two modes, the first mode bypasses the PGA (gain),
67cb7a01acSMauro Carvalho Chehab 	   the second goes through the PGA. Hence there are three
68cb7a01acSMauro Carvalho Chehab 	   possible inputs to choose from. */
69cb7a01acSMauro Carvalho Chehab 	if (input > 2) {
70cb7a01acSMauro Carvalho Chehab 		v4l2_err(sd, "Invalid input %d.\n", input);
71cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
72cb7a01acSMauro Carvalho Chehab 	}
73cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x01, 0x01 + (input << 4));
74cb7a01acSMauro Carvalho Chehab 	return 0;
75cb7a01acSMauro Carvalho Chehab }
76cb7a01acSMauro Carvalho Chehab 
cs53l32a_s_ctrl(struct v4l2_ctrl * ctrl)77cb7a01acSMauro Carvalho Chehab static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
78cb7a01acSMauro Carvalho Chehab {
79cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = to_sd(ctrl);
80cb7a01acSMauro Carvalho Chehab 
81cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
82cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_MUTE:
83cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
84cb7a01acSMauro Carvalho Chehab 		return 0;
85cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
86cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x04, (u8)ctrl->val);
87cb7a01acSMauro Carvalho Chehab 		cs53l32a_write(sd, 0x05, (u8)ctrl->val);
88cb7a01acSMauro Carvalho Chehab 		return 0;
89cb7a01acSMauro Carvalho Chehab 	}
90cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
91cb7a01acSMauro Carvalho Chehab }
92cb7a01acSMauro Carvalho Chehab 
cs53l32a_log_status(struct v4l2_subdev * sd)93cb7a01acSMauro Carvalho Chehab static int cs53l32a_log_status(struct v4l2_subdev *sd)
94cb7a01acSMauro Carvalho Chehab {
95cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state = to_state(sd);
96cb7a01acSMauro Carvalho Chehab 	u8 v = cs53l32a_read(sd, 0x01);
97cb7a01acSMauro Carvalho Chehab 
98cb7a01acSMauro Carvalho Chehab 	v4l2_info(sd, "Input:  %d\n", (v >> 4) & 3);
99cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
100cb7a01acSMauro Carvalho Chehab 	return 0;
101cb7a01acSMauro Carvalho Chehab }
102cb7a01acSMauro Carvalho Chehab 
103cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
104cb7a01acSMauro Carvalho Chehab 
105cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
106cb7a01acSMauro Carvalho Chehab 	.s_ctrl = cs53l32a_s_ctrl,
107cb7a01acSMauro Carvalho Chehab };
108cb7a01acSMauro Carvalho Chehab 
109cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
110cb7a01acSMauro Carvalho Chehab 	.log_status = cs53l32a_log_status,
111cb7a01acSMauro Carvalho Chehab };
112cb7a01acSMauro Carvalho Chehab 
113cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
114cb7a01acSMauro Carvalho Chehab 	.s_routing = cs53l32a_s_routing,
115cb7a01acSMauro Carvalho Chehab };
116cb7a01acSMauro Carvalho Chehab 
117cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops cs53l32a_ops = {
118cb7a01acSMauro Carvalho Chehab 	.core = &cs53l32a_core_ops,
119cb7a01acSMauro Carvalho Chehab 	.audio = &cs53l32a_audio_ops,
120cb7a01acSMauro Carvalho Chehab };
121cb7a01acSMauro Carvalho Chehab 
122cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
123cb7a01acSMauro Carvalho Chehab 
124cb7a01acSMauro Carvalho Chehab /* i2c implementation */
125cb7a01acSMauro Carvalho Chehab 
126cb7a01acSMauro Carvalho Chehab /*
127cb7a01acSMauro Carvalho Chehab  * Generic i2c probe
128cb7a01acSMauro Carvalho Chehab  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
129cb7a01acSMauro Carvalho Chehab  */
130cb7a01acSMauro Carvalho Chehab 
cs53l32a_probe(struct i2c_client * client)1314b215eebSUwe Kleine-König static int cs53l32a_probe(struct i2c_client *client)
132cb7a01acSMauro Carvalho Chehab {
1334b215eebSUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
134cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state;
135cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
136cb7a01acSMauro Carvalho Chehab 	int i;
137cb7a01acSMauro Carvalho Chehab 
138cb7a01acSMauro Carvalho Chehab 	/* Check if the adapter supports the needed features */
139cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
140cb7a01acSMauro Carvalho Chehab 		return -EIO;
141cb7a01acSMauro Carvalho Chehab 
142cb7a01acSMauro Carvalho Chehab 	if (!id)
143c0decac1SMauro Carvalho Chehab 		strscpy(client->name, "cs53l32a", sizeof(client->name));
144cb7a01acSMauro Carvalho Chehab 
145cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
146cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
147cb7a01acSMauro Carvalho Chehab 
148c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
149cb7a01acSMauro Carvalho Chehab 	if (state == NULL)
150cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
151cb7a01acSMauro Carvalho Chehab 	sd = &state->sd;
152cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
153cb7a01acSMauro Carvalho Chehab 
154cb7a01acSMauro Carvalho Chehab 	for (i = 1; i <= 7; i++) {
155cb7a01acSMauro Carvalho Chehab 		u8 v = cs53l32a_read(sd, i);
156cb7a01acSMauro Carvalho Chehab 
157cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
158cb7a01acSMauro Carvalho Chehab 	}
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&state->hdl, 2);
161cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
162cb7a01acSMauro Carvalho Chehab 			V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
163cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
164cb7a01acSMauro Carvalho Chehab 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
165cb7a01acSMauro Carvalho Chehab 	sd->ctrl_handler = &state->hdl;
166cb7a01acSMauro Carvalho Chehab 	if (state->hdl.error) {
167cb7a01acSMauro Carvalho Chehab 		int err = state->hdl.error;
168cb7a01acSMauro Carvalho Chehab 
169cb7a01acSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&state->hdl);
170cb7a01acSMauro Carvalho Chehab 		return err;
171cb7a01acSMauro Carvalho Chehab 	}
172cb7a01acSMauro Carvalho Chehab 
173cb7a01acSMauro Carvalho Chehab 	/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x01, 0x21);
176cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x02, 0x29);
177cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x03, 0x30);
178cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x04, 0x00);
179cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x05, 0x00);
180cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x06, 0x00);
181cb7a01acSMauro Carvalho Chehab 	cs53l32a_write(sd, 0x07, 0x00);
182cb7a01acSMauro Carvalho Chehab 
183cb7a01acSMauro Carvalho Chehab 	/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
184cb7a01acSMauro Carvalho Chehab 
185cb7a01acSMauro Carvalho Chehab 	for (i = 1; i <= 7; i++) {
186cb7a01acSMauro Carvalho Chehab 		u8 v = cs53l32a_read(sd, i);
187cb7a01acSMauro Carvalho Chehab 
188cb7a01acSMauro Carvalho Chehab 		v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
189cb7a01acSMauro Carvalho Chehab 	}
190cb7a01acSMauro Carvalho Chehab 	return 0;
191cb7a01acSMauro Carvalho Chehab }
192cb7a01acSMauro Carvalho Chehab 
cs53l32a_remove(struct i2c_client * client)193ed5c2f5fSUwe Kleine-König static void cs53l32a_remove(struct i2c_client *client)
194cb7a01acSMauro Carvalho Chehab {
195cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
196cb7a01acSMauro Carvalho Chehab 	struct cs53l32a_state *state = to_state(sd);
197cb7a01acSMauro Carvalho Chehab 
198cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
199cb7a01acSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&state->hdl);
200cb7a01acSMauro Carvalho Chehab }
201cb7a01acSMauro Carvalho Chehab 
202cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id cs53l32a_id[] = {
203cb7a01acSMauro Carvalho Chehab 	{ "cs53l32a", 0 },
204cb7a01acSMauro Carvalho Chehab 	{ }
205cb7a01acSMauro Carvalho Chehab };
206cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
207cb7a01acSMauro Carvalho Chehab 
208cb7a01acSMauro Carvalho Chehab static struct i2c_driver cs53l32a_driver = {
209cb7a01acSMauro Carvalho Chehab 	.driver = {
210cb7a01acSMauro Carvalho Chehab 		.name	= "cs53l32a",
211cb7a01acSMauro Carvalho Chehab 	},
212*aaeb31c0SUwe Kleine-König 	.probe		= cs53l32a_probe,
213cb7a01acSMauro Carvalho Chehab 	.remove		= cs53l32a_remove,
214cb7a01acSMauro Carvalho Chehab 	.id_table	= cs53l32a_id,
215cb7a01acSMauro Carvalho Chehab };
216cb7a01acSMauro Carvalho Chehab 
217cb7a01acSMauro Carvalho Chehab module_i2c_driver(cs53l32a_driver);
218