Blame view

sound/i2c/i2c.c 8.29 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   *   Generic i2c interface for ALSA
   *
   *   (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
5
   *   Modified for the ALSA driver by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/errno.h>
  #include <sound/core.h>
  #include <sound/i2c.h>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
28
  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
  MODULE_DESCRIPTION("Generic i2c interface for ALSA");
  MODULE_LICENSE("GPL");
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
31
32
33
34
35
36
37
38
  static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,
  				 unsigned char *bytes, int count);
  static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
  				 unsigned char *bytes, int count);
  static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus,
  				 unsigned short addr);
  
  static struct snd_i2c_ops snd_i2c_bit_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
  	.sendbytes = snd_i2c_bit_sendbytes,
  	.readbytes = snd_i2c_bit_readbytes,
  	.probeaddr = snd_i2c_bit_probeaddr,
  };
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
43
  static int snd_i2c_bus_free(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
45
46
  	struct snd_i2c_bus *slave;
  	struct snd_i2c_device *device;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47

5e246b850   Takashi Iwai   ALSA: Kill snd_as...
48
49
  	if (snd_BUG_ON(!bus))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  	while (!list_empty(&bus->devices)) {
  		device = snd_i2c_device(bus->devices.next);
  		snd_i2c_device_free(device);
  	}
  	if (bus->master)
  		list_del(&bus->buses);
  	else {
  		while (!list_empty(&bus->buses)) {
  			slave = snd_i2c_slave_bus(bus->buses.next);
  			snd_device_free(bus->card, slave);
  		}
  	}
  	if (bus->private_free)
  		bus->private_free(bus);
  	kfree(bus);
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
67
  static int snd_i2c_bus_dev_free(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
69
  	struct snd_i2c_bus *bus = device->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  	return snd_i2c_bus_free(bus);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
72
73
  int snd_i2c_bus_create(struct snd_card *card, const char *name,
  		       struct snd_i2c_bus *master, struct snd_i2c_bus **ri2c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
75
  	struct snd_i2c_bus *bus;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  	int err;
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
77
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
  		.dev_free =	snd_i2c_bus_dev_free,
  	};
  
  	*ri2c = NULL;
561b220a4   Takashi Iwai   [ALSA] Replace wi...
82
  	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
  	if (bus == NULL)
  		return -ENOMEM;
ef9f0a42d   Ingo Molnar   [ALSA] semaphore ...
85
  	mutex_init(&bus->lock_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
91
92
93
94
  	INIT_LIST_HEAD(&bus->devices);
  	INIT_LIST_HEAD(&bus->buses);
  	bus->card = card;
  	bus->ops = &snd_i2c_bit_ops;
  	if (master) {
  		list_add_tail(&bus->buses, &master->buses);
  		bus->master = master;
  	}
  	strlcpy(bus->name, name, sizeof(bus->name));
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
95
96
  	err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);
  	if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
  		snd_i2c_bus_free(bus);
  		return err;
  	}
  	*ri2c = bus;
  	return 0;
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
103
  EXPORT_SYMBOL(snd_i2c_bus_create);
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
104
105
  int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,
  			  unsigned char addr, struct snd_i2c_device **rdevice)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
107
  	struct snd_i2c_device *device;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  
  	*rdevice = NULL;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
110
111
  	if (snd_BUG_ON(!bus))
  		return -EINVAL;
561b220a4   Takashi Iwai   [ALSA] Replace wi...
112
  	device = kzalloc(sizeof(*device), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
  	if (device == NULL)
  		return -ENOMEM;
  	device->addr = addr;
  	strlcpy(device->name, name, sizeof(device->name));
  	list_add_tail(&device->list, &bus->devices);
  	device->bus = bus;
  	*rdevice = device;
  	return 0;
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
122
  EXPORT_SYMBOL(snd_i2c_device_create);
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
123
  int snd_i2c_device_free(struct snd_i2c_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
  {
  	if (device->bus)
  		list_del(&device->list);
  	if (device->private_free)
  		device->private_free(device);
  	kfree(device);
  	return 0;
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
132
  EXPORT_SYMBOL(snd_i2c_device_free);
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
133
  int snd_i2c_sendbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
  {
  	return device->bus->ops->sendbytes(device, bytes, count);
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
137
  EXPORT_SYMBOL(snd_i2c_sendbytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138

97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
139
  int snd_i2c_readbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
  {
  	return device->bus->ops->readbytes(device, bytes, count);
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
143
  EXPORT_SYMBOL(snd_i2c_readbytes);
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
144
  int snd_i2c_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
  {
  	return bus->ops->probeaddr(bus, addr);
  }
57c65c116   Takashi Iwai   [ALSA] i2c - Move...
148
  EXPORT_SYMBOL(snd_i2c_probeaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  /*
   *  bit-operations
   */
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
152
  static inline void snd_i2c_bit_hw_start(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
  {
  	if (bus->hw_ops.bit->start)
  		bus->hw_ops.bit->start(bus);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
157
  static inline void snd_i2c_bit_hw_stop(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
  {
  	if (bus->hw_ops.bit->stop)
  		bus->hw_ops.bit->stop(bus);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
162
  static void snd_i2c_bit_direction(struct snd_i2c_bus *bus, int clock, int data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
  {
  	if (bus->hw_ops.bit->direction)
  		bus->hw_ops.bit->direction(bus, clock, data);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
167
  static void snd_i2c_bit_set(struct snd_i2c_bus *bus, int clock, int data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
  {
  	bus->hw_ops.bit->setlines(bus, clock, data);
  }
  
  #if 0
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
173
  static int snd_i2c_bit_clock(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
  {
  	if (bus->hw_ops.bit->getclock)
  		return bus->hw_ops.bit->getclock(bus);
  	return -ENXIO;
  }
  #endif
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
180
  static int snd_i2c_bit_data(struct snd_i2c_bus *bus, int ack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
  {
  	return bus->hw_ops.bit->getdata(bus, ack);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
184
  static void snd_i2c_bit_start(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
  {
  	snd_i2c_bit_hw_start(bus);
  	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
  	snd_i2c_bit_set(bus, 1, 1);
  	snd_i2c_bit_set(bus, 1, 0);
  	snd_i2c_bit_set(bus, 0, 0);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
192
  static void snd_i2c_bit_stop(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
  {
  	snd_i2c_bit_set(bus, 0, 0);
  	snd_i2c_bit_set(bus, 1, 0);
  	snd_i2c_bit_set(bus, 1, 1);
  	snd_i2c_bit_hw_stop(bus);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
199
  static void snd_i2c_bit_send(struct snd_i2c_bus *bus, int data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
  {
  	snd_i2c_bit_set(bus, 0, data);
  	snd_i2c_bit_set(bus, 1, data);
  	snd_i2c_bit_set(bus, 0, data);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
205
  static int snd_i2c_bit_ack(struct snd_i2c_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
215
216
  {
  	int ack;
  
  	snd_i2c_bit_set(bus, 0, 1);
  	snd_i2c_bit_set(bus, 1, 1);
  	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */
  	ack = snd_i2c_bit_data(bus, 1);
  	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
  	snd_i2c_bit_set(bus, 0, 1);
  	return ack ? -EIO : 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
217
  static int snd_i2c_bit_sendbyte(struct snd_i2c_bus *bus, unsigned char data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
  {
  	int i, err;
  
  	for (i = 7; i >= 0; i--)
  		snd_i2c_bit_send(bus, !!(data & (1 << i)));
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
223
224
  	err = snd_i2c_bit_ack(bus);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
  		return err;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
228
  static int snd_i2c_bit_readbyte(struct snd_i2c_bus *bus, int last)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  {
  	int i;
  	unsigned char data = 0;
  
  	snd_i2c_bit_set(bus, 0, 1);
  	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */
  	for (i = 7; i >= 0; i--) {
  		snd_i2c_bit_set(bus, 1, 1);
  		if (snd_i2c_bit_data(bus, 0))
  			data |= (1 << i);
  		snd_i2c_bit_set(bus, 0, 1);
  	}
  	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
  	snd_i2c_bit_send(bus, !!last);
  	return data;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
245
246
  static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,
  				 unsigned char *bytes, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
248
  	struct snd_i2c_bus *bus = device->bus;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
253
  	int err, res = 0;
  
  	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
  		return -EIO;		/* not yet implemented */
  	snd_i2c_bit_start(bus);
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
254
255
  	err = snd_i2c_bit_sendbyte(bus, device->addr << 1);
  	if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
  		snd_i2c_bit_hw_stop(bus);
  		return err;
  	}
  	while (count-- > 0) {
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
260
261
  		err = snd_i2c_bit_sendbyte(bus, *bytes++);
  		if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
269
  			snd_i2c_bit_hw_stop(bus);
  			return err;
  		}
  		res++;
  	}
  	snd_i2c_bit_stop(bus);
  	return res;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
270
271
  static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
  				 unsigned char *bytes, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
273
  	struct snd_i2c_bus *bus = device->bus;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
  	int err, res = 0;
  
  	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
  		return -EIO;		/* not yet implemented */
  	snd_i2c_bit_start(bus);
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
279
280
  	err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1);
  	if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
  		snd_i2c_bit_hw_stop(bus);
  		return err;
  	}
  	while (count-- > 0) {
1cff399ec   Brian Waters   ALSA: i2c: Fixed ...
285
286
  		err = snd_i2c_bit_readbyte(bus, count == 0);
  		if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
  			snd_i2c_bit_hw_stop(bus);
  			return err;
  		}
  		*bytes++ = (unsigned char)err;
  		res++;
  	}
  	snd_i2c_bit_stop(bus);
  	return res;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
296
  static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
303
304
305
306
307
308
  {
  	int err;
  
  	if (addr & 0x8000)	/* 10-bit address */
  		return -EIO;	/* not yet implemented */
  	if (addr & 0x7f80)	/* invalid address */
  		return -EINVAL;
  	snd_i2c_bit_start(bus);
  	err = snd_i2c_bit_sendbyte(bus, addr << 1);
  	snd_i2c_bit_stop(bus);
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
315
316
317
318
319
320
  
  static int __init alsa_i2c_init(void)
  {
  	return 0;
  }
  
  static void __exit alsa_i2c_exit(void)
  {
  }
  
  module_init(alsa_i2c_init)
  module_exit(alsa_i2c_exit)