11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tea6415c - i2c-driver for the tea6415c by SGS Thomson
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
5a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil    Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    The tea6415c is a bus controlled video-matrix-switch
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    with 8 inputs and 6 outputs.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    It is cascadable, i.e. it can be found at the addresses
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    0x86 and 0x06 on the i2c-bus.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    For detailed informations download the specifications directly
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    from SGS Thomson at http://www.st.com
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This program is free software; you can redistribute it and/or modify
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    it under the terms of the GNU General Public License vs published by
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    the Free Software Foundation; either version 2 of the License, or
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    (at your option) any later version.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This program is distributed in the hope that it will be useful,
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    but WITHOUT ANY WARRANTY; without even the implied warranty of
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    GNU General Public License for more details.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    You should have received a copy of the GNU General Public License
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    along with this program; if not, write to the Free Software
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30a8733ca5141c256322ab5ea9fd3074942a209bbaMauro Carvalho Chehab
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioctl.h>
335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/i2c.h>
3585d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil#include <media/v4l2-device.h>
361b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil#include <media/v4l2-chip-ident.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "tea6415c.h"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39a832781cd383e70929c0ceece23f8a5b62e2152bHans VerkuilMODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
40a832781cd383e70929c0ceece23f8a5b62e2152bHans VerkuilMODULE_DESCRIPTION("tea6415c driver");
41a832781cd383e70929c0ceece23f8a5b62e2152bHans VerkuilMODULE_LICENSE("GPL");
42f87086e302300fdff1bd32049deb7a7f3e3de7daHans Verkuil
43a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuilstatic int debug;
44a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuilmodule_param(debug, int, 0644);
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46a832781cd383e70929c0ceece23f8a5b62e2152bHans VerkuilMODULE_PARM_DESC(debug, "Debug level (0-1)");
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil/* makes a connection between the input-pin 'i' and the output-pin 'o' */
505325b4272a53b43f55b82cc369c310c2fcacdca1Hans Verkuilstatic int tea6415c_s_routing(struct v4l2_subdev *sd,
515325b4272a53b43f55b82cc369c310c2fcacdca1Hans Verkuil			      u32 i, u32 o, u32 config)
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
531b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	struct i2c_client *client = v4l2_get_subdevdata(sd);
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 byte = 0;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
56a8733ca5141c256322ab5ea9fd3074942a209bbaMauro Carvalho Chehab
571b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if the pins are valid */
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (0 == ((1 == i ||  3 == i ||  5 == i ||  6 == i ||  8 == i || 10 == i || 20 == i || 11 == i)
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
621b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil		return -EINVAL;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* to understand this, have a look at the tea6415c-specs (p.5) */
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (o) {
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 18:
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x00;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 14:
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x20;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 16:
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x10;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 17:
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x08;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 15:
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x18;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 13:
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte = 0x28;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (i) {
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 5:
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x00;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 8:
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x04;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 3:
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x02;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 20:
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x06;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 6:
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x01;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 10:
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x05;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x03;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 11:
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		byte |= 0x07;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = i2c_smbus_write_byte(client, byte);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
1151b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil		v4l2_dbg(1, debug, sd,
116a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil			"i2c_smbus_write_byte() failed, ret:%d\n", ret);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuilstatic int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1241b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	struct i2c_client *client = v4l2_get_subdevdata(sd);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0);
12785d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil}
12885d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
12985d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil/* ----------------------------------------------------------------------- */
13085d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
13185d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuilstatic const struct v4l2_subdev_core_ops tea6415c_core_ops = {
1321b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	.g_chip_ident = tea6415c_g_chip_ident,
1331b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil};
1341b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil
1351b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuilstatic const struct v4l2_subdev_video_ops tea6415c_video_ops = {
1361b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	.s_routing = tea6415c_s_routing,
13785d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil};
13885d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
13985d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuilstatic const struct v4l2_subdev_ops tea6415c_ops = {
14085d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	.core = &tea6415c_core_ops,
1411b8dac150a01e2312d8e3fedd6462a0ec34c96d0Hans Verkuil	.video = &tea6415c_video_ops,
14285d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil};
14385d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
144a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuilstatic int tea6415c_probe(struct i2c_client *client,
145a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil			  const struct i2c_device_id *id)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14785d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	struct v4l2_subdev *sd;
14885d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
149a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	/* let's see whether this adapter can support what we need */
150a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
15176e4a9a7164263d8ffe816920f84a91e7dfee444Axel Lin		return -EIO;
152a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil
153a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	v4l_info(client, "chip found @ 0x%x (%s)\n",
154a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil			client->addr << 1, client->adapter->name);
15580845a33165278f3236812009e9c568ba8c29938Herton Ronaldo Krzesinski	sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
15685d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	if (sd == NULL)
15785d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil		return -ENOMEM;
15885d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
15985d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	return 0;
16085d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil}
16185d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
16285d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuilstatic int tea6415c_remove(struct i2c_client *client)
16385d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil{
16485d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	struct v4l2_subdev *sd = i2c_get_clientdata(client);
16585d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil
16685d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	v4l2_device_unregister_subdev(sd);
16785d826b01751c17bc70a97bcacc1886f0c79917dHans Verkuil	kfree(sd);
168a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	return 0;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuilstatic const struct i2c_device_id tea6415c_id[] = {
172a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	{ "tea6415c", 0 },
173a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil	{ }
174a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil};
175a832781cd383e70929c0ceece23f8a5b62e2152bHans VerkuilMODULE_DEVICE_TABLE(i2c, tea6415c_id);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1770c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuilstatic struct i2c_driver tea6415c_driver = {
1780c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil	.driver = {
1790c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil		.owner	= THIS_MODULE,
1800c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil		.name	= "tea6415c",
1810c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil	},
1820c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil	.probe		= tea6415c_probe,
1830c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil	.remove		= tea6415c_remove,
1840c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil	.id_table	= tea6415c_id,
185a832781cd383e70929c0ceece23f8a5b62e2152bHans Verkuil};
1860c748826fe55c15b7f9d7566865ca0c85af1f72fHans Verkuil
187c6e8d86fffd8edf1bfccbd441b1812ee919fe3d5Axel Linmodule_i2c_driver(tea6415c_driver);
188