Rev 425 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
425 | giacomo | 1 | /* |
2 | tda9840.h - i2c-driver for the tda9840 by SGS Thomson |
||
3 | |||
4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> |
||
5 | |||
6 | The tda9840 is a stereo/dual sound processor with digital |
||
7 | identification. It can be found at address 0x42 on the i2c-bus. |
||
8 | |||
9 | For detailed informations download the specifications directly |
||
10 | from SGS Thomson at http://www.st.com |
||
11 | |||
12 | This program is free software; you can redistribute it and/or modify |
||
13 | it under the terms of the GNU General Public License as published by |
||
14 | the Free Software Foundation; either version 2 of the License, or |
||
15 | (at your option) any later version. |
||
16 | |||
17 | This program is distributed in the hope that it will be useful, |
||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
20 | GNU General Public License for more details. |
||
21 | |||
22 | You should have received a copy of the GNU General Public License |
||
23 | along with this program; if not, write to the Free Software |
||
24 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
25 | */ |
||
26 | |||
27 | #include <linux/version.h> |
||
28 | #include <linux/module.h> |
||
29 | #include <linux/kernel.h> |
||
30 | #include <linux/poll.h> |
||
31 | #include <linux/slab.h> |
||
32 | #include <linux/i2c.h> |
||
33 | #include <linux/init.h> |
||
34 | |||
35 | #include "tda9840.h" |
||
36 | |||
37 | static int debug = 0; /* insmod parameter */ |
||
38 | MODULE_PARM(debug,"i"); |
||
39 | #define dprintk if (debug) printk |
||
40 | |||
41 | #define SWITCH 0x00 |
||
42 | #define LEVEL_ADJUST 0x02 |
||
43 | #define STEREO_ADJUST 0x03 |
||
44 | #define TEST 0x04 |
||
45 | |||
46 | /* addresses to scan, found only at 0x42 (7-Bit) */ |
||
47 | static unsigned short normal_i2c[] = {I2C_TDA9840, I2C_CLIENT_END}; |
||
48 | static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; |
||
49 | |||
50 | /* magic definition of all other variables and things */ |
||
51 | I2C_CLIENT_INSMOD; |
||
52 | |||
53 | /* unique ID allocation */ |
||
54 | static int tda9840_id = 0; |
||
55 | |||
56 | static struct i2c_driver driver; |
||
57 | |||
58 | static int tda9840_command(struct i2c_client *client, unsigned int cmd, void* arg) |
||
59 | { |
||
60 | int result = 0; |
||
61 | |||
62 | switch (cmd) { |
||
63 | case TDA9840_SWITCH: |
||
64 | { |
||
65 | int byte = *(int*)arg; |
||
66 | |||
67 | dprintk("tda9840.o: TDA9840_SWITCH: 0x%02x\n",byte); |
||
68 | |||
69 | if ( byte != TDA9840_SET_MONO |
||
70 | && byte != TDA9840_SET_MUTE |
||
71 | && byte != TDA9840_SET_STEREO |
||
72 | && byte != TDA9840_SET_LANG1 |
||
73 | && byte != TDA9840_SET_LANG2 |
||
74 | && byte != TDA9840_SET_BOTH |
||
75 | && byte != TDA9840_SET_BOTH_R |
||
76 | && byte != TDA9840_SET_EXTERNAL ) { |
||
77 | return -EINVAL; |
||
78 | } |
||
79 | |||
80 | if ( 0 != (result = i2c_smbus_write_byte_data(client, SWITCH, byte))) { |
||
81 | printk("tda9840.o: TDA9840_SWITCH error.\n"); |
||
82 | return -EFAULT; |
||
83 | } |
||
84 | |||
85 | return 0; |
||
86 | } |
||
87 | |||
88 | case TDA9840_LEVEL_ADJUST: |
||
89 | { |
||
90 | int byte = *(int*)arg; |
||
91 | |||
92 | dprintk("tda9840.o: TDA9840_LEVEL_ADJUST: %d\n",byte); |
||
93 | |||
94 | /* check for correct range */ |
||
95 | if ( byte > 25 || byte < -20 ) |
||
96 | return -EINVAL; |
||
97 | |||
98 | /* calculate actual value to set, see specs, page 18 */ |
||
99 | byte /= 5; |
||
100 | if ( 0 < byte ) |
||
101 | byte += 0x8; |
||
102 | else |
||
103 | byte = -byte; |
||
104 | |||
105 | if ( 0 != (result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte))) { |
||
106 | printk("tda9840.o: TDA9840_LEVEL_ADJUST error.\n"); |
||
107 | return -EFAULT; |
||
108 | } |
||
109 | |||
110 | return 0; |
||
111 | } |
||
112 | |||
113 | case TDA9840_STEREO_ADJUST: |
||
114 | { |
||
115 | int byte = *(int*)arg; |
||
116 | |||
117 | dprintk("tda9840.o: TDA9840_STEREO_ADJUST: %d\n",byte); |
||
118 | |||
119 | /* check for correct range */ |
||
120 | if ( byte > 25 || byte < -24 ) |
||
121 | return -EINVAL; |
||
122 | |||
123 | /* calculate actual value to set */ |
||
124 | byte /= 5; |
||
125 | if ( 0 < byte ) |
||
126 | byte += 0x20; |
||
127 | else |
||
128 | byte = -byte; |
||
129 | |||
130 | if ( 0 != (result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte))) { |
||
131 | printk("tda9840.o: TDA9840_STEREO_ADJUST error.\n"); |
||
132 | return -EFAULT; |
||
133 | } |
||
134 | |||
135 | return 0; |
||
136 | } |
||
137 | |||
138 | case TDA9840_DETECT: |
||
139 | { |
||
140 | int byte = 0x0; |
||
141 | |||
142 | if ( -1 == (byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST))) { |
||
143 | printk("tda9840.o: TDA9840_DETECT error while reading.\n"); |
||
144 | return -EFAULT; |
||
145 | } |
||
146 | |||
147 | if( 0 != (byte & 0x80)) { |
||
148 | dprintk("tda9840.o: TDA9840_DETECT, register contents invalid.\n"); |
||
149 | return -EFAULT; |
||
150 | } |
||
151 | |||
152 | dprintk("tda9840.o: TDA9840_DETECT, result: 0x%02x (original byte)\n",byte); |
||
153 | |||
154 | return ((byte & 0x60) >> 5); |
||
155 | } |
||
156 | |||
157 | case TDA9840_TEST: |
||
158 | { |
||
159 | int byte = *(int*)arg; |
||
160 | |||
161 | dprintk("tda9840.o: TDA9840_TEST: 0x%02x\n",byte); |
||
162 | |||
163 | /* mask out irrelevant bits */ |
||
164 | byte &= 0x3; |
||
165 | |||
166 | if ( 0 != (result = i2c_smbus_write_byte_data(client, TEST, byte))) { |
||
167 | printk("tda9840.o: TDA9840_TEST error.\n"); |
||
168 | return -EFAULT; |
||
169 | } |
||
170 | |||
171 | return 0; |
||
172 | } |
||
173 | |||
174 | default: |
||
175 | return -ENOIOCTLCMD; |
||
176 | } |
||
177 | |||
178 | return 0; |
||
179 | } |
||
180 | |||
181 | static int tda9840_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind) |
||
182 | { |
||
183 | struct i2c_client *client; |
||
184 | int result = 0; |
||
185 | |||
186 | int byte = 0x0; |
||
187 | |||
188 | /* let's see whether this adapter can support what we need */ |
||
189 | if ( 0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA|I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { |
||
190 | return 0; |
||
191 | } |
||
192 | |||
193 | /* allocate memory for client structure */ |
||
194 | client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); |
||
195 | if (0 == client) { |
||
196 | printk("tda9840.o: not enough kernel memory.\n"); |
||
197 | return -ENOMEM; |
||
198 | } |
||
199 | memset(client, 0, sizeof(struct i2c_client)); |
||
200 | |||
201 | /* fill client structure */ |
||
202 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
203 | sprintf(client->name,"tda9840 (0x%02x)", address); |
||
204 | #else |
||
205 | sprintf(client->dev.name,"tda9840 (0x%02x)", address); |
||
206 | #endif |
||
207 | client->id = tda9840_id++; |
||
208 | client->flags = 0; |
||
209 | client->addr = address; |
||
210 | client->adapter = adapter; |
||
211 | client->driver = &driver; |
||
212 | |||
213 | /* tell the i2c layer a new client has arrived */ |
||
214 | if (0 != (result = i2c_attach_client(client))) { |
||
215 | kfree(client); |
||
216 | return result; |
||
217 | } |
||
218 | |||
219 | /* set initial values for level & stereo - adjustment, mode */ |
||
220 | byte = 0; |
||
221 | if ( 0 != (result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte))) { |
||
222 | printk("tda9840.o: could not initialize ic #1. continuing anyway. (result:%d)\n",result); |
||
223 | } |
||
224 | |||
225 | if ( 0 != (result = tda9840_command(client, TDA9840_STEREO_ADJUST, &byte))) { |
||
226 | printk("tda9840.o: could not initialize ic #2. continuing anyway. (result:%d)\n",result); |
||
227 | } |
||
228 | |||
229 | byte = TDA9840_SET_MONO; |
||
230 | if ( 0 != (result = tda9840_command(client, TDA9840_SWITCH, &byte))) { |
||
231 | printk("tda9840.o: could not initialize ic #3. continuing anyway. (result:%d)\n",result); |
||
232 | } |
||
233 | |||
234 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
235 | printk("tda9840.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->name[0]); |
||
236 | #else |
||
237 | printk("tda9840.o: detected @ 0x%02x on adapter %s\n",address,&client->adapter->dev.name[0]); |
||
238 | #endif |
||
239 | return 0; |
||
240 | } |
||
241 | |||
242 | static int tda9840_attach(struct i2c_adapter *adapter) |
||
243 | { |
||
244 | /* let's see whether this is a know adapter we can attach to */ |
||
245 | if( adapter->id != I2C_ALGO_SAA7146 ) { |
||
246 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
247 | dprintk("tda9840.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->name,adapter->id); |
||
248 | #else |
||
249 | dprintk("tda9840.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id); |
||
250 | #endif |
||
251 | return -ENODEV; |
||
252 | } |
||
253 | |||
254 | return i2c_probe(adapter,&addr_data,&tda9840_detect); |
||
255 | } |
||
256 | |||
257 | static int tda9840_detach(struct i2c_client *client) |
||
258 | { |
||
259 | int err = 0; |
||
260 | |||
261 | if ( 0 != (err = i2c_detach_client(client))) { |
||
262 | printk("tda9840.o: Client deregistration failed, client not detached.\n"); |
||
263 | return err; |
||
264 | } |
||
265 | |||
266 | kfree(client); |
||
267 | |||
268 | return 0; |
||
269 | } |
||
270 | |||
271 | static void tda9840_inc_use(struct i2c_client *client) |
||
272 | { |
||
273 | #ifdef MODULE |
||
274 | MOD_INC_USE_COUNT; |
||
275 | #endif |
||
276 | } |
||
277 | |||
278 | static void tda9840_dec_use(struct i2c_client *client) |
||
279 | { |
||
280 | #ifdef MODULE |
||
281 | MOD_DEC_USE_COUNT; |
||
282 | #endif |
||
283 | } |
||
284 | |||
285 | static struct i2c_driver driver = { |
||
286 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54) |
||
287 | .owner = THIS_MODULE, |
||
288 | #endif |
||
289 | .name = "tda9840 driver", |
||
290 | .id = I2C_DRIVERID_TDA9840, |
||
291 | .flags = I2C_DF_NOTIFY, |
||
292 | .attach_adapter = tda9840_attach, |
||
293 | .detach_client = tda9840_detach, |
||
294 | .command = tda9840_command, |
||
295 | .inc_use = tda9840_inc_use, |
||
296 | .dec_use = tda9840_dec_use, |
||
297 | }; |
||
298 | |||
299 | static int tda9840_init_module(void) |
||
300 | { |
||
301 | i2c_add_driver(&driver); |
||
302 | return 0; |
||
303 | } |
||
304 | |||
305 | static void tda9840_cleanup_module(void) |
||
306 | { |
||
307 | i2c_del_driver(&driver); |
||
308 | } |
||
309 | |||
310 | module_init(tda9840_init_module); |
||
311 | module_exit(tda9840_cleanup_module); |
||
312 | |||
313 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); |
||
314 | MODULE_DESCRIPTION("tda9840 driver"); |
||
315 | MODULE_LICENSE("GPL"); |
||
316 |