diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-13 17:03:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-13 17:03:59 -0400 |
commit | cf2fa66055d718ae13e62451bb546505f63906a2 (patch) | |
tree | e206d3f04e74a34e9aa88d21af6c26eea21d4121 /drivers/media/video/tda9840.c | |
parent | 4501a466f28788485604ee42641d7a5fe7258d16 (diff) | |
parent | 57f51dbc45f65f7ee1e8c8f77200bb8000e3e271 (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (313 commits)
V4L/DVB (9186): Added support for Prof 7300 DVB-S/S2 cards
V4L/DVB (9185): S2API: Ensure we have a reasonable ROLLOFF default
V4L/DVB (9184): cx24116: Change the default SNR units back to percentage by default.
V4L/DVB (9183): S2API: Return error of the caller provides 0 commands.
V4L/DVB (9182): S2API: Added support for DTV_HIERARCHY
V4L/DVB (9181): S2API: Add support fot DTV_GUARD_INTERVAL and DTV_TRANSMISSION_MODE
V4L/DVB (9180): S2API: Added support for DTV_CODE_RATE_HP/LP
V4L/DVB (9179): S2API: frontend.h cleanup
V4L/DVB (9178): cx24116: Add module parameter to return SNR as ESNO.
V4L/DVB (9177): S2API: Change _8PSK / _16APSK to PSK_8 and APSK_16
V4L/DVB (9176): Add support for DvbWorld USB cards with STV0288 demodulator.
V4L/DVB (9175): Remove NULL pointer in stb6000 driver.
V4L/DVB (9174): Allow custom inittab for ST STV0288 demodulator.
V4L/DVB (9173): S2API: Remove the hardcoded command limit during validation
V4L/DVB (9172): S2API: Bugfix related to DVB-S / DVB-S2 tuning for the legacy API.
V4L/DVB (9171): S2API: Stop an OOPS if illegal commands are dumped in S2API.
V4L/DVB (9170): cx24116: Sanity checking to data input via S2API to the cx24116 demod.
V4L/DVB (9169): uvcvideo: Support two new Bison Electronics webcams.
V4L/DVB (9168): Add support for MSI TV@nywhere Plus remote
V4L/DVB: v4l2-dev: remove duplicated #include
...
Diffstat (limited to 'drivers/media/video/tda9840.c')
-rw-r--r-- | drivers/media/video/tda9840.c | 260 |
1 files changed, 116 insertions, 144 deletions
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 2437c1a269c5..1c391f0328fd 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c | |||
@@ -2,6 +2,7 @@ | |||
2 | tda9840 - i2c-driver for the tda9840 by SGS Thomson | 2 | tda9840 - i2c-driver for the tda9840 by SGS Thomson |
3 | 3 | ||
4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> | 4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> |
5 | Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | 6 | ||
6 | The tda9840 is a stereo/dual sound processor with digital | 7 | The tda9840 is a stereo/dual sound processor with digital |
7 | identification. It can be found at address 0x84 on the i2c-bus. | 8 | identification. It can be found at address 0x84 on the i2c-bus. |
@@ -28,59 +29,118 @@ | |||
28 | #include <linux/module.h> | 29 | #include <linux/module.h> |
29 | #include <linux/ioctl.h> | 30 | #include <linux/ioctl.h> |
30 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
31 | 32 | #include <media/v4l2-common.h> | |
33 | #include <media/v4l2-i2c-drv-legacy.h> | ||
32 | #include "tda9840.h" | 34 | #include "tda9840.h" |
33 | 35 | ||
34 | static int debug; /* insmod parameter */ | 36 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); |
37 | MODULE_DESCRIPTION("tda9840 driver"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | static int debug; | ||
35 | module_param(debug, int, 0644); | 41 | module_param(debug, int, 0644); |
36 | MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); | ||
37 | 42 | ||
38 | #define dprintk(args...) \ | 43 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); |
39 | do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) | ||
40 | 44 | ||
41 | #define SWITCH 0x00 | 45 | #define SWITCH 0x00 |
42 | #define LEVEL_ADJUST 0x02 | 46 | #define LEVEL_ADJUST 0x02 |
43 | #define STEREO_ADJUST 0x03 | 47 | #define STEREO_ADJUST 0x03 |
44 | #define TEST 0x04 | 48 | #define TEST 0x04 |
45 | 49 | ||
50 | #define TDA9840_SET_MUTE 0x00 | ||
51 | #define TDA9840_SET_MONO 0x10 | ||
52 | #define TDA9840_SET_STEREO 0x2a | ||
53 | #define TDA9840_SET_LANG1 0x12 | ||
54 | #define TDA9840_SET_LANG2 0x1e | ||
55 | #define TDA9840_SET_BOTH 0x1a | ||
56 | #define TDA9840_SET_BOTH_R 0x16 | ||
57 | #define TDA9840_SET_EXTERNAL 0x7a | ||
58 | |||
46 | /* addresses to scan, found only at 0x42 (7-Bit) */ | 59 | /* addresses to scan, found only at 0x42 (7-Bit) */ |
47 | static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; | 60 | static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; |
48 | 61 | ||
49 | /* magic definition of all other variables and things */ | 62 | /* magic definition of all other variables and things */ |
50 | I2C_CLIENT_INSMOD; | 63 | I2C_CLIENT_INSMOD; |
51 | 64 | ||
52 | static struct i2c_driver driver; | 65 | static void tda9840_write(struct i2c_client *client, u8 reg, u8 val) |
53 | static struct i2c_client client_template; | 66 | { |
67 | if (i2c_smbus_write_byte_data(client, reg, val)) | ||
68 | v4l_dbg(1, debug, client, "error writing %02x to %02x\n", | ||
69 | val, reg); | ||
70 | } | ||
54 | 71 | ||
55 | static int command(struct i2c_client *client, unsigned int cmd, void *arg) | 72 | static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) |
56 | { | 73 | { |
57 | int result; | ||
58 | int byte = *(int *)arg; | 74 | int byte = *(int *)arg; |
59 | 75 | ||
60 | switch (cmd) { | 76 | switch (cmd) { |
61 | case TDA9840_SWITCH: | 77 | case VIDIOC_S_TUNER: { |
62 | 78 | struct v4l2_tuner *t = arg; | |
63 | dprintk("TDA9840_SWITCH: 0x%02x\n", byte); | 79 | int byte; |
64 | 80 | ||
65 | if (byte != TDA9840_SET_MONO | 81 | if (t->index) |
66 | && byte != TDA9840_SET_MUTE | ||
67 | && byte != TDA9840_SET_STEREO | ||
68 | && byte != TDA9840_SET_LANG1 | ||
69 | && byte != TDA9840_SET_LANG2 | ||
70 | && byte != TDA9840_SET_BOTH | ||
71 | && byte != TDA9840_SET_BOTH_R | ||
72 | && byte != TDA9840_SET_EXTERNAL) { | ||
73 | return -EINVAL; | 82 | return -EINVAL; |
83 | |||
84 | switch (t->audmode) { | ||
85 | case V4L2_TUNER_MODE_STEREO: | ||
86 | byte = TDA9840_SET_STEREO; | ||
87 | break; | ||
88 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
89 | byte = TDA9840_SET_BOTH; | ||
90 | break; | ||
91 | case V4L2_TUNER_MODE_LANG1: | ||
92 | byte = TDA9840_SET_LANG1; | ||
93 | break; | ||
94 | case V4L2_TUNER_MODE_LANG2: | ||
95 | byte = TDA9840_SET_LANG2; | ||
96 | break; | ||
97 | default: | ||
98 | byte = TDA9840_SET_MONO; | ||
99 | break; | ||
100 | } | ||
101 | v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte); | ||
102 | tda9840_write(client, SWITCH, byte); | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | case VIDIOC_G_TUNER: { | ||
107 | struct v4l2_tuner *t = arg; | ||
108 | u8 byte; | ||
109 | |||
110 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
111 | if (1 != i2c_master_recv(client, &byte, 1)) { | ||
112 | v4l_dbg(1, debug, client, | ||
113 | "i2c_master_recv() failed\n"); | ||
114 | return -EIO; | ||
74 | } | 115 | } |
75 | 116 | ||
76 | result = i2c_smbus_write_byte_data(client, SWITCH, byte); | 117 | if (byte & 0x80) { |
77 | if (result) | 118 | v4l_dbg(1, debug, client, |
78 | dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); | 119 | "TDA9840_DETECT: register contents invalid\n"); |
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte); | ||
124 | |||
125 | switch (byte & 0x60) { | ||
126 | case 0x00: | ||
127 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
128 | break; | ||
129 | case 0x20: | ||
130 | t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
131 | break; | ||
132 | case 0x40: | ||
133 | t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; | ||
134 | break; | ||
135 | default: /* Incorrect detect */ | ||
136 | t->rxsubchans = V4L2_TUNER_MODE_MONO; | ||
137 | break; | ||
138 | } | ||
79 | break; | 139 | break; |
140 | } | ||
80 | 141 | ||
81 | case TDA9840_LEVEL_ADJUST: | 142 | case TDA9840_LEVEL_ADJUST: |
82 | 143 | v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte); | |
83 | dprintk("TDA9840_LEVEL_ADJUST: %d\n", byte); | ||
84 | 144 | ||
85 | /* check for correct range */ | 145 | /* check for correct range */ |
86 | if (byte > 25 || byte < -20) | 146 | if (byte > 25 || byte < -20) |
@@ -92,15 +152,11 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
92 | byte += 0x8; | 152 | byte += 0x8; |
93 | else | 153 | else |
94 | byte = -byte; | 154 | byte = -byte; |
95 | 155 | tda9840_write(client, LEVEL_ADJUST, byte); | |
96 | result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte); | ||
97 | if (result) | ||
98 | dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); | ||
99 | break; | 156 | break; |
100 | 157 | ||
101 | case TDA9840_STEREO_ADJUST: | 158 | case TDA9840_STEREO_ADJUST: |
102 | 159 | v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte); | |
103 | dprintk("TDA9840_STEREO_ADJUST: %d\n", byte); | ||
104 | 160 | ||
105 | /* check for correct range */ | 161 | /* check for correct range */ |
106 | if (byte > 25 || byte < -24) | 162 | if (byte > 25 || byte < -24) |
@@ -113,143 +169,59 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) | |||
113 | else | 169 | else |
114 | byte = -byte; | 170 | byte = -byte; |
115 | 171 | ||
116 | result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte); | 172 | tda9840_write(client, STEREO_ADJUST, byte); |
117 | if (result) | ||
118 | dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); | ||
119 | break; | ||
120 | |||
121 | case TDA9840_DETECT: { | ||
122 | int *ret = (int *)arg; | ||
123 | |||
124 | byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST); | ||
125 | if (byte == -1) { | ||
126 | dprintk("i2c_smbus_read_byte_data() failed\n"); | ||
127 | return -EIO; | ||
128 | } | ||
129 | |||
130 | if (0 != (byte & 0x80)) { | ||
131 | dprintk("TDA9840_DETECT: register contents invalid\n"); | ||
132 | return -EINVAL; | ||
133 | } | ||
134 | |||
135 | dprintk("TDA9840_DETECT: byte: 0x%02x\n", byte); | ||
136 | *ret = ((byte & 0x60) >> 5); | ||
137 | result = 0; | ||
138 | break; | ||
139 | } | ||
140 | case TDA9840_TEST: | ||
141 | dprintk("TDA9840_TEST: 0x%02x\n", byte); | ||
142 | |||
143 | /* mask out irrelevant bits */ | ||
144 | byte &= 0x3; | ||
145 | |||
146 | result = i2c_smbus_write_byte_data(client, TEST, byte); | ||
147 | if (result) | ||
148 | dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); | ||
149 | break; | 173 | break; |
150 | default: | 174 | default: |
151 | return -ENOIOCTLCMD; | 175 | return -ENOIOCTLCMD; |
152 | } | 176 | } |
153 | 177 | ||
154 | if (result) | ||
155 | return -EIO; | ||
156 | |||
157 | return 0; | 178 | return 0; |
158 | } | 179 | } |
159 | 180 | ||
160 | static int detect(struct i2c_adapter *adapter, int address, int kind) | 181 | static int tda9840_probe(struct i2c_client *client, |
182 | const struct i2c_device_id *id) | ||
161 | { | 183 | { |
162 | struct i2c_client *client; | 184 | int result; |
163 | int result = 0; | 185 | int byte; |
164 | |||
165 | int byte = 0x0; | ||
166 | 186 | ||
167 | /* let's see whether this adapter can support what we need */ | 187 | /* let's see whether this adapter can support what we need */ |
168 | if (0 == i2c_check_functionality(adapter, | 188 | if (!i2c_check_functionality(client->adapter, |
169 | I2C_FUNC_SMBUS_READ_BYTE_DATA | | 189 | I2C_FUNC_SMBUS_READ_BYTE_DATA | |
170 | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { | 190 | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) |
171 | return 0; | 191 | return 0; |
172 | } | ||
173 | |||
174 | /* allocate memory for client structure */ | ||
175 | client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
176 | if (!client) { | ||
177 | printk("not enough kernel memory\n"); | ||
178 | return -ENOMEM; | ||
179 | } | ||
180 | |||
181 | /* fill client structure */ | ||
182 | memcpy(client, &client_template, sizeof(struct i2c_client)); | ||
183 | client->addr = address; | ||
184 | client->adapter = adapter; | ||
185 | 192 | ||
186 | /* tell the i2c layer a new client has arrived */ | 193 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
187 | if (0 != (result = i2c_attach_client(client))) { | 194 | client->addr << 1, client->adapter->name); |
188 | kfree(client); | ||
189 | return result; | ||
190 | } | ||
191 | 195 | ||
192 | /* set initial values for level & stereo - adjustment, mode */ | 196 | /* set initial values for level & stereo - adjustment, mode */ |
193 | byte = 0; | 197 | byte = 0; |
194 | result = command(client, TDA9840_LEVEL_ADJUST, &byte); | 198 | result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte); |
195 | result += command(client, TDA9840_STEREO_ADJUST, &byte); | 199 | result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte); |
196 | byte = TDA9840_SET_MONO; | 200 | tda9840_write(client, SWITCH, TDA9840_SET_STEREO); |
197 | result = command(client, TDA9840_SWITCH, &byte); | ||
198 | if (result) { | 201 | if (result) { |
199 | dprintk("could not initialize tda9840\n"); | 202 | v4l_dbg(1, debug, client, "could not initialize tda9840\n"); |
200 | return -ENODEV; | 203 | return -ENODEV; |
201 | } | 204 | } |
202 | |||
203 | printk("tda9840: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); | ||
204 | return 0; | 205 | return 0; |
205 | } | 206 | } |
206 | 207 | ||
207 | static int attach(struct i2c_adapter *adapter) | 208 | static int tda9840_legacy_probe(struct i2c_adapter *adapter) |
208 | { | ||
209 | /* let's see whether this is a know adapter we can attach to */ | ||
210 | if (adapter->id != I2C_HW_SAA7146) { | ||
211 | dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); | ||
212 | return -ENODEV; | ||
213 | } | ||
214 | |||
215 | return i2c_probe(adapter, &addr_data, &detect); | ||
216 | } | ||
217 | |||
218 | static int detach(struct i2c_client *client) | ||
219 | { | 209 | { |
220 | int ret = i2c_detach_client(client); | 210 | /* Let's see whether this is a known adapter we can attach to. |
221 | kfree(client); | 211 | Prevents conflicts with tvaudio.c. */ |
222 | return ret; | 212 | return adapter->id == I2C_HW_SAA7146; |
223 | } | 213 | } |
224 | 214 | static const struct i2c_device_id tda9840_id[] = { | |
225 | static struct i2c_driver driver = { | 215 | { "tda9840", 0 }, |
226 | .driver = { | 216 | { } |
227 | .name = "tda9840", | ||
228 | }, | ||
229 | .id = I2C_DRIVERID_TDA9840, | ||
230 | .attach_adapter = attach, | ||
231 | .detach_client = detach, | ||
232 | .command = command, | ||
233 | }; | 217 | }; |
218 | MODULE_DEVICE_TABLE(i2c, tda9840_id); | ||
234 | 219 | ||
235 | static struct i2c_client client_template = { | 220 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { |
236 | .name = "tda9840", | 221 | .name = "tda9840", |
237 | .driver = &driver, | 222 | .driverid = I2C_DRIVERID_TDA9840, |
223 | .command = tda9840_command, | ||
224 | .probe = tda9840_probe, | ||
225 | .legacy_probe = tda9840_legacy_probe, | ||
226 | .id_table = tda9840_id, | ||
238 | }; | 227 | }; |
239 | |||
240 | static int __init this_module_init(void) | ||
241 | { | ||
242 | return i2c_add_driver(&driver); | ||
243 | } | ||
244 | |||
245 | static void __exit this_module_exit(void) | ||
246 | { | ||
247 | i2c_del_driver(&driver); | ||
248 | } | ||
249 | |||
250 | module_init(this_module_init); | ||
251 | module_exit(this_module_exit); | ||
252 | |||
253 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); | ||
254 | MODULE_DESCRIPTION("tda9840 driver"); | ||
255 | MODULE_LICENSE("GPL"); | ||