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