174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab tea6420 - i2c-driver for the tea6420 by SGS Thomson
4cb7a01acSMauro Carvalho Chehab
5cb7a01acSMauro Carvalho Chehab Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
6cb7a01acSMauro Carvalho Chehab Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
7cb7a01acSMauro Carvalho Chehab
8cb7a01acSMauro Carvalho Chehab The tea6420 is a bus controlled audio-matrix with 5 stereo inputs,
9cb7a01acSMauro Carvalho Chehab 4 stereo outputs and gain control for each output.
10cb7a01acSMauro Carvalho Chehab It is cascadable, i.e. it can be found at the addresses 0x98
11cb7a01acSMauro Carvalho Chehab and 0x9a on the i2c-bus.
12cb7a01acSMauro Carvalho Chehab
13f8a7647dSMauro Carvalho Chehab For detailed information download the specifications directly
14cb7a01acSMauro Carvalho Chehab from SGS Thomson at http://www.st.com
15cb7a01acSMauro Carvalho Chehab
16cb7a01acSMauro Carvalho Chehab */
17cb7a01acSMauro Carvalho Chehab
18cb7a01acSMauro Carvalho Chehab
19cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
20cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
21cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
22cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
23cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
24cb7a01acSMauro Carvalho Chehab #include "tea6420.h"
25cb7a01acSMauro Carvalho Chehab
26cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
27cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("tea6420 driver");
28cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
29cb7a01acSMauro Carvalho Chehab
30cb7a01acSMauro Carvalho Chehab static int debug;
31cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0644);
32cb7a01acSMauro Carvalho Chehab
33cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
34cb7a01acSMauro Carvalho Chehab
35cb7a01acSMauro Carvalho Chehab
36cb7a01acSMauro Carvalho Chehab /* make a connection between the input 'i' and the output 'o'
37cb7a01acSMauro Carvalho Chehab with gain 'g' (note: i = 6 means 'mute') */
tea6420_s_routing(struct v4l2_subdev * sd,u32 i,u32 o,u32 config)38cb7a01acSMauro Carvalho Chehab static int tea6420_s_routing(struct v4l2_subdev *sd,
39cb7a01acSMauro Carvalho Chehab u32 i, u32 o, u32 config)
40cb7a01acSMauro Carvalho Chehab {
41cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd);
42cb7a01acSMauro Carvalho Chehab int g = (o >> 4) & 0xf;
43cb7a01acSMauro Carvalho Chehab u8 byte;
44cb7a01acSMauro Carvalho Chehab int ret;
45cb7a01acSMauro Carvalho Chehab
46cb7a01acSMauro Carvalho Chehab o &= 0xf;
47cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g);
48cb7a01acSMauro Carvalho Chehab
49cb7a01acSMauro Carvalho Chehab /* check if the parameters are valid */
50cb7a01acSMauro Carvalho Chehab if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
51cb7a01acSMauro Carvalho Chehab return -EINVAL;
52cb7a01acSMauro Carvalho Chehab
53cb7a01acSMauro Carvalho Chehab byte = ((o - 1) << 5);
54cb7a01acSMauro Carvalho Chehab byte |= (i - 1);
55cb7a01acSMauro Carvalho Chehab
56cb7a01acSMauro Carvalho Chehab /* to understand this, have a look at the tea6420-specs (p.5) */
57cb7a01acSMauro Carvalho Chehab switch (g) {
58cb7a01acSMauro Carvalho Chehab case 0:
59cb7a01acSMauro Carvalho Chehab byte |= (3 << 3);
60cb7a01acSMauro Carvalho Chehab break;
61cb7a01acSMauro Carvalho Chehab case 2:
62cb7a01acSMauro Carvalho Chehab byte |= (2 << 3);
63cb7a01acSMauro Carvalho Chehab break;
64cb7a01acSMauro Carvalho Chehab case 4:
65cb7a01acSMauro Carvalho Chehab byte |= (1 << 3);
66cb7a01acSMauro Carvalho Chehab break;
67cb7a01acSMauro Carvalho Chehab case 6:
68cb7a01acSMauro Carvalho Chehab break;
69cb7a01acSMauro Carvalho Chehab }
70cb7a01acSMauro Carvalho Chehab
71cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_write_byte(client, byte);
72cb7a01acSMauro Carvalho Chehab if (ret) {
73cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd,
74cb7a01acSMauro Carvalho Chehab "i2c_smbus_write_byte() failed, ret:%d\n", ret);
75cb7a01acSMauro Carvalho Chehab return -EIO;
76cb7a01acSMauro Carvalho Chehab }
77cb7a01acSMauro Carvalho Chehab return 0;
78cb7a01acSMauro Carvalho Chehab }
79cb7a01acSMauro Carvalho Chehab
80cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
81cb7a01acSMauro Carvalho Chehab
82cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_audio_ops tea6420_audio_ops = {
83cb7a01acSMauro Carvalho Chehab .s_routing = tea6420_s_routing,
84cb7a01acSMauro Carvalho Chehab };
85cb7a01acSMauro Carvalho Chehab
86cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops tea6420_ops = {
87cb7a01acSMauro Carvalho Chehab .audio = &tea6420_audio_ops,
88cb7a01acSMauro Carvalho Chehab };
89cb7a01acSMauro Carvalho Chehab
tea6420_probe(struct i2c_client * client)908569336fSUwe Kleine-König static int tea6420_probe(struct i2c_client *client)
91cb7a01acSMauro Carvalho Chehab {
92cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd;
93cb7a01acSMauro Carvalho Chehab int err, i;
94cb7a01acSMauro Carvalho Chehab
95cb7a01acSMauro Carvalho Chehab /* let's see whether this adapter can support what we need */
96cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
97cb7a01acSMauro Carvalho Chehab return -EIO;
98cb7a01acSMauro Carvalho Chehab
99cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%x (%s)\n",
100cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name);
101cb7a01acSMauro Carvalho Chehab
102c02b211dSLaurent Pinchart sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
103cb7a01acSMauro Carvalho Chehab if (sd == NULL)
104cb7a01acSMauro Carvalho Chehab return -ENOMEM;
105cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
106cb7a01acSMauro Carvalho Chehab
107cb7a01acSMauro Carvalho Chehab /* set initial values: set "mute"-input to all outputs at gain 0 */
108cb7a01acSMauro Carvalho Chehab err = 0;
109cb7a01acSMauro Carvalho Chehab for (i = 1; i < 5; i++)
110cb7a01acSMauro Carvalho Chehab err += tea6420_s_routing(sd, 6, i, 0);
111cb7a01acSMauro Carvalho Chehab if (err) {
112cb7a01acSMauro Carvalho Chehab v4l_dbg(1, debug, client, "could not initialize tea6420\n");
113cb7a01acSMauro Carvalho Chehab return -ENODEV;
114cb7a01acSMauro Carvalho Chehab }
115cb7a01acSMauro Carvalho Chehab return 0;
116cb7a01acSMauro Carvalho Chehab }
117cb7a01acSMauro Carvalho Chehab
tea6420_remove(struct i2c_client * client)118ed5c2f5fSUwe Kleine-König static void tea6420_remove(struct i2c_client *client)
119cb7a01acSMauro Carvalho Chehab {
120cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client);
121cb7a01acSMauro Carvalho Chehab
122cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd);
123cb7a01acSMauro Carvalho Chehab }
124cb7a01acSMauro Carvalho Chehab
125cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id tea6420_id[] = {
126cb7a01acSMauro Carvalho Chehab { "tea6420", 0 },
127cb7a01acSMauro Carvalho Chehab { }
128cb7a01acSMauro Carvalho Chehab };
129cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, tea6420_id);
130cb7a01acSMauro Carvalho Chehab
131cb7a01acSMauro Carvalho Chehab static struct i2c_driver tea6420_driver = {
132cb7a01acSMauro Carvalho Chehab .driver = {
133cb7a01acSMauro Carvalho Chehab .name = "tea6420",
134cb7a01acSMauro Carvalho Chehab },
135*aaeb31c0SUwe Kleine-König .probe = tea6420_probe,
136cb7a01acSMauro Carvalho Chehab .remove = tea6420_remove,
137cb7a01acSMauro Carvalho Chehab .id_table = tea6420_id,
138cb7a01acSMauro Carvalho Chehab };
139cb7a01acSMauro Carvalho Chehab
140cb7a01acSMauro Carvalho Chehab module_i2c_driver(tea6420_driver);
141