xref: /openbmc/linux/drivers/media/i2c/tea6420.c (revision aaeb31c0)
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